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.

870 lines
29KB

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