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.

867 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.5.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. return $translations[$phrase];
  148. }
  149. function addURLParameter($urlParams, $paramName, $paramValue) {
  150. $start = "";
  151. if (preg_match ("#^\?(.*)#", $urlParams, $matches)) {
  152. $start = "?";
  153. $urlParams = $matches[1];
  154. }
  155. $params = array();
  156. parse_str($urlParams, $params);
  157. if (empty ($paramValue) && $paramValue != 0) {
  158. unset ($params[$paramName]);
  159. } else {
  160. $params[$paramName] = $paramValue;
  161. }
  162. return $start . http_build_query($params);
  163. }
  164. class Link
  165. {
  166. const OPDS_THUMBNAIL_TYPE = "http://opds-spec.org/image/thumbnail";
  167. const OPDS_IMAGE_TYPE = "http://opds-spec.org/image";
  168. const OPDS_ACQUISITION_TYPE = "http://opds-spec.org/acquisition";
  169. const OPDS_NAVIGATION_TYPE = "application/atom+xml;profile=opds-catalog;kind=navigation";
  170. const OPDS_PAGING_TYPE = "application/atom+xml;profile=opds-catalog;kind=acquisition";
  171. public $href;
  172. public $type;
  173. public $rel;
  174. public $title;
  175. public $facetGroup;
  176. public $activeFacet;
  177. public function __construct($phref, $ptype, $prel = NULL, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
  178. $this->href = $phref;
  179. $this->type = $ptype;
  180. $this->rel = $prel;
  181. $this->title = $ptitle;
  182. $this->facetGroup = $pfacetGroup;
  183. $this->activeFacet = $pactiveFacet;
  184. }
  185. public function hrefXhtml () {
  186. return $this->href;
  187. }
  188. }
  189. class LinkNavigation extends Link
  190. {
  191. public function __construct($phref, $prel = NULL, $ptitle = NULL) {
  192. parent::__construct ($phref, Link::OPDS_NAVIGATION_TYPE, $prel, $ptitle);
  193. if (!is_null (GetUrlParam (DB))) $this->href = addURLParameter ($this->href, DB, GetUrlParam (DB));
  194. if (!preg_match ("#^\?(.*)#", $this->href) && !empty ($this->href)) $this->href = "?" . $this->href;
  195. if (preg_match ("/(bookdetail|getJSON).php/", $_SERVER["SCRIPT_NAME"])) {
  196. $this->href = "index.php" . $this->href;
  197. } else {
  198. $this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
  199. }
  200. }
  201. }
  202. class LinkFacet extends Link
  203. {
  204. public function __construct($phref, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
  205. parent::__construct ($phref, Link::OPDS_PAGING_TYPE, "http://opds-spec.org/facet", $ptitle, $pfacetGroup, $pactiveFacet);
  206. if (!is_null (GetUrlParam (DB))) $this->href = addURLParameter ($this->href, DB, GetUrlParam (DB));
  207. $this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
  208. }
  209. }
  210. class Entry
  211. {
  212. public $title;
  213. public $id;
  214. public $content;
  215. public $contentType;
  216. public $linkArray;
  217. public $localUpdated;
  218. private static $updated = NULL;
  219. public static $icons = array(
  220. Author::ALL_AUTHORS_ID => 'images/author.png',
  221. Serie::ALL_SERIES_ID => 'images/serie.png',
  222. Book::ALL_RECENT_BOOKS_ID => 'images/recent.png',
  223. Tag::ALL_TAGS_ID => 'images/tag.png',
  224. Language::ALL_LANGUAGES_ID => 'images/language.png',
  225. CustomColumn::ALL_CUSTOMS_ID => 'images/tag.png',
  226. "calibre:books$" => 'images/allbook.png',
  227. "calibre:books:letter" => 'images/allbook.png'
  228. );
  229. public function getUpdatedTime () {
  230. if (!is_null ($this->localUpdated)) {
  231. return date (DATE_ATOM, $this->localUpdated);
  232. }
  233. if (is_null (self::$updated)) {
  234. self::$updated = time();
  235. }
  236. return date (DATE_ATOM, self::$updated);
  237. }
  238. public function getContentArray () {
  239. $navlink = "#";
  240. foreach ($this->linkArray as $link) {
  241. if ($link->type != Link::OPDS_NAVIGATION_TYPE) { continue; }
  242. $navlink = $link->hrefXhtml ();
  243. }
  244. return array ( "title" => $this->title, "content" => $this->content, "navlink" => $navlink );
  245. }
  246. public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray) {
  247. global $config;
  248. $this->title = $ptitle;
  249. $this->id = $pid;
  250. $this->content = $pcontent;
  251. $this->contentType = $pcontentType;
  252. $this->linkArray = $plinkArray;
  253. if ($config['cops_show_icons'] == 1)
  254. {
  255. foreach (self::$icons as $reg => $image)
  256. {
  257. if (preg_match ("/" . $reg . "/", $pid)) {
  258. array_push ($this->linkArray, new Link (getUrlWithVersion ($image), "image/png", Link::OPDS_THUMBNAIL_TYPE));
  259. break;
  260. }
  261. }
  262. }
  263. if (!is_null (GetUrlParam (DB))) $this->id = GetUrlParam (DB) . ":" . $this->id;
  264. }
  265. }
  266. class EntryBook extends Entry
  267. {
  268. public $book;
  269. public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $pbook) {
  270. parent::__construct ($ptitle, $pid, $pcontent, $pcontentType, $plinkArray);
  271. $this->book = $pbook;
  272. $this->localUpdated = $pbook->timestamp;
  273. }
  274. public function getContentArray () {
  275. $entry = array ( "title" => $this->title);
  276. $entry ["book"] = $this->book->getContentArray ();
  277. return $entry;
  278. }
  279. public function getCoverThumbnail () {
  280. foreach ($this->linkArray as $link) {
  281. if ($link->rel == Link::OPDS_THUMBNAIL_TYPE)
  282. return $link->hrefXhtml ();
  283. }
  284. return null;
  285. }
  286. public function getCover () {
  287. foreach ($this->linkArray as $link) {
  288. if ($link->rel == Link::OPDS_IMAGE_TYPE)
  289. return $link->hrefXhtml ();
  290. }
  291. return null;
  292. }
  293. }
  294. class Page
  295. {
  296. public $title;
  297. public $subtitle = "";
  298. public $idPage;
  299. public $idGet;
  300. public $query;
  301. public $favicon;
  302. public $n;
  303. public $book;
  304. public $totalNumber = -1;
  305. public $entryArray = array();
  306. public static function getPage ($pageId, $id, $query, $n)
  307. {
  308. switch ($pageId) {
  309. case Base::PAGE_ALL_AUTHORS :
  310. return new PageAllAuthors ($id, $query, $n);
  311. case Base::PAGE_AUTHORS_FIRST_LETTER :
  312. return new PageAllAuthorsLetter ($id, $query, $n);
  313. case Base::PAGE_AUTHOR_DETAIL :
  314. return new PageAuthorDetail ($id, $query, $n);
  315. case Base::PAGE_ALL_TAGS :
  316. return new PageAllTags ($id, $query, $n);
  317. case Base::PAGE_TAG_DETAIL :
  318. return new PageTagDetail ($id, $query, $n);
  319. case Base::PAGE_ALL_LANGUAGES :
  320. return new PageAllLanguages ($id, $query, $n);
  321. case Base::PAGE_LANGUAGE_DETAIL :
  322. return new PageLanguageDetail ($id, $query, $n);
  323. case Base::PAGE_ALL_CUSTOMS :
  324. return new PageAllCustoms ($id, $query, $n);
  325. case Base::PAGE_CUSTOM_DETAIL :
  326. return new PageCustomDetail ($id, $query, $n);
  327. case Base::PAGE_ALL_SERIES :
  328. return new PageAllSeries ($id, $query, $n);
  329. case Base::PAGE_ALL_BOOKS :
  330. return new PageAllBooks ($id, $query, $n);
  331. case Base::PAGE_ALL_BOOKS_LETTER:
  332. return new PageAllBooksLetter ($id, $query, $n);
  333. case Base::PAGE_ALL_RECENT_BOOKS :
  334. return new PageRecentBooks ($id, $query, $n);
  335. case Base::PAGE_SERIE_DETAIL :
  336. return new PageSerieDetail ($id, $query, $n);
  337. case Base::PAGE_OPENSEARCH_QUERY :
  338. return new PageQueryResult ($id, $query, $n);
  339. case Base::PAGE_BOOK_DETAIL :
  340. return new PageBookDetail ($id, $query, $n);
  341. case Base::PAGE_ABOUT :
  342. return new PageAbout ($id, $query, $n);
  343. case Base::PAGE_CUSTOMIZE :
  344. return new PageCustomize ($id, $query, $n);
  345. default:
  346. $page = new Page ($id, $query, $n);
  347. $page->idPage = "cops:catalog";
  348. return $page;
  349. }
  350. }
  351. public function __construct($pid, $pquery, $pn) {
  352. global $config;
  353. $this->idGet = $pid;
  354. $this->query = $pquery;
  355. $this->n = $pn;
  356. $this->favicon = $config['cops_icon'];
  357. }
  358. public function InitializeContent ()
  359. {
  360. global $config;
  361. $this->title = $config['cops_title_default'];
  362. $this->subtitle = $config['cops_subtitle_default'];
  363. $database = GetUrlParam (DB);
  364. if (is_array ($config['calibre_directory']) && is_null ($database)) {
  365. $i = 0;
  366. foreach ($config['calibre_directory'] as $key => $value) {
  367. $nBooks = Book::getBookCount ($i);
  368. array_push ($this->entryArray, new Entry ($key, "{$i}:cops:catalog",
  369. str_format (localize ("bookword", $nBooks), $nBooks), "text",
  370. array ( new LinkNavigation ("?" . DB . "={$i}"))));
  371. $i++;
  372. Base::clearDb ();
  373. }
  374. } else {
  375. array_push ($this->entryArray, Author::getCount());
  376. $series = Serie::getCount();
  377. if (!is_null ($series)) array_push ($this->entryArray, $series);
  378. $tags = Tag::getCount();
  379. if (!is_null ($tags)) array_push ($this->entryArray, $tags);
  380. $languages = Language::getCount();
  381. if (!is_null ($languages)) array_push ($this->entryArray, $languages);
  382. foreach ($config['cops_calibre_custom_column'] as $lookup) {
  383. $customId = CustomColumn::getCustomId ($lookup);
  384. if (!is_null ($customId)) {
  385. array_push ($this->entryArray, CustomColumn::getCount($customId));
  386. }
  387. }
  388. $this->entryArray = array_merge ($this->entryArray, Book::getCount());
  389. if (!is_null ($database)) $this->title = Base::getDbName ();
  390. }
  391. }
  392. public function isPaginated ()
  393. {
  394. global $config;
  395. return (getCurrentOption ("max_item_per_page") != -1 &&
  396. $this->totalNumber != -1 &&
  397. $this->totalNumber > getCurrentOption ("max_item_per_page"));
  398. }
  399. public function getNextLink ()
  400. {
  401. global $config;
  402. $currentUrl = $_SERVER['QUERY_STRING'];
  403. $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
  404. if (($this->n) * getCurrentOption ("max_item_per_page") < $this->totalNumber) {
  405. return new LinkNavigation ($currentUrl . "&n=" . ($this->n + 1), "next", "Page suivante");
  406. }
  407. return NULL;
  408. }
  409. public function getPrevLink ()
  410. {
  411. global $config;
  412. $currentUrl = $_SERVER['QUERY_STRING'];
  413. $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
  414. if ($this->n > 1) {
  415. return new LinkNavigation ($currentUrl . "&n=" . ($this->n - 1), "previous", "Page precedente");
  416. }
  417. return NULL;
  418. }
  419. public function getMaxPage ()
  420. {
  421. global $config;
  422. return ceil ($this->totalNumber / getCurrentOption ("max_item_per_page"));
  423. }
  424. public function containsBook ()
  425. {
  426. if (count ($this->entryArray) == 0) return false;
  427. if (get_class ($this->entryArray [0]) == "EntryBook") return true;
  428. return false;
  429. }
  430. }
  431. class PageAllAuthors extends Page
  432. {
  433. public function InitializeContent ()
  434. {
  435. global $config;
  436. $this->title = localize("authors.title");
  437. if ($config['cops_author_split_first_letter'] == 1) {
  438. $this->entryArray = Author::getAllAuthorsByFirstLetter();
  439. }
  440. else {
  441. $this->entryArray = Author::getAllAuthors();
  442. }
  443. $this->idPage = Author::ALL_AUTHORS_ID;
  444. }
  445. }
  446. class PageAllAuthorsLetter extends Page
  447. {
  448. public function InitializeContent ()
  449. {
  450. global $config;
  451. $this->idPage = Author::getEntryIdByLetter ($this->idGet);
  452. $this->entryArray = Author::getAuthorsByStartingLetter ($this->idGet);
  453. $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("authorword", count ($this->entryArray)), count ($this->entryArray)), $this->idGet);
  454. }
  455. }
  456. class PageAuthorDetail extends Page
  457. {
  458. public function InitializeContent ()
  459. {
  460. $author = Author::getAuthorById ($this->idGet);
  461. $this->idPage = $author->getEntryId ();
  462. $this->title = $author->name;
  463. list ($this->entryArray, $this->totalNumber) = Book::getBooksByAuthor ($this->idGet, $this->n);
  464. }
  465. }
  466. class PageAllTags extends Page
  467. {
  468. public function InitializeContent ()
  469. {
  470. $this->title = localize("tags.title");
  471. $this->entryArray = Tag::getAllTags();
  472. $this->idPage = Tag::ALL_TAGS_ID;
  473. }
  474. }
  475. class PageAllLanguages extends Page
  476. {
  477. public function InitializeContent ()
  478. {
  479. $this->title = localize("languages.title");
  480. $this->entryArray = Language::getAllLanguages();
  481. $this->idPage = Language::ALL_LANGUAGES_ID;
  482. }
  483. }
  484. class PageCustomDetail extends Page
  485. {
  486. public function InitializeContent ()
  487. {
  488. $customId = getURLParam ("custom", NULL);
  489. $custom = CustomColumn::getCustomById ($customId, $this->idGet);
  490. $this->idPage = $custom->getEntryId ();
  491. $this->title = $custom->name;
  492. list ($this->entryArray, $this->totalNumber) = Book::getBooksByCustom ($customId, $this->idGet, $this->n);
  493. }
  494. }
  495. class PageAllCustoms extends Page
  496. {
  497. public function InitializeContent ()
  498. {
  499. $customId = getURLParam ("custom", NULL);
  500. $this->title = CustomColumn::getAllTitle ($customId);
  501. $this->entryArray = CustomColumn::getAllCustoms($customId);
  502. $this->idPage = CustomColumn::getAllCustomsId ($customId);
  503. }
  504. }
  505. class PageTagDetail extends Page
  506. {
  507. public function InitializeContent ()
  508. {
  509. $tag = Tag::getTagById ($this->idGet);
  510. $this->idPage = $tag->getEntryId ();
  511. $this->title = $tag->name;
  512. list ($this->entryArray, $this->totalNumber) = Book::getBooksByTag ($this->idGet, $this->n);
  513. }
  514. }
  515. class PageLanguageDetail extends Page
  516. {
  517. public function InitializeContent ()
  518. {
  519. $language = Language::getLanguageById ($this->idGet);
  520. $this->idPage = $language->getEntryId ();
  521. $this->title = $language->lang_code;
  522. list ($this->entryArray, $this->totalNumber) = Book::getBooksByLanguage ($this->idGet, $this->n);
  523. }
  524. }
  525. class PageAllSeries extends Page
  526. {
  527. public function InitializeContent ()
  528. {
  529. $this->title = localize("series.title");
  530. $this->entryArray = Serie::getAllSeries();
  531. $this->idPage = Serie::ALL_SERIES_ID;
  532. }
  533. }
  534. class PageSerieDetail extends Page
  535. {
  536. public function InitializeContent ()
  537. {
  538. $serie = Serie::getSerieById ($this->idGet);
  539. $this->title = $serie->name;
  540. list ($this->entryArray, $this->totalNumber) = Book::getBooksBySeries ($this->idGet, $this->n);
  541. $this->idPage = $serie->getEntryId ();
  542. }
  543. }
  544. class PageAllBooks extends Page
  545. {
  546. public function InitializeContent ()
  547. {
  548. $this->title = localize ("allbooks.title");
  549. $this->entryArray = Book::getAllBooks ();
  550. $this->idPage = Book::ALL_BOOKS_ID;
  551. }
  552. }
  553. class PageAllBooksLetter extends Page
  554. {
  555. public function InitializeContent ()
  556. {
  557. list ($this->entryArray, $this->totalNumber) = Book::getBooksByStartingLetter ($this->idGet, $this->n);
  558. $this->idPage = Book::getEntryIdByLetter ($this->idGet);
  559. $count = $this->totalNumber;
  560. if ($count == -1)
  561. $count = count ($this->entryArray);
  562. $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("bookword", $count), $count), $this->idGet);
  563. }
  564. }
  565. class PageRecentBooks extends Page
  566. {
  567. public function InitializeContent ()
  568. {
  569. $this->title = localize ("recent.title");
  570. $this->entryArray = Book::getAllRecentBooks ();
  571. $this->idPage = Book::ALL_RECENT_BOOKS_ID;
  572. }
  573. }
  574. class PageQueryResult extends Page
  575. {
  576. public function InitializeContent ()
  577. {
  578. global $config;
  579. $this->title = str_format (localize ("search.result"), $this->query);
  580. $currentPage = getURLParam ("current", NULL);
  581. // Special case when we are doing a search and no database is selected
  582. if (is_array ($config['calibre_directory']) && is_null (GetUrlParam (DB))) {
  583. $i = 0;
  584. foreach ($config['calibre_directory'] as $key => $value) {
  585. Base::clearDb ();
  586. list ($array, $totalNumber) = Book::getBooksByQuery ($this->query, $this->n, $i);
  587. array_push ($this->entryArray, new Entry ($key, DB . ":query:{$i}",
  588. str_format (localize ("bookword", count($array)), count($array)), "text",
  589. array ( new LinkNavigation ("?" . DB . "={$i}&page=9&query=" . $this->query))));
  590. $i++;
  591. }
  592. return;
  593. }
  594. switch ($currentPage) {
  595. case Base::PAGE_ALL_AUTHORS :
  596. case Base::PAGE_AUTHORS_FIRST_LETTER :
  597. $this->entryArray = Author::getAuthorsByStartingLetter ('%' . $this->query);
  598. break;
  599. default:
  600. list ($this->entryArray, $this->totalNumber) = Book::getBooksByQuery ($this->query, $this->n);
  601. }
  602. }
  603. }
  604. class PageBookDetail extends Page
  605. {
  606. public function InitializeContent ()
  607. {
  608. $this->book = Book::getBookById ($this->idGet);
  609. $this->title = $this->book->title;
  610. }
  611. }
  612. class PageAbout extends Page
  613. {
  614. public function InitializeContent ()
  615. {
  616. $this->title = localize ("about.title");
  617. }
  618. }
  619. class PageCustomize extends Page
  620. {
  621. public function InitializeContent ()
  622. {
  623. global $config;
  624. $this->title = localize ("customize.title");
  625. $this->entryArray = array ();
  626. $use_fancybox = "";
  627. if (getCurrentOption ("use_fancyapps") == 1) {
  628. $use_fancybox = "checked='checked'";
  629. }
  630. $html_tag_filter = "";
  631. if (getCurrentOption ("html_tag_filter") == 1) {
  632. $html_tag_filter = "checked='checked'";
  633. }
  634. $content = "";
  635. if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
  636. $content .= '<select id="style" onchange="updateCookie (this);">';
  637. }
  638. foreach (glob ("styles/style-*.css") as $filename) {
  639. if (preg_match ('/styles\/style-(.*?)\.css/', $filename, $m)) {
  640. $filename = $m [1];
  641. }
  642. $selected = "";
  643. if (getCurrentOption ("style") == $filename) {
  644. if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
  645. $selected = "selected='selected'";
  646. } else {
  647. $selected = "checked='checked'";
  648. }
  649. }
  650. if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
  651. $content .= "<option value='{$filename}' {$selected}>{$filename}</option>";
  652. } else {
  653. $content .= "<input type='radio' onchange='updateCookieFromCheckbox (this);' id='style-{$filename}' name='style' value='{$filename}' {$selected} /><label for='style-{$filename}'> {$filename} </label>";
  654. }
  655. }
  656. if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
  657. $content .= '</select>';
  658. }
  659. array_push ($this->entryArray, new Entry (localize ("customize.style"), "",
  660. $content, "text",
  661. array ()));
  662. $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="use_fancyapps" ' . $use_fancybox . ' />';
  663. array_push ($this->entryArray, new Entry (localize ("customize.fancybox"), "",
  664. $content, "text",
  665. array ()));
  666. $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]+$" />';
  667. array_push ($this->entryArray, new Entry (localize ("customize.paging"), "",
  668. $content, "text",
  669. array ()));
  670. $content = '<input type="email" onchange="updateCookie (this);" id="email" value="' . getCurrentOption ("email") . '" />';
  671. array_push ($this->entryArray, new Entry (localize ("customize.email"), "",
  672. $content, "text",
  673. array ()));
  674. $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="html_tag_filter" ' . $html_tag_filter . ' />';
  675. array_push ($this->entryArray, new Entry (localize ("customize.filter"), "",
  676. $content, "text",
  677. array ()));
  678. }
  679. }
  680. abstract class Base
  681. {
  682. const PAGE_INDEX = "index";
  683. const PAGE_ALL_AUTHORS = "1";
  684. const PAGE_AUTHORS_FIRST_LETTER = "2";
  685. const PAGE_AUTHOR_DETAIL = "3";
  686. const PAGE_ALL_BOOKS = "4";
  687. const PAGE_ALL_BOOKS_LETTER = "5";
  688. const PAGE_ALL_SERIES = "6";
  689. const PAGE_SERIE_DETAIL = "7";
  690. const PAGE_OPENSEARCH = "8";
  691. const PAGE_OPENSEARCH_QUERY = "9";
  692. const PAGE_ALL_RECENT_BOOKS = "10";
  693. const PAGE_ALL_TAGS = "11";
  694. const PAGE_TAG_DETAIL = "12";
  695. const PAGE_BOOK_DETAIL = "13";
  696. const PAGE_ALL_CUSTOMS = "14";
  697. const PAGE_CUSTOM_DETAIL = "15";
  698. const PAGE_ABOUT = "16";
  699. const PAGE_ALL_LANGUAGES = "17";
  700. const PAGE_LANGUAGE_DETAIL = "18";
  701. const PAGE_CUSTOMIZE = "19";
  702. const COMPATIBILITY_XML_ALDIKO = "aldiko";
  703. private static $db = NULL;
  704. public static function getDbList () {
  705. global $config;
  706. if (is_array ($config['calibre_directory'])) {
  707. return $config['calibre_directory'];
  708. } else {
  709. return array ("" => $config['calibre_directory']);
  710. }
  711. }
  712. public static function getDbName ($database = NULL) {
  713. global $config;
  714. if (is_array ($config['calibre_directory'])) {
  715. if (is_null ($database)) $database = GetUrlParam (DB, 0);
  716. $array = array_keys ($config['calibre_directory']);
  717. return $array[$database];
  718. }
  719. return "";
  720. }
  721. public static function getDbDirectory ($database = NULL) {
  722. global $config;
  723. if (is_array ($config['calibre_directory'])) {
  724. if (is_null ($database)) $database = GetUrlParam (DB, 0);
  725. $array = array_values ($config['calibre_directory']);
  726. return $array[$database];
  727. }
  728. return $config['calibre_directory'];
  729. }
  730. public static function getDbFileName ($database = NULL) {
  731. return self::getDbDirectory ($database) .'metadata.db';
  732. }
  733. public static function getDb ($database = NULL) {
  734. global $config;
  735. if (is_null (self::$db)) {
  736. try {
  737. self::$db = new PDO('sqlite:'. self::getDbFileName ($database));
  738. } catch (Exception $e) {
  739. header("location: checkconfig.php?err=1");
  740. exit();
  741. }
  742. }
  743. return self::$db;
  744. }
  745. public static function clearDb () {
  746. self::$db = NULL;
  747. }
  748. public static function executeQuery($query, $columns, $filter, $params, $n, $database = NULL) {
  749. global $config;
  750. $totalResult = -1;
  751. if (getCurrentOption ("max_item_per_page") != -1 && $n != -1)
  752. {
  753. // First check total number of results
  754. $result = self::getDb ($database)->prepare (str_format ($query, "count(*)", $filter));
  755. $result->execute ($params);
  756. $totalResult = $result->fetchColumn ();
  757. // Next modify the query and params
  758. $query .= " limit ?, ?";
  759. array_push ($params, ($n - 1) * getCurrentOption ("max_item_per_page"), getCurrentOption ("max_item_per_page"));
  760. }
  761. $result = self::getDb ($database)->prepare(str_format ($query, $columns, $filter));
  762. $result->execute ($params);
  763. return array ($totalResult, $result);
  764. }
  765. }