Completed
Push — master ( ae89f3...6df809 )
by Sébastien
8s
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