1) Исправления в связи со сменой API MySQL
[openlib.git] / www / base.php
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 <sebastien@slucas.fr>
7  */
8
9 define ("VERSION", "0.6.1");
10 date_default_timezone_set($config['default_timezone']);
11  
12 function getURLParam ($name, $default = NULL) {
13     if (!empty ($_GET) && isset($_GET[$name]) && $_GET[$name] != "") {
14         return $_GET[$name];
15     }
16     return $default;
17 }
18
19 function getCurrentOption ($option) {
20     global $config;
21     if (isset($_COOKIE[$option])) {
22         return $_COOKIE[$option];
23     }
24     if ($option == "style") {
25         return "default";
26     }
27     
28     if (isset($config ["cops_" . $option])) {
29         return $config ["cops_" . $option];
30     }
31     
32     return "";
33 }
34
35 function getCurrentCss () {
36     return "styles/style-" . getCurrentOption ("style") . ".css";
37 }
38
39 function getUrlWithVersion ($url) {
40     return $url . "?v=" . VERSION;
41 }
42
43 function xml2xhtml($xml) {
44     return preg_replace_callback('#<(\w+)([^>]*)\s*/>#s', create_function('$m', '
45         $xhtml_tags = array("br", "hr", "input", "frame", "img", "area", "link", "col", "base", "basefont", "param");
46         return in_array($m[1], $xhtml_tags) ? "<$m[1]$m[2] />" : "<$m[1]$m[2]></$m[1]>";
47     '), $xml);
48 }
49
50 function display_xml_error($error)
51 {
52     $return .= str_repeat('-', $error->column) . "^\n";
53
54     switch ($error->level) {
55         case LIBXML_ERR_WARNING:
56             $return .= "Warning $error->code: ";
57             break;
58          case LIBXML_ERR_ERROR:
59             $return .= "Error $error->code: ";
60             break;
61         case LIBXML_ERR_FATAL:
62             $return .= "Fatal Error $error->code: ";
63             break;
64     }
65
66     $return .= trim($error->message) .
67                "\n  Line: $error->line" .
68                "\n  Column: $error->column";
69
70     if ($error->file) {
71         $return .= "\n  File: $error->file";
72     }
73
74     return "$return\n\n--------------------------------------------\n\n";
75 }
76
77 function are_libxml_errors_ok ()
78 {
79     $errors = libxml_get_errors();
80     
81     foreach ($errors as $error) {
82         if ($error->code == 801) return false;
83     }
84     return true;
85 }
86
87 function html2xhtml ($html) {
88     $doc = new DOMDocument();
89     libxml_use_internal_errors(true);
90     
91     $doc->loadHTML('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' . 
92                         $html  . '</body></html>'); // Load the HTML
93     $output = $doc->saveXML($doc->documentElement); // Transform to an Ansi xml stream
94     $output = xml2xhtml($output);
95     if (preg_match ('#<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></meta></head><body>(.*)</body></html>#ms', $output, $matches)) {
96         $output = $matches [1]; // Remove <html><body>
97     }
98     /* 
99     // In case of error with summary, use it to debug
100     $errors = libxml_get_errors();
101
102     foreach ($errors as $error) {
103         $output .= display_xml_error($error);
104     }
105     */
106     
107     if (!are_libxml_errors_ok ()) $output = "HTML code not valid.";
108     
109     libxml_use_internal_errors(false);
110     return $output;
111 }
112
113 /**
114  * This method is a direct copy-paste from
115  * http://tmont.com/blargh/2010/1/string-format-in-php
116  */
117 function str_format($format) {
118     $args = func_get_args();
119     $format = array_shift($args);
120     
121     preg_match_all('/(?=\{)\{(\d+)\}(?!\})/', $format, $matches, PREG_OFFSET_CAPTURE);
122     $offset = 0;
123     foreach ($matches[1] as $data) {
124         $i = $data[0];
125         $format = substr_replace($format, @$args[$i], $offset + $data[1] - 1, 2 + strlen($i));
126         $offset += strlen(@$args[$i]) - 2 - strlen($i);
127     }
128     
129     return $format;
130 }
131
132 /**
133  * This method is based on this page
134  * http://www.mind-it.info/2010/02/22/a-simple-approach-to-localization-in-php/
135  */
136 function localize($phrase, $count=-1) {
137
138     if ($count == 0)
139         $phrase .= ".none";
140     if ($count == 1)
141         $phrase .= ".one";
142     if ($count > 1)
143         $phrase .= ".many";
144
145     /* Static keyword is used to ensure the file is loaded only once */
146     static $translations = NULL;
147     /* If no instance of $translations has occured load the language file */
148     if (is_null($translations)) {
149         $lang = "en";
150         if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
151         {
152             $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
153         }
154         $lang_file_en = NULL;
155         $lang_file = 'lang/Localization_' . $lang . '.json';
156         if (!file_exists($lang_file)) {
157             $lang_file = 'lang/' . 'Localization_en.json';
158         }
159         elseif ($lang != "en") {
160             $lang_file_en = 'lang/' . 'Localization_en.json';
161         }
162         $lang_file_content = file_get_contents($lang_file);
163         /* Load the language file as a JSON object and transform it into an associative array */
164         $translations = json_decode($lang_file_content, true);
165         if ($lang_file_en)
166         {
167             $lang_file_content = file_get_contents($lang_file_en);
168             $translations_en = json_decode($lang_file_content, true);
169             $translations = array_merge ($translations_en, $translations);
170         }
171     }
172     if (array_key_exists ($phrase, $translations)) {
173         return $translations[$phrase];
174     }
175     return $phrase;
176 }
177
178 function addURLParameter($urlParams, $paramName, $paramValue) {
179     $start = "";
180     if (preg_match ("#^\?(.*)#", $urlParams, $matches)) {
181         $start = "?";
182         $urlParams = $matches[1];
183     }
184     $params = array();
185     parse_str($urlParams, $params);
186     if (empty ($paramValue) && $paramValue != 0) {
187         unset ($params[$paramName]);
188     } else {
189         $params[$paramName] = $paramValue;   
190     }
191     return $start . http_build_query($params);
192 }
193
194 class Link
195 {
196     const OPDS_THUMBNAIL_TYPE = "http://opds-spec.org/image/thumbnail";
197     const OPDS_IMAGE_TYPE = "http://opds-spec.org/image";
198     const OPDS_ACQUISITION_TYPE = "http://opds-spec.org/acquisition/open-access";
199     const OPDS_NAVIGATION_TYPE = "application/atom+xml;profile=opds-catalog;kind=navigation";
200     const OPDS_PAGING_TYPE = "application/atom+xml;profile=opds-catalog;kind=acquisition";
201     
202     public $href;
203     public $type;
204     public $rel;
205     public $title;
206     public $facetGroup;
207     public $activeFacet;
208     
209     public function __construct($phref, $ptype, $prel = NULL, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
210         $this->href = $phref;
211         $this->type = $ptype;
212         $this->rel = $prel;
213         $this->title = $ptitle;
214         $this->facetGroup = $pfacetGroup;
215         $this->activeFacet = $pactiveFacet;
216     }
217     
218     public function hrefXhtml () {
219         return $this->href;
220     }
221 }
222
223 class LinkNavigation extends Link
224 {
225     public function __construct($phref, $prel = NULL, $ptitle = NULL) {
226         parent::__construct ($phref, Link::OPDS_NAVIGATION_TYPE, $prel, $ptitle);
227         if (!preg_match ("#^\?(.*)#", $this->href) && !empty ($this->href)) $this->href = "?" . $this->href;
228         if (preg_match ("/(bookdetail|getJSON).php/", $_SERVER["SCRIPT_NAME"])) {
229             $this->href = "index.php" . $this->href;
230         } else {
231             $this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
232         }
233     }
234 }
235
236 class LinkFacet extends Link
237 {
238     public function __construct($phref, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
239         parent::__construct ($phref, Link::OPDS_PAGING_TYPE, "http://opds-spec.org/facet", $ptitle, $pfacetGroup, $pactiveFacet);
240         $this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
241     }
242 }
243
244 class Entry
245 {
246     public $title;
247     public $id;
248     public $content;
249     public $contentType;
250     public $linkArray;
251     public $localUpdated;
252     private static $updated = NULL;
253     
254     public static $icons = array(
255         Author::ALL_AUTHORS_ID       => 'images/author.png',
256         Serie::ALL_SERIES_ID         => 'images/serie.png',
257         Book::ALL_RECENT_BOOKS_ID    => 'images/recent.png',
258         Tag::ALL_TAGS_ID             => 'images/tag.png',
259         Language::ALL_LANGUAGES_ID   => 'images/language.png',
260         "calibre:books$"             => 'images/allbook.png',
261         "calibre:books:letter"       => 'images/allbook.png'
262     );
263     
264     public function getUpdatedTime () {
265         if (!is_null ($this->localUpdated)) {
266             return date (DATE_ATOM, $this->localUpdated);
267         }
268         if (is_null (self::$updated)) {
269             self::$updated = time();
270         }
271         return date (DATE_ATOM, self::$updated);
272     }
273     
274     public function getContentArray () {
275         $navlink = "#";
276         foreach ($this->linkArray as $link) { 
277             if ($link->type != Link::OPDS_NAVIGATION_TYPE) { continue; }
278             
279             $navlink = $link->hrefXhtml ();
280         }
281         return array ( "title" => $this->title, "content" => $this->content, "navlink" => $navlink );
282     }
283  
284     public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray) {
285         global $config;
286         $this->title = $ptitle;
287         $this->id = $pid;
288         $this->content = $pcontent;
289         $this->contentType = $pcontentType;
290         $this->linkArray = $plinkArray;
291         
292         if ($config['cops_show_icons'] == 1)
293         {
294             foreach (self::$icons as $reg => $image)
295             {
296                 if (preg_match ("/" . $reg . "/", $pid)) {
297                     array_push ($this->linkArray, new Link (getUrlWithVersion ($image), "image/png", Link::OPDS_THUMBNAIL_TYPE));
298                     break;
299                 }
300             }
301         }
302         
303     }
304 }
305
306 class EntryBook extends Entry
307 {
308     public $book;
309     
310     public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $pbook) {
311         parent::__construct ($ptitle, $pid, $pcontent, $pcontentType, $plinkArray);
312         $this->book = $pbook;
313         $this->localUpdated = $pbook->timestamp;
314     }
315     
316     public function getContentArray () {
317         $entry = array ( "title" => $this->title);
318         $entry ["book"] = $this->book->getContentArray ();
319         return $entry;
320     }
321     
322     public function getCoverThumbnail () {
323         foreach ($this->linkArray as $link) {
324             if ($link->rel == Link::OPDS_THUMBNAIL_TYPE)
325                 return $link->hrefXhtml ();
326         }
327         return null;
328     }
329     
330     public function getCover () {
331         foreach ($this->linkArray as $link) {
332             if ($link->rel == Link::OPDS_IMAGE_TYPE)
333                 return $link->hrefXhtml ();
334         }
335         return null;
336     }
337 }
338
339 class Page
340 {
341     public $title;
342     public $subtitle = "";
343     public $idPage;
344     public $idGet;
345     public $query;
346     public $favicon;
347     public $n;
348     public $book;
349     public $totalNumber = -1;
350     public $entryArray = array();
351     
352     public static function getPage ($pageId, $id, $query, $n)
353     {
354         switch ($pageId) {
355             case Base::PAGE_AUTHORS_FIRST_LETTER :
356                 return new PageAllAuthorsLetter ($id, $query, $n);
357             case Base::PAGE_AUTHORS_STARTING_LETTERS :
358                 return new PageAuthorsLetter ($id, $query, $n);
359             case Base::PAGE_AUTHOR_DETAIL :
360                 return new PageAuthorDetail ($id, $query, $n);
361             case Base::PAGE_ALL_TAGS :
362                 return new PageAllTags ($id, $query, $n);
363             case Base::PAGE_TAG_DETAIL :
364                 return new PageTagDetail ($id, $query, $n);
365             case Base::PAGE_ALL_LANGUAGES :
366                 return new PageAllLanguages ($id, $query, $n);
367             case Base::PAGE_LANGUAGE_DETAIL :
368                 return new PageLanguageDetail ($id, $query, $n);             
369             case Base::PAGE_ALL_SERIES :
370                 return new PageAllSeries ($id, $query, $n);
371             case Base::PAGE_SERIES_STARTING_LETTERS :
372                 return new PageAllSeriesStartingLetters ($id, $query, $n);
373             case Base::PAGE_ALL_BOOKS :
374                 return new PageAllBooks ($id, $query, $n);
375             case Base::PAGE_ALL_BOOKS_LETTER:
376                 return new PageAllBooksLetter ($id, $query, $n);
377             case Base::PAGE_ALL_RECENT_BOOKS :
378                 return new PageRecentBooks ($id, $query, $n);
379             case Base::PAGE_SERIE_DETAIL : 
380                 return new PageSerieDetail ($id, $query, $n);
381             case Base::PAGE_OPENSEARCH_QUERY :
382                 return new PageQueryResult ($id, $query, $n);
383             case Base::PAGE_BOOK_DETAIL :
384                 return new PageBookDetail ($id, $query, $n);
385             case Base::PAGE_ABOUT :
386                 return new PageAbout ($id, $query, $n);
387             case Base::PAGE_CUSTOMIZE : 
388                 return new PageCustomize ($id, $query, $n);
389             default:
390                 $page = new Page ($id, $query, $n);
391                 $page->idPage = "cops:catalog";
392                 return $page;
393         }
394     }
395     
396     public function __construct($pid, $pquery, $pn) {
397         global $config;
398         
399         $this->idGet = $pid;
400         $this->query = $pquery;
401         $this->n = $pn;
402         $this->favicon = $config['cops_icon'];
403     }
404     
405     public function InitializeContent () 
406     {
407         global $config;
408         $this->title = $config['cops_title_default'];
409         $this->subtitle = $config['cops_subtitle_default'];
410         array_push ($this->entryArray, Author::getCount());
411         $series = Serie::getCount();
412         if (!is_null ($series)) array_push ($this->entryArray, $series);
413         $tags = Tag::getCount();
414         if (!is_null ($tags)) array_push ($this->entryArray, $tags);
415         $languages = Language::getCount();
416         if (!is_null ($languages)) array_push ($this->entryArray, $languages);
417         $this->entryArray = array_merge ($this->entryArray, Book::getCount());
418     }
419
420     public function isPaginated ()
421     {
422         global $config;
423         return (getCurrentOption ("max_item_per_page") != -1 && 
424                 $this->totalNumber != -1 && 
425                 $this->totalNumber > getCurrentOption ("max_item_per_page"));
426     }
427     
428     public function getNextLink ()
429     {
430         global $config;
431         $currentUrl = $_SERVER['QUERY_STRING'];
432         $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
433         if (($this->n) * getCurrentOption ("max_item_per_page") < $this->totalNumber) {
434             return new LinkNavigation ($currentUrl . "&n=" . ($this->n + 1), "next", "Page suivante");
435         }
436         return NULL;
437     }
438     
439     public function getPrevLink ()
440     {
441         global $config;
442         $currentUrl = $_SERVER['QUERY_STRING'];
443         $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
444         if ($this->n > 1) {
445             return new LinkNavigation ($currentUrl . "&n=" . ($this->n - 1), "previous", "Page precedente");
446         }
447         return NULL;
448     }
449     
450     public function getMaxPage ()
451     {
452         global $config;
453         return ceil ($this->totalNumber / getCurrentOption ("max_item_per_page"));
454     }
455     
456     public function containsBook ()
457     {
458         if (count ($this->entryArray) == 0) return false;
459         if (get_class ($this->entryArray [0]) == "EntryBook") return true;
460         return false;
461     }
462
463 }
464
465 class PageAllAuthorsLetter extends Page
466 {
467     public function InitializeContent () 
468     {
469         global $config;
470         
471         $this->idPage = Author::getEntryIdByLetter ($this->idGet);
472         $this->entryArray = Author::getAuthorsByFirstLetter ($this->idGet);
473         if ($this->idGet) {
474           $this->title = localize("authors.starting").' '.$this->idGet;
475         } else {
476           $this->title = localize("authors.title");
477         }
478     }
479 }
480
481 class PageAuthorsLetter extends Page
482 {
483     public function InitializeContent () 
484     {
485         global $config;
486         
487         $this->idPage = Author::getEntryIdByLetter ($this->idGet);
488         $this->entryArray = Author::getAuthorsByStartingLetter ($this->idGet);
489         $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("authorword", count ($this->entryArray)), count ($this->entryArray)), $this->idGet);
490     }
491 }
492
493 class PageAuthorDetail extends Page
494 {
495     public function InitializeContent () 
496     {
497         $author = Author::getAuthorById ($this->idGet);
498         $this->idPage = $author->getEntryId ();
499         $this->title = $author->name;
500         list ($this->entryArray, $this->totalNumber) = Book::getBooksByAuthor ($this->idGet, $this->n);
501     }
502 }
503
504 class PageAllTags extends Page
505 {
506     public function InitializeContent () 
507     {
508         $this->title = localize("tags.title");
509         $array = Tag::getAllTags();
510         asort($array);
511         $this->entryArray = $array;
512         $this->idPage = Tag::ALL_TAGS_ID;
513     }
514 }
515
516 class PageAllLanguages extends Page
517 {
518     public function InitializeContent () 
519     {
520         $this->title = localize("languages.title");
521         $array = Language::getAllLanguages();
522         asort($array);
523         $this->entryArray = $array;
524         $this->idPage = Language::ALL_LANGUAGES_ID;
525     }
526 }
527
528 class PageTagDetail extends Page
529 {
530     public function InitializeContent () 
531     {
532         $tag = Tag::getTagById ($this->idGet);
533         $this->idPage = $tag->getEntryId ();
534         $this->title = $tag->name;
535         list ($this->entryArray, $this->totalNumber) = Book::getBooksByTag ($this->idGet, $this->n);
536     }
537 }
538
539 class PageLanguageDetail extends Page
540 {
541     public function InitializeContent () 
542     {
543         $language = Language::getLanguageById ($this->idGet);
544         $this->idPage = $language->getEntryId ();
545         $this->title = $language->lang_code;
546         list ($this->entryArray, $this->totalNumber) = Book::getBooksByLanguage ($this->idGet, $this->n);
547     }
548 }
549
550 class PageAllSeries extends Page
551 {
552     public function InitializeContent () 
553     {
554         if ($this->idGet) {
555           $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("series.title")), $this->idGet);
556         } else {
557           $this->title = localize("series.title");
558         }  
559         $this->entryArray = Serie::getAllSeries($this->idGet);
560         $this->idPage = Serie::getEntryIdByLetters($this->idGet);
561     }
562 }
563
564 class PageAllSeriesStartingLetters extends Page
565 {
566     public function InitializeContent () 
567     {
568         $this->entryArray = Serie::getAllSeriesStartingLetters($this->idGet);
569         $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("series.title")), $this->idGet);
570         $this->idPage = Serie::getEntryIdByLetters($this->idGet);
571     }
572 }
573
574 class PageSerieDetail extends Page
575 {
576     public function InitializeContent () 
577     {
578         $serie = Serie::getSerieById ($this->idGet);
579         $this->title = $serie->name;
580         list ($this->entryArray, $this->totalNumber) = Book::getBooksBySeries ($this->idGet, $this->n);
581         $this->idPage = $serie->getEntryId ();
582     }
583 }
584
585 class PageAllBooks extends Page
586 {
587     public function InitializeContent () 
588     {
589         $this->entryArray = Book::getAllBooks ($this->idGet);
590         if ($this->entryArray->count==1) {
591           $this->title = $this->idGet;
592         } elseif ($this->idGet) {
593           $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("bookword.title")), $this->idGet);
594         } else {
595           $this->title = localize ("allbooks.title");
596         }  
597         $this->idPage = Book::ALL_BOOKS_ID;
598     }
599 }
600
601 class PageAllBooksLetter extends Page
602 {
603     public function InitializeContent () 
604     {
605         list ($this->entryArray, $this->totalNumber) = Book::getBooksByStartingLetter ($this->idGet, $this->n);
606         $this->idPage = Book::getEntryIdByLetter ($this->idGet);
607         
608         $count = $this->totalNumber;
609         if ($count == -1)
610             $count = count ($this->entryArray);
611         
612         if ($count==1) {
613           $this->title = $this->idGet;
614         } else {
615           $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("bookword", $count), $count), $this->idGet);
616         }  
617     }
618 }
619
620 class PageRecentBooks extends Page
621 {
622     public function InitializeContent () 
623     {
624         $this->title = localize ("recent.title");
625         $this->entryArray = Book::getAllRecentBooks ();
626         $this->idPage = Book::ALL_RECENT_BOOKS_ID;
627     }
628 }
629
630 class PageQueryResult extends Page
631 {
632     public function InitializeContent () 
633     {
634         global $config;
635         $this->title = str_format (localize ("search.result"), $this->query);
636         $currentPage = getURLParam ("current", NULL);
637         
638         switch ($currentPage) {
639             case Base::PAGE_AUTHORS_FIRST_LETTER :
640                 $this->entryArray = Author::getAuthorsByStartingLetter ('%' . $this->query);
641                 break;
642             default:
643                 list ($this->entryArray, $this->totalNumber) = Book::getBooksByQuery ($this->query, $this->n);
644         }
645     }
646 }
647
648 class PageBookDetail extends Page
649 {
650     public function InitializeContent () 
651     {
652         $this->book = Book::getBookById ($this->idGet);
653         $this->title = $this->book->title;
654     }
655 }
656
657 class PageAbout extends Page
658 {
659     public function InitializeContent () 
660     {
661         $this->title = localize ("about.title");
662     }
663 }
664
665 class PageCustomize extends Page
666 {
667     public function InitializeContent () 
668     {
669         global $config;
670         $this->title = localize ("customize.title");
671         $this->entryArray = array ();
672         
673         $use_fancybox = "";
674         if (getCurrentOption ("use_fancyapps") == 1) {
675             $use_fancybox = "checked='checked'";
676         }
677         $html_tag_filter = "";
678         if (getCurrentOption ("html_tag_filter") == 1) {
679             $html_tag_filter = "checked='checked'";
680         }
681         
682         
683         $content = "";
684         if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
685             $content .= '<select id="style" onchange="updateCookie (this);">';
686         }
687             foreach (glob ("styles/style-*.css") as $filename) {
688                 if (preg_match ('/styles\/style-(.*?)\.css/', $filename, $m)) {
689                     $filename = $m [1];
690                 }
691                 $selected = "";
692                 if (getCurrentOption ("style") == $filename) {
693                     if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
694                         $selected = "selected='selected'";
695                     } else {
696                         $selected = "checked='checked'";
697                     }
698                 }
699                 if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
700                     $content .= "<option value='{$filename}' {$selected}>{$filename}</option>";
701                 } else {
702                     $content .= "<input type='radio' onchange='updateCookieFromCheckbox (this);' id='style-{$filename}' name='style' value='{$filename}' {$selected} /><label for='style-{$filename}'> {$filename} </label>";
703                 }
704             }
705         if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
706             $content .= '</select>';
707         }
708         array_push ($this->entryArray, new Entry (localize ("customize.style"), "", 
709                                         $content, "text", 
710                                         array ()));
711         
712         $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="use_fancyapps" ' . $use_fancybox . ' />';
713         array_push ($this->entryArray, new Entry (localize ("customize.fancybox"), "", 
714                                         $content, "text", 
715                                         array ()));
716         $content = '<input type="number" onchange="updateCookie (this);" id="max_item_per_page" value="' . getCurrentOption ("max_item_per_page") . '" min="-1" max="1200" pattern="^[-+]?[0-9]+$" />';
717         array_push ($this->entryArray, new Entry (localize ("customize.paging"), "", 
718                                         $content, "text", 
719                                         array ()));
720         $content = '<input type="number" onchange="updateCookie (this);" id="max_books_pages" value="' . getCurrentOption ("max_books_pages") . '" min="-1" max="50" pattern="^[-+]?[0-9]+$" />';
721         array_push ($this->entryArray, new Entry (localize ("customize.pages"), "", 
722                                         $content, "text", 
723                                         array ()));
724         $content = '<input type="text" onchange="updateCookie (this);" id="email" value="' . getCurrentOption ("email") . '" />';
725         array_push ($this->entryArray, new Entry (localize ("customize.email"), "", 
726                                         $content, "text", 
727                                         array ()));
728         $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="html_tag_filter" ' . $html_tag_filter . ' />';
729         array_push ($this->entryArray, new Entry (localize ("customize.filter"), "", 
730                                         $content, "text", 
731                                         array ()));
732     }
733 }
734
735
736 abstract class Base
737 {
738     const PAGE_INDEX = "index";
739     const PAGE_AUTHORS_FIRST_LETTER = "AUTH";
740     const PAGE_AUTHORS_STARTING_LETTERS = "AUTHST";
741     const PAGE_AUTHOR_DETAIL = "AUTHDET";
742     const PAGE_ALL_BOOKS = "BOOK";
743     const PAGE_ALL_BOOKS_LETTER = "BOOKST";
744     const PAGE_ALL_SERIES = "SER";
745     const PAGE_SERIES_STARTING_LETTERS = "SERST";
746     const PAGE_SERIE_DETAIL = "SERDET";
747     const PAGE_OPENSEARCH = "OPEN";
748     const PAGE_OPENSEARCH_QUERY = "OPENQ";
749     const PAGE_ALL_RECENT_BOOKS = "RECENT";
750     const PAGE_ALL_TAGS = "GENR";
751     const PAGE_TAG_DETAIL = "GENRDET";
752     const PAGE_BOOK_DETAIL = "BOOKDET";
753     const PAGE_ABOUT = "ABOUT";
754     const PAGE_ALL_LANGUAGES = "LANG";
755     const PAGE_LANGUAGE_DETAIL = "LANGDET";   
756     const PAGE_CUSTOMIZE = "OPTS"; 
757
758     const COMPATIBILITY_XML_ALDIKO = "aldiko";
759     
760     private static $db = NULL;
761
762     public static function getFileDirectory () {
763         global $config;
764         return $config['file_directory'];
765     }
766
767     public static function getDbHost () {
768         global $config;
769         return $config['db_host'];
770     }
771
772     public static function getDbUser () {
773         global $config;
774         return $config['db_user'];
775     }
776
777     public static function getDbPasswd () {
778         global $config;
779         return $config['db_passwd'];
780     }
781
782     public static function getDb () {
783         global $config;
784         if (is_null (self::$db)) {
785             try {
786                 self::$db = new PDO('mysql:host='. self::getDbHost (), self::getDbUser(), self::getDbPasswd());
787                 self::$db -> exec("set names utf8");
788             } catch (Exception $e) {
789                 header("location: checkconfig.php?err=1");
790                 exit();
791             }
792         }
793         return self::$db;
794     }
795     
796     public static function clearDb () {
797         self::$db = NULL;
798     }
799     
800     public static function executeQuery($query, $columns, $filter, $params, $n) {
801         global $config;
802         $totalResult = -1;
803         
804         if (getCurrentOption ("max_item_per_page") != -1 && $n != -1)
805         {
806             // First check total number of results
807             $query_str = str_format ($query, "count(*)", $filter);
808             $result = self::getDb ()->prepare ($query_str);
809             $result->execute ($params);
810             $totalResult = $result->fetchColumn (0);
811             
812             // Next modify the query and params
813             $query .= " limit ".(($n - 1) * getCurrentOption ("max_item_per_page")).", ".getCurrentOption ("max_item_per_page");
814         }
815
816         $query_str = str_format($query, $columns, $filter);
817         $result = self::getDb ()->prepare($query_str);
818         $result->execute ($params);
819         return array ($totalResult, $result);
820     }
821
822     public static function booksPages ()
823     {
824         global $config;
825         $result = getCurrentOption("max_books_pages");
826         if ($result=="") { $result = 10; }
827         return $result;
828     }
829     
830     public static function maxItemsPerPage()
831     {
832         global $config;
833         $result = getCurrentOption("max_item_per_page");
834         if ($result=="") { $result = 25; }
835         return $result;
836     }
837
838
839 }