Completed
Push — master ( 267988...a9e5cc )
by Sébastien
13:40
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 6
    /* @var string the (string) representation of the value */
19 6
    public $value;
20 6
    /* @var CustomColumnType the custom column that contains the value */
21 6
    public $customColumnType;
22 6
    /* @var string the value encoded for HTML displaying */
23
    public $htmlvalue;
24 3
25 3
    /**
26
     * CustomColumn constructor.
27
     *
28 6
     * @param integer $pid id of the chosen value
29 6
     * @param string $pvalue string representation of the value
30
     * @param CustomColumnType $pcustomColumnType the CustomColumn this value lives in
31
     */
32 10
    public function __construct($pid, $pvalue, $pcustomColumnType)
33 10
    {
34
        $this->valueID = $pid;
35
        $this->value = $pvalue;
36 6
        $this->customColumnType = $pcustomColumnType;
37 6
        $this->htmlvalue = $this->customColumnType->encodeHTMLValue($this->value);
38
    }
39
40 6
    /**
41 6
     * Get the URI to show all books with this value
42
     *
43
     * @return string
44 7
     */
45 7
    public function getUri()
46
    {
47
        return $this->customColumnType->getUri($this->valueID);
48 4
    }
49 4
50
    /**
51
     * Get the EntryID to show all books with this value
52 7
     *
53 7
     * @return string
54 7
     */
55 7
    public function getEntryId()
56 7
    {
57
        return $this->customColumnType->getEntryId($this->valueID);
58
    }
59 4
60 4
    /**
61 4
     * Get the query to find all books with this value
62 4
     * the returning array has two values:
63 4
     *  - first the query (string)
64
     *  - second an array of all PreparedStatement parameters
65
     *
66
     * @return array
67
     */
68 4
    public function getQuery()
69 4
    {
70 4
        return $this->customColumnType->getQuery($this->valueID);
71 4
    }
72 4
73 4
    /**
74
     * Return the value of this column as an HTML snippet
75
     *
76 3
     * @return string
77 3
     */
78 3
    public function getHTMLEncodedValue()
79 3
    {
80 3
        return $this->htmlvalue;
81
    }
82
83
    /**
84
     * Craete an CustomColumn by CustomColumnID and ValueID
85 3
     *
86 3
     * @param integer $customId the id of the customColumn
87
     * @param integer $id the id of the chosen value
88
     * @return CustomColumn|null
89
     */
90 3
    public static function createCustom($customId, $id)
91 3
    {
92 3
        $columnType = CustomColumnType::createByCustomID($customId);
93
94 3
        return $columnType->getCustom($id);
95 3
    }
96 3
}
97 3
98 3
/**
99 3
 * 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
    protected function __construct($pcustomId, $pdatatype)
132
    {
133
        $this->columnTitle = self::getTitleByCustomID($pcustomId);
134
        $this->customId = $pcustomId;
135
        $this->datatype = $pdatatype;
136
        $this->customValues = NULL;
137
    }
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
    public function getUri($id)
146
    {
147
        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
    public function getUriAllCustoms()
156
    {
157
        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
    public function getEntryId($id)
167
    {
168
        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
    public function getAllCustomsId()
177
    {
178
        return self::ALL_CUSTOMS_ID . ":" . $this->customId;
179
    }
180
181
    /**
182
     * The title of this column
183
     *
184
     * @return string
185
     */
186
    public function getTitle()
