Completed
Pull Request — master (#274)
by Markus
07:36
created

customcolumn.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * COPS (Calibre OPDS PHP Server) class file
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Sébastien Lucas <[email protected]>
7
 */
8
9
require_once('base.php');
10
11
/**
12
 * A CustomColumn with an value
13
 */
14
class CustomColumn extends Base
15
{
16
    /* @var string|integer the ID of the value */
17
    public $valueID;
18
    /* @var string the (string) representation of the value */
19
    public $value;
20
    /* @var CustomColumnType the custom column that contains the value */
21
    public $customColumnType;
22
    /* @var string the value encoded for HTML displaying */
23
    public $htmlvalue;
24
25
    /**
26
     * CustomColumn constructor.
27
     *
28
     * @param integer $pid id of the chosen value
29
     * @param string $pvalue string representation of the value
30
     * @param CustomColumnType $pcustomColumnType the CustomColumn this value lives in
31
     */
32 7
    public function __construct($pid, $pvalue, $pcustomColumnType)
33
    {
34 7
        $this->valueID = $pid;
35 7
        $this->value = $pvalue;
36 7
        $this->customColumnType = $pcustomColumnType;
37 7
        $this->htmlvalue = $this->customColumnType->encodeHTMLValue($this->value);
38 7
    }
39
40
    /**
41
     * Get the URI to show all books with this value
42
     *
43
     * @return string
44
     */
45
    public function getUri()
46
    {
47
        return $this->customColumnType->getUri($this->valueID);
48
    }
49
50
    /**
51
     * Get the EntryID to show all books with this value
52
     *
53
     * @return string
54
     */
55 4
    public function getEntryId()
56
    {
57 4
        return $this->customColumnType->getEntryId($this->valueID);
58
    }
59
60
    /**
61
     * Get the query to find all books with this value
62
     * the returning array has two values:
63
     *  - first the query (string)
64
     *  - second an array of all PreparedStatement parameters
65
     *
66
     * @return array
67
     */
68 6
    public function getQuery()
69
    {
70 6
        return $this->customColumnType->getQuery($this->valueID);
71
    }
72
73
    /**
74
     * Return the value of this column as an HTML snippet
75
     *
76
     * @return string
77
     */
78
    public function getHTMLEncodedValue()
79
    {
80
        return $this->htmlvalue;
81
    }
82
83
    /**
84
     * Craete an CustomColumn by CustomColumnID and ValueID
85
     *
86
     * @param integer $customId the id of the customColumn
87
     * @param integer $id the id of the chosen value
88
     * @return CustomColumn|null
89
     */
90 4
    public static function createCustom($customId, $id)
91
    {
92 4
        $columnType = CustomColumnType::createByCustomID($customId);
93
94 4
        return $columnType->getCustom($id);
95
    }
96
97
    /**
98
     * Return this object as an array
99
     *
100
     * @return array
101
     */
102 1
    public function toArray()
103
    {
104
        return array(
105 1
            'valueID' => $this->valueID,
106 1
            'value' => $this->value,
107 1
            'customColumnType' => (array) $this->customColumnType,
108 1
            'htmlvalue' => $this->htmlvalue);
109
    }
110
}
111
112
/**
113
 * A single calibre custom column
114
 */
115
abstract class CustomColumnType extends Base
116
{
117
    const ALL_CUSTOMS_ID       = "cops:custom";
118
119
    const CUSTOM_TYPE_TEXT      = "text";        // type 1 + 2
120
    const CUSTOM_TYPE_COMMENT   = "comments";    // type 3
121
    const CUSTOM_TYPE_SERIES    = "series";      // type 4
122
    const CUSTOM_TYPE_ENUM      = "enumeration"; // type 5
123
    const CUSTOM_TYPE_DATE      = "datetime";    // type 6
124
    const CUSTOM_TYPE_FLOAT     = "float";       // type 7
125
    const CUSTOM_TYPE_INT       = "int";         // type 8
126
    const CUSTOM_TYPE_RATING    = "rating";      // type 9
127
    const CUSTOM_TYPE_BOOL      = "bool";        // type 10
128
    const CUSTOM_TYPE_COMPOSITE = "composite";   // type 11 + 12
129
130
    /** @var array[integer]CustomColumnType  */
131
    private static $customColumnCacheID = array();
132
133
    /** @var array[string]CustomColumnType  */
134
    private static $customColumnCacheLookup = array();
135
136
    /** @var integer the id of this column */
137
    public $customId;
138
    /** @var string name/title of this column */
139
    public $columnTitle;
140
    /** @var string the datatype of this column (one of the CUSTOM_TYPE_* constant values) */
141
    public $datatype;
142
    /** @var null|Entry[] */
143
    private $customValues = NULL;
144
145 14
    protected function __construct($pcustomId, $pdatatype)
146
    {
147 14
        $this->columnTitle = self::getTitleByCustomID($pcustomId);
148 14
        $this->customId = $pcustomId;
149 14
        $this->datatype = $pdatatype;
150 14
        $this->customValues = NULL;
151 14
    }
152
153
    /**
154
     * The URI to show all book swith a specific value in this column
155
     *
156
     * @param string|integer $id the id of the value to show
157
     * @return string
158
     */
159 13
    public function getUri($id)
160
    {
161 13
        return "?page=" . parent::PAGE_CUSTOM_DETAIL . "&custom={$this->customId}&id={$id}";
162
    }
163
164
    /**
165
     * The URI to show all the values of this column
166
     *
167
     * @return string
168
     */
169 25
    public function getUriAllCustoms()
170
    {
171 25
        return "?page=" . parent::PAGE_ALL_CUSTOMS . "&custom={$this->customId}";
172
    }
173
174
    /**
175
     * The EntryID to show all book swith a specific value in this column
176
     *
177
     * @param string|integer $id the id of the value to show
178
     * @return string
179
     */
180 17
    public function getEntryId($id)
181
    {
182 17
        return self::ALL_CUSTOMS_ID . ":" . $this->customId . ":" . $id;
183
    }
184
185
    /**
186
     * The EntryID to show all the values of this column
187
     *
188
     * @return string
189
     */
190 37
    public function getAllCustomsId()
191
    {
192 37
        return self::ALL_CUSTOMS_ID . ":" . $this->customId;
193
    }
194
195
    /**
196
     * The title of this column
197
     *
198
     * @return string
199
     */
200 37
    public function getTitle()
201
    {
202 37
        return $this->columnTitle;
203
    }
204
205
    /**
206
     * The description of this column as it is definied in the database
207
     *
208
     * @return string|null
209
     */
210 20
    public function getDatabaseDescription()
211
    {
212 20
        $result = $this->getDb()->prepare('SELECT display FROM custom_columns WHERE id = ?');
213 20
        $result->execute(array($this->customId));
214 20
        if ($post = $result->fetchObject()) {
215 20
            $json = json_decode($post->display);
216 20
            return (isset($json->description) && !empty($json->description)) ? $json->description : NULL;
217
        }
218
        return NULL;
219
    }
220
221
    /**
222
     * Get the Entry for this column
223
     * This is used in the initializeContent method to display e.g. the index page
224
     *
225
     * @return Entry
226
     */
227 14
    public function getCount()
228
    {
229 14
        $ptitle = $this->getTitle();
230 14
        $pid = $this->getAllCustomsId();
231 14
        $pcontent = $this->getDescription();
232 14
        $pcontentType = $this->datatype;
233 14
        $plinkArray = array(new LinkNavigation($this->getUriAllCustoms()));
234 14
        $pclass = "";
235 14
        $pcount = $this->getDistinctValueCount();
236
237 14
        return new Entry($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $pclass, $pcount);
238
    }
239
240
    /**
241
     * Get the amount of distinct values for this column
242
     *
243
     * @return int
244
     */
245 16
    protected function getDistinctValueCount()
246
    {
247 16
        return count($this->getAllCustomValues());
248
    }
249
250
    /**
251
     * Encode a value of this column ready to be displayed in an HTML document
252
     *
253
     * @param integer|string $value
254
     * @return string
255
     */
256 7
    public function encodeHTMLValue($value)
257
    {
258 7
        return htmlspecialchars($value);
259
    }
260
261
    /**
262
     * Get the datatype of a CustomColumn by its customID
263
     *
264
     * @param integer $customId
265
     * @return string|null
266
     */
267 17
    private static function getDatatypeByCustomID($customId)
268
    {
269 17
        $result = parent::getDb()->prepare('SELECT datatype FROM custom_columns WHERE id = ?');
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getDb() instead of getDatatypeByCustomID()). Are you sure this is correct? If so, you might want to change this to $this->getDb().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
270 17
        $result->execute(array($customId));
271 17
        if ($post = $result->fetchObject()) {
272 16
            return $post->datatype;
273
        }
274 1
        return NULL;
275
    }
276
277
    /**
278
     * Create a CustomColumnType by CustomID
279
     *
280
     * @param integer $customId the id of the custom column
281
     * @return CustomColumnType|null
282
     * @throws Exception If the $customId is not found or the datatype is unknown
283
     */
284 42
    public static function createByCustomID($customId)
285
    {
286
        // Reuse already created CustomColumns for performance
287 42
        if (array_key_exists($customId, self::$customColumnCacheID))
288 42
            return self::$customColumnCacheID[$customId];
289
290 17
        $datatype = self::getDatatypeByCustomID($customId);
291
292
        switch ($datatype) {
293 17
            case self::CUSTOM_TYPE_TEXT:
294 5
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeText($customId);
295 12
            case self::CUSTOM_TYPE_SERIES:
296 2
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeSeries($customId);
297 10
            case self::CUSTOM_TYPE_ENUM:
298 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeEnumeration($customId);
299 9
            case self::CUSTOM_TYPE_COMMENT:
300 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeComment($customId);
301 8
            case self::CUSTOM_TYPE_DATE:
302 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeDate($customId);
303 7
            case self::CUSTOM_TYPE_FLOAT:
304 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeFloat($customId);
305 6
            case self::CUSTOM_TYPE_INT:
306 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeInteger($customId);
307 5
            case self::CUSTOM_TYPE_RATING:
308 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeRating($customId);
309 4
            case self::CUSTOM_TYPE_BOOL:
310 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeBool($customId);
311 3
            case self::CUSTOM_TYPE_COMPOSITE:
312 2
                return NULL; //TODO Currently not supported
313 1
            default:
314 1
                throw new Exception("Unkown column type: " . $datatype);
315 1
        }
316
    }
317
318
    /**
319
     * Create a CustomColumnType by its lookup name
320
     *
321
     * @param string $lookup the lookup-name of the custom column
322
     * @return CustomColumnType|null
323
     */
324 33
    public static function createByLookup($lookup)
325
    {
326
        // Reuse already created CustomColumns for performance
327 33
        if (array_key_exists($lookup, self::$customColumnCacheLookup))
328 33
            return self::$customColumnCacheLookup[$lookup];
329
330 17
        $result = parent::getDb()->prepare('SELECT id FROM custom_columns WHERE label = ?');
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getDb() instead of createByLookup()). Are you sure this is correct? If so, you might want to change this to $this->getDb().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
331 17
        $result->execute(array($lookup));
332 17
        if ($post = $result->fetchObject()) {
333 16
            return self::$customColumnCacheLookup[$lookup] = self::createByCustomID($post->id);
334
        }
335 1
        return self::$customColumnCacheLookup[$lookup] = NULL;
336
    }
