Completed
Pull Request — master (#274)
by Markus
06:26
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 1
    public function getHTMLEncodedValue()
79
    {
80 1
        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
/**
99
 * A single calibre custom column
100
 */
101
abstract class CustomColumnType extends Base
102
{
103
    const ALL_CUSTOMS_ID       = "cops:custom";
104
105
    const CUSTOM_TYPE_TEXT      = "text";        // type 1 + 2
106
    const CUSTOM_TYPE_COMMENT   = "comments";    // type 3
107
    const CUSTOM_TYPE_SERIES    = "series";      // type 4
108
    const CUSTOM_TYPE_ENUM      = "enumeration"; // type 5
109
    const CUSTOM_TYPE_DATE      = "datetime";    // type 6
110
    const CUSTOM_TYPE_FLOAT     = "float";       // type 7
111
    const CUSTOM_TYPE_INT       = "int";         // type 8
112
    const CUSTOM_TYPE_RATING    = "rating";      // type 9
113
    const CUSTOM_TYPE_BOOL      = "bool";        // type 10
114
    const CUSTOM_TYPE_COMPOSITE = "composite";   // type 11 + 12
115
116
    /** @var array[integer]CustomColumnType  */
117
    private static $customColumnCacheID = array();
118
119
    /** @var array[string]CustomColumnType  */
120
    private static $customColumnCacheLookup = array();
121
122
    /** @var integer the id of this column */
123
    public $customId;
124
    /** @var string name/title of this column */
125
    public $columnTitle;
126
    /** @var string the datatype of this column (one of the CUSTOM_TYPE_* constant values) */
127
    public $datatype;
128
    /** @var null|Entry[] */
129
    private $customValues = NULL;
130
131 14
    protected function __construct($pcustomId, $pdatatype)
132
    {
133 14
        $this->columnTitle = self::getTitleByCustomID($pcustomId);
134 14
        $this->customId = $pcustomId;
135 14
        $this->datatype = $pdatatype;
136 14
        $this->customValues = NULL;
137 14
    }
138
139
    /**
140
     * The URI to show all book swith a specific value in this column
141
     *
142
     * @param string|integer $id the id of the value to show
143
     * @return string
144
     */
145 13
    public function getUri($id)
146
    {
147 13
        return "?page=" . parent::PAGE_CUSTOM_DETAIL . "&custom={$this->customId}&id={$id}";
148
    }
149
150
    /**
151
     * The URI to show all the values of this column
152
     *
153
     * @return string
154
     */
155 25
    public function getUriAllCustoms()
156
    {
157 25
        return "?page=" . parent::PAGE_ALL_CUSTOMS . "&custom={$this->customId}";
158
    }
159
160
    /**
161
     * The EntryID to show all book swith a specific value in this column
162
     *
163
     * @param string|integer $id the id of the value to show
164
     * @return string
165
     */
166 17
    public function getEntryId($id)
167
    {
168 17
        return self::ALL_CUSTOMS_ID . ":" . $this->customId . ":" . $id;
169
    }
170
171
    /**
172
     * The EntryID to show all the values of this column
173
     *
174
     * @return string
175
     */
176 37
    public function getAllCustomsId()
177
    {
178 37
        return self::ALL_CUSTOMS_ID . ":" . $this->customId;
179
    }
180
181
    /**
182
     * The title of this column
183
     *
184
     * @return string
185
     */
186 38
    public function getTitle()
187
    {
188 38
        return $this->columnTitle;
189
    }
190
191
    /**
192
     * The description of this column as it is definied in the database
193
     *
194
     * @return string|null
195
     */
196 20
    public function getDatabaseDescription()
197
    {
198 20
        $result = $this->getDb()->prepare('SELECT display FROM custom_columns WHERE id = ?');
199 20
        $result->execute(array($this->customId));
200 20
        if ($post = $result->fetchObject()) {
201 20
            $json = json_decode($post->display);
202 20
            return (isset($json->description) && !empty($json->description)) ? $json->description : NULL;
203
        }
204
        return NULL;
205
    }
206
207
    /**
208
     * Get the Entry for this column
209
     * This is used in the initializeContent method to display e.g. the index page
210
     *
211
     * @return Entry
212
     */
213 14
    public function getCount()
214
    {
215 14
        $ptitle = $this->getTitle();
216 14
        $pid = $this->getAllCustomsId();
217 14
        $pcontent = $this->getDescription();
218 14
        $pcontentType = $this->datatype;
219 14
        $plinkArray = array(new LinkNavigation($this->getUriAllCustoms()));
220 14
        $pclass = "";
221 14
        $pcount = $this->getDistinctValueCount();
222
223 14
        return new Entry($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $pclass, $pcount);
224
    }
225
226
    /**
227
     * Get the amount of distinct values for this column
228
     *
229
     * @return int
230
     */
231 16
    protected function getDistinctValueCount()
232
    {
233 16
        return count($this->getAllCustomValues());
234
    }
235
236
    /**
237
     * Encode a value of this column ready to be displayed in an HTML document
238
     *
239
     * @param integer|string $value
240
     * @return string
241
     */
242 7
    public function encodeHTMLValue($value)
243
    {
244 7
        return htmlspecialchars($value);
245
    }
246
247
    /**
248
     * Get the datatype of a CustomColumn by its customID
249
     *
250
     * @param integer $customId
251
     * @return string|null
252
     */
253 17
    private static function getDatatypeByCustomID($customId)
254
    {
255 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...
256 17
        $result->execute(array($customId));
257 17
        if ($post = $result->fetchObject()) {
258 16
            return $post->datatype;
259
        }
260 1
        return NULL;
261
    }
262
263
    /**
264
     * Create a CustomColumnType by CustomID
265
     *
266
     * @param integer $customId the id of the custom column
267
     * @return CustomColumnType|null
268
     * @throws Exception If the $customId is not found or the datatype is unknown
269
     */
270 42
    public static function createByCustomID($customId)
271
    {
272
        // Reuse already created CustomColumns for performance
273 42
        if (array_key_exists($customId, self::$customColumnCacheID))
274 42
            return self::$customColumnCacheID[$customId];
275
276 17
        $datatype = self::getDatatypeByCustomID($customId);
277
278
        switch ($datatype) {
279 17
            case self::CUSTOM_TYPE_TEXT:
280 5
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeText($customId);
281 12
            case self::CUSTOM_TYPE_SERIES:
282 2
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeSeries($customId);
283 10
            case self::CUSTOM_TYPE_ENUM:
284 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeEnumeration($customId);
285 9
            case self::CUSTOM_TYPE_COMMENT:
286 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeComment($customId);
287 8
            case self::CUSTOM_TYPE_DATE:
288 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeDate($customId);
289 7
            case self::CUSTOM_TYPE_FLOAT:
290 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeFloat($customId);
291 6
            case self::CUSTOM_TYPE_INT:
292 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeInteger($customId);
293 5
            case self::CUSTOM_TYPE_RATING:
294 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeRating($customId);
295 4
            case self::CUSTOM_TYPE_BOOL:
296 1
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeBool($customId);
297 3
            case self::CUSTOM_TYPE_COMPOSITE:
298 2
                return NULL; //TODO Currently not supported
299 1
            default:
300 1
                throw new Exception("Unkown column type: " . $datatype);
301 1
        }
302
    }
303
304
    /**
305
     * Create a CustomColumnType by its lookup name
306
     *
307
     * @param string $lookup the lookup-name of the custom column
308
     * @return CustomColumnType|null
309
     */
310 33
    public static function createByLookup($lookup)
311
    {
312
        // Reuse already created CustomColumns for performance
313 33
        if (array_key_exists($lookup, self::$customColumnCacheLookup))
314 33
            return self::$customColumnCacheLookup[$lookup];
315
316 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...
317 17
        $result->execute(array($lookup));
318 17
        if ($post = $result->fetchObject()) {
319 16
            return self::$customColumnCacheLookup[$lookup] = self::createByCustomID($post->id);
320
        }
321 1
        return self::$customColumnCacheLookup[$lookup] = NULL;
322
    }
323
324
    /**
325
     * Return an entry array for all possible (in the DB used) values of this column
326
     * These are the values used in the getUriAllCustoms() page
327
     *
328
     * @return Entry[]
329
     */
330 36
    public function getAllCustomValues()
331
    {
332
        // lazy loading
333 36
        if ($this->customValues == NULL)
334 36
            $this->customValues = $this->getAllCustomValuesFromDatabase();
335
336 36
        return $this->customValues;
337
    }
338
339
    /**
340
     * Get the title of a CustomColumn by its customID
341
     *
342
     * @param integer $customId
343
     * @return string
344
     */
345 14
    protected static function getTitleByCustomID($customId)
346
    {
347 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...
348 14
        $result->execute(array($customId));
349 14
        if ($post = $result->fetchObject()) {
350 14
            return $post->name;
351
        }
352
        return "";
353
    }
354
355
    /**
356
     * Get the query to find all books with a specific value of this column
357
     * the returning array has two values:
358
     *  - first the query (string)
359
     *  - second an array of all PreparedStatement parameters
360
     *
361
     * @param string|integer $id the id of the searched value
362
     * @return array
363
     */
364
    abstract public function getQuery($id);
365
366
    /**
367
     * Get a CustomColumn for a specified (by ID) value
368
     *
369
     * @param string|integer $id the id of the searched value
370
     * @return CustomColumn
371
     */
372
    abstract public function getCustom($id);
373
374
    /**
375
     * Return an entry array for all possible (in the DB used) values of this column by querying the database
376
     *
377
     * @return Entry[]
378
     */
379
    abstract protected function getAllCustomValuesFromDatabase();
380
381
    /**
382
     * The description used in the index page
383
     *
384
     * @return string
385
     */
386
    abstract public function getDescription();
387
388
    /**
389
     * Find the value of this column for a specific book
390
     *
391
     * @param Book $book
392
     * @return CustomColumn
393
     */
394
    public abstract function getCustomByBook($book);
395
396
    /**
397
     * Is this column searchable by value
398
     * only searchable columns can be displayed on the index page
399
     *
400
     * @return bool
401
     */
402
    public abstract function isSearchable();
403
}
404
405
class CustomColumnTypeText extends CustomColumnType
406
{
407 5
    protected function __construct($pcustomId)
408
    {
409 5
        parent::__construct($pcustomId, self::CUSTOM_TYPE_TEXT);
410 5
    }
411
412
    /**
413
     * Get the name of the sqlite table for this column
414
     *
415
     * @return string|null
416
     */
417 11
    private function getTableName()
418
    {
419 11
        return "custom_column_{$this->customId}";
420
    }
421
422
    /**
423
     * Get the name of the linking sqlite table for this column
424
     * (or NULL if there is no linktable)
425
     *
426
     * @return string|null
427
     */
428 11
    private function getTableLinkName()
429
    {
430 11
        return "books_custom_column_{$this->customId}_link";
431
    }
432
433
    /**
434
     * Get the name of the linking column in the linktable
435
     *
436
     * @return string|null
437
     */
438 11
    private function getTableLinkColumn()
439
    {
440 11
        return "value";
441
    }
442
443 4
    public function getQuery($id)
444
    {
445 4
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
446 4
        return array($query, array($id));
447
    }
448
449 4
    public function getCustom($id)
450
    {
451 4
        $result = $this->getDb()->prepare(str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName()));
452 4
        $result->execute(array($id));
453 4
        if ($post = $result->fetchObject()) {
454 4
            return new CustomColumn($id, $post->name, $this);
455
        }
456
        return NULL;
457
    }
458
459 5
    protected function getAllCustomValuesFromDatabase()
460
    {
461 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";
462 5
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
463
464 5
        $result = $this->getDb()->query($query);
465 5
        $entryArray = array();
466 5
        while ($post = $result->fetchObject())
467
        {
468 5
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
469 5
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($post->id)));
470
471 5
            $entry = new Entry($post->name, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
472
473 5
            array_push($entryArray, $entry);
474 5
        }
475 5
        return $entryArray;
476
    }
477
478 9
    public function getDescription()
479
    {
480 9
        $desc = $this->getDatabaseDescription();
481 9
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
482 9
        return $desc;
483
    }
484
485 2
    public function getCustomByBook($book)
486
    {
487 2
        $queryFormat = "SELECT {0}.id AS id, {1}.{2} AS name FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = {3} ORDER BY {0}.value";
488 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
489
490 2
        $result = $this->getDb()->query($query);
491 2
        if ($post = $result->fetchObject()) {
492 2
            return new CustomColumn($post->id, $post->name, $this);
493
        }
494
        return new CustomColumn(NULL, "", $this);
495
    }
496
497 9
    public function isSearchable()
498
    {
499 9
        return true;
500
    }
501
}
502
503
class CustomColumnTypeSeries extends CustomColumnType
504
{
505 2
    protected function __construct($pcustomId)
506
    {
507 2
        parent::__construct($pcustomId, self::CUSTOM_TYPE_SERIES);
508 2
    }
509
510
    /**
511
     * Get the name of the sqlite table for this column
512
     *
513
     * @return string|null
514
     */
515 7
    private function getTableName()
516
    {
517 7
        return "custom_column_{$this->customId}";
518
    }
519
520
    /**
521
     * Get the name of the linking sqlite table for this column
522
     * (or NULL if there is no linktable)
523
     *
524
     * @return string|null
525
     */
526 7
    private function getTableLinkName()
527
    {
528 7
        return "books_custom_column_{$this->customId}_link";
529
    }
530
531
    /**
532
     * Get the name of the linking column in the linktable
533
     *
534
     * @return string|null
535
     */
536 7
    private function getTableLinkColumn()
537
    {
538 7
        return "value";
539
    }
540
541 3
    public function getQuery($id)
542
    {
543 3
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
544 3
        return array($query, array($id));
545
    }
546
547 3
    public function getCustom($id)
548
    {
549 3
        $result = $this->getDb()->prepare(str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName()));
550 3
        $result->execute(array($id));
551 3
        if ($post = $result->fetchObject()) {
552 3
            return new CustomColumn($id, $post->name, $this);
553
        }
554
        return NULL;
555
    }
