Calibre OPDS (and HTML) PHP Server : web-based light alternative to Calibre content server / Calibre2OPDS to serve ebooks (epub, mobi, pdf, ...) http://blog.slucas.fr/en/oss/calibre-opds-php-server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

base.php 41KB

12 years ago
12 years ago
10 years ago
10 years ago
12 years ago
12 years ago
12 years ago
10 years ago
10 years ago
11 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
**2012-11-22** **Added global support for publishers** Files modified: *base.php* - changed class Entry, - adding a constant ```cops:publishers``` to the icon array for the feed. - changed class Page - added branches to the page selector switch - changed Page->public function InitializeContent - added call to pull publisher count from database - changed class PageAllBooks - changed it so ```getCurrentOption``` is actually used... - added page descendant class ```PageAllPublishers``` - handles pulling the publishers category from database - added page descendant class ```PagePublisherDetail``` - handles pulling the books per publisher data from database - changed class PageQueryResult - added constant and switches for publisher search scope - abstract class Base - added constants for the publisher pages *book.php* - added require statement for publisher.php - added ```SQL_BOOKS_BY_PUBLISHER``` query to retrieve books by publisher. - changed class Book - added query constant - added publisher item - added test in case no known publisher - added publishername and url array elements for the JSON output - added public function ```getPublisher``` - added public static function ```getBooksByPublisher``` to fire the query - changed function getJson - added publisher category to search - added publishername (single) and publishertitle(plural) localization entries to i18n translation array *index.php* - added require statement for publisher.php *lang/Localization_en.json - added new localization entries for publisher labels (see below) ``` "publisher.alphabetical.many":"Alphabetical index of the {0} publishers", "publisher.alphabetical.none":"Alphabetical index of absolutely no publisher", "publisher.alphabetical.one":"Alphabetical index of the single publisher", "publisher.name":"Publisher", "publisher.title":"Publishers", "publisherword.many":"{0} publishers", "publisherword.none":"No publisher", "publisherword.one":"1 publisher", "search.result.publisher":"Search result for *{0}* in publishers", ``` *templates\bookdetail.html* - added publisher label and item to bookdetail popup *test\bookTest.php* - added indices and names of publishers added to testdatabase as comment - added test function ```testGetBooksByPublisher``` - changed test function testGetBookById to add assertion for publisher name - changed test function testTypeaheadSearch to add search on partial publisher name. *test\pageTest.php* - changed test function testPageIndex to insert publisher category and adjust page indices - changed test function testPageIndexWithCustomColum to adjust for the changed page indices - added test function testPageAllPublishers - added test function testPagePublishersDetail - added test function testPageSearchScopePublishers *test\BaseWithSomeBooks\metadata.db* - added 5 publishers spread across all 14 books, replacing the original publisher Feedbooks Files added: *publisher.php*
10 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
**2012-11-22** **Added global support for publishers** Files modified: *base.php* - changed class Entry, - adding a constant ```cops:publishers``` to the icon array for the feed. - changed class Page - added branches to the page selector switch - changed Page->public function InitializeContent - added call to pull publisher count from database - changed class PageAllBooks - changed it so ```getCurrentOption``` is actually used... - added page descendant class ```PageAllPublishers``` - handles pulling the publishers category from database - added page descendant class ```PagePublisherDetail``` - handles pulling the books per publisher data from database - changed class PageQueryResult - added constant and switches for publisher search scope - abstract class Base - added constants for the publisher pages *book.php* - added require statement for publisher.php - added ```SQL_BOOKS_BY_PUBLISHER``` query to retrieve books by publisher. - changed class Book - added query constant - added publisher item - added test in case no known publisher - added publishername and url array elements for the JSON output - added public function ```getPublisher``` - added public static function ```getBooksByPublisher``` to fire the query - changed function getJson - added publisher category to search - added publishername (single) and publishertitle(plural) localization entries to i18n translation array *index.php* - added require statement for publisher.php *lang/Localization_en.json - added new localization entries for publisher labels (see below) ``` "publisher.alphabetical.many":"Alphabetical index of the {0} publishers", "publisher.alphabetical.none":"Alphabetical index of absolutely no publisher", "publisher.alphabetical.one":"Alphabetical index of the single publisher", "publisher.name":"Publisher", "publisher.title":"Publishers", "publisherword.many":"{0} publishers", "publisherword.none":"No publisher", "publisherword.one":"1 publisher", "search.result.publisher":"Search result for *{0}* in publishers", ``` *templates\bookdetail.html* - added publisher label and item to bookdetail popup *test\bookTest.php* - added indices and names of publishers added to testdatabase as comment - added test function ```testGetBooksByPublisher``` - changed test function testGetBookById to add assertion for publisher name - changed test function testTypeaheadSearch to add search on partial publisher name. *test\pageTest.php* - changed test function testPageIndex to insert publisher category and adjust page indices - changed test function testPageIndexWithCustomColum to adjust for the changed page indices - added test function testPageAllPublishers - added test function testPagePublishersDetail - added test function testPageSearchScopePublishers *test\BaseWithSomeBooks\metadata.db* - added 5 publishers spread across all 14 books, replacing the original publisher Feedbooks Files added: *publisher.php*
10 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
11 years ago
11 years ago
12 years ago
12 years ago
**2012-11-22** **Added global support for publishers** Files modified: *base.php* - changed class Entry, - adding a constant ```cops:publishers``` to the icon array for the feed. - changed class Page - added branches to the page selector switch - changed Page->public function InitializeContent - added call to pull publisher count from database - changed class PageAllBooks - changed it so ```getCurrentOption``` is actually used... - added page descendant class ```PageAllPublishers``` - handles pulling the publishers category from database - added page descendant class ```PagePublisherDetail``` - handles pulling the books per publisher data from database - changed class PageQueryResult - added constant and switches for publisher search scope - abstract class Base - added constants for the publisher pages *book.php* - added require statement for publisher.php - added ```SQL_BOOKS_BY_PUBLISHER``` query to retrieve books by publisher. - changed class Book - added query constant - added publisher item - added test in case no known publisher - added publishername and url array elements for the JSON output - added public function ```getPublisher``` - added public static function ```getBooksByPublisher``` to fire the query - changed function getJson - added publisher category to search - added publishername (single) and publishertitle(plural) localization entries to i18n translation array *index.php* - added require statement for publisher.php *lang/Localization_en.json - added new localization entries for publisher labels (see below) ``` "publisher.alphabetical.many":"Alphabetical index of the {0} publishers", "publisher.alphabetical.none":"Alphabetical index of absolutely no publisher", "publisher.alphabetical.one":"Alphabetical index of the single publisher", "publisher.name":"Publisher", "publisher.title":"Publishers", "publisherword.many":"{0} publishers", "publisherword.none":"No publisher", "publisherword.one":"1 publisher", "search.result.publisher":"Search result for *{0}* in publishers", ``` *templates\bookdetail.html* - added publisher label and item to bookdetail popup *test\bookTest.php* - added indices and names of publishers added to testdatabase as comment - added test function ```testGetBooksByPublisher``` - changed test function testGetBookById to add assertion for publisher name - changed test function testTypeaheadSearch to add search on partial publisher name. *test\pageTest.php* - changed test function testPageIndex to insert publisher category and adjust page indices - changed test function testPageIndexWithCustomColum to adjust for the changed page indices - added test function testPageAllPublishers - added test function testPagePublishersDetail - added test function testPageSearchScopePublishers *test\BaseWithSomeBooks\metadata.db* - added 5 publishers spread across all 14 books, replacing the original publisher Feedbooks Files added: *publisher.php*
10 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
**2012-11-22** **Added global support for publishers** Files modified: *base.php* - changed class Entry, - adding a constant ```cops:publishers``` to the icon array for the feed. - changed class Page - added branches to the page selector switch - changed Page->public function InitializeContent - added call to pull publisher count from database - changed class PageAllBooks - changed it so ```getCurrentOption``` is actually used... - added page descendant class ```PageAllPublishers``` - handles pulling the publishers category from database - added page descendant class ```PagePublisherDetail``` - handles pulling the books per publisher data from database - changed class PageQueryResult - added constant and switches for publisher search scope - abstract class Base - added constants for the publisher pages *book.php* - added require statement for publisher.php - added ```SQL_BOOKS_BY_PUBLISHER``` query to retrieve books by publisher. - changed class Book - added query constant - added publisher item - added test in case no known publisher - added publishername and url array elements for the JSON output - added public function ```getPublisher``` - added public static function ```getBooksByPublisher``` to fire the query - changed function getJson - added publisher category to search - added publishername (single) and publishertitle(plural) localization entries to i18n translation array *index.php* - added require statement for publisher.php *lang/Localization_en.json - added new localization entries for publisher labels (see below) ``` "publisher.alphabetical.many":"Alphabetical index of the {0} publishers", "publisher.alphabetical.none":"Alphabetical index of absolutely no publisher", "publisher.alphabetical.one":"Alphabetical index of the single publisher", "publisher.name":"Publisher", "publisher.title":"Publishers", "publisherword.many":"{0} publishers", "publisherword.none":"No publisher", "publisherword.one":"1 publisher", "search.result.publisher":"Search result for *{0}* in publishers", ``` *templates\bookdetail.html* - added publisher label and item to bookdetail popup *test\bookTest.php* - added indices and names of publishers added to testdatabase as comment - added test function ```testGetBooksByPublisher``` - changed test function testGetBookById to add assertion for publisher name - changed test function testTypeaheadSearch to add search on partial publisher name. *test\pageTest.php* - changed test function testPageIndex to insert publisher category and adjust page indices - changed test function testPageIndexWithCustomColum to adjust for the changed page indices - added test function testPageAllPublishers - added test function testPagePublishersDetail - added test function testPageSearchScopePublishers *test\BaseWithSomeBooks\metadata.db* - added 5 publishers spread across all 14 books, replacing the original publisher Feedbooks Files added: *publisher.php*
10 years ago
**2012-11-22** **Added global support for publishers** Files modified: *base.php* - changed class Entry, - adding a constant ```cops:publishers``` to the icon array for the feed. - changed class Page - added branches to the page selector switch - changed Page->public function InitializeContent - added call to pull publisher count from database - changed class PageAllBooks - changed it so ```getCurrentOption``` is actually used... - added page descendant class ```PageAllPublishers``` - handles pulling the publishers category from database - added page descendant class ```PagePublisherDetail``` - handles pulling the books per publisher data from database - changed class PageQueryResult - added constant and switches for publisher search scope - abstract class Base - added constants for the publisher pages *book.php* - added require statement for publisher.php - added ```SQL_BOOKS_BY_PUBLISHER``` query to retrieve books by publisher. - changed class Book - added query constant - added publisher item - added test in case no known publisher - added publishername and url array elements for the JSON output - added public function ```getPublisher``` - added public static function ```getBooksByPublisher``` to fire the query - changed function getJson - added publisher category to search - added publishername (single) and publishertitle(plural) localization entries to i18n translation array *index.php* - added require statement for publisher.php *lang/Localization_en.json - added new localization entries for publisher labels (see below) ``` "publisher.alphabetical.many":"Alphabetical index of the {0} publishers", "publisher.alphabetical.none":"Alphabetical index of absolutely no publisher", "publisher.alphabetical.one":"Alphabetical index of the single publisher", "publisher.name":"Publisher", "publisher.title":"Publishers", "publisherword.many":"{0} publishers", "publisherword.none":"No publisher", "publisherword.one":"1 publisher", "search.result.publisher":"Search result for *{0}* in publishers", ``` *templates\bookdetail.html* - added publisher label and item to bookdetail popup *test\bookTest.php* - added indices and names of publishers added to testdatabase as comment - added test function ```testGetBooksByPublisher``` - changed test function testGetBookById to add assertion for publisher name - changed test function testTypeaheadSearch to add search on partial publisher name. *test\pageTest.php* - changed test function testPageIndex to insert publisher category and adjust page indices - changed test function testPageIndexWithCustomColum to adjust for the changed page indices - added test function testPageAllPublishers - added test function testPagePublishersDetail - added test function testPageSearchScopePublishers *test\BaseWithSomeBooks\metadata.db* - added 5 publishers spread across all 14 books, replacing the original publisher Feedbooks Files added: *publisher.php*
10 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
**2012-11-22** **Added global support for publishers** Files modified: *base.php* - changed class Entry, - adding a constant ```cops:publishers``` to the icon array for the feed. - changed class Page - added branches to the page selector switch - changed Page->public function InitializeContent - added call to pull publisher count from database - changed class PageAllBooks - changed it so ```getCurrentOption``` is actually used... - added page descendant class ```PageAllPublishers``` - handles pulling the publishers category from database - added page descendant class ```PagePublisherDetail``` - handles pulling the books per publisher data from database - changed class PageQueryResult - added constant and switches for publisher search scope - abstract class Base - added constants for the publisher pages *book.php* - added require statement for publisher.php - added ```SQL_BOOKS_BY_PUBLISHER``` query to retrieve books by publisher. - changed class Book - added query constant - added publisher item - added test in case no known publisher - added publishername and url array elements for the JSON output - added public function ```getPublisher``` - added public static function ```getBooksByPublisher``` to fire the query - changed function getJson - added publisher category to search - added publishername (single) and publishertitle(plural) localization entries to i18n translation array *index.php* - added require statement for publisher.php *lang/Localization_en.json - added new localization entries for publisher labels (see below) ``` "publisher.alphabetical.many":"Alphabetical index of the {0} publishers", "publisher.alphabetical.none":"Alphabetical index of absolutely no publisher", "publisher.alphabetical.one":"Alphabetical index of the single publisher", "publisher.name":"Publisher", "publisher.title":"Publishers", "publisherword.many":"{0} publishers", "publisherword.none":"No publisher", "publisherword.one":"1 publisher", "search.result.publisher":"Search result for *{0}* in publishers", ``` *templates\bookdetail.html* - added publisher label and item to bookdetail popup *test\bookTest.php* - added indices and names of publishers added to testdatabase as comment - added test function ```testGetBooksByPublisher``` - changed test function testGetBookById to add assertion for publisher name - changed test function testTypeaheadSearch to add search on partial publisher name. *test\pageTest.php* - changed test function testPageIndex to insert publisher category and adjust page indices - changed test function testPageIndexWithCustomColum to adjust for the changed page indices - added test function testPageAllPublishers - added test function testPagePublishersDetail - added test function testPageSearchScopePublishers *test\BaseWithSomeBooks\metadata.db* - added 5 publishers spread across all 14 books, replacing the original publisher Feedbooks Files added: *publisher.php*
10 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
**2012-11-22** **Added global support for publishers** Files modified: *base.php* - changed class Entry, - adding a constant ```cops:publishers``` to the icon array for the feed. - changed class Page - added branches to the page selector switch - changed Page->public function InitializeContent - added call to pull publisher count from database - changed class PageAllBooks - changed it so ```getCurrentOption``` is actually used... - added page descendant class ```PageAllPublishers``` - handles pulling the publishers category from database - added page descendant class ```PagePublisherDetail``` - handles pulling the books per publisher data from database - changed class PageQueryResult - added constant and switches for publisher search scope - abstract class Base - added constants for the publisher pages *book.php* - added require statement for publisher.php - added ```SQL_BOOKS_BY_PUBLISHER``` query to retrieve books by publisher. - changed class Book - added query constant - added publisher item - added test in case no known publisher - added publishername and url array elements for the JSON output - added public function ```getPublisher``` - added public static function ```getBooksByPublisher``` to fire the query - changed function getJson - added publisher category to search - added publishername (single) and publishertitle(plural) localization entries to i18n translation array *index.php* - added require statement for publisher.php *lang/Localization_en.json - added new localization entries for publisher labels (see below) ``` "publisher.alphabetical.many":"Alphabetical index of the {0} publishers", "publisher.alphabetical.none":"Alphabetical index of absolutely no publisher", "publisher.alphabetical.one":"Alphabetical index of the single publisher", "publisher.name":"Publisher", "publisher.title":"Publishers", "publisherword.many":"{0} publishers", "publisherword.none":"No publisher", "publisherword.one":"1 publisher", "search.result.publisher":"Search result for *{0}* in publishers", ``` *templates\bookdetail.html* - added publisher label and item to bookdetail popup *test\bookTest.php* - added indices and names of publishers added to testdatabase as comment - added test function ```testGetBooksByPublisher``` - changed test function testGetBookById to add assertion for publisher name - changed test function testTypeaheadSearch to add search on partial publisher name. *test\pageTest.php* - changed test function testPageIndex to insert publisher category and adjust page indices - changed test function testPageIndexWithCustomColum to adjust for the changed page indices - added test function testPageAllPublishers - added test function testPagePublishersDetail - added test function testPageSearchScopePublishers *test\BaseWithSomeBooks\metadata.db* - added 5 publishers spread across all 14 books, replacing the original publisher Feedbooks Files added: *publisher.php*
10 years ago
12 years ago
12 years ago
12 years ago
10 years ago
12 years ago
12 years ago
12 years ago
12 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191
  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. define ("VERSION", "0.9.1RC1");
  9. define ("DB", "db");
  10. date_default_timezone_set($config['default_timezone']);
  11. function useServerSideRendering () {
  12. global $config;
  13. return preg_match("/" . $config['cops_server_side_render'] . "/", $_SERVER['HTTP_USER_AGENT']);
  14. }
  15. function serverSideRender ($data) {
  16. // Get the templates
  17. $theme = getCurrentTemplate ();
  18. $header = file_get_contents('templates/' . $theme . '/header.html');
  19. $footer = file_get_contents('templates/' . $theme . '/footer.html');
  20. $main = file_get_contents('templates/' . $theme . '/main.html');
  21. $bookdetail = file_get_contents('templates/' . $theme . '/bookdetail.html');
  22. $page = file_get_contents('templates/' . $theme . '/page.html');
  23. // Generate the function for the template
  24. $template = new doT ();
  25. $dot = $template->template ($page, array ("bookdetail" => $bookdetail,
  26. "header" => $header,
  27. "footer" => $footer,
  28. "main" => $main));
  29. // Execute the template
  30. if (!empty ($data)) {
  31. return $dot ($data);
  32. }
  33. return NULL;
  34. }
  35. function getQueryString () {
  36. if ( isset($_SERVER['QUERY_STRING']) ) {
  37. return $_SERVER['QUERY_STRING'];
  38. }
  39. return "";
  40. }
  41. function notFound () {
  42. header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
  43. header("Status: 404 Not Found");
  44. $_SERVER['REDIRECT_STATUS'] = 404;
  45. }
  46. function getURLParam ($name, $default = NULL) {
  47. if (!empty ($_GET) && isset($_GET[$name]) && $_GET[$name] != "") {
  48. return $_GET[$name];
  49. }
  50. return $default;
  51. }
  52. function getCurrentOption ($option) {
  53. global $config;
  54. if (isset($_COOKIE[$option])) {
  55. if (isset($config ["cops_" . $option]) && is_array ($config ["cops_" . $option])) {
  56. return explode (",", $_COOKIE[$option]);
  57. } else {
  58. return $_COOKIE[$option];
  59. }
  60. }
  61. if ($option == "style") {
  62. return "default";
  63. }
  64. if (isset($config ["cops_" . $option])) {
  65. return $config ["cops_" . $option];
  66. }
  67. return "";
  68. }
  69. function getCurrentCss () {
  70. return "templates/" . getCurrentTemplate () . "/styles/style-" . getCurrentOption ("style") . ".css";
  71. }
  72. function getCurrentTemplate () {
  73. return "default";
  74. }
  75. function getUrlWithVersion ($url) {
  76. return $url . "?v=" . VERSION;
  77. }
  78. function xml2xhtml($xml) {
  79. return preg_replace_callback('#<(\w+)([^>]*)\s*/>#s', create_function('$m', '
  80. $xhtml_tags = array("br", "hr", "input", "frame", "img", "area", "link", "col", "base", "basefont", "param");
  81. return in_array($m[1], $xhtml_tags) ? "<$m[1]$m[2] />" : "<$m[1]$m[2]></$m[1]>";
  82. '), $xml);
  83. }
  84. function display_xml_error($error)
  85. {
  86. $return = "";
  87. $return .= str_repeat('-', $error->column) . "^\n";
  88. switch ($error->level) {
  89. case LIBXML_ERR_WARNING:
  90. $return .= "Warning $error->code: ";
  91. break;
  92. case LIBXML_ERR_ERROR:
  93. $return .= "Error $error->code: ";
  94. break;
  95. case LIBXML_ERR_FATAL:
  96. $return .= "Fatal Error $error->code: ";
  97. break;
  98. }
  99. $return .= trim($error->message) .
  100. "\n Line: $error->line" .
  101. "\n Column: $error->column";
  102. if ($error->file) {
  103. $return .= "\n File: $error->file";
  104. }
  105. return "$return\n\n--------------------------------------------\n\n";
  106. }
  107. function are_libxml_errors_ok ()
  108. {
  109. $errors = libxml_get_errors();
  110. foreach ($errors as $error) {
  111. if ($error->code == 801) return false;
  112. }
  113. return true;
  114. }
  115. function html2xhtml ($html) {
  116. $doc = new DOMDocument();
  117. libxml_use_internal_errors(true);
  118. $doc->loadHTML('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' .
  119. $html . '</body></html>'); // Load the HTML
  120. $output = $doc->saveXML($doc->documentElement); // Transform to an Ansi xml stream
  121. $output = xml2xhtml($output);
  122. if (preg_match ('#<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></meta></head><body>(.*)</body></html>#ms', $output, $matches)) {
  123. $output = $matches [1]; // Remove <html><body>
  124. }
  125. /*
  126. // In case of error with summary, use it to debug
  127. $errors = libxml_get_errors();
  128. foreach ($errors as $error) {
  129. $output .= display_xml_error($error);
  130. }
  131. */
  132. if (!are_libxml_errors_ok ()) $output = "HTML code not valid.";
  133. libxml_use_internal_errors(false);
  134. return $output;
  135. }
  136. /**
  137. * This method is a direct copy-paste from
  138. * http://tmont.com/blargh/2010/1/string-format-in-php
  139. */
  140. function str_format($format) {
  141. $args = func_get_args();
  142. $format = array_shift($args);
  143. preg_match_all('/(?=\{)\{(\d+)\}(?!\})/', $format, $matches, PREG_OFFSET_CAPTURE);
  144. $offset = 0;
  145. foreach ($matches[1] as $data) {
  146. $i = $data[0];
  147. $format = substr_replace($format, @$args[$i], $offset + $data[1] - 1, 2 + strlen($i));
  148. $offset += strlen(@$args[$i]) - 2 - strlen($i);
  149. }
  150. return $format;
  151. }
  152. /**
  153. * This method is based on this page
  154. * http://www.mind-it.info/2010/02/22/a-simple-approach-to-localization-in-php/
  155. */
  156. function localize($phrase, $count=-1, $reset=false) {
  157. if ($count == 0)
  158. $phrase .= ".none";
  159. if ($count == 1)
  160. $phrase .= ".one";
  161. if ($count > 1)
  162. $phrase .= ".many";
  163. /* Static keyword is used to ensure the file is loaded only once */
  164. static $translations = NULL;
  165. if ($reset) {
  166. $translations = NULL;
  167. }
  168. /* If no instance of $translations has occured load the language file */
  169. if (is_null($translations)) {
  170. $lang = "en";
  171. if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  172. {
  173. $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
  174. }
  175. $lang_file_en = NULL;
  176. $lang_file = dirname(__FILE__). '/lang/Localization_' . $lang . '.json';
  177. if (!file_exists($lang_file)) {
  178. $lang_file = dirname(__FILE__). '/lang/' . 'Localization_en.json';
  179. }
  180. elseif ($lang != "en") {
  181. $lang_file_en = dirname(__FILE__). '/lang/' . 'Localization_en.json';
  182. }
  183. $lang_file_content = file_get_contents($lang_file);
  184. /* Load the language file as a JSON object and transform it into an associative array */
  185. $translations = json_decode($lang_file_content, true);
  186. /* Clean the array of all unfinished translations */
  187. foreach (array_keys ($translations) as $key) {
  188. if (preg_match ("/^##TODO##/", $key)) {
  189. unset ($translations [$key]);
  190. }
  191. }
  192. if ($lang_file_en)
  193. {
  194. $lang_file_content = file_get_contents($lang_file_en);
  195. $translations_en = json_decode($lang_file_content, true);
  196. $translations = array_merge ($translations_en, $translations);
  197. }
  198. }
  199. if (array_key_exists ($phrase, $translations)) {
  200. return $translations[$phrase];
  201. }
  202. return $phrase;
  203. }
  204. function addURLParameter($urlParams, $paramName, $paramValue) {
  205. if (empty ($urlParams)) {
  206. $urlParams = "";
  207. }
  208. $start = "";
  209. if (preg_match ("#^\?(.*)#", $urlParams, $matches)) {
  210. $start = "?";
  211. $urlParams = $matches[1];
  212. }
  213. $params = array();
  214. parse_str($urlParams, $params);
  215. if (empty ($paramValue) && $paramValue != 0) {
  216. unset ($params[$paramName]);
  217. } else {
  218. $params[$paramName] = $paramValue;
  219. }
  220. return $start . http_build_query($params);
  221. }
  222. class Link
  223. {
  224. const OPDS_THUMBNAIL_TYPE = "http://opds-spec.org/image/thumbnail";
  225. const OPDS_IMAGE_TYPE = "http://opds-spec.org/image";
  226. const OPDS_ACQUISITION_TYPE = "http://opds-spec.org/acquisition";
  227. const OPDS_NAVIGATION_TYPE = "application/atom+xml;profile=opds-catalog;kind=navigation";
  228. const OPDS_PAGING_TYPE = "application/atom+xml;profile=opds-catalog;kind=acquisition";
  229. public $href;
  230. public $type;
  231. public $rel;
  232. public $title;
  233. public $facetGroup;
  234. public $activeFacet;
  235. public function __construct($phref, $ptype, $prel = NULL, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
  236. $this->href = $phref;
  237. $this->type = $ptype;
  238. $this->rel = $prel;
  239. $this->title = $ptitle;
  240. $this->facetGroup = $pfacetGroup;
  241. $this->activeFacet = $pactiveFacet;
  242. }
  243. public function hrefXhtml () {
  244. return $this->href;
  245. }
  246. }
  247. class LinkNavigation extends Link
  248. {
  249. public function __construct($phref, $prel = NULL, $ptitle = NULL) {
  250. parent::__construct ($phref, Link::OPDS_NAVIGATION_TYPE, $prel, $ptitle);
  251. if (!is_null (GetUrlParam (DB))) $this->href = addURLParameter ($this->href, DB, GetUrlParam (DB));
  252. if (!preg_match ("#^\?(.*)#", $this->href) && !empty ($this->href)) $this->href = "?" . $this->href;
  253. if (preg_match ("/(bookdetail|getJSON).php/", $_SERVER["SCRIPT_NAME"])) {
  254. $this->href = "index.php" . $this->href;
  255. } else {
  256. $this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
  257. }
  258. }
  259. }
  260. class LinkFacet extends Link
  261. {
  262. public function __construct($phref, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
  263. parent::__construct ($phref, Link::OPDS_PAGING_TYPE, "http://opds-spec.org/facet", $ptitle, $pfacetGroup, $pactiveFacet);
  264. if (!is_null (GetUrlParam (DB))) $this->href = addURLParameter ($this->href, DB, GetUrlParam (DB));
  265. $this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
  266. }
  267. }
  268. class Entry
  269. {
  270. public $title;
  271. public $id;
  272. public $content;
  273. public $contentType;
  274. public $linkArray;
  275. public $localUpdated;
  276. public $className;
  277. private static $updated = NULL;
  278. public static $icons = array(
  279. Author::ALL_AUTHORS_ID => 'images/author.png',
  280. Serie::ALL_SERIES_ID => 'images/serie.png',
  281. Book::ALL_RECENT_BOOKS_ID => 'images/recent.png',
  282. Tag::ALL_TAGS_ID => 'images/tag.png',
  283. Language::ALL_LANGUAGES_ID => 'images/language.png',
  284. CustomColumn::ALL_CUSTOMS_ID => 'images/tag.png',
  285. "cops:books$" => 'images/allbook.png',
  286. "cops:books:letter" => 'images/allbook.png',
  287. Publisher::ALL_PUBLISHERS_ID => 'images/publisher.png'
  288. );
  289. public function getUpdatedTime () {
  290. if (!is_null ($this->localUpdated)) {
  291. return date (DATE_ATOM, $this->localUpdated);
  292. }
  293. if (is_null (self::$updated)) {
  294. self::$updated = time();
  295. }
  296. return date (DATE_ATOM, self::$updated);
  297. }
  298. public function getNavLink () {
  299. foreach ($this->linkArray as $link) {
  300. if ($link->type != Link::OPDS_NAVIGATION_TYPE) { continue; }
  301. return $link->hrefXhtml ();
  302. }
  303. return "#";
  304. }
  305. public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $pclass = "") {
  306. global $config;
  307. $this->title = $ptitle;
  308. $this->id = $pid;
  309. $this->content = $pcontent;
  310. $this->contentType = $pcontentType;
  311. $this->linkArray = $plinkArray;
  312. $this->className = $pclass;
  313. if ($config['cops_show_icons'] == 1)
  314. {
  315. foreach (self::$icons as $reg => $image)
  316. {
  317. if (preg_match ("/" . $reg . "/", $pid)) {
  318. array_push ($this->linkArray, new Link (getUrlWithVersion ($image), "image/png", Link::OPDS_THUMBNAIL_TYPE));
  319. break;
  320. }
  321. }
  322. }
  323. if (!is_null (GetUrlParam (DB))) $this->id = str_replace ("cops:", "cops:" . GetUrlParam (DB) . ":", $this->id);
  324. }
  325. }
  326. class EntryBook extends Entry
  327. {
  328. public $book;
  329. public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $pbook) {
  330. parent::__construct ($ptitle, $pid, $pcontent, $pcontentType, $plinkArray);
  331. $this->book = $pbook;
  332. $this->localUpdated = $pbook->timestamp;
  333. }
  334. public function getCoverThumbnail () {
  335. foreach ($this->linkArray as $link) {
  336. if ($link->rel == Link::OPDS_THUMBNAIL_TYPE)
  337. return $link->hrefXhtml ();
  338. }
  339. return null;
  340. }
  341. public function getCover () {
  342. foreach ($this->linkArray as $link) {
  343. if ($link->rel == Link::OPDS_IMAGE_TYPE)
  344. return $link->hrefXhtml ();
  345. }
  346. return null;
  347. }
  348. }
  349. class Page
  350. {
  351. public $title;
  352. public $subtitle = "";
  353. public $authorName = "";
  354. public $authorUri = "";
  355. public $authorEmail = "";
  356. public $idPage;
  357. public $idGet;
  358. public $query;
  359. public $favicon;
  360. public $n;
  361. public $book;
  362. public $totalNumber = -1;
  363. public $entryArray = array();
  364. public static function getPage ($pageId, $id, $query, $n)
  365. {
  366. switch ($pageId) {
  367. case Base::PAGE_ALL_AUTHORS :
  368. return new PageAllAuthors ($id, $query, $n);
  369. case Base::PAGE_AUTHORS_FIRST_LETTER :
  370. return new PageAllAuthorsLetter ($id, $query, $n);
  371. case Base::PAGE_AUTHOR_DETAIL :
  372. return new PageAuthorDetail ($id, $query, $n);
  373. case Base::PAGE_ALL_TAGS :
  374. return new PageAllTags ($id, $query, $n);
  375. case Base::PAGE_TAG_DETAIL :
  376. return new PageTagDetail ($id, $query, $n);
  377. case Base::PAGE_ALL_LANGUAGES :
  378. return new PageAllLanguages ($id, $query, $n);
  379. case Base::PAGE_LANGUAGE_DETAIL :
  380. return new PageLanguageDetail ($id, $query, $n);
  381. case Base::PAGE_ALL_CUSTOMS :
  382. return new PageAllCustoms ($id, $query, $n);
  383. case Base::PAGE_CUSTOM_DETAIL :
  384. return new PageCustomDetail ($id, $query, $n);
  385. case Base::PAGE_ALL_RATINGS :
  386. return new PageAllRating ($id, $query, $n);
  387. case Base::PAGE_RATING_DETAIL :
  388. return new PageRatingDetail ($id, $query, $n);
  389. case Base::PAGE_ALL_SERIES :
  390. return new PageAllSeries ($id, $query, $n);
  391. case Base::PAGE_ALL_BOOKS :
  392. return new PageAllBooks ($id, $query, $n);
  393. case Base::PAGE_ALL_BOOKS_LETTER:
  394. return new PageAllBooksLetter ($id, $query, $n);
  395. case Base::PAGE_ALL_RECENT_BOOKS :
  396. return new PageRecentBooks ($id, $query, $n);
  397. case Base::PAGE_SERIE_DETAIL :
  398. return new PageSerieDetail ($id, $query, $n);
  399. case Base::PAGE_OPENSEARCH_QUERY :
  400. return new PageQueryResult ($id, $query, $n);
  401. case Base::PAGE_BOOK_DETAIL :
  402. return new PageBookDetail ($id, $query, $n);
  403. case Base::PAGE_ALL_PUBLISHERS:
  404. return new PageAllPublishers ($id, $query, $n);
  405. case Base::PAGE_PUBLISHER_DETAIL :
  406. return new PagePublisherDetail ($id, $query, $n);
  407. case Base::PAGE_ABOUT :
  408. return new PageAbout ($id, $query, $n);
  409. case Base::PAGE_CUSTOMIZE :
  410. return new PageCustomize ($id, $query, $n);
  411. default:
  412. $page = new Page ($id, $query, $n);
  413. $page->idPage = "cops:catalog";
  414. return $page;
  415. }
  416. }
  417. public function __construct($pid, $pquery, $pn) {
  418. global $config;
  419. $this->idGet = $pid;
  420. $this->query = $pquery;
  421. $this->n = $pn;
  422. $this->favicon = $config['cops_icon'];
  423. $this->authorName = empty($config['cops_author_name']) ? utf8_encode('S�bastien Lucas') : $config['cops_author_name'];
  424. $this->authorUri = empty($config['cops_author_uri']) ? 'http://blog.slucas.fr' : $config['cops_author_uri'];
  425. $this->authorEmail = empty($config['cops_author_email']) ? 'sebastien@slucas.fr' : $config['cops_author_email'];
  426. }
  427. public function InitializeContent ()
  428. {
  429. global $config;
  430. $this->title = $config['cops_title_default'];
  431. $this->subtitle = $config['cops_subtitle_default'];
  432. if (Base::noDatabaseSelected ()) {
  433. $i = 0;
  434. foreach (Base::getDbNameList () as $key) {
  435. $nBooks = Book::getBookCount ($i);
  436. array_push ($this->entryArray, new Entry ($key, "cops:{$i}:catalog",
  437. str_format (localize ("bookword", $nBooks), $nBooks), "text",
  438. array ( new LinkNavigation ("?" . DB . "={$i}"))));
  439. $i++;
  440. Base::clearDb ();
  441. }
  442. } else {
  443. if (!in_array (PageQueryResult::SCOPE_AUTHOR, getCurrentOption ('ignored_categories'))) {
  444. array_push ($this->entryArray, Author::getCount());
  445. }
  446. if (!in_array (PageQueryResult::SCOPE_SERIES, getCurrentOption ('ignored_categories'))) {
  447. $series = Serie::getCount();
  448. if (!is_null ($series)) array_push ($this->entryArray, $series);
  449. }
  450. if (!in_array (PageQueryResult::SCOPE_PUBLISHER, getCurrentOption ('ignored_categories'))) {
  451. $publisher = Publisher::getCount();
  452. if (!is_null ($publisher)) array_push ($this->entryArray, $publisher);
  453. }
  454. if (!in_array (PageQueryResult::SCOPE_TAG, getCurrentOption ('ignored_categories'))) {
  455. $tags = Tag::getCount();
  456. if (!is_null ($tags)) array_push ($this->entryArray, $tags);
  457. }
  458. if (!in_array (PageQueryResult::SCOPE_RATING, getCurrentOption ('ignored_categories'))) {
  459. $rating = Rating::getCount();
  460. if (!is_null ($rating)) array_push ($this->entryArray, $rating);
  461. }
  462. if (!in_array ("language", getCurrentOption ('ignored_categories'))) {
  463. $languages = Language::getCount();
  464. if (!is_null ($languages)) array_push ($this->entryArray, $languages);
  465. }
  466. foreach ($config['cops_calibre_custom_column'] as $lookup) {
  467. $customId = CustomColumn::getCustomId ($lookup);
  468. if (!is_null ($customId)) {
  469. array_push ($this->entryArray, CustomColumn::getCount($customId));
  470. }
  471. }
  472. $this->entryArray = array_merge ($this->entryArray, Book::getCount());
  473. if (Base::isMultipleDatabaseEnabled ()) $this->title = Base::getDbName ();
  474. }
  475. }
  476. public function isPaginated ()
  477. {
  478. return (getCurrentOption ("max_item_per_page") != -1 &&
  479. $this->totalNumber != -1 &&
  480. $this->totalNumber > getCurrentOption ("max_item_per_page"));
  481. }
  482. public function getNextLink ()
  483. {
  484. $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . getQueryString ());
  485. if (($this->n) * getCurrentOption ("max_item_per_page") < $this->totalNumber) {
  486. return new LinkNavigation ($currentUrl . "&n=" . ($this->n + 1), "next", localize ("paging.next.alternate"));
  487. }
  488. return NULL;
  489. }
  490. public function getPrevLink ()
  491. {
  492. $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . getQueryString ());
  493. if ($this->n > 1) {
  494. return new LinkNavigation ($currentUrl . "&n=" . ($this->n - 1), "previous", localize ("paging.previous.alternate"));
  495. }
  496. return NULL;
  497. }
  498. public function getMaxPage ()
  499. {
  500. return ceil ($this->totalNumber / getCurrentOption ("max_item_per_page"));
  501. }
  502. public function containsBook ()
  503. {
  504. if (count ($this->entryArray) == 0) return false;
  505. if (get_class ($this->entryArray [0]) == "EntryBook") return true;
  506. return false;
  507. }
  508. }
  509. class PageAllAuthors extends Page
  510. {
  511. public function InitializeContent ()
  512. {
  513. $this->title = localize("authors.title");
  514. if (getCurrentOption ("author_split_first_letter") == 1) {
  515. $this->entryArray = Author::getAllAuthorsByFirstLetter();
  516. }
  517. else {
  518. $this->entryArray = Author::getAllAuthors();
  519. }
  520. $this->idPage = Author::ALL_AUTHORS_ID;
  521. }
  522. }
  523. class PageAllAuthorsLetter extends Page
  524. {
  525. public function InitializeContent ()
  526. {
  527. $this->idPage = Author::getEntryIdByLetter ($this->idGet);
  528. $this->entryArray = Author::getAuthorsByStartingLetter ($this->idGet);
  529. $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("authorword", count ($this->entryArray)), count ($this->entryArray)), $this->idGet);
  530. }
  531. }
  532. class PageAuthorDetail extends Page
  533. {
  534. public function InitializeContent ()
  535. {
  536. $author = Author::getAuthorById ($this->idGet);
  537. $this->idPage = $author->getEntryId ();
  538. $this->title = $author->name;
  539. list ($this->entryArray, $this->totalNumber) = Book::getBooksByAuthor ($this->idGet, $this->n);
  540. }
  541. }
  542. class PageAllPublishers extends Page
  543. {
  544. public function InitializeContent ()
  545. {
  546. $this->title = localize("publishers.title");
  547. $this->entryArray = Publisher::getAllPublishers();
  548. $this->idPage = Publisher::ALL_PUBLISHERS_ID;
  549. }
  550. }
  551. class PagePublisherDetail extends Page
  552. {
  553. public function InitializeContent ()
  554. {
  555. $publisher = Publisher::getPublisherById ($this->idGet);
  556. $this->title = $publisher->name;
  557. list ($this->entryArray, $this->totalNumber) = Book::getBooksByPublisher ($this->idGet, $this->n);
  558. $this->idPage = $publisher->getEntryId ();
  559. }
  560. }
  561. class PageAllTags extends Page
  562. {
  563. public function InitializeContent ()
  564. {
  565. $this->title = localize("tags.title");
  566. $this->entryArray = Tag::getAllTags();
  567. $this->idPage = Tag::ALL_TAGS_ID;
  568. }
  569. }
  570. class PageAllLanguages extends Page
  571. {
  572. public function InitializeContent ()
  573. {
  574. $this->title = localize("languages.title");
  575. $this->entryArray = Language::getAllLanguages();
  576. $this->idPage = Language::ALL_LANGUAGES_ID;
  577. }
  578. }
  579. class PageCustomDetail extends Page
  580. {
  581. public function InitializeContent ()
  582. {
  583. $customId = getURLParam ("custom", NULL);
  584. $custom = CustomColumn::getCustomById ($customId, $this->idGet);
  585. $this->idPage = $custom->getEntryId ();
  586. $this->title = $custom->name;
  587. list ($this->entryArray, $this->totalNumber) = Book::getBooksByCustom ($customId, $this->idGet, $this->n);
  588. }
  589. }
  590. class PageAllCustoms extends Page
  591. {
  592. public function InitializeContent ()
  593. {
  594. $customId = getURLParam ("custom", NULL);
  595. $this->title = CustomColumn::getAllTitle ($customId);
  596. $this->entryArray = CustomColumn::getAllCustoms($customId);
  597. $this->idPage = CustomColumn::getAllCustomsId ($customId);
  598. }
  599. }
  600. class PageTagDetail extends Page
  601. {
  602. public function InitializeContent ()
  603. {
  604. $tag = Tag::getTagById ($this->idGet);
  605. $this->idPage = $tag->getEntryId ();
  606. $this->title = $tag->name;
  607. list ($this->entryArray, $this->totalNumber) = Book::getBooksByTag ($this->idGet, $this->n);
  608. }
  609. }
  610. class PageLanguageDetail extends Page
  611. {
  612. public function InitializeContent ()
  613. {
  614. $language = Language::getLanguageById ($this->idGet);
  615. $this->idPage = $language->getEntryId ();
  616. $this->title = $language->lang_code;
  617. list ($this->entryArray, $this->totalNumber) = Book::getBooksByLanguage ($this->idGet, $this->n);
  618. }
  619. }
  620. class PageAllSeries extends Page
  621. {
  622. public function InitializeContent ()
  623. {
  624. $this->title = localize("series.title");
  625. $this->entryArray = Serie::getAllSeries();
  626. $this->idPage = Serie::ALL_SERIES_ID;
  627. }
  628. }
  629. class PageSerieDetail extends Page
  630. {
  631. public function InitializeContent ()
  632. {
  633. $serie = Serie::getSerieById ($this->idGet);
  634. $this->title = $serie->name;
  635. list ($this->entryArray, $this->totalNumber) = Book::getBooksBySeries ($this->idGet, $this->n);
  636. $this->idPage = $serie->getEntryId ();
  637. }
  638. }
  639. class PageAllRating extends Page
  640. {
  641. public function InitializeContent ()
  642. {
  643. $this->title = localize("ratings.title");
  644. $this->entryArray = Rating::getAllRatings();
  645. $this->idPage = Rating::ALL_RATING_ID;
  646. }
  647. }
  648. class PageRatingDetail extends Page
  649. {
  650. public function InitializeContent ()
  651. {
  652. $rating = Rating::getRatingById ($this->idGet);
  653. $this->idPage = $rating->getEntryId ();
  654. $this->title =str_format (localize ("ratingword", $rating->name/2), $rating->name/2);
  655. list ($this->entryArray, $this->totalNumber) = Book::getBooksByRating ($this->idGet, $this->n);
  656. }
  657. }
  658. class PageAllBooks extends Page
  659. {
  660. public function InitializeContent ()
  661. {
  662. $this->title = localize ("allbooks.title");
  663. if (getCurrentOption ("titles_split_first_letter") == 1) {
  664. $this->entryArray = Book::getAllBooks();
  665. }
  666. else {
  667. list ($this->entryArray, $this->totalNumber) = Book::getBooks ($this->n);
  668. }
  669. $this->idPage = Book::ALL_BOOKS_ID;
  670. }
  671. }
  672. class PageAllBooksLetter extends Page
  673. {
  674. public function InitializeContent ()
  675. {
  676. list ($this->entryArray, $this->totalNumber) = Book::getBooksByStartingLetter ($this->idGet, $this->n);
  677. $this->idPage = Book::getEntryIdByLetter ($this->idGet);
  678. $count = $this->totalNumber;
  679. if ($count == -1)
  680. $count = count ($this->entryArray);
  681. $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("bookword", $count), $count), $this->idGet);
  682. }
  683. }
  684. class PageRecentBooks extends Page
  685. {
  686. public function InitializeContent ()
  687. {
  688. $this->title = localize ("recent.title");
  689. $this->entryArray = Book::getAllRecentBooks ();
  690. $this->idPage = Book::ALL_RECENT_BOOKS_ID;
  691. }
  692. }
  693. class PageQueryResult extends Page
  694. {
  695. const SCOPE_TAG = "tag";
  696. const SCOPE_RATING = "rating";
  697. const SCOPE_SERIES = "series";
  698. const SCOPE_AUTHOR = "author";
  699. const SCOPE_BOOK = "book";
  700. const SCOPE_PUBLISHER = "publisher";
  701. private function useTypeahead () {
  702. return !is_null (getURLParam ("search"));
  703. }
  704. private function searchByScope ($scope, $limit = FALSE) {
  705. $n = $this->n;
  706. $numberPerPage = NULL;
  707. if ($limit) {
  708. $n = 1;
  709. $numberPerPage = 5;
  710. }
  711. switch ($scope) {
  712. case self::SCOPE_BOOK :
  713. $array = Book::getBooksByStartingLetter ('%' . $this->query, $n, NULL, $numberPerPage);
  714. break;
  715. case self::SCOPE_AUTHOR :
  716. $array = Author::getAuthorsByStartingLetter ('%' . $this->query);
  717. break;
  718. case self::SCOPE_SERIES :
  719. $array = Serie::getAllSeriesByQuery ($this->query);
  720. break;
  721. case self::SCOPE_TAG :
  722. $array = Tag::getAllTagsByQuery ($this->query, $n, NULL, $numberPerPage);
  723. break;
  724. case self::SCOPE_PUBLISHER :
  725. $array = Publisher::getAllPublishersByQuery ($this->query);
  726. break;
  727. default:
  728. $array = Book::getBooksByQuery (
  729. array ("all" => "%" . $this->query . "%"), $n);
  730. }
  731. return $array;
  732. }
  733. public function doSearchByCategory () {
  734. $database = GetUrlParam (DB);
  735. $out = array ();
  736. $pagequery = Base::PAGE_OPENSEARCH_QUERY;
  737. $dbArray = array ("");
  738. $d = $database;
  739. $query = $this->query;
  740. // Special case when no databases were chosen, we search on all databases
  741. if (Base::noDatabaseSelected ()) {
  742. $dbArray = Base::getDbNameList ();
  743. $d = 0;
  744. }
  745. foreach ($dbArray as $key) {
  746. if (Base::noDatabaseSelected ()) {
  747. array_push ($this->entryArray, new Entry ($key, DB . ":query:{$d}",
  748. " ", "text",
  749. array ( new LinkNavigation ("?" . DB . "={$d}")), "tt-header"));
  750. Base::getDb ($d);
  751. }
  752. foreach (array (PageQueryResult::SCOPE_BOOK,
  753. PageQueryResult::SCOPE_AUTHOR,
  754. PageQueryResult::SCOPE_SERIES,
  755. PageQueryResult::SCOPE_TAG,
  756. PageQueryResult::SCOPE_PUBLISHER) as $key) {
  757. if (in_array($key, getCurrentOption ('ignored_categories'))) {
  758. continue;
  759. }
  760. $array = $this->searchByScope ($key, TRUE);
  761. $i = 0;
  762. if (count ($array) == 2 && is_array ($array [0])) {
  763. $total = $array [1];
  764. $array = $array [0];
  765. } else {
  766. $total = count($array);
  767. }
  768. if ($total > 0) {
  769. // Comment to help the perl i18n script
  770. // str_format (localize("bookword", count($array))
  771. // str_format (localize("authorword", count($array))
  772. // str_format (localize("seriesword", count($array))
  773. // str_format (localize("tagword", count($array))
  774. // str_format (localize("publisherword", count($array))
  775. array_push ($this->entryArray, new Entry (str_format (localize ("search.result.{$key}"), $this->query), DB . ":query:{$d}:{$key}",
  776. str_format (localize("{$key}word", $total), $total), "text",
  777. array ( new LinkNavigation ("?page={$pagequery}&query={$query}&db={$d}&scope={$key}")),
  778. Base::noDatabaseSelected () ? "" : "tt-header"));
  779. }
  780. if (!Base::noDatabaseSelected () && $this->useTypeahead ()) {
  781. foreach ($array as $entry) {
  782. array_push ($this->entryArray, $entry);
  783. $i++;
  784. if ($i > 4) { break; };
  785. }
  786. }
  787. }
  788. $d++;
  789. if (Base::noDatabaseSelected ()) {
  790. Base::clearDb ();
  791. }
  792. }
  793. return $out;
  794. }
  795. public function InitializeContent ()
  796. {
  797. $scope = getURLParam ("scope");
  798. if (empty ($scope)) {
  799. $this->title = str_format (localize ("search.result"), $this->query);
  800. } else {
  801. // Comment to help the perl i18n script
  802. // str_format (localize ("search.result.author"), $this->query)
  803. // str_format (localize ("search.result.tag"), $this->query)
  804. // str_format (localize ("search.result.series"), $this->query)
  805. // str_format (localize ("search.result.book"), $this->query)
  806. // str_format (localize ("search.result.publisher"), $this->query)
  807. $this->title = str_format (localize ("search.result.{$scope}"), $this->query);
  808. }
  809. $crit = "%" . $this->query . "%";
  810. // Special case when we are doing a search and no database is selected
  811. if (Base::noDatabaseSelected () && !$this->useTypeahead ()) {
  812. $i = 0;
  813. foreach (Base::getDbNameList () as $key) {
  814. Base::clearDb ();
  815. list ($array, $totalNumber) = Book::getBooksByQuery (array ("all" => $crit), 1, $i, 1);
  816. array_push ($this->entryArray, new Entry ($key, DB . ":query:{$i}",
  817. str_format (localize ("bookword", $totalNumber), $totalNumber), "text",
  818. array ( new LinkNavigation ("?" . DB . "={$i}&page=9&query=" . $this->query))));
  819. $i++;
  820. }
  821. return;
  822. }
  823. if (empty ($scope)) {
  824. $this->doSearchByCategory ();
  825. return;
  826. }
  827. $array = $this->searchByScope ($scope);
  828. if (count ($array) == 2 && is_array ($array [0])) {
  829. list ($this->entryArray, $this->totalNumber) = $array;
  830. } else {
  831. $this->entryArray = $array;
  832. }
  833. }
  834. }
  835. class PageBookDetail extends Page
  836. {
  837. public function InitializeContent ()
  838. {
  839. $this->book = Book::getBookById ($this->idGet);
  840. $this->title = $this->book->title;
  841. }
  842. }
  843. class PageAbout extends Page
  844. {
  845. public function InitializeContent ()
  846. {
  847. $this->title = localize ("about.title");
  848. }
  849. }
  850. class PageCustomize extends Page
  851. {
  852. private function isChecked ($key, $testedValue = 1) {
  853. $value = getCurrentOption ($key);
  854. if (is_array ($value)) {
  855. if (in_array ($testedValue, $value)) {
  856. return "checked='checked'";
  857. }
  858. } else {
  859. if ($value == $testedValue) {
  860. return "checked='checked'";
  861. }
  862. }
  863. return "";
  864. }
  865. private function isSelected ($key, $value) {
  866. if (getCurrentOption ($key) == $value) {
  867. return "selected='selected'";
  868. }
  869. return "";
  870. }
  871. private function getStyleList () {
  872. $result = array ();
  873. foreach (glob ("templates/" . getCurrentTemplate () . "/styles/style-*.css") as $filename) {
  874. if (preg_match ('/styles\/style-(.*?)\.css/', $filename, $m)) {
  875. array_push ($result, $m [1]);
  876. }
  877. }
  878. return $result;
  879. }
  880. public function InitializeContent ()
  881. {
  882. $this->title = localize ("customize.title");
  883. $this->entryArray = array ();
  884. $ignoredBaseArray = array (PageQueryResult::SCOPE_AUTHOR,
  885. PageQueryResult::SCOPE_TAG,
  886. PageQueryResult::SCOPE_SERIES,
  887. PageQueryResult::SCOPE_PUBLISHER,
  888. PageQueryResult::SCOPE_RATING,
  889. "language");
  890. $content = "";
  891. if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
  892. $content .= '<select id="style" onchange="updateCookie (this);">';
  893. foreach ($this-> getStyleList () as $filename) {
  894. $content .= "<option value='{$filename}' " . $this->isSelected ("style", $filename) . ">{$filename}</option>";
  895. }
  896. $content .= '</select>';
  897. } else {
  898. foreach ($this-> getStyleList () as $filename) {
  899. $content .= "<input type='radio' onchange='updateCookieFromCheckbox (this);' id='style-{$filename}' name='style' value='{$filename}' " . $this->isChecked ("style", $filename) . " /><label for='style-{$filename}'> {$filename} </label>";
  900. }
  901. }
  902. array_push ($this->entryArray, new Entry (localize ("customize.style"), "",
  903. $content, "text",
  904. array ()));
  905. if (!useServerSideRendering ()) {
  906. $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="use_fancyapps" ' . $this->isChecked ("use_fancyapps") . ' />';
  907. array_push ($this->entryArray, new Entry (localize ("customize.fancybox"), "",
  908. $content, "text",
  909. array ()));
  910. }
  911. $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]+$" />';
  912. array_push ($this->entryArray, new Entry (localize ("customize.paging"), "",
  913. $content, "text",
  914. array ()));
  915. $content = '<input type="text" onchange="updateCookie (this);" id="email" value="' . getCurrentOption ("email") . '" />';
  916. array_push ($this->entryArray, new Entry (localize ("customize.email"), "",
  917. $content, "text",
  918. array ()));
  919. $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="html_tag_filter" ' . $this->isChecked ("html_tag_filter") . ' />';
  920. array_push ($this->entryArray, new Entry (localize ("customize.filter"), "",
  921. $content, "text",
  922. array ()));
  923. $content = "";
  924. foreach ($ignoredBaseArray as $key) {
  925. $keyPlural = preg_replace ('/(ss)$/', 's', $key . "s");
  926. $content .= '<input type="checkbox" name="ignored_categories[]" onchange="updateCookieFromCheckboxGroup (this);" id="ignored_categories_' . $key . '" ' . $this->isChecked ("ignored_categories", $key) . ' > ' . localize ("{$keyPlural}.title") . '</input> ';
  927. }
  928. array_push ($this->entryArray, new Entry (localize ("customize.ignored"), "",
  929. $content, "text",
  930. array ()));
  931. }
  932. }
  933. abstract class Base
  934. {
  935. const PAGE_INDEX = "index";
  936. const PAGE_ALL_AUTHORS = "1";
  937. const PAGE_AUTHORS_FIRST_LETTER = "2";
  938. const PAGE_AUTHOR_DETAIL = "3";
  939. const PAGE_ALL_BOOKS = "4";
  940. const PAGE_ALL_BOOKS_LETTER = "5";
  941. const PAGE_ALL_SERIES = "6";
  942. const PAGE_SERIE_DETAIL = "7";
  943. const PAGE_OPENSEARCH = "8";
  944. const PAGE_OPENSEARCH_QUERY = "9";
  945. const PAGE_ALL_RECENT_BOOKS = "10";
  946. const PAGE_ALL_TAGS = "11";
  947. const PAGE_TAG_DETAIL = "12";
  948. const PAGE_BOOK_DETAIL = "13";
  949. const PAGE_ALL_CUSTOMS = "14";
  950. const PAGE_CUSTOM_DETAIL = "15";
  951. const PAGE_ABOUT = "16";
  952. const PAGE_ALL_LANGUAGES = "17";
  953. const PAGE_LANGUAGE_DETAIL = "18";
  954. const PAGE_CUSTOMIZE = "19";
  955. const PAGE_ALL_PUBLISHERS = "20";
  956. const PAGE_PUBLISHER_DETAIL = "21";
  957. const PAGE_ALL_RATINGS = "22";
  958. const PAGE_RATING_DETAIL = "23";
  959. const COMPATIBILITY_XML_ALDIKO = "aldiko";
  960. private static $db = NULL;
  961. public static function isMultipleDatabaseEnabled () {
  962. global $config;
  963. return is_array ($config['calibre_directory']);
  964. }
  965. public static function useAbsolutePath () {
  966. global $config;
  967. $path = self::getDbDirectory();
  968. return preg_match ('/^\//', $path) || // Linux /
  969. preg_match ('/^\w\:/', $path); // Windows X:
  970. }
  971. public static function noDatabaseSelected () {
  972. return self::isMultipleDatabaseEnabled () && is_null (GetUrlParam (DB));
  973. }
  974. public static function getDbList () {
  975. global $config;
  976. if (self::isMultipleDatabaseEnabled ()) {
  977. return $config['calibre_directory'];
  978. } else {
  979. return array ("" => $config['calibre_directory']);
  980. }
  981. }
  982. public static function getDbNameList () {
  983. global $config;
  984. if (self::isMultipleDatabaseEnabled ()) {
  985. return array_keys ($config['calibre_directory']);
  986. } else {
  987. return array ("");
  988. }
  989. }
  990. public static function getDbName ($database = NULL) {
  991. global $config;
  992. if (self::isMultipleDatabaseEnabled ()) {
  993. if (is_null ($database)) $database = GetUrlParam (DB, 0);
  994. $array = array_keys ($config['calibre_directory']);
  995. return $array[$database];
  996. }
  997. return "";
  998. }
  999. public static function getDbDirectory ($database = NULL) {
  1000. global $config;
  1001. if (self::isMultipleDatabaseEnabled ()) {
  1002. if (is_null ($database)) $database = GetUrlParam (DB, 0);
  1003. $array = array_values ($config['calibre_directory']);
  1004. return $array[$database];
  1005. }
  1006. return $config['calibre_directory'];
  1007. }
  1008. public static function getDbFileName ($database = NULL) {
  1009. return self::getDbDirectory ($database) .'metadata.db';
  1010. }
  1011. private static function error () {
  1012. if (php_sapi_name() != "cli") {
  1013. header("location: checkconfig.php?err=1");
  1014. }
  1015. throw new Exception('Database not found.');
  1016. }
  1017. public static function getDb ($database = NULL) {
  1018. if (is_null (self::$db)) {
  1019. try {
  1020. if (is_readable (self::getDbFileName ($database))) {
  1021. self::$db = new PDO('sqlite:'. self::getDbFileName ($database));
  1022. } else {
  1023. self::error ();
  1024. }
  1025. } catch (Exception $e) {
  1026. self::error ();
  1027. }
  1028. }
  1029. return self::$db;
  1030. }
  1031. public static function checkDatabaseAvailability () {
  1032. if (self::noDatabaseSelected ()) {
  1033. for ($i = 0; $i < count (self::getDbList ()); $i++) {
  1034. self::getDb ($i);
  1035. self::clearDb ();
  1036. }
  1037. } else {
  1038. self::getDb ();
  1039. }
  1040. return true;
  1041. }
  1042. public static function clearDb () {
  1043. self::$db = NULL;
  1044. }
  1045. public static function executeQuery($query, $columns, $filter, $params, $n, $database = NULL, $numberPerPage = NULL) {
  1046. $totalResult = -1;
  1047. if (is_null ($numberPerPage)) {
  1048. $numberPerPage = getCurrentOption ("max_item_per_page");
  1049. }
  1050. if ($numberPerPage != -1 && $n != -1)
  1051. {
  1052. // First check total number of results
  1053. $result = self::getDb ($database)->prepare (str_format ($query, "count(*)", $filter));
  1054. $result->execute ($params);
  1055. $totalResult = $result->fetchColumn ();
  1056. // Next modify the query and params
  1057. $query .= " limit ?, ?";
  1058. array_push ($params, ($n - 1) * $numberPerPage, $numberPerPage);
  1059. }
  1060. $result = self::getDb ($database)->prepare(str_format ($query, $columns, $filter));
  1061. $result->execute ($params);
  1062. return array ($totalResult, $result);
  1063. }
  1064. }