337
338
    /**
339
     * Return an entry array for all possible (in the DB used) values of this column
340
     * These are the values used in the getUriAllCustoms() page
341
     *
342
     * @return Entry[]
343
     */
344 36
    public function getAllCustomValues()
345
    {
346
        // lazy loading
347 36
        if ($this->customValues == NULL)
348 36
            $this->customValues = $this->getAllCustomValuesFromDatabase();
349
350 36
        return $this->customValues;
351
    }
352
353
    /**
354
     * Get the title of a CustomColumn by its customID
355
     *
356
     * @param integer $customId
357
     * @return string
358
     */
359 14
    protected static function getTitleByCustomID($customId)
360
    {
361 14
        $result = parent::getDb()->prepare('SELECT name FROM custom_columns WHERE id = ?');
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getDb() instead of getTitleByCustomID()). Are you sure this is correct? If so, you might want to change this to $this->getDb().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
362 14
        $result->execute(array($customId));
363 14
        if ($post = $result->fetchObject()) {
364 14
            return $post->name;
365
        }
366
        return "";
367
    }
368
369
    /**
370
     * Get the query to find all books with a specific value of this column
371
     * the returning array has two values:
372
     *  - first the query (string)
373
     *  - second an array of all PreparedStatement parameters
374
     *
375
     * @param string|integer $id the id of the searched value
376
     * @return array
377
     */