556
557 2
    protected function getAllCustomValuesFromDatabase()
558
    {
559 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";
560 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
561
562 2
        $result = $this->getDb()->query($query);
563 2
        $entryArray = array();
564 2
        while ($post = $result->fetchObject()) {
565 2
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
566 2
            $entryPLinkArray = array(new LinkNavigation($this->getUri($post->id)));
567
568 2
            $entry = new Entry($post->name, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
569
570 2
            array_push($entryArray, $entry);
571 2
        }
572 2
        return $entryArray;
573
    }
574
575 5
    public function getDescription()
576
    {
577 5
        return str_format(localize("customcolumn.description.series", $this->getDistinctValueCount()), $this->getDistinctValueCount());
578
    }
579
580 2
    public function getCustomByBook($book)
581
    {
582 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}";
583 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
584
585 2
        $result = $this->getDb()->query($query);
586 2
        if ($post = $result->fetchObject()) {
587 1
            return new CustomColumn($post->id, $post->name . " [" . $post->extra . "]", $this);
588
        }
589 1
        return new CustomColumn(NULL, "", $this);
590
    }
591
592 5
    public function isSearchable()
593
    {
594 5
        return true;
595
    }
596
}
597
598
class CustomColumnTypeEnumeration extends CustomColumnType
599
{
600 1
    protected function __construct($pcustomId)
601
    {
602 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_ENUM);
603 1
    }