187
    {
188
        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
    public function getDatabaseDescription()
197
    {
198
        $result = $this->getDb()->prepare('SELECT display FROM custom_columns WHERE id = ?');
199
        $result->execute(array($this->customId));
200
        if ($post = $result->fetchObject()) {
201
            $json = json_decode($post->display);
202
            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
    public function getCount()
214
    {
215
        $ptitle = $this->getTitle();
216
        $pid = $this->getAllCustomsId();
217
        $pcontent = $this->getDescription();
218
        $pcontentType = $this->datatype;
219
        $plinkArray = array(new LinkNavigation($this->getUriAllCustoms()));
220
        $pclass = "";
221
        $pcount = $this->getDistinctValueCount();
222
223
        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
    protected function getDistinctValueCount()
232
    {
233
        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
    public function encodeHTMLValue($value)
243
    {
244
        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
    private static function getDatatypeByCustomID($customId)
254
    {
255
        $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
        $result->execute(array($customId));
257
        if ($post = $result->fetchObject()) {
258
            return $post->datatype;
259
        }
260
        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
    public static function createByCustomID($customId)
271
    {
272
        // Reuse already created CustomColumns for performance
273
        if (array_key_exists($customId, self::$customColumnCacheID))
274
            return self::$customColumnCacheID[$customId];
275
276
        $datatype = self::getDatatypeByCustomID($customId);
277
278
        switch ($datatype) {
279
            case self::CUSTOM_TYPE_TEXT:
280
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeText($customId);
281
            case self::CUSTOM_TYPE_SERIES:
282
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeSeries($customId);
283
            case self::CUSTOM_TYPE_ENUM:
284
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeEnumeration($customId);
285
            case self::CUSTOM_TYPE_COMMENT:
286
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeComment($customId);
287
            case self::CUSTOM_TYPE_DATE:
288
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeDate($customId);
289
            case self::CUSTOM_TYPE_FLOAT:
290
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeFloat($customId);
291
            case self::CUSTOM_TYPE_INT:
292
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeInteger($customId);
293
            case self::CUSTOM_TYPE_RATING:
294
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeRating($customId);
295
            case self::CUSTOM_TYPE_BOOL:
296
                return self::$customColumnCacheID[$customId] = new CustomColumnTypeBool($customId);
297
            case self::CUSTOM_TYPE_COMPOSITE:
298
                return NULL; //TODO Currently not supported
299
            default:
300
                throw new Exception("Unkown column type: " . $datatype);
301
        }
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
    public static function createByLookup($lookup)
311
    {
312
        // Reuse already created CustomColumns for performance
313
        if (array_key_exists($lookup, self::$customColumnCacheLookup))
314
            return self::$customColumnCacheLookup[$lookup];
315
316
        $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
        $result->execute(array($lookup));
318
        if ($post = $result->fetchObject()) {
319
            return self::$customColumnCacheLookup[$lookup] = self::createByCustomID($post->id);
320
        }
321
        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
    public function getAllCustomValues()
331
    {
332
        // lazy loading
333
        if ($this->customValues == NULL)
334
            $this->customValues = $this->getAllCustomValuesFromDatabase();
335
336
        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
    protected static function getTitleByCustomID($customId)
346
    {
347
        $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
        $result->execute(array($customId));
349
        if ($post = $result->fetchObject()) {
350
            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
    protected function __construct($pcustomId)
408
    {
409
        parent::__construct($pcustomId, self::CUSTOM_TYPE_TEXT);
410
    }
411
412
    /**
413
     * Get the name of the sqlite table for this column
414
     *
415
     * @return string|null
416
     */
417
    private function getTableName()
418
    {
419
        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
    private function getTableLinkName()
429
    {
430
        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
    private function getTableLinkColumn()
439
    {
440
        return "value";
441
    }
442
443
    public function getQuery($id)
444
    {
445
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
446
        return array($query, array($id));
447
    }
448
449
    public function getCustom($id)
450
    {
451
        $result = $this->getDb()->prepare(str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName()));
452
        $result->execute(array($id));
453
        if ($post = $result->fetchObject()) {
454
            return new CustomColumn($id, $post->name, $this);
455
        }
456
        return NULL;
457
    }
458
459
    protected function getAllCustomValuesFromDatabase()
460
    {
461
        $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
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
463
464
        $result = $this->getDb()->query($query);
465
        $entryArray = array();
466
        while ($post = $result->fetchObject())
467
        {
468
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
469
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($post->id)));
470
471
            $entry = new Entry($post->name, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
472
473
            array_push($entryArray, $entry);
474
        }
475
        return $entryArray;
476
    }
477
478
    public function getDescription()
479
    {
480
        $desc = $this->getDatabaseDescription();
481
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
482
        return $desc;
483
    }
484
485
    public function getCustomByBook($book)
486
    {
487
        $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
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
489
490
        $result = $this->getDb()->query($query);
491
        if ($post = $result->fetchObject()) {
492
            return new CustomColumn($post->id, $post->name, $this);
493
        }
494
        return new CustomColumn(NULL, "", $this);
495
    }
496
497
    public function isSearchable()
498
    {
499
        return true;
500
    }
501
}
502
503
class CustomColumnTypeSeries extends CustomColumnType
504
{
505
    protected function __construct($pcustomId)
506
    {
507
        parent::__construct($pcustomId, self::CUSTOM_TYPE_SERIES);
508
    }
509
510
    /**
511
     * Get the name of the sqlite table for this column
512
     *
513
     * @return string|null
514
     */
515
    private function getTableName()
516
    {
517
        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
    private function getTableLinkName()
527
    {
528
        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
    private function getTableLinkColumn()
537
    {
538
        return "value";
539
    }
540
541
    public function getQuery($id)
542
    {
543
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
544
        return array($query, array($id));
545
    }
546
547
    public function getCustom($id)
548
    {
549
        $result = $this->getDb()->prepare(str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName()));
550
        $result->execute(array($id));
551
        if ($post = $result->fetchObject()) {
552
            return new CustomColumn($id, $post->name, $this);
553
        }
554
        return NULL;
555
    }
556
557
    protected function getAllCustomValuesFromDatabase()
558
    {
559
        $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
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
561
562
        $result = $this->getDb()->query($query);
563
        $entryArray = array();
564
        while ($post = $result->fetchObject()) {
565
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
566
            $entryPLinkArray = array(new LinkNavigation($this->getUri($post->id)));
567
568
            $entry = new Entry($post->name, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
569
570
            array_push($entryArray, $entry);
571
        }
572
        return $entryArray;
573
    }
574
575
    public function getDescription()
576
    {
577
        return str_format(localize("customcolumn.description.series", $this->getDistinctValueCount()), $this->getDistinctValueCount());
578
    }
579
580
    public function getCustomByBook($book)
581
    {
582
        $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
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
584
585
        $result = $this->getDb()->query($query);
586
        if ($post = $result->fetchObject()) {
587
            return new CustomColumn($post->id, $post->name . " [" . $post->extra . "]", $this);
588
        }
589
        return new CustomColumn(NULL, "", $this);
590
    }
591
592
    public function isSearchable()
593
    {
594
        return true;
595
    }
596
}
597
598
class CustomColumnTypeEnumeration extends CustomColumnType
599
{
600
    protected function __construct($pcustomId)
601
    {
602
        parent::__construct($pcustomId, self::CUSTOM_TYPE_ENUM);
603
    }
604
605
    /**
606
     * Get the name of the sqlite table for this column
607
     *
608
     * @return string|null
609
     */
610
    private function getTableName()
611
    {
612
        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
    private function getTableLinkName()
622
    {
623
        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
    private function getTableLinkColumn()
632
    {
633
        return "value";
634
    }
635
636
    public function getQuery($id)
637
    {
638
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
639
        return array($query, array($id));
640
    }
641
642
    public function getCustom($id)
643
    {
644
        $result = $this->getDb()->prepare(str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName()));
645
        $result->execute(array($id));
646
        if ($post = $result->fetchObject()) {
647
            return new CustomColumn ($id, $post->name, $this);
648
        }
649
        return NULL;
650
    }
651
652
    protected function getAllCustomValuesFromDatabase()
653
    {
654
        $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
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
656
657
        $result = $this->getDb()->query($query);
658
        $entryArray = array();
659
        while ($post = $result->fetchObject()) {
660
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
661
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($post->id)));
662
663
            $entry = new Entry ($post->name, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
664
665
            array_push($entryArray, $entry);
666
        }
667
        return $entryArray;
668
    }
669
670
    public function getDescription()
671
    {
672
        return str_format(localize("customcolumn.description.enum", $this->getDistinctValueCount()), $this->getDistinctValueCount());
673
    }
674
675
    public function getCustomByBook($book)
676
    {
677
        $queryFormat = "SELECT {0}.id AS id, {1}.{2} AS name FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = {3}";
678
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
679
680
        $result = $this->getDb()->query($query);
681
        if ($post = $result->fetchObject()) {
682
            return new CustomColumn($post->id, $post->name, $this);
683
        }
684
        return new CustomColumn(NULL, localize("customcolumn.enum.unknown"), $this);
685
    }
686
687
    public function isSearchable()
688
    {
689
        return true;
690
    }
691
}
692
693
class CustomColumnTypeDate extends CustomColumnType
694
{
695
    protected function __construct($pcustomId)
696
    {
697
        parent::__construct($pcustomId, self::CUSTOM_TYPE_DATE);
698
    }
699
700
    /**
701
     * Get the name of the sqlite table for this column
702
     *
703
     * @return string|null
704
     */
705
    private function getTableName()
706
    {
707
        return "custom_column_{$this->customId}";
708
    }
709
710
    public function getQuery($id)
711
    {
712
        $date = new DateTime($id);
713
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DATE, "{0}", "{1}", $this->getTableName());
714
        return array($query, array($date->format("Y-m-d")));
715
    }
716
717
    public function getCustom($id)
718
    {
719
        $date = new DateTime($id);
720
721
        return new CustomColumn($id, $date->format(localize("customcolumn.date.format")), $this);
722
    }
723
724
    protected function getAllCustomValuesFromDatabase()
725
    {
726
        $queryFormat = "SELECT date(value) AS datevalue, count(*) AS count FROM {0} GROUP BY datevalue";
727
        $query = str_format($queryFormat, $this->getTableName());
728
        $result = $this->getDb()->query($query);
729
730
        $entryArray = array();
731
        while ($post = $result->fetchObject()) {
732
            $date = new DateTime($post->datevalue);
733
            $id = $date->format("Y-m-d");
734
735
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
736
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($id)));
737
738
            $entry = new Entry($date->format(localize("customcolumn.date.format")), $this->getEntryId($id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
739
740
            array_push($entryArray, $entry);
741
        }
742
743
        return $entryArray;
744
    }
745
746
    public function getDescription()
747
    {
748
        $desc = $this->getDatabaseDescription();
749
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
750
        return $desc;
751
    }
752
753
    public function getCustomByBook($book)
754
    {
755
        $queryFormat = "SELECT date({0}.value) AS datevalue FROM {0} WHERE {0}.book = {1}";
756
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
757
758
        $result = $this->getDb()->query($query);
759
        if ($post = $result->fetchObject()) {
760
            $date = new DateTime($post->datevalue);
761
762
            return new CustomColumn($date->format("Y-m-d"), $date->format(localize("customcolumn.date.format")), $this);
763
        }
764
        return new CustomColumn(NULL, localize("customcolumn.date.unknown"), $this);
765
    }
766
767
    public function isSearchable()
768
    {
769
        return true;
770
    }
771
}
772
773
class CustomColumnTypeRating extends CustomColumnType
774
{
775
    protected function __construct($pcustomId)
776
    {
777
        parent::__construct($pcustomId, self::CUSTOM_TYPE_RATING);
778
    }
779
780
    /**
781
     * Get the name of the sqlite table for this column
782
     *
783
     * @return string|null
784
     */
785
    private function getTableName()
786
    {
787
        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
    private function getTableLinkName()
797
    {
798
        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
    private function getTableLinkColumn()
807
    {
808
        return "value";
809
    }
810
811
    public function getQuery($id)
812
    {
813
        if ($id == 0) {
814
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_RATING_NULL, "{0}", "{1}", $this->getTableLinkName(), $this->getTableName(), $this->getTableLinkColumn());
815
            return array($query, array());
816
        } else {
817
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_RATING, "{0}", "{1}", $this->getTableLinkName(), $this->getTableName(), $this->getTableLinkColumn());
818
            return array($query, array($id));
819
        }
820
    }
821
822
    public function getCustom($id)
823
    {
824
        return new CustomColumn ($id, str_format(localize("customcolumn.stars", $id / 2), $id / 2), $this);
825
    }
826
827
    protected function getAllCustomValuesFromDatabase()
828
    {
829
        $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
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName());
831
        $result = $this->getDb()->query($query);
832
833
        $countArray = array(0 => 0, 2 => 0, 4 => 0, 6 => 0, 8 => 0, 10 => 0);
834
        while ($row = $result->fetchObject()) {
835
            $countArray[$row->value] = $row->count;
836
        }
837
838
        $entryArray = array();
839
840
        for ($i = 0; $i <= 5; $i++) {
841
            $count = $countArray[$i * 2];
842
            $name = str_format(localize("customcolumn.stars", $i), $i);
843
            $entryid = $this->getEntryId($i * 2);
844
            $content = str_format(localize("bookword", $count), $count);
845
            $linkarray = array(new LinkNavigation($this->getUri($i * 2)));
846
            $entry = new Entry($name, $entryid, $content, $this->datatype, $linkarray, "", $count);
847
            array_push($entryArray, $entry);
848
        }
849
850
        return $entryArray;
851
    }
852
853
    public function getDescription()
854
    {
855
        return localize("customcolumn.description.rating");
856
    }
857
858
    public function getCustomByBook($book)
859
    {
860
        $queryFormat = "SELECT {0}.value AS value FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = {3}";
861
        $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn(), $book->id);
862
863
        $result = $this->getDb()->query($query);
864
        if ($post = $result->fetchObject()) {
865
            return new CustomColumn($post->value, str_format(localize("customcolumn.stars", $post->value / 2), $post->value / 2), $this);
866
        }
867
        return new CustomColumn(NULL, localize("customcolumn.rating.unknown"), $this);
868
    }
869
870
    public function isSearchable()
871
    {
872
        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
    protected function __construct($pcustomId)
886
    {
887
        parent::__construct($pcustomId, self::CUSTOM_TYPE_BOOL);
888
    }
889
890
    /**
891
     * Get the name of the sqlite table for this column
892
     *
893
     * @return string|null
894
     */
895
    private function getTableName()
896
    {
897
        return "custom_column_{$this->customId}";
898
    }
899
900
    public function getQuery($id)
901
    {
902
        if ($id == -1) {
903
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_BOOL_NULL, "{0}", "{1}", $this->getTableName());
904
            return array($query, array());
905
        } else if ($id == 0) {
906
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_BOOL_FALSE, "{0}", "{1}", $this->getTableName());
907
            return array($query, array());
908
        } else if ($id == 1) {
909
            $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_BOOL_TRUE, "{0}", "{1}", $this->getTableName());
910
            return array($query, array());
911
        } else {
912
            return NULL;
913
        }
914
    }
915
916
    public function getCustom($id)
917
    {
918
        return new CustomColumn($id, localize($this->BOOLEAN_NAMES[$id]), $this);
919
    }
920
921
    protected function getAllCustomValuesFromDatabase()
922
    {
923
        $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
        $query = str_format($queryFormat, $this->getTableName());
925
        $result = $this->getDb()->query($query);
926
927
        $entryArray = array();
928
        while ($post = $result->fetchObject()) {
929
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
930
            $entryPLinkArray = array(new LinkNavigation ($this->getUri($post->id)));
931
932
            $entry = new Entry(localize($this->BOOLEAN_NAMES[$post->id]), $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
933
934
            array_push($entryArray, $entry);
935
        }
936
        return $entryArray;
937
    }
938
939
    public function getDescription()
940
    {
941
        return localize("customcolumn.description.bool");
942
    }
943
944
    public function getCustomByBook($book)
945
    {
946
        $queryFormat = "SELECT {0}.value AS boolvalue FROM {0} WHERE {0}.book = {1}";
947
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
948
949
        $result = $this->getDb()->query($query);
950
        if ($post = $result->fetchObject()) {
951
            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
    public function isSearchable()
958
    {
959
        return true;
960
    }
961
}
962
963
class CustomColumnTypeInteger extends CustomColumnType
964
{
965
    protected function __construct($pcustomId)
966
    {
967
        parent::__construct($pcustomId, self::CUSTOM_TYPE_INT);
968
    }
969
970
    /**
971
     * Get the name of the sqlite table for this column
972
     *
973
     * @return string|null
974
     */
975
    private function getTableName()
976
    {
977
        return "custom_column_{$this->customId}";
978
    }
979
980
    public function getQuery($id)
981
    {
982
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DIRECT, "{0}", "{1}", $this->getTableName());
983
        return array($query, array($id));
984
    }
985
986
    public function getCustom($id)
987
    {
988
        return new CustomColumn($id, $id, $this);
989
    }
990
991
    protected function getAllCustomValuesFromDatabase()
992
    {
993
        $queryFormat = "SELECT value AS id, count(*) AS count FROM {0} GROUP BY value";
994
        $query = str_format($queryFormat, $this->getTableName());
995
996
        $result = $this->getDb()->query($query);
997
        $entryArray = array();
998
        while ($post = $result->fetchObject()) {
999
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
1000
            $entryPLinkArray = array(new LinkNavigation($this->getUri($post->id)));
1001
1002
            $entry = new Entry($post->id, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
1003
1004
            array_push($entryArray, $entry);
1005
        }
1006
        return $entryArray;
1007
    }
1008
1009
    public function getDescription()
1010
    {
1011
        $desc = $this->getDatabaseDescription();
1012
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
1013
        return $desc;
1014
    }
1015
1016
    public function getCustomByBook($book)
1017
    {
1018
        $queryFormat = "SELECT {0}.value AS value FROM {0} WHERE {0}.book = {1}";
1019
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
1020
1021
        $result = $this->getDb()->query($query);
1022
        if ($post = $result->fetchObject()) {
1023
            return new CustomColumn($post->value, $post->value, $this);
1024
        }
1025
        return new CustomColumn(NULL, localize("customcolumn.int.unknown"), $this);
1026
    }
1027
1028
    public function isSearchable()
1029
    {
1030
        return true;
1031
    }
1032
}
1033
1034
class CustomColumnTypeFloat extends CustomColumnType
1035
{
1036
    protected function __construct($pcustomId)
1037
    {
1038
        parent::__construct($pcustomId, self::CUSTOM_TYPE_FLOAT);
1039
    }
1040
1041
    /**
1042
     * Get the name of the sqlite table for this column
1043
     *
1044
     * @return string|null
1045
     */
1046
    private function getTableName()
1047
    {
1048
        return "custom_column_{$this->customId}";
1049
    }
1050
1051
    public function getQuery($id)
1052
    {
1053
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DIRECT, "{0}", "{1}", $this->getTableName());
1054
        return array($query, array($id));
1055
    }
1056
1057
    public function getCustom($id)
1058
    {
1059
        return new CustomColumn($id, $id, $this);
1060
    }
1061
1062
    protected function getAllCustomValuesFromDatabase()
1063
    {
1064
        $queryFormat = "SELECT value AS id, count(*) AS count FROM {0} GROUP BY value";
1065
        $query = str_format($queryFormat, $this->getTableName());
1066
1067
        $result = $this->getDb()->query($query);
1068
        $entryArray = array();
1069
        while ($post = $result->fetchObject()) {
1070
            $entryPContent = str_format(localize("bookword", $post->count), $post->count);
1071
            $entryPLinkArray = array(new LinkNavigation($this->getUri($post->id)));
1072
1073
            $entry = new Entry($post->id, $this->getEntryId($post->id), $entryPContent, $this->datatype, $entryPLinkArray, "", $post->count);
1074
1075
            array_push($entryArray, $entry);
1076
        }
1077
        return $entryArray;
1078
    }
1079
1080
    public function getDescription()
1081
    {
1082
        $desc = $this->getDatabaseDescription();
1083
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
1084
        return $desc;
1085
    }
1086
1087
    public function getCustomByBook($book)
1088
    {
1089
        $queryFormat = "SELECT {0}.value AS value FROM {0} WHERE {0}.book = {1}";
1090
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
1091
1092
        $result = $this->getDb()->query($query);
1093
        if ($post = $result->fetchObject()) {
1094
            return new CustomColumn($post->value, $post->value, $this);
1095
        }
1096
        return new CustomColumn(NULL, localize("customcolumn.float.unknown"), $this);
1097
    }
1098
1099
    public function isSearchable()
1100
    {
1101
        return true;
1102
    }
1103
}
1104
1105
class CustomColumnTypeComment extends CustomColumnType
1106
{
1107
    protected function __construct($pcustomId)
1108
    {
1109
        parent::__construct($pcustomId, self::CUSTOM_TYPE_COMMENT);
1110
    }
1111
1112
    /**
1113
     * Get the name of the sqlite table for this column
1114
     *
1115
     * @return string|null
1116
     */
1117
    private function getTableName()
1118
    {
1119
        return "custom_column_{$this->customId}";
1120
    }
1121
1122
    public function getQuery($id)
1123
    {
1124
        $query = str_format(Book::SQL_BOOKS_BY_CUSTOM_DIRECT_ID, "{0}", "{1}", $this->getTableName());
1125
        return array($query, array($id));
1126
    }
1127
1128
    public function getCustom($id)
1129
    {
1130
        return new CustomColumn($id, $id, $this);
1131
    }
1132
1133
    public function encodeHTMLValue($value)
1134
    {
1135
        return "<div>" . $value . "</div>"; // no htmlspecialchars, this is already HTML
1136
    }
1137
1138
    protected function getAllCustomValuesFromDatabase()
1139
    {
1140
        return NULL;
1141
    }
1142
1143
    public function getDescription()
1144
    {
1145
        $desc = $this->getDatabaseDescription();
1146
        if ($desc === NULL || empty($desc)) $desc = str_format(localize("customcolumn.description"), $this->getTitle());
1147
        return $desc;
1148
    }
1149
1150
    public function getCustomByBook($book)
1151
    {
1152
        $queryFormat = "SELECT {0}.id AS id, {0}.value AS value FROM {0} WHERE {0}.book = {1}";
1153
        $query = str_format($queryFormat, $this->getTableName(), $book->id);
1154
1155
        $result = $this->getDb()->query($query);
1156
        if ($post = $result->fetchObject()) {
1157
            return new CustomColumn($post->id, $post->value, $this);
1158
        }
1159
        return new CustomColumn(NULL, localize("customcolumn.float.unknown"), $this);
1160
    }
1161
1162
    public function isSearchable()
1163
    {
1164
        return false;
1165
    }
1166
}
1167