378
    abstract public function getQuery($id);
379
380
    /**
381
     * Get a CustomColumn for a specified (by ID) value
382
     *
383
     * @param string|integer $id the id of the searched value
384
     * @return CustomColumn
385
     */
386
    abstract public function getCustom($id);
387
388
    /**
389
     * Return an entry array for all possible (in the DB used) values of this column by querying the database
390
     *
391
     * @return Entry[]
392
     */
393
    abstract protected function getAllCustomValuesFromDatabase();
394
395
    /**
396
     * The description used in the index page
397
     *
398
     * @return string
399
     */
400
    abstract public function getDescription();
401
402
    /**
403
     * Find the value of this column for a specific book
404
     *
405
     * @param Book $book
406
     * @return CustomColumn
407
     */
408
    public abstract function getCustomByBook($book);
409
410
    /**
411
     * Is this column searchable by value
412
     * only searchable columns can be displayed on the index page
413
     *
414
     * @return bool
415
     */
416
    public abstract function isSearchable();
417
}
418
419
class CustomColumnTypeText extends CustomColumnType
420
{
421 5
    protected function __construct($pcustomId)
422
    {
423 5
        parent::__construct($pcustomId, self::CUSTOM_TYPE_TEXT);
424 5
    }
425
426
    /**
427
     * Get the name of the sqlite table for this column
428
     *
429
     * @return string|null
430
     */
431 11
    private function getTableName()
432
    {
433 11
        return "custom_column_{$this->customId}";
434
    }
435
436
    /**
437
     * Get the name of the linking sqlite table for this column
438
     * (or NULL if there is no linktable)
439
     *
440
     * @return string|null
441
     */
442 11
    private function getTableLinkName()
443
    {
444 11
        return "books_custom_column_{$this->customId}_link";
445
    }
446
447
    /**
448
     * Get the name of the linking column in the linktable
449
     *
450
     * @return string|null
451
     */
452 11
    private function getTableLinkColumn()
453
    {
454 11
        return "value";
455
    }
456
457 4
    public function getQuery($id)
458
    {
459 4
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
460 4
        return array($query, array($id));
461
    }
462
463 4
    public function getCustom($id)
464
    {
465 4
        $result = $this->getDb()->prepare(str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName()));
466 4
        $result->execute(array($id));
467 4
        if ($post = $result->fetchObject()) {
468 4
            return new CustomColumn($id, $post->name, $this);
469
        }
470
        return NULL;
471
    }