604
605
    /**
606
     * Get the name of the sqlite table for this column
607
     *
608
     * @return string|null
609
     */
610 5
    private function getTableName()
611
    {
612 5
        return "custom_column_{$this->customId}";
613
    }
614
615
    /**
616
     * Get the name of the linking sqlite table for this column
617
     * (or NULL if there is no linktable)
618
     *
619
     * @return string|null
620
     */
621 5
    private function getTableLinkName()
622
    {
623 5
        return "books_custom_column_{$this->customId}_link";
624
    }
625
626
    /**
627
     * Get the name of the linking column in the linktable
628
     *
629
     * @return string|null
630
     */
631 5
    private function getTableLinkColumn()
632
    {
633 5
        return "value";
634
    }
635
636 2
    public function getQuery($id)
637
    {
638 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
639 2
        return array($query, array($id));
640
    }
641
642 2
    public function getCustom($id)
643
    {
644 2
        $result = $this->getDb()->prepare(str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName()));
645 2
        $result->execute(array($id));
646 2
        if ($post = $result->fetchObject()) {
647 2
            return new CustomColumn ($id, $post->name, $this);
648
        }
649
        return NULL;
650
    }
651
652 1
    protected function getAllCustomValuesFromDatabase()
653
    {
654 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";
655 1
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
656
657 1
        $result = $this->getDb()->query($query);
658 1
        $entryArray = array();
659 1
        while ($post = $result->fetchObject()) {
660 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
661 1
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($post->id)));
662
663 1
            $entry = new Entry ($post->name, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
664
665 1
            array_push($entryArray, $entry);
666 1
        }