472
473 5
    protected function getAllCustomValuesFromDatabase()
474
    {
475 5
        $queryFormat = "SELECT {0}.id AS id, {0}.value AS name, count(*) AS count FROM {0}, {1} WHERE {0}.id = {1}.{2} GROUP BY {0}.id, {0}.value ORDER BY {0}.value";
476 5
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
477
478 5
        $result = $this->getDb()->query($query);
479 5
        $entryArray = array();
480 5
        while ($post = $result->fetchObject())
481
        {
482 5
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
483 5
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($post->id)));
484
485 5
            $entry = new Entry($post->name, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
486
487 5
            array_push($entryArray, $entry);
488 5
        }
489 5
        return $entryArray;
490
    }
491
492 9
    public function getDescription()
493
    {
494 9
        $desc = $this->getDatabaseDescription();
495 9
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
496 9
        return $desc;
497
    }
498
499 2
    public function getCustomByBook($book)
500
    {
501 2
        $queryFormat = "SELECT {0}.id AS id, {0}.{2} AS name FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = {3} ORDER BY {0}.value";
502 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
503
504 2
        $result = $this->getDb()->query($query);
505 2
        if ($post = $result->fetchObject()) {
506 2
            return new CustomColumn($post->id, $post->name, $this);
507
        }
508
        return new CustomColumn(NULL, "", $this);
509
    }
510
511 9
    public function isSearchable()
512
    {
513 9
        return true;
514
    }
515
}
516
517
class CustomColumnTypeSeries extends CustomColumnType
518
{
519 2
    protected function __construct($pcustomId)
520
    {
521 2
        parent::__construct($pcustomId, self::CUSTOM_TYPE_SERIES);
522 2
    }
523
524
    /**
525
     * Get the name of the sqlite table for this column
526
     *
527
     * @return string|null
528
     */
529 7
    private function getTableName()
530
    {
531 7
        return "custom_column_{$this->customId}";
532
    }
533
534
    /**
535
     * Get the name of the linking sqlite table for this column
536
     * (or NULL if there is no linktable)
537
     *
538
     * @return string|null
539
     */
540 7
    private function getTableLinkName()
541
    {
542 7
        return "books_custom_column_{$this->customId}_link";
543
    }
544
545
    /**
546
     * Get the name of the linking column in the linktable
547
     *
548
     * @return string|null
549
     */
550 7
    private function getTableLinkColumn()
551
    {
552 7
        return "value";
553
    }
554
555 3
    public function getQuery($id)
556
    {
557 3
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
558 3
        return array($query, array($id));
559
    }
560
561 3
    public function getCustom($id)
562
    {
563 3
        $result = $this->getDb()->prepare(str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName()));
564 3
        $result->execute(array($id));
565 3
        if ($post = $result->fetchObject()) {
566 3
            return new CustomColumn($id, $post->name, $this);
567
        }
568
        return NULL;
569
    }
570
571 2
    protected function getAllCustomValuesFromDatabase()
572
    {
573 2
        $queryFormat = "SELECT {0}.id AS id, {0}.value AS name, count(*) AS count FROM {0}, {1} WHERE {0}.id = {1}.{2} GROUP BY {0}.id, {0}.value ORDER BY {0}.value";
574 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
575
576 2
        $result = $this->getDb()->query($query);
577 2
        $entryArray = array();
578 2
        while ($post = $result->fetchObject()) {
579 2
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
580 2
            $entryPLinkArray = array(new LinkNavigation($this->getUri($post->id)));
581
582 2
            $entry = new Entry($post->name, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
583
584 2
            array_push($entryArray, $entry);
585 2
        }
586 2
        return $entryArray;
587
    }
588
589 5
    public function getDescription()
590
    {
591 5
        return str_format(localize("customcolumn.description.series", $this->getDistinctValueCount()), $this->getDistinctValueCount());
592
    }
593
594 2
    public function getCustomByBook($book)
595
    {
596 2
        $queryFormat = "SELECT {0}.id AS id, {1}.{2} AS name, {1}.extra AS extra FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = {3}";
597 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
598
599 2
        $result = $this->getDb()->query($query);
600 2
        if ($post = $result->fetchObject()) {
601 1
            return new CustomColumn($post->id, $post->name . " [" . $post->extra . "]", $this);
602
        }
603 1
        return new CustomColumn(NULL, "", $this);
604
    }
605
606 5
    public function isSearchable()
607
    {
608 5
        return true;
609
    }
610
}
611
612
class CustomColumnTypeEnumeration extends CustomColumnType
613
{
614 1
    protected function __construct($pcustomId)
615
    {
616 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_ENUM);
617 1
    }
618
619
    /**
620
     * Get the name of the sqlite table for this column
621
     *
622
     * @return string|null
623
     */
624 5
    private function getTableName()
625
    {
626 5
        return "custom_column_{$this->customId}";
627
    }
628
629
    /**
630
     * Get the name of the linking sqlite table for this column
631
     * (or NULL if there is no linktable)
632
     *
633
     * @return string|null
634
     */
635 5
    private function getTableLinkName()
636
    {
637 5
        return "books_custom_column_{$this->customId}_link";
638
    }
639
640
    /**
641
     * Get the name of the linking column in the linktable
642
     *
643
     * @return string|null
644
     */
645 5
    private function getTableLinkColumn()
646
    {
647 5
        return "value";
648
    }
649
650 2
    public function getQuery($id)
651
    {
652 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
653 2
        return array($query, array($id));
654
    }
655
656 2
    public function getCustom($id)
657
    {
658 2
        $result = $this->getDb()->prepare(str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName()));
659 2
        $result->execute(array($id));
660 2
        if ($post = $result->fetchObject()) {
661 2
            return new CustomColumn ($id, $post->name, $this);
662
        }
663
        return NULL;
664
    }
665
666 1
    protected function getAllCustomValuesFromDatabase()
667
    {
668 1
        $queryFormat = "SELECT {0}.id AS id, {0}.value AS name, count(*) AS count FROM {0}, {1} WHERE {0}.id = {1}.{2} GROUP BY {0}.id, {0}.value ORDER BY {0}.value";
669 1
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
670
671 1
        $result = $this->getDb()->query($query);
672 1
        $entryArray = array();
673 1
        while ($post = $result->fetchObject()) {
674 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
675 1
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($post->id)));
676
677 1
            $entry = new Entry ($post->name, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
678
679 1
            array_push($entryArray, $entry);
680 1
        }
681 1
        return $entryArray;
682
    }
683
684 3
    public function getDescription()
685
    {
686 3
        return str_format(localize("customcolumn.description.enum", $this->getDistinctValueCount()), $this->getDistinctValueCount());
687
    }
688
689 2
    public function getCustomByBook($book)
690
    {
691 2
        $queryFormat = "SELECT {0}.id AS id, {0}.{2} AS name FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = {3}";
692 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
693
694 2
        $result = $this->getDb()->query($query);
695 2
        if ($post = $result->fetchObject()) {
696 2
            return new CustomColumn($post->id, $post->name, $this);
697
        }
698
        return new CustomColumn(NULL, localize("customcolumn.enum.unknown"), $this);
699
    }
700
701 3
    public function isSearchable()
702
    {
703 3
        return true;
704
    }
705
}
706
707
class CustomColumnTypeDate extends CustomColumnType
708
{
709 1
    protected function __construct($pcustomId)
710
    {
711 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_DATE);
712 1
    }
713
714
    /**
715
     * Get the name of the sqlite table for this column
716
     *
717
     * @return string|null
718
     */
719 5
    private function getTableName()
720
    {
721 5
        return "custom_column_{$this->customId}";
722
    }
723
724 2
    public function getQuery($id)
725
    {
726 2
        $date = new DateTime($id);
727 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DATE, "{0}", "{1}", $this->getTableName());
728 2
        return array($query, array($date->format("Y-m-d")));
729
    }
730
731 2
    public function getCustom($id)
732
    {
733 2
        $date = new DateTime($id);
734
735 2
        return new CustomColumn($id, $date->format(localize("customcolumn.date.format")), $this);
736
    }
737
738 1
    protected function getAllCustomValuesFromDatabase()
739
    {
740 1
        $queryFormat = "SELECT date(value) AS datevalue, count(*) AS count FROM {0} GROUP BY datevalue";
741 1
        $query = str_format($queryFormat, $this->getTableName());
742 1
        $result = $this->getDb()->query($query);
743
744 1
        $entryArray = array();
745 1
        while ($post = $result->fetchObject()) {
746 1
            $date = new DateTime($post->datevalue);
747 1
            $id = $date->format("Y-m-d");
748
749 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
750 1
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($id)));
751
752 1
            $entry = new Entry($date->format(localize("customcolumn.date.format")), $this->getEntryId($id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
753
754 1
            array_push($entryArray, $entry);
755 1
        }
756
757 1
        return $entryArray;
758
    }