667 1
        return $entryArray;
668
    }
669
670 3
    public function getDescription()
671
    {
672 3
        return str_format(localize("customcolumn.description.enum", $this->getDistinctValueCount()), $this->getDistinctValueCount());
673
    }
674
675 2
    public function getCustomByBook($book)
676
    {
677 2
        $queryFormat = "SELECT {0}.id AS id, {1}.{2} AS name FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = {3}";
678 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
679
680 2
        $result = $this->getDb()->query($query);
681 2
        if ($post = $result->fetchObject()) {
682 2
            return new CustomColumn($post->id, $post->name, $this);
683
        }
684
        return new CustomColumn(NULL, localize("customcolumn.enum.unknown"), $this);
685
    }
686
687 3
    public function isSearchable()
688
    {
689 3
        return true;
690
    }
691
}
692
693
class CustomColumnTypeDate extends CustomColumnType
694
{
695 1
    protected function __construct($pcustomId)
696
    {
697 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_DATE);
698 1
    }
699
700
    /**
701
     * Get the name of the sqlite table for this column
702
     *
703
     * @return string|null
704
     */
705 5
    private function getTableName()
706
    {
707 5
        return "custom_column_{$this->customId}";
708
    }
709
710 2
    public function getQuery($id)
711
    {
712 2
        $date = new DateTime($id);
713 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DATE, "{0}", "{1}", $this->getTableName());
714 2
        return array($query, array($date->format("Y-m-d")));