759
760 3
    public function getDescription()
761
    {
762 3
        $desc = $this->getDatabaseDescription();
763 3
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
764 3
        return $desc;
765
    }
766
767 2
    public function getCustomByBook($book)
768
    {
769 2
        $queryFormat = "SELECT date({0}.value) AS datevalue FROM {0} WHERE {0}.book = {1}";
770 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
771
772 2
        $result = $this->getDb()->query($query);
773 2
        if ($post = $result->fetchObject()) {
774 1
            $date = new DateTime($post->datevalue);
775
776 1
            return new CustomColumn($date->format("Y-m-d"), $date->format(localize("customcolumn.date.format")), $this);
777
        }
778 1
        return new CustomColumn(NULL, localize("customcolumn.date.unknown"), $this);
779
    }
780
781 3
    public function isSearchable()
782
    {
783 3
        return true;
784
    }
785
}
786
787
class CustomColumnTypeRating extends CustomColumnType
788
{
789 1
    protected function __construct($pcustomId)
790
    {
791 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_RATING);
792 1
    }
793
794
    /**
795
     * Get the name of the sqlite table for this column
796
     *
797
     * @return string|null
798
     */
799 5
    private function getTableName()
800
    {
801 5
        return "custom_column_{$this->customId}";
802
    }
803
804
    /**
805
     * Get the name of the linking sqlite table for this column
806
     * (or NULL if there is no linktable)
807
     *
808
     * @return string|null
809
     */
810 5
    private function getTableLinkName()
811
    {
812 5
        return "books_custom_column_{$this->customId}_link";
813
    }
814
815
    /**
816
     * Get the name of the linking column in the linktable
817
     *
818
     * @return string|null
819
     */
820 4
    private function getTableLinkColumn()
821
    {
822 4
        return "value";
823
    }
824
825 2
    public function getQuery($id)
826
    {
827 2
        if ($id == 0) {
828 2
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_RATING_NULL, "{0}", "{1}", $this->getTableLinkName(), $this->getTableName(), $this->getTableLinkColumn());
829 2
            return array($query, array());
830
        } else {
831 2
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_RATING, "{0}", "{1}", $this->getTableLinkName(), $this->getTableName(), $this->getTableLinkColumn());
832 2
            return array($query, array($id));
833
        }
834
    }
835
836 2
    public function getCustom($id)
837
    {
838 2
        return new CustomColumn ($id, str_format(localize("customcolumn.stars", $id / 2), $id / 2), $this);
839
    }
840
841 1
    protected function getAllCustomValuesFromDatabase()
842
    {
843 1
        $queryFormat = "SELECT coalesce({0}.value, 0) AS value, count(*) AS count FROM books  LEFT JOIN {1} ON  books.id = {1}.book LEFT JOIN {0} ON {0}.id = {1}.value GROUP BY coalesce({0}.value, -1)";
844 1
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName());
845 1
        $result = $this->getDb()->query($query);
846
847 1
        $countArray = array(0 => 0, 2 => 0, 4 => 0, 6 => 0, 8 => 0, 10 => 0);
848 1
        while ($row = $result->fetchObject()) {
849 1
            $countArray[$row->value] = $row->count;
850 1
        }
851
852 1
        $entryArray = array();
853
854 1
        for ($i = 0; $i <= 5; $i++) {
855 1
            $count = $countArray[$i * 2];
856 1
            $name = str_format(localize("customcolumn.stars", $i), $i);
857 1
            $entryid = $this->getEntryId($i * 2);
858 1
            $content = str_format(localize("bookword", $count), $count);
859 1
            $linkarray = array(new LinkNavigation($this->getUri($i * 2)));
860 1
            $entry = new Entry($name, $entryid, $content, $this->datatype, $linkarray, "", $count);
861 1
            array_push($entryArray, $entry);
862 1
        }
863
864 1
        return $entryArray;
865
    }
866
867 3
    public function getDescription()
868
    {
869 3
        return localize("customcolumn.description.rating");
870
    }
871
872 2
    public function getCustomByBook($book)
873
    {
874 2
        $queryFormat = "SELECT {0}.value AS value FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = {3}";
875 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
876
877 2
        $result = $this->getDb()->query($query);
878 2
        if ($post = $result->fetchObject()) {
879 1
            return new CustomColumn($post->value, str_format(localize("customcolumn.stars", $post->value / 2), $post->value / 2), $this);
880
        }
881 1
        return new CustomColumn(NULL, localize("customcolumn.rating.unknown"), $this);
882
    }
883
884 3
    public function isSearchable()
885
    {
886 3
        return true;
887
    }