715
    }
716
717 2
    public function getCustom($id)
718
    {
719 2
        $date = new DateTime($id);
720
721 2
        return new CustomColumn($id, $date->format(localize("customcolumn.date.format")), $this);
722
    }
723
724 1
    protected function getAllCustomValuesFromDatabase()
725
    {
726 1
        $queryFormat = "SELECT date(value) AS datevalue, count(*) AS count FROM {0} GROUP BY datevalue";
727 1
        $query = str_format($queryFormat, $this->getTableName());
728 1
        $result = $this->getDb()->query($query);
729
730 1
        $entryArray = array();
731 1
        while ($post = $result->fetchObject()) {
732 1
            $date = new DateTime($post->datevalue);
733 1
            $id = $date->format("Y-m-d");
734
735 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
736 1
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($id)));
737
738 1
            $entry = new Entry($date->format(localize("customcolumn.date.format")), $this->getEntryId($id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
739
740 1
            array_push($entryArray, $entry);
741 1
        }
742
743 1
        return $entryArray;
744
    }
745
746 3
    public function getDescription()
747
    {
748 3
        $desc = $this->getDatabaseDescription();
749 3
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
750 3
        return $desc;
751
    }
752
753 2
    public function getCustomByBook($book)
754
    {
755 2
        $queryFormat = "SELECT date({0}.value) AS datevalue FROM {0} WHERE {0}.book = {1}";
756 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
757
758 2
        $result = $this->getDb()->query($query);
759 2
        if ($post = $result->fetchObject()) {
760 1
            $date = new DateTime($post->datevalue);
761
762 1
            return new CustomColumn($date->format("Y-m-d"), $date->format(localize("customcolumn.date.format")), $this);
763
        }
764 1
        return new CustomColumn(NULL, localize("customcolumn.date.unknown"), $this);
765
    }
766
767 3
    public function isSearchable()
768
    {
769 3
        return true;
770
    }
771
}
772
773
class CustomColumnTypeRating extends CustomColumnType
774
{
775 1
    protected function __construct($pcustomId)
776
    {
777 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_RATING);
778 1
    }
779
780
    /**
781
     * Get the name of the sqlite table for this column
782
     *
783
     * @return string|null
784
     */
785 5
    private function getTableName()
786
    {
787 5
        return "custom_column_{$this->customId}";
788
    }
789
790
    /**
791
     * Get the name of the linking sqlite table for this column
792
     * (or NULL if there is no linktable)
793
     *
794
     * @return string|null
795
     */
796 5
    private function getTableLinkName()
797
    {
798 5
        return "books_custom_column_{$this->customId}_link";
799
    }
800
801
    /**
802
     * Get the name of the linking column in the linktable
803
     *
804
     * @return string|null
805
     */
806 4
    private function getTableLinkColumn()
807
    {
808 4
        return "value";
809
    }
810
811 2
    public function getQuery($id)
812
    {
813 2
        if ($id == 0) {
814 2
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_RATING_NULL, "{0}", "{1}", $this->getTableLinkName(), $this->getTableName(), $this->getTableLinkColumn());
815 2
            return array($query, array());
816
        } else {
817 2
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_RATING, "{0}", "{1}", $this->getTableLinkName(), $this->getTableName(), $this->getTableLinkColumn());
818 2
            return array($query, array($id));
819
        }
820
    }
821
822 2
    public function getCustom($id)
823
    {
824 2
        return new CustomColumn ($id, str_format(localize("customcolumn.stars", $id / 2), $id / 2), $this);
825
    }
826
827 1
    protected function getAllCustomValuesFromDatabase()
828
    {
829 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)";
830 1
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName());
831 1
        $result = $this->getDb()->query($query);
832
833 1
        $countArray = array(0 => 0, 2 => 0, 4 => 0, 6 => 0, 8 => 0, 10 => 0);
834 1
        while ($row = $result->fetchObject()) {
835 1
            $countArray[$row->value] = $row->count;
836 1
        }
837
838 1
        $entryArray = array();
839
840 1
        for ($i = 0; $i <= 5; $i++) {
841 1
            $count = $countArray[$i * 2];
842 1
            $name = str_format(localize("customcolumn.stars", $i), $i);
843 1
            $entryid = $this->getEntryId($i * 2);
844 1
            $content = str_format(localize("bookword", $count), $count);
845 1
            $linkarray = array(new LinkNavigation($this->getUri($i * 2)));
846 1
            $entry = new Entry($name, $entryid, $content, $this->datatype, $linkarray, "", $count);
847 1
            array_push($entryArray, $entry);
848 1
        }
849
850 1
        return $entryArray;
851
    }
852
853 3
    public function getDescription()
854
    {
855 3
        return localize("customcolumn.description.rating");
856
    }
857
858 2
    public function getCustomByBook($book)
859
    {
860 2
        $queryFormat = "SELECT {0}.value AS value FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = {3}";
861 2
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
862
863 2
        $result = $this->getDb()->query($query);
864 2
        if ($post = $result->fetchObject()) {
865 1
            return new CustomColumn($post->value, str_format(localize("customcolumn.stars", $post->value / 2), $post->value / 2), $this);
866
        }
867 1
        return new CustomColumn(NULL, localize("customcolumn.rating.unknown"), $this);
868
    }
869
870 3
    public function isSearchable()
871
    {
872 3
        return true;
873
    }
874
}
875
876
class CustomColumnTypeBool extends CustomColumnType
877
{
878
    // PHP pre 5.6 does not support const arrays
879
    private $BOOLEAN_NAMES = array(
880
        -1 => "customcolumn.boolean.unknown", // localize("customcolumn.boolean.unknown")
881
        00 => "customcolumn.boolean.no",      // localize("customcolumn.boolean.no")
882
        +1 => "customcolumn.boolean.yes",     // localize("customcolumn.boolean.yes")
883
    );
884
885 1
    protected function __construct($pcustomId)
886
    {
887 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_BOOL);
888 1
    }
889
890
    /**
891
     * Get the name of the sqlite table for this column
892
     *
893
     * @return string|null
894
     */
895 5
    private function getTableName()
896
    {
897 5
        return "custom_column_{$this->customId}";
898
    }
899
900 3
    public function getQuery($id)
901
    {
902 3
        if ($id == -1) {
903 2
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_BOOL_NULL, "{0}", "{1}", $this->getTableName());
904 2
            return array($query, array());
905 3
        } else if ($id == 0) {
906 3
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_BOOL_FALSE, "{0}", "{1}", $this->getTableName());
907 3
            return array($query, array());