888
}
889
890
class CustomColumnTypeBool extends CustomColumnType
891
{
892
    // PHP pre 5.6 does not support const arrays
893
    private $BOOLEAN_NAMES = array(
894
        -1 => "customcolumn.boolean.unknown", // localize("customcolumn.boolean.unknown")
895
        00 => "customcolumn.boolean.no",      // localize("customcolumn.boolean.no")
896
        +1 => "customcolumn.boolean.yes",     // localize("customcolumn.boolean.yes")
897
    );
898
899 1
    protected function __construct($pcustomId)
900
    {
901 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_BOOL);
902 1
    }
903
904
    /**
905
     * Get the name of the sqlite table for this column
906
     *
907
     * @return string|null
908
     */
909 5
    private function getTableName()
910
    {
911 5
        return "custom_column_{$this->customId}";
912
    }
913
914 3
    public function getQuery($id)
915
    {
916 3
        if ($id == -1) {
917 2
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_BOOL_NULL, "{0}", "{1}", $this->getTableName());
918 2
            return array($query, array());
919 3
        } else if ($id == 0) {
920 3
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_BOOL_FALSE, "{0}", "{1}", $this->getTableName());
921 3
            return array($query, array());
922 2
        } else if ($id == 1) {
923 2
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_BOOL_TRUE, "{0}", "{1}", $this->getTableName());
924 2
            return array($query, array());
925
        } else {
926
            return NULL;
927
        }
928
    }
929
930 3
    public function getCustom($id)
931
    {
932 3
        return new CustomColumn($id, localize($this->BOOLEAN_NAMES[$id]), $this);
933
    }
934
935 1
    protected function getAllCustomValuesFromDatabase()
936
    {
937 1
        $queryFormat = "SELECT coalesce({0}.value, -1) AS id, count(*) AS count FROM books LEFT JOIN {0} ON  books.id = {0}.book GROUP BY {0}.value ORDER BY {0}.value";
938 1
        $query = str_format($queryFormat, $this->getTableName());
939 1
        $result = $this->getDb()->query($query);
940
941 1
        $entryArray = array();
942 1
        while ($post = $result->fetchObject()) {
943 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
944 1
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($post->id)));
945
946 1
            $entry = new Entry(localize($this->BOOLEAN_NAMES[$post->id]), $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
947
948 1
            array_push($entryArray, $entry);
949 1
        }
950 1
        return $entryArray;
951
    }
952
953 3
    public function getDescription()
954
    {
955 3
        return localize("customcolumn.description.bool");
956
    }
957
958 2
    public function getCustomByBook($book)
959
    {
960 2
        $queryFormat = "SELECT {0}.value AS boolvalue FROM {0} WHERE {0}.book = {1}";
961 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
962
963 2
        $result = $this->getDb()->query($query);
964 2
        if ($post = $result->fetchObject()) {
965 2
            return new CustomColumn($post->boolvalue, localize($this->BOOLEAN_NAMES[$post->boolvalue]), $this);
966
        } else {
967
            return new CustomColumn(-1, localize($this->BOOLEAN_NAMES[-1]), $this);
968
        }
969
    }
970
971 3
    public function isSearchable()
972
    {
973 3
        return true;
974
    }
975
}
976
977
class CustomColumnTypeInteger extends CustomColumnType
978
{
979 1
    protected function __construct($pcustomId)
980
    {
981 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_INT);
982 1
    }
983
984
    /**
985
     * Get the name of the sqlite table for this column
986
     *
987
     * @return string|null
988
     */
989 5
    private function getTableName()
990
    {
991 5
        return "custom_column_{$this->customId}";
992
    }
993
994 2
    public function getQuery($id)
995
    {
996 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DIRECT, "{0}", "{1}", $this->getTableName());
997 2
        return array($query, array($id));
998
    }
999
1000 2
    public function getCustom($id)
1001
    {
1002 2
        return new CustomColumn($id, $id, $this);
1003
    }
1004
1005 1
    protected function getAllCustomValuesFromDatabase()
1006
    {
1007 1
        $queryFormat = "SELECT value AS id, count(*) AS count FROM {0} GROUP BY value";
1008 1
        $query = str_format($queryFormat, $this->getTableName());
1009
1010 1
        $result = $this->getDb()->query($query);
1011 1
        $entryArray = array();
1012 1
        while ($post = $result->fetchObject()) {
1013 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
1014 1
            $entryPLinkArray = array(new LinkNavigation($this->getUri($post->id)));
1015
1016 1
            $entry = new Entry($post->id, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
1017
1018 1
            array_push($entryArray, $entry);
1019 1
        }
1020 1
        return $entryArray;
1021
    }
1022
1023 3
    public function getDescription()
1024
    {
1025 3
        $desc = $this->getDatabaseDescription();
1026 3
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
1027 3
        return $desc;
1028
    }
1029
1030 2
    public function getCustomByBook($book)
1031
    {
1032 2
        $queryFormat = "SELECT {0}.value AS value FROM {0} WHERE {0}.book = {1}";
1033 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
1034
1035 2
        $result = $this->getDb()->query($query);
1036 2
        if ($post = $result->fetchObject()) {
1037 1
            return new CustomColumn($post->value, $post->value, $this);
1038
        }
1039 1
        return new CustomColumn(NULL, localize("customcolumn.int.unknown"), $this);
1040
    }
1041
1042 3
    public function isSearchable()
1043
    {
1044 3
        return true;
1045
    }
1046
}
1047
1048
class CustomColumnTypeFloat extends CustomColumnType
1049
{
1050 1
    protected function __construct($pcustomId)
1051
    {
1052 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_FLOAT);
1053 1
    }