908 2
        } else if ($id == 1) {
909 2
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_BOOL_TRUE, "{0}", "{1}", $this->getTableName());
910 2
            return array($query, array());
911
        } else {
912
            return NULL;
913
        }
914
    }
915
916 3
    public function getCustom($id)
917
    {
918 3
        return new CustomColumn($id, localize($this->BOOLEAN_NAMES[$id]), $this);
919
    }
920
921 1
    protected function getAllCustomValuesFromDatabase()
922
    {
923 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";
924 1
        $query = str_format($queryFormat, $this->getTableName());
925 1
        $result = $this->getDb()->query($query);
926
927 1
        $entryArray = array();
928 1
        while ($post = $result->fetchObject()) {
929 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
930 1
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($post->id)));
931
932 1
            $entry = new Entry(localize($this->BOOLEAN_NAMES[$post->id]), $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
933
934 1
            array_push($entryArray, $entry);
935 1
        }
936 1
        return $entryArray;
937
    }
938
939 3
    public function getDescription()
940
    {
941 3
        return localize("customcolumn.description.bool");
942
    }
943
944 2
    public function getCustomByBook($book)
945
    {
946 2
        $queryFormat = "SELECT {0}.value AS boolvalue FROM {0} WHERE {0}.book = {1}";
947 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
948
949 2
        $result = $this->getDb()->query($query);
950 2
        if ($post = $result->fetchObject()) {
951 2
            return new CustomColumn($post->boolvalue, localize($this->BOOLEAN_NAMES[$post->boolvalue]), $this);
952
        } else {
953
            return new CustomColumn(-1, localize($this->BOOLEAN_NAMES[-1]), $this);
954
        }
955
    }
956
957 3
    public function isSearchable()
958
    {
959 3
        return true;
960
    }
961
}
962
963
class CustomColumnTypeInteger extends CustomColumnType
964
{
965 1
    protected function __construct($pcustomId)
966
    {
967 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_INT);
968 1
    }
969
970
    /**
971
     * Get the name of the sqlite table for this column
972
     *
973
     * @return string|null
974
     */
975 5
    private function getTableName()
976
    {
977 5
        return "custom_column_{$this->customId}";
978
    }
979
980 2
    public function getQuery($id)
981
    {
982 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DIRECT, "{0}", "{1}", $this->getTableName());
983 2
        return array($query, array($id));
984
    }
985
986 2
    public function getCustom($id)
987
    {
988 2
        return new CustomColumn($id, $id, $this);
989
    }
990
991 1
    protected function getAllCustomValuesFromDatabase()
992
    {
993 1
        $queryFormat = "SELECT value AS id, count(*) AS count FROM {0} GROUP BY value";
994 1
        $query = str_format($queryFormat, $this->getTableName());
995
996 1
        $result = $this->getDb()->query($query);
997 1
        $entryArray = array();
998 1
        while ($post = $result->fetchObject()) {
999 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
1000 1
            $entryPLinkArray = array(new LinkNavigation($this->getUri($post->id)));
1001
1002 1
            $entry = new Entry($post->id, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
1003
1004 1
            array_push($entryArray, $entry);
1005 1
        }
1006 1
        return $entryArray;
1007
    }
1008
1009 3
    public function getDescription()
1010
    {
1011 3
        $desc = $this->getDatabaseDescription();
1012 3
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
1013 3
        return $desc;
1014
    }
1015
1016 2
    public function getCustomByBook($book)
1017
    {
1018 2
        $queryFormat = "SELECT {0}.value AS value FROM {0} WHERE {0}.book = {1}";
1019 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
1020
1021 2
        $result = $this->getDb()->query($query);
1022 2
        if ($post = $result->fetchObject()) {
1023 1
            return new CustomColumn($post->value, $post->value, $this);
1024
        }
1025 1
        return new CustomColumn(NULL, localize("customcolumn.int.unknown"), $this);
1026
    }
1027
1028 3
    public function isSearchable()
1029
    {
1030 3
        return true;
1031
    }
1032
}
1033
1034
class CustomColumnTypeFloat extends CustomColumnType
1035
{
1036 1
    protected function __construct($pcustomId)
1037
    {
1038 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_FLOAT);
1039 1
    }
1040
1041
    /**
1042
     * Get the name of the sqlite table for this column
1043
     *
1044
     * @return string|null
1045
     */
1046 5
    private function getTableName()
1047
    {
1048 5
        return "custom_column_{$this->customId}";
1049
    }
1050
1051 2
    public function getQuery($id)
1052
    {
1053 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DIRECT, "{0}", "{1}", $this->getTableName());
1054 2
        return array($query, array($id));
1055
    }
1056
1057 2
    public function getCustom($id)
1058
    {
1059 2
        return new CustomColumn($id, $id, $this);
1060
    }
1061
1062 1
    protected function getAllCustomValuesFromDatabase()
1063
    {
1064 1
        $queryFormat = "SELECT value AS id, count(*) AS count FROM {0} GROUP BY value";
1065 1
        $query = str_format($queryFormat, $this->getTableName());
1066
1067 1
        $result = $this->getDb()->query($query);
1068 1
        $entryArray = array();
1069 1
        while ($post = $result->fetchObject()) {
1070 1
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
1071 1
            $entryPLinkArray = array(new LinkNavigation($this->getUri($post->id)));
1072
1073 1
            $entry = new Entry($post->id, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
1074
1075 1
            array_push($entryArray, $entry);
1076 1
        }
1077 1
        return $entryArray;
1078
    }
1079
1080 3
    public function getDescription()
1081
    {
1082 3
        $desc = $this->getDatabaseDescription();
1083 3
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
1084 3
        return $desc;
1085
    }
1086
1087 2
    public function getCustomByBook($book)
1088
    {
1089 2
        $queryFormat = "SELECT {0}.value AS value FROM {0} WHERE {0}.book = {1}";
1090 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
1091
1092 2
        $result = $this->getDb()->query($query);
1093 2
        if ($post = $result->fetchObject()) {
1094 2
            return new CustomColumn($post->value, $post->value, $this);
1095
        }
1096
        return new CustomColumn(NULL, localize("customcolumn.float.unknown"), $this);
1097
    }
1098
1099 3
    public function isSearchable()
1100
    {
1101 3
        return true;
1102
    }
1103
}
1104
1105
class CustomColumnTypeComment extends CustomColumnType
1106
{
1107 1
    protected function __construct($pcustomId)
1108
    {
1109 1
        parent::__construct($pcustomId, self::CUSTOM_TYPE_COMMENT);
1110 1
    }
1111
1112
    /**
1113
     * Get the name of the sqlite table for this column
1114
     *
1115
     * @return string|null
1116
     */
1117 4
    private function getTableName()
1118
    {
1119 4
        return "custom_column_{$this->customId}";
1120
    }
1121
1122 2
    public function getQuery($id)
1123
    {
1124 2
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DIRECT_ID, "{0}", "{1}", $this->getTableName());
1125 2
        return array($query, array($id));
1126
    }
1127
1128 2
    public function getCustom($id)
1129
    {
1130 2
        return new CustomColumn($id, $id, $this);
1131
    }
1132
1133 4
    public function encodeHTMLValue($value)
1134
    {
1135 4
        return "<div>" . $value . "</div>"; // no htmlspecialchars, this is already HTML
1136
    }
1137
1138
    protected function getAllCustomValuesFromDatabase()
1139
    {
1140
        return NULL;
1141
    }
1142
1143 1
    public function getDescription()
1144
    {
1145 1
        $desc = $this->getDatabaseDescription();
1146 1
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
1147 1
        return $desc;
1148
    }
1149
1150 2
    public function getCustomByBook($book)
1151
    {
1152 2
        $queryFormat = "SELECT {0}.id AS id, {0}.value AS value FROM {0} WHERE {0}.book = {1}";
1153 2
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
1154
1155 2
        $result = $this->getDb()->query($query);
1156 2
        if ($post = $result->fetchObject()) {
1157 1
            return new CustomColumn($post->id, $post->value, $this);
1158
        }
1159 1
        return new CustomColumn(NULL, localize("customcolumn.float.unknown"), $this);
1160
    }
1161
1162 3
    public function isSearchable()
1163
    {
1164 3
        return false;
1165
    }
1166
}
1167