1054
1055
    /**
1056
     * Get the name of the sqlite table for this column
1057
     *
1058
     * @return string|null
1059
     */
1060 5
    private function getTableName()
1061
    {
1062 5
        return "custom_column_{$this->customId}";
1063
    }
1064
1065 2
    public function getQuery($id)
1066
    {
1067 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DIRECT, "{0}", "{1}", $this->getTableName());
1068 2
        return array($query, array($id));
1069
    }
1070
1071 2
    public function getCustom($id)
1072
    {
1073 2
        return new CustomColumn($id, $id, $this);
1074
    }
1075
1076 1
    protected function getAllCustomValuesFromDatabase()
1077
    {
1078 1
        $queryFormat = "SELECT value AS id, count(*) AS count FROM {0} GROUP BY value";
1079 1
        $query = str_format($queryFormat, $this->getTableName());
1080
1081 1
        $result = $this->getDb()->query($query);
1082 1
        $entryArray = array();
1083 1
        while ($post = $result->fetchObject()) {
1084 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
1085 1
            $entryPLinkArray = array(new LinkNavigation($this->getUri($post->id)));
1086
1087 1
            $entry = new Entry($post->id, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
1088
1089 1
            array_push($entryArray, $entry);
1090 1
        }
1091 1
        return $entryArray;
1092
    }
1093
1094 3
    public function getDescription()
1095
    {
1096 3
        $desc = $this->getDatabaseDescription();
1097 3
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
1098 3
        return $desc;
1099
    }
1100
1101 2
    public function getCustomByBook($book)
1102
    {
1103 2
        $queryFormat = "SELECT {0}.value AS value FROM {0} WHERE {0}.book = {1}";
1104 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
1105
1106 2
        $result = $this->getDb()->query($query);
1107 2
        if ($post = $result->fetchObject()) {
1108 2
            return new CustomColumn($post->value, $post->value, $this);
1109
        }
1110
        return new CustomColumn(NULL, localize("customcolumn.float.unknown"), $this);
1111
    }
1112
1113 3
    public function isSearchable()
1114
    {
1115 3
        return true;
1116
    }
1117
}
1118
1119
class CustomColumnTypeComment extends CustomColumnType
1120
{
1121 1
    protected function __construct($pcustomId)
1122
    {
1123 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_COMMENT);
1124 1
    }
1125
1126
    /**
1127
     * Get the name of the sqlite table for this column
1128
     *
1129
     * @return string|null
1130
     */
1131 4
    private function getTableName()
1132
    {
1133 4
        return "custom_column_{$this->customId}";
1134
    }
1135
1136 2
    public function getQuery($id)
1137
    {
1138 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DIRECT_ID, "{0}", "{1}", $this->getTableName());
1139 2
        return array($query, array($id));
1140
    }
1141
1142 2
    public function getCustom($id)
1143
    {
1144 2
        return new CustomColumn($id, $id, $this);
1145
    }
1146
1147 4
    public function encodeHTMLValue($value)
1148
    {
1149 4
        return "<div>" . $value . "</div>"; // no htmlspecialchars, this is already HTML
1150
    }
1151
1152
    protected function getAllCustomValuesFromDatabase()
1153
    {
1154
        return NULL;
1155
    }
1156
1157 1
    public function getDescription()
1158
    {
1159 1
        $desc = $this->getDatabaseDescription();
1160 1
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
1161 1
        return $desc;
1162
    }
1163
1164 2
    public function getCustomByBook($book)
1165
    {
1166 2
        $queryFormat = "SELECT {0}.id AS id, {0}.value AS value FROM {0} WHERE {0}.book = {1}";
1167 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
1168
1169 2
        $result = $this->getDb()->query($query);
1170 2
        if ($post = $result->fetchObject()) {
1171 1
            return new CustomColumn($post->id, $post->value, $this);
1172
        }
1173 1
        return new CustomColumn(NULL, localize("customcolumn.float.unknown"), $this);
1174
    }
1175
1176 3
    public function isSearchable()
1177
    {
1178 3
        return false;
1179
    }
1180
}
1181