basic OPDS (and HTML) catalog provider for eBooks
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.

1081 lines
55KB

  1. <?php
  2. #############################################################################
  3. # miniCalOPe (c) 2010-2020 by Itzchak Rehberg #
  4. # written by Itzchak Rehberg <izzysoft AT qumran DOT org> #
  5. # https://www.izzysoft.de/ #
  6. # ------------------------------------------------------------------------- #
  7. # This program is free software; you can redistribute and/or modify it #
  8. # under the terms of the GNU General Public License (see doc/LICENSE) #
  9. #############################################################################
  10. require_once('./lib/class.logging.php'); // must come first as it also defines some CONST
  11. require_once('./config.php');
  12. require_once('./lib/common.php');
  13. require_once('./lib/class.filefuncs.php');
  14. $filefuncs = new filefuncs($logger,$use_markdown,$bookformats,$bookdesc_ext,$bookmeta_ext,$check_xml,$skip_broken_xml);
  15. if ( isset($_SERVER['HTTPS']) ) $baseurl = str_replace('http://','https://',$baseurl);
  16. // Setup templates
  17. require_once('./lib/class.template.php');
  18. $pageformat = req_word('pageformat');
  19. switch($pageformat) {
  20. case 'html' : $pageformat = 'html'; break;
  21. default : $pageformat = 'opds'; break;
  22. }
  23. $t = new Template("tpl/$pageformat");
  24. // Setup translations
  25. require_once('./lib/class.translation.php');
  26. $transl = new translation(dirname(__FILE__).'/lang','en');
  27. $transl->get_translations();
  28. function trans($key,$m1="",$m2="",$m3="",$m4="",$m5="") {
  29. return $GLOBALS['transl']->transl($key,$m1,$m2,$m3,$m4,$m5);
  30. }
  31. $pubdate = date('c',filemtime($dbfile));
  32. $pubdate_human = date('Y-m-d H:i',filemtime($dbfile));
  33. #================================================================[ Helpers ]===
  34. /** Setup common template values
  35. * @function set_basics
  36. * @param object tpl template object
  37. */
  38. function set_basics(&$tpl) {
  39. foreach(array('owner','homepage','email','pubdate','pubdate_human','baseurl','relurl') as $item) $tpl->set_var($item,$GLOBALS[$item]);
  40. $tpl->set_var('lang',$GLOBALS['use_lang']);
  41. $tpl->set_var('site_title',$GLOBALS['sitetitle']);
  42. $tpl->set_var('last_update',trans('last_update'));
  43. $tpl->set_var('created_by',trans('created_by'));
  44. foreach(array('start_page','this_page','first_page','prev_page','next_page','last_page') as $page) $tpl->set_var($page,trans($page));
  45. foreach(array('sort_alpha','sort_bookcount','sort_date','sort_author') as $sort) $tpl->set_var($sort,trans($sort));
  46. }
  47. /** Obtain information on available files for a given book
  48. * @function get_filenames
  49. * @param object db database object for Calibre DB
  50. * @param integer bookid ID of the book to obtain the file infor for
  51. * @param optional string format get information just for the given format. If not set, get all formats
  52. * @return array files [0..n] of array(name,size,format,path)
  53. */
  54. function get_filenames(&$db,$bookid,$format='') {
  55. $db->query("SELECT path FROM books WHERE id=$bookid");
  56. $db->next_record();
  57. $path = $GLOBALS['bookroot'].$db->f('path');
  58. $query = "SELECT format,uncompressed_size,name FROM data WHERE book=$bookid";
  59. if (!empty($format)) $query .= " AND format='$format'";
  60. $db->query($query);
  61. $files = array();
  62. while ($db->next_record()) {
  63. $files[] = array('name'=>$db->f('name'),'size'=>$db->f('uncompressed_size'),'format'=>$db->f('format'),'path'=>$path);
  64. }
  65. return $files;
  66. }
  67. /** Get the ID by name
  68. * looks up the ID for a given name in multiple runs:<OL><LI>exact match</LI><LI>like ('%given name%')</LI><LI>similar ('%given%name%')</LI><LI>none</LI></OL>
  69. * the SQL Query base needs to ensure (via alias if necessary) the ID column is named "id"
  70. * @function get_idbyname
  71. * @param string query SQL Query base up to "WHERE"
  72. * @param string nf name field (e.g. "name", "title"...)
  73. * @param string name name to search for
  74. */
  75. function get_idbyname($query,$nf,$name) {
  76. GLOBAL $db;
  77. $db->query($query . " $nf='$name'");
  78. if ( $db->next_record() ) return array('match'=>'exact','name'=>$name,'id'=>$db->f('id'));
  79. $name = "%$name%";
  80. $db->query($query . " $nf like '$name'");
  81. if ( $db->next_record() ) return array('match'=>'like','name'=>$name,'id'=>$db->f('id'));
  82. $name = strtolower( preg_replace('!\s+!','%',$name) );
  83. $db->query($query . " lower($nf) like '$name'");
  84. if ( $db->next_record() ) return array('match'=>'similar','name'=>$name,'id'=>$db->f('id'));
  85. return array('match'=>'none','name'=>$name,'id'=>0);
  86. }
  87. /** Get ISBNSearch URLs
  88. * @function get_isbnurls
  89. * @param string isbn ISBN to search for
  90. * @return array
  91. */
  92. function get_isbnurls($isbn) {
  93. $csv = new csv(";",'"',TRUE,FALSE);
  94. $csv->import('./lib/isbnsearch.csv');
  95. $list = [];
  96. $isbn = preg_replace('![^0-9X]!','',$isbn);
  97. foreach ($csv->data as $data) {
  98. if ( in_array($data['name'],$GLOBALS['isbnservices']) ) {
  99. $url = str_replace('{isbn}',$isbn,$data['url']);
  100. if (preg_match('!^http://www\.amazon\.!i',$data['url'])) {
  101. $url = str_replace('{amazonID}',$GLOBALS['amazonID'],$url);
  102. }
  103. $list[] = array('name'=>$data['name'],'url'=>$url);
  104. }
  105. }
  106. return $list;
  107. }
  108. /** Get BookSearch URLs
  109. * @function get_booksearchurls
  110. * @param string auth author to search for
  111. * @param string titl title to search for
  112. * @return array
  113. */
  114. function get_booksearchurls($auth,$titl) {
  115. $csv = new csv(";",'"',TRUE,FALSE);
  116. $csv->import('./lib/booksearch.csv');
  117. $list = [];
  118. $full = urlencode("${auth} ${titl}");
  119. $auth = urlencode($auth);
  120. $titl = urlencode($titl);
  121. foreach ($csv->data as $data) {
  122. if ( in_array($data['name'],$GLOBALS['booksearchservices']) ) {
  123. $url = str_replace('{author}',$auth,$data['url']);
  124. $url = str_replace('{title}',$titl,$url);
  125. $url = str_replace('{fulltext}',$full,$url);
  126. if (preg_match('!^http://www\.amazon\.!i',$data['url'])) {
  127. $url = str_replace('{amazonID}',$GLOBALS['amazonID'],$url);
  128. }
  129. $list[] = array('name'=>$data['name'],'url'=>$url);
  130. }
  131. }
  132. return $list;
  133. }
  134. /** Setup book formats (supported formats with mimetypes etc.)
  135. * @function get_formats
  136. * @return array formats
  137. */
  138. function get_formats() {
  139. $csv = new csv(";",'"',TRUE,FALSE);
  140. $csv->import('./lib/formats.csv');
  141. $list = [];
  142. foreach ($csv->data as $data) {
  143. $list[$data['name']] = array('mimetype'=>$data['mimetype'],'ftype_human'=>$data['ftype_human'],'ftitle'=>$data['ftitle']);
  144. }
  145. return $list;
  146. }
  147. /** Setup list of user-defined donation link info
  148. * @function get_donationinfo
  149. * @param string csvfile (full path and) name of the CSV file to read data from. Example file: doc/donationlinks.csv
  150. * @return array donationinfo associative array: name=>[link,img,alt,comment]
  151. */
  152. function get_donationinfo($csvfile) {
  153. $csv = new csv(";",'"',TRUE,FALSE);
  154. $csv->import($csvfile);
  155. $list = [];
  156. foreach ($csv->data as $data) {
  157. $list[$data['name']] = [ 'link'=>$data['link_url'],'img'=>$data['image_url'],'alt'=>$data['alt_text'],'comment'=>$data['comment'] ];
  158. }
  159. return $list;
  160. }
  161. /** Do the pagination for lists
  162. * @function paginate
  163. * @param string url URL without the pagination element (offset)
  164. * @param integer offset offset for the current page
  165. * @param integer all total hits
  166. */
  167. function paginate($url,$offset,$all) {
  168. $tpl = $GLOBALS['t'];
  169. $perpage = $GLOBALS['perpage'];
  170. $curpage = floor(($offset + $perpage) / $perpage); // floor in case of "manual override per URL"
  171. $pagecount = floor($all/$perpage);
  172. $tpl->set_var('offset',$offset);
  173. $tpl->set_var('start',$offset +1);
  174. $tpl->set_var('total',$all);
  175. $tpl->set_var('per_page',$perpage);
  176. if ($offset==0) { // first page
  177. $tpl->set_var('icon1','3left_grey.png');
  178. $tpl->set_var('iconx','2left_grey.png'); // HTML only
  179. $tpl->set_var('icon2','1left_grey.png');
  180. $tpl->set_var('link1_open','');
  181. $tpl->set_var('linkx_open',''); // HTML only
  182. $tpl->set_var('link2_open','');
  183. $tpl->set_var('link_close','');
  184. $tpl->set_var('poffset','0'); // OPDS only
  185. $tpl->parse('prev','prevblock');
  186. } else { // somewhere after
  187. $tpl->set_var('icon1','3left.png');
  188. $tpl->set_var('iconx','2left.png');
  189. $tpl->set_var('icon2','1left.png');
  190. $poff = max(0,$offset - $perpage);
  191. $tpl->set_var('poffset',$poff); // OPDS only
  192. $loffset = ($curpage < 6) ? 0 : ($curpage -6) * $perpage; // offset is at lower bound of perpage
  193. $skiptitle = ($loffset == 0) ? '' : '-5';
  194. $tpl->set_var('link1_open','<A HREF="'.$GLOBALS['relurl'].$url.'&amp;offset=0" TITLE="'.trans('first_page').'">');
  195. $tpl->set_var('linkx_open','<A HREF="'.$GLOBALS['relurl'].$url.'&amp;offset='.$loffset.'" TITLE="'.$skiptitle.'">');
  196. $tpl->set_var('link2_open','<A HREF="'.$GLOBALS['relurl'].$url.'&amp;offset='.$poff.'" TITLE="'.trans('prev_page').'">');
  197. $tpl->set_var('link_close','</A>');
  198. $tpl->parse('prev','prevblock');
  199. }
  200. if ($all <= $offset + $perpage) { // last page
  201. $tpl->set_var('icon1','1right_grey.png');
  202. $tpl->set_var('iconx','2right_grey.png');
  203. $tpl->set_var('icon2','3right_grey.png');
  204. $tpl->set_var('link1_open','');
  205. $tpl->set_var('linkx_open','');
  206. $tpl->set_var('link2_open','');
  207. $tpl->set_var('link_close','');
  208. $noff = $loff = floor($all/$perpage)*$perpage;
  209. $tpl->set_var('noffset',$noff); // OPDS only
  210. $tpl->set_var('loffset',$loff); // OPDS only
  211. $tpl->parse('next','nextblock');
  212. } else { // somewhere before
  213. $tpl->set_var('icon1','1right.png');
  214. $tpl->set_var('iconx','2right.png');
  215. $tpl->set_var('icon2','3right.png');
  216. $noff = $offset + $perpage; $loff = floor($all/$perpage)*$perpage;
  217. $tpl->set_var('noffset',$noff); // OPDS only
  218. $tpl->set_var('loffset',$loff); // OPDS only
  219. $roffset = ($pagecount - $curpage < 6) ? $pagecount * $perpage : ($curpage + 4) * $perpage; // offset is at lower bound of perpage
  220. $skiptitle = ($pagecount - $curpage < 6) ? '' : '+5';
  221. $tpl->set_var('link1_open','<A HREF="'.$GLOBALS['relurl'].$url.'&amp;offset='.$noff.'" TITLE="'.trans('next_page').'">');
  222. $tpl->set_var('linkx_open','<A HREF="'.$GLOBALS['relurl'].$url.'&amp;offset='.$roffset.'" TITLE="'.$skiptitle.'">');
  223. $tpl->set_var('link2_open','<A HREF="'.$GLOBALS['relurl'].$url.'&amp;offset='.$loff.'" TITLE="'.trans('last_page').'">');
  224. $tpl->set_var('link_close','</A>');
  225. $tpl->parse('next','nextblock');
  226. }
  227. }
  228. /** Parse titles
  229. * As the same output template and structure is used by prefix=titles
  230. * + prefix=searchresults, common stuff goes here
  231. * @function parse_titles
  232. * @param object tpl Template instance
  233. * @param object db db instance
  234. * @param integer offset
  235. * @param integer all
  236. * @param string url URL for pagination
  237. * @param optional string prefix Usually 'titles' - but alternatively 'searchresults'
  238. * @param optional string searchvals additional url parameters for pagination
  239. */
  240. function parse_titles(&$tpl,&$db,$offset,$all,$url,$prefix='titles',$searchvals='') {
  241. $tpl->set_file(array("template"=>"titles.tpl"));
  242. $tpl->set_block('template','itemblock','item');
  243. $tpl->set_block('template','prevblock','prev');
  244. $tpl->set_block('template','nextblock','next');
  245. set_basics($tpl);
  246. $tpl->set_var('prefix',$prefix);
  247. switch($prefix) {
  248. case 'searchresults':
  249. $tpl->set_var('title_list',trans('searchresults'));
  250. break;
  251. default:
  252. $tpl->set_var('title_list',trans('titles'));
  253. break;
  254. }
  255. $tpl->set_var('searchvals',$searchvals);
  256. $tpl->set_var('offset',$offset);
  257. $tpl->set_var('per_page',$GLOBALS['perpage']);
  258. $tpl->set_var('total',$all); // OPDS only
  259. $tpl->set_var('num_allbooks',$GLOBALS['allbookcount']);
  260. if ($GLOBALS['allbookcount']==1) $tpl->set_var('allbooks',trans('book'));
  261. else $tpl->set_var('allbooks',trans('books'));
  262. // pagination:
  263. $tpl->set_var('sortorder',$GLOBALS['sortorder']);
  264. paginate("$url&amp;pageformat=".$GLOBALS['pageformat'],$offset,$all);
  265. // records:
  266. $more = FALSE;
  267. while ( $db->next_record() ) {
  268. $tpl->set_var('bid',$db->f('id'));
  269. $tpl->set_var('title',str_replace('& ','&amp; ',$db->f('title')) .' '.trans('by').' '. $db->f('name'));
  270. $tpl->set_var('isbn',$db->f('isbn'));
  271. //$tpl->set_var('pubdate',str_replace(' ','T',$db->f('timestamp')).$GLOBALS['timezone']); // pubdate is per page (current template)
  272. //$tpl->set_var('pubdate_human', $db->f('timestamp'));
  273. $tpl->parse('item','itemblock',$more);
  274. $more = TRUE;
  275. }
  276. $tpl->set_var('pubdate',str_replace(' ','T',$GLOBALS['pubdate']).$GLOBALS['timezone']);
  277. $tpl->pparse("out","template");
  278. }
  279. // We need the database
  280. require_once('./lib/db_sqlite3.php');
  281. $db = new db_sqlite();
  282. $db->Database = $dbfile;
  283. $db->query('SELECT COUNT(id) AS num FROM books');
  284. $db->next_record();
  285. $allbookcount = $db->f('num');
  286. #========================================================[ Process request ]===
  287. $prefix = req_word('prefix');
  288. if ( empty($prefix) && empty($_REQUEST['action']) ) { // Startpage
  289. if ( $pageformat=='html' && $ads_asap_initial && !empty($ads_asap_pubkey) && !empty($ads_asap_privkey) && !empty($amazonID) ) { // care for ads
  290. require_once('./lib/asap.php');
  291. if ( $ads_asap_webvertizer && !empty($ads_asap_webvertizer_domain) ) setAutoAds('start','initial','regex');
  292. $asap = getAds($ads_asap_default_string,$ads_asap_lang);
  293. $adblock = getAdBlock($asap);
  294. } else $adblock = '';
  295. $t->set_file(array("template"=>"index.tpl"));
  296. set_basics($t);
  297. if ( !empty($adblock) ) $t->set_var('ad_css','<LINK REL="stylesheet" TYPE="text/css" HREF="'.$relurl.'tpl/html/asap.css">');
  298. $t->set_var('author_list',trans('authors'));
  299. $t->set_var('title_list',trans('titles'));
  300. $t->set_var('tags_list',trans('tags'));
  301. $t->set_var('series_list',trans('series'));
  302. $t->set_var('search_form',trans('search'));
  303. $t->set_var('num_allbooks',$allbookcount);
  304. if ($allbookcount==1) $t->set_var('allbooks',trans('book'));
  305. else $t->set_var('allbooks',trans('books'));
  306. if ($pageformat=='html') $t->set_var('adblock',$adblock);
  307. else header("Content-type: application/atom+xml;profile=opds-catalog");
  308. $t->pparse("out","template");
  309. exit;
  310. }
  311. $offset = req_int('offset');
  312. switch($prefix) {
  313. //---------------------------------------------[ search form requested ]---
  314. case 'search':
  315. $tpl = new Template('tpl/html'); // NO OPDS TEMPLATE FOR THIS!
  316. $t->set_file(array("template"=>"search.tpl"));
  317. $t->set_block('template','tagselblock','tagsel');
  318. set_basics($t);
  319. $t->set_var('form_action','?prefix=searchresults&amp;lang='.$GLOBALS['use_lang'].'&amp;pageformat='.$pageformat);
  320. $t->set_var('bsearch',trans('book_search_title'));
  321. $t->set_var('author_title',trans('author'));
  322. $t->set_var('book_title',trans('book'));
  323. $t->set_var('series_title',trans('serie'));
  324. $t->set_var('tags_title',trans('tags'));
  325. $t->set_var('desc_title',trans('comment'));
  326. $t->set_var('submit_title',trans('search_do'));
  327. $t->set_var('num_allbooks',$allbookcount);
  328. if ($allbookcount==1) $t->set_var('allbooks',trans('book'));
  329. else $t->set_var('allbooks',trans('books'));
  330. $db->query('SELECT id,name FROM tags ORDER BY name');
  331. $more = FALSE;
  332. while ( $db->next_record() ) {
  333. $t->set_var('optname',$db->f('name'));
  334. $t->set_var('optval',$db->f('id'));
  335. $t->parse('tagsel','tagselblock',$more);
  336. $more = TRUE;
  337. }
  338. // Ads (ASAP):
  339. if ( $pageformat=='html' && $ads_asap_initial && !empty($ads_asap_pubkey) && !empty($ads_asap_privkey) && !empty($amazonID) ) { // care for ads
  340. require_once('./lib/asap.php');
  341. if ( $ads_asap_webvertizer && !empty($ads_asap_webvertizer_domain) ) setAutoAds('authors','initial','regex');
  342. $asap = getAds($ads_asap_default_string,$ads_asap_lang);
  343. $adblock = getAdBlock($asap);
  344. } else $adblock = '';
  345. if ( !empty($adblock) ) $t->set_var('ad_css','<LINK REL="stylesheet" TYPE="text/css" HREF="'.$relurl.'tpl/html/asap.css">');
  346. if ($pageformat=='html') $t->set_var('adblock',$adblock);
  347. // Done:
  348. $t->pparse("out","template");
  349. exit;
  350. break;
  351. //-------------------------------------[ OpenSearch document requested ]---
  352. case 'ods':
  353. header("Content-type: application/opensearchdescription+xml");
  354. echo '<?xml version="1.0" encoding="UTF-8"?>'."\n"
  355. . ' <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">'."\n"
  356. . " <ShortName>$sitetitle</ShortName>\n"
  357. . " <Description>$sitetitle: ".trans('book_search_title')."</Description>\n"
  358. . " <Tags>ebooks</Tags>\n"
  359. . " <Contact>$email</Contact>\n"
  360. . ' <Url type="application/atom+xml" template="'.$baseurl.'?lang='.$use_lang.'&amp;pageformat=opds&amp;prefix=searchresults&amp;q={searchTerms}"/>'."\n"
  361. . ' <Url type="text/html" template="'.$baseurl.'?lang='.$use_lang.'&amp;pageformat=html&amp;prefix=searchresults&amp;q={searchTerms}"/>'."\n"
  362. . ' <Image height="22" width="22" type="image/png">'.$baseurl."tpl/icons/find.png</Image>\n"
  363. . " <OutputEncoding>UTF-8</OutputEncoding>\n"
  364. . " <InputEncoding>UTF-8</InputEncoding>\n"
  365. . " <Language>".$use_lang."</Language>\n"
  366. . " </OpenSearchDescription>\n";
  367. exit;
  368. //--------------------------------------[ search result list requested ]---
  369. case 'searchresults':
  370. $sall = req_alnumwild('q');
  371. $saut = req_alnumwild('author');
  372. $stit = req_alnumwild('title');
  373. $sser = req_alnumwild('series');
  374. $stxt = req_alnumwild('desc');
  375. $stag = req_intarr('tags');
  376. $logger->debug("q=$sall;author=$saut;title=$stit;series=$sser;desc=$stxt;tags=".implode(',',$stag).";offset=$offset",'SEARCH');
  377. $searchreq = "q=$sall&amp;author=$saut&amp;title=$stit&amp;series=$sser&amp;desc=$stxt&amp;tags=".implode(',',$stag);
  378. $sortorder = req_word('sort_order');
  379. switch($sortorder) {
  380. case 'title': $order = ' ORDER BY b.title'; $sortorder='title'; break;
  381. case 'name' : $order = ' ORDER BY a.name'; $sortorder='name'; break;
  382. case 'time' : $order = ' ORDER BY b.timestamp DESC'; $sortorder='time'; break;
  383. default : $order = ''; $sortorder=''; break;
  384. }
  385. $select = 'SELECT b.id,b.title,b.isbn,a.name,b.timestamp FROM books b,books_authors_link bl,authors a';
  386. $where = $searchvals = '';
  387. if ( empty($sall) ) {
  388. if ( !empty($saut) ) {
  389. $where .= " AND lower(a.name) LIKE '%".strtolower($saut)."%'";
  390. $searchvals .= '&amp;author='.urlencode($saut);
  391. }
  392. if ( !empty($stit) ) {
  393. $where .= " AND lower(b.title) LIKE '%".strtolower($stit)."%'";
  394. $searchvals .= '&amp;title='.urlencode($stit);
  395. }
  396. if ( !empty($stag) ) {
  397. $select .= ',books_tags_link bt';
  398. $where .= ' AND bt.book=b.id AND bt.tag IN ('.implode(',',$stag).')';
  399. foreach ($stag as $tt) $searchvals .= '&amp;tags[]='.$tt;
  400. }
  401. if ( !empty($sser) ) {
  402. $select .= ',books_series_link bs,series s';
  403. $where .= " AND bs.book=b.id AND bs.series=s.id AND lower(s.name) LIKE '%".strtolower($sser)."%'";
  404. $searchvals .= '&amp;series='.urlencode($sser);
  405. }
  406. if ( !empty($stxt) ) {
  407. $select .= ',comments c';
  408. $where .= " AND c.book=b.id AND lower(c.text) LIKE '%".strtolower($stxt)."%'";
  409. $searchvals .= '&amp;desc='.urlencode($stxt);
  410. }
  411. } else {
  412. $sterm = '%'.strtolower($sall).'%';
  413. $select .= ',comments c';
  414. $where .= " AND c.book=b.id AND ( lower(c.text) LIKE '$sterm' OR lower(b.title) LIKE '$sterm' OR lower(a.name) LIKE '$sterm' )";
  415. $searchvals .= '&amp;q='.urlencode($sall);
  416. }
  417. $select .= ' WHERE b.id=bl.book and a.id=bl.author '.$where;
  418. $logger->debug($select.$order,'SEARCH');
  419. $all = $db->lim_query($select.$order, $offset, $perpage);
  420. parse_titles($t,$db,$offset,$all,'?prefix=searchresults&amp;lang='.$GLOBALS['use_lang'].'&amp;sort_order='.$sortorder.'&amp;'.$searchreq,'searchresults',$searchvals);
  421. exit;
  422. break;
  423. //-----------------------------------------[ list of authors requested ]---
  424. case 'authors':
  425. $db->query('SELECT COUNT(id) AS num FROM authors');
  426. if ($db->next_record()) $num_authors = $db->f("num");
  427. else $num_authors = 0;
  428. $t->set_file(array("template"=>"authors.tpl"));
  429. $t->set_block('template','itemblock','item');
  430. $t->set_block('template','prevblock','prev');
  431. $t->set_block('template','nextblock','next');
  432. set_basics($t);
  433. $t->set_var('author_list',trans('authors'));
  434. $t->set_var('total',$num_authors);
  435. $t->set_var('per_page',$perpage);
  436. $t->set_var('start',1);
  437. $t->set_var('offset',$offset);
  438. $sortorder = req_word('sort_order');
  439. switch($sortorder) {
  440. case 'books': $order = ' ORDER BY num DESC'; $sortorder='books'; break;
  441. case 'title':
  442. default : $order = ' ORDER BY name'; $sortorder='title'; break;
  443. }
  444. if ($num_authors>0) {
  445. $all = $db->lim_query('SELECT a.id id,a.name name,COUNT(b.id) num FROM authors a,books_authors_link ba, books b WHERE a.id=ba.author AND b.id=ba.book group by a.id'.$order, $offset, $perpage);
  446. $more = FALSE;
  447. $authors = array();
  448. while ( $db->next_record() ) {
  449. $num_books = $db->f('num');
  450. $t->set_var('name',$db->f('name'));
  451. $t->set_var('id',$db->f('id'));
  452. $t->set_var('num_books',$num_books);
  453. if ($num_books==1) $t->set_var('books',trans('book'));
  454. else $t->set_var('books',trans('books'));
  455. $t->parse('item','itemblock',$more);
  456. $more = TRUE;
  457. }
  458. }
  459. $t->set_var('sortorder',$sortorder);
  460. // pagination:
  461. paginate('?prefix=authors&amp;lang='.$GLOBALS['use_lang'].'&amp;sort_order='.$sortorder.'&amp;pageformat='.$pageformat,$offset,$num_authors);
  462. // Ads (ASAP):
  463. if ( $pageformat=='html' && $ads_asap_initial && !empty($ads_asap_pubkey) && !empty($ads_asap_privkey) && !empty($amazonID) ) { // care for ads
  464. require_once('./lib/asap.php');
  465. if ( $ads_asap_webvertizer && !empty($ads_asap_webvertizer_domain) ) setAutoAds('authors','initial','regex');
  466. $asap = getAds($ads_asap_default_string,$ads_asap_lang);
  467. $adblock = getAdBlock($asap);
  468. } else $adblock = '';
  469. if ( !empty($adblock) ) $t->set_var('ad_css','<LINK REL="stylesheet" TYPE="text/css" HREF="'.$relurl.'tpl/html/asap.css">');
  470. if ($pageformat=='html') $t->set_var('adblock',$adblock);
  471. else header("Content-type: application/atom+xml;profile=opds-catalog");
  472. // Done:
  473. $t->pparse("out","template");
  474. exit;
  475. //------------------------[ List of books for a given author requested ]---
  476. case 'author_id':
  477. $aname = req_alnum('name');
  478. if ( empty($aname) ) $aid = req_int('query');
  479. else {
  480. $sarr = get_idbyname('SELECT id FROM authors WHERE','name',$aname);
  481. $aid = (int) $sarr['id'];
  482. }
  483. $db->query('SELECT COUNT(id) AS num FROM books WHERE id IN (SELECT book FROM books_authors_link WHERE author='.$aid.')');
  484. if ($db->next_record()) $num_books = $db->f("num");
  485. else $num_books = 0;
  486. $t->set_file(array("template"=>"author.tpl"));
  487. $t->set_block('template','itemblock','item');
  488. $t->set_block('template','prevblock','prev');
  489. $t->set_block('template','nextblock','next');
  490. set_basics($t);
  491. $t->set_var('back_to_authors',trans('back_to_authors'));
  492. $t->set_var('aid',$aid);
  493. $t->set_var('wikibase',$wikibase);
  494. $db->query('SELECT name FROM authors WHERE id='.$aid);
  495. $db->next_record();
  496. $t->set_var('wikiauthor',str_replace(' ','_',$db->f('name')));
  497. $bookauthor = $db->f('name');
  498. $t->set_var('books_by_whom',trans('books_by_whom',$bookauthor));
  499. $sortorder = req_word('sort_order');
  500. switch($sortorder) {
  501. case 'title': $order = ' ORDER BY title'; $sortorder='title'; break;
  502. case 'date' : $order = ' ORDER BY timestamp DESC'; $sortorder='date'; break;
  503. default : $order = '';
  504. }
  505. $all = $db->lim_query('SELECT id,title,isbn,timestamp FROM books WHERE id IN (SELECT book FROM books_authors_link WHERE author='.$aid.')'.$order, $offset, $perpage);
  506. $more = FALSE;
  507. while ( $db->next_record() ) {
  508. $t->set_var('bid',$db->f('id'));
  509. $t->set_var('title',$db->f('title'));
  510. $t->set_var('isbn',$db->f('isbn'));
  511. $t->parse('item','itemblock',$more);
  512. $more = TRUE;
  513. }
  514. // pagination:
  515. $t->set_var('sortorder',$sortorder);
  516. paginate('?prefix=author_id&amp;lang='.$GLOBALS['use_lang'].'&amp;sort_order='.$sortorder.'&amp;query='.$aid.'&amp;pageformat='.$pageformat,$offset,$all);
  517. // Ads (ASAP):
  518. if ( $pageformat=='html' && $ads_asap_initial && !empty($ads_asap_pubkey) && !empty($ads_asap_privkey) && !empty($amazonID) ) { // care for ads
  519. require_once('./lib/asap.php');
  520. if ( $ads_asap_webvertizer && !empty($ads_asap_webvertizer_domain) ) setAutoAds('authors','list','regex');
  521. $asap = getAds($ads_asap_default_string,$ads_asap_lang);
  522. $adblock = getAdBlock($asap);
  523. } else $adblock = '';
  524. if ( !empty($adblock) ) $t->set_var('ad_css','<LINK REL="stylesheet" TYPE="text/css" HREF="'.$relurl.'tpl/html/asap.css">');
  525. if ($pageformat=='html') $t->set_var('adblock',$adblock);
  526. else header("Content-type: application/atom+xml;profile=opds-catalog");
  527. // Done:
  528. $t->pparse("out","template");
  529. exit;
  530. //------------------------------[ List of all books by title requested ]---
  531. case 'titles':
  532. $sortorder = req_word('sort_order');
  533. switch($sortorder) {
  534. case 'title': $order = ' ORDER BY b.title'; $sortorder='title'; break;
  535. case 'name' : $order = ' ORDER BY a.name'; $sortorder='name'; break;
  536. case 'time' : $order = ' ORDER BY b.timestamp DESC'; $sortorder='time'; break;
  537. default : $order = ''; $sortorder=''; break;
  538. }
  539. $all = $db->lim_query('SELECT b.id,b.title,b.isbn,a.name,b.timestamp FROM books b,books_authors_link bl,authors a WHERE b.id=bl.book and a.id=bl.author '.$order, $offset, $perpage);
  540. parse_titles($t,$db,$offset,$all,'?prefix=titles&amp;lang='.$GLOBALS['use_lang'].'&amp;sort_order='.$sortorder);
  541. exit;
  542. //----------------------------------------[ List of all tags requested ]---
  543. case 'tags':
  544. $sortorder = req_word('sort_order');
  545. switch($sortorder) {
  546. case 'books': $order = ' ORDER BY num DESC'; $sortorder='books'; break;
  547. case 'title':
  548. default : $order = ' ORDER BY name'; $sortorder='title'; break;
  549. }
  550. $all = $db->lim_query('SELECT t.id id,t.name name, count(b.id) num FROM tags t,books_tags_link bt, books b WHERE bt.book=b.id and bt.tag = t.id GROUP BY t.id'.$order, $offset, $perpage);
  551. $t->set_file(array("template"=>"tags.tpl"));
  552. $t->set_block('template','itemblock','item');
  553. $t->set_block('template','prevblock','prev');
  554. $t->set_block('template','nextblock','next');
  555. set_basics($t);
  556. $t->set_var('tags_list',trans('tags'));
  557. $t->set_var('num_allbooks',$allbookcount);
  558. if ($allbookcount==1) $t->set_var('allbooks',trans('book'));
  559. else $t->set_var('allbooks',trans('books'));
  560. $more = FALSE;
  561. #id,name,num_books,books
  562. while ( $db->next_record() ) {
  563. $tag['num_books'] = $db->f('num');
  564. $t->set_var('id',$db->f('id'));
  565. $t->set_var('name',$db->f('name'));
  566. $t->set_var('num_books',$tag['num_books']);
  567. if ($tag['num_books']==1) $t->set_var('books',trans('book'));
  568. else $t->set_var('books',trans('books'));
  569. $t->parse('item','itemblock',$more);
  570. $more = TRUE;
  571. }
  572. // pagination:
  573. $t->set_var('sortorder',$sortorder);
  574. paginate('?prefix=tags&amp;lang='.$GLOBALS['use_lang'].'&amp;sort_order='.$sortorder.'&amp;pageformat='.$pageformat,$offset,$all);
  575. // Ads (ASAP):
  576. if ( $pageformat=='html' && $ads_asap_initial && !empty($ads_asap_pubkey) && !empty($ads_asap_privkey) && !empty($amazonID) ) { // care for ads
  577. require_once('./lib/asap.php');
  578. if ( $ads_asap_webvertizer && !empty($ads_asap_webvertizer_domain) ) setAutoAds('tags','initial','regex');
  579. $asap = getAds($ads_asap_default_string,$ads_asap_lang);
  580. $adblock = getAdBlock($asap);
  581. } else $adblock = '';
  582. if ( !empty($adblock) ) $t->set_var('ad_css','<LINK REL="stylesheet" TYPE="text/css" HREF="'.$relurl.'tpl/html/asap.css">');
  583. if ($pageformat=='html') $t->set_var('adblock',$adblock);
  584. else header("Content-type: application/atom+xml;profile=opds-catalog");
  585. // Done:
  586. $t->pparse("out","template");
  587. exit;
  588. //---------------------------[ List of books for a given tag requested ]---
  589. case 'tag_id':
  590. $t->set_file(array("template"=>"tag.tpl"));
  591. $t->set_block('template','itemblock','item');
  592. $t->set_block('template','prevblock','prev');
  593. $t->set_block('template','nextblock','next');
  594. set_basics($t);
  595. $t->set_var('tags_list',trans('tags'));
  596. $tag_name = req_alnum('name');
  597. if ( empty($tag_name) ) $tag_id = req_int('query');
  598. else {
  599. $sarr = get_idbyname('SELECT id FROM tags WHERE','name',$tag_name);
  600. $tag_id = (int) $sarr['id'];
  601. }
  602. $db->query('SELECT name FROM tags WHERE id='.$tag_id);
  603. $db->next_record();
  604. $tagname = $db->f('name');
  605. $t->set_var('books_with_tag',trans('books_with_tag',$tagname));
  606. $t->set_var('aid',$tag_id);
  607. $sortorder = req_word('sort_order');
  608. switch($sortorder) {
  609. case 'title' : $order = ' ORDER BY b.title'; $sortorder = 'title'; break;
  610. case 'author': $order = ' ORDER BY a.name'; $sortorder = 'author'; break;
  611. default : $order = '';
  612. }
  613. $all = $db->lim_query('SELECT b.id,b.title,b.isbn,a.name FROM books b,authors a, books_authors_link l WHERE b.id=l.book AND a.id=l.author AND b.id IN (SELECT book FROM books_tags_link WHERE tag='.$tag_id.')'.$order, $offset, $perpage);
  614. $books = array();
  615. while ( $db->next_record() ) {
  616. $bid = $db->f('id');
  617. if ( isset($books[$bid]) ) {
  618. $books[$bid]['author'] .= ', '.$db->f('name');
  619. } else {
  620. $books[$bid] = array('id'=>$bid,'title'=>$db->f('title'),'isbn'=>$db->f('isbn'),'author'=>$db->f('name'));
  621. }
  622. }
  623. $more = FALSE;
  624. foreach ( $books as $book ) {
  625. $t->set_var('bid',$book['id']);
  626. $t->set_var('title_by_author',trans('title_by_author',$book['title'],$book['author']));
  627. $t->set_var('isbn',$book['isbn']);
  628. $t->parse('item','itemblock',$more);
  629. $more = TRUE;
  630. }
  631. // pagination:
  632. $t->set_var('sortorder',$sortorder);
  633. paginate('?prefix=tag_id&amp;lang='.$GLOBALS['use_lang'].'&amp;sort_order='.$sortorder.'&amp;query='.$tag_id.'&amp;pageformat='.$pageformat,$offset,$all);
  634. // Ads (ASAP):
  635. if ( $pageformat=='html' && $ads_asap_initial && !empty($ads_asap_pubkey) && !empty($ads_asap_privkey) && !empty($amazonID) ) { // care for ads
  636. require_once('./lib/asap.php');
  637. if ( $ads_asap_webvertizer && !empty($ads_asap_webvertizer_domain) ) setAutoAds($tagname,'genre','regex');
  638. // tag/genre specific ad string
  639. $adstring = str_replace('keywords::',"keywords::+$tagname +",$ads_asap_default_string);
  640. if ( !empty($ads_asap_genre_strings) && file_exists($ads_asap_genre_strings) ) {
  641. $asap_genres = @json_decode(file_get_contents($ads_asap_genre_strings));
  642. if ( is_object($asap_genres) ) {
  643. if ( isset($asap_genres->$tagname) ) $adstring = $asap_genres->$tagname;
  644. }
  645. }
  646. $asap = @getAds($adstring,$ads_asap_lang); // need to hide error message for some genres
  647. // TODO: genre specific string
  648. $adblock = getAdBlock($asap);
  649. } else $adblock = '';
  650. if ( !empty($adblock) ) $t->set_var('ad_css','<LINK REL="stylesheet" TYPE="text/css" HREF="'.$relurl.'tpl/html/asap.css">');
  651. if ($pageformat=='html') $t->set_var('adblock',$adblock);
  652. else header("Content-type: application/atom+xml;profile=opds-catalog");
  653. // Done:
  654. $t->pparse("out","template");
  655. exit;
  656. //----------------------------------------------------[ List of series ]---
  657. case 'series':
  658. $t->set_file(array("template"=>"series.tpl"));
  659. $t->set_block('template','itemblock','item');
  660. $t->set_block('template','prevblock','prev');
  661. $t->set_block('template','nextblock','next');
  662. set_basics($t);
  663. $t->set_var('series_list',trans('series'));
  664. $sortorder = req_word('sort_order');
  665. switch($sortorder) {
  666. case 'title': $order = ' ORDER BY name'; $sortorder='title'; break;
  667. case 'books': $order = ' ORDER BY num DESC'; $sortorder='books'; break;
  668. default : $order = '';
  669. }
  670. $all = $db->lim_query('SELECT s.id id,s.name name, count(b.id) num FROM series s, books b, books_series_link bs WHERE bs.book=b.id AND bs.series=s.id GROUP BY s.id'.$order, $offset, $perpage);
  671. $t->set_var('num_allbooks',$allbookcount);
  672. if ($allbookcount==1) $t->set_var('allbooks',trans('book'));
  673. else $t->set_var('allbooks',trans('books'));
  674. $more = FALSE;
  675. #id,name,num_books,books
  676. while ( $db->next_record() ) {
  677. $tag['num_books'] = $db->f('num');
  678. $t->set_var('id',$db->f('id'));
  679. $t->set_var('name',$db->f('name'));
  680. $t->set_var('num_books',$tag['num_books']);
  681. if ($tag['num_books']==1) $t->set_var('books',trans('book'));
  682. else $t->set_var('books',trans('books'));
  683. $t->parse('item','itemblock',$more);
  684. $more = TRUE;
  685. }
  686. // pagination:
  687. $t->set_var('sortorder',$sortorder);
  688. paginate('?prefix=series&amp;lang='.$GLOBALS['use_lang'].'&amp;sort_order='.$sortorder.'&amp;pageformat='.$pageformat,$offset,$all);
  689. // Ads (ASAP):
  690. if ( $pageformat=='html' && $ads_asap_initial && !empty($ads_asap_pubkey) && !empty($ads_asap_privkey) && !empty($amazonID) ) { // care for ads
  691. require_once('./lib/asap.php');
  692. if ( $ads_asap_webvertizer && !empty($ads_asap_webvertizer_domain) ) setAutoAds('series','initial','regex');
  693. $asap = getAds($ads_asap_default_string,$ads_asap_lang);
  694. $adblock = getAdBlock($asap);
  695. } else $adblock = '';
  696. if ( !empty($adblock) ) $t->set_var('ad_css','<LINK REL="stylesheet" TYPE="text/css" HREF="'.$relurl.'tpl/html/asap.css">');
  697. if ($pageformat=='html') $t->set_var('adblock',$adblock);
  698. else header("Content-type: application/atom+xml;profile=opds-catalog");
  699. // Done:
  700. $t->pparse("out","template");
  701. exit;
  702. //-------------------------[ List of books for a given serie requested ]---
  703. case 'series_id':
  704. $t->set_file(array("template"=>"serie.tpl"));
  705. $t->set_block('template','itemblock','item');
  706. $t->set_block('template','prevblock','prev');
  707. $t->set_block('template','nextblock','next');
  708. set_basics($t);
  709. $t->set_var('back_to_series',trans('back_to_series'));
  710. $t->set_var('sort_index',trans('sort_index'));
  711. $series_name = req_alnum('name');
  712. if ( empty($series_name) ) $series_id = req_int('query');
  713. else {
  714. $sarr = get_idbyname('SELECT id,name FROM series WHERE','name',$series_name);
  715. $series_id = (int) $sarr['id'];
  716. }
  717. $db->query('SELECT name FROM series WHERE id='.$series_id);
  718. $db->next_record();
  719. $seriesname = $db->f('name');
  720. $t->set_var('books_in_serie',trans('books_in_serie',$seriesname));
  721. $t->set_var('aid',$series_id);
  722. $sortorder = req_word('sort_order');
  723. switch($sortorder) {
  724. case 'title' : $order = ' ORDER BY b.title'; $sortorder = 'title'; break;
  725. case 'author': $order = ' ORDER BY a.name'; $sortorder = 'author'; break;
  726. case 'index' : $order = ' ORDER BY b.series_index'; $sortorder = 'index'; break;
  727. default : $order = '';
  728. }
  729. $all = $db->lim_query('SELECT b.id id,b.title title,b.isbn isbn,b.series_index series_index, a.name name from books b, books_series_link bs, books_authors_link ba, authors a WHERE bs.series='.$series_id.' AND bs.book=b.id AND ba.book=b.id AND ba.author=a.id'.$order, $offset, $perpage);
  730. $books = array();
  731. while ( $db->next_record() ) {
  732. $bid = $db->f('id');
  733. if ( isset($books[$bid]) ) {
  734. $books[$bid]['author'] .= ', '.$db->f('name');
  735. } else {
  736. $books[$bid] = array('id'=>$bid,'title'=>$db->f('title'),'isbn'=>$db->f('isbn'),'author'=>$db->f('name'));
  737. }
  738. }
  739. $more = FALSE;
  740. foreach ( $books as $book ) {
  741. $t->set_var('bid',$book['id']);
  742. $t->set_var('title_by_author',trans('title_by_author',$book['title'],$book['author']));
  743. $t->set_var('isbn',$book['isbn']);
  744. $t->parse('item','itemblock',$more);
  745. $more = TRUE;
  746. }
  747. // pagination:
  748. $t->set_var('sortorder',$sortorder);
  749. paginate('?prefix=series_id&amp;lang='.$GLOBALS['use_lang'].'&amp;sort_order='.$sortorder.'&amp;query='.$series_id.'&amp;pageformat='.$pageformat,$offset,$all);
  750. // Ads (ASAP):
  751. if ( $pageformat=='html' && $ads_asap_initial && !empty($ads_asap_pubkey) && !empty($ads_asap_privkey) && !empty($amazonID) ) { // care for ads
  752. require_once('./lib/asap.php');
  753. if ( $ads_asap_webvertizer && !empty($ads_asap_webvertizer_domain) ) setAutoAds('series','list','regex');
  754. $sn = preg_replace('!.*\(.*?(\w+)\)$!u','$1',$seriesname);
  755. if ( !empty($sn) ) $adstring = str_replace('keywords::',"keywords::+$sn +",$ads_asap_default_string);
  756. else $adstring = $ads_asap_default_string;
  757. $asap = @getAds($adstring,$ads_asap_lang); // need to hide error message for some terms
  758. $adblock = getAdBlock($asap);
  759. } else $adblock = '';
  760. if ( !empty($adblock) ) $t->set_var('ad_css','<LINK REL="stylesheet" TYPE="text/css" HREF="'.$relurl.'tpl/html/asap.css">');
  761. if ($pageformat=='html') $t->set_var('adblock',$adblock);
  762. else header("Content-type: application/atom+xml;profile=opds-catalog");
  763. // Done:
  764. $t->pparse("out","template");
  765. exit;
  766. //----------------------------------------------[ Handle a single book ]---
  767. case '':
  768. // Display book details
  769. if (isset($_REQUEST['action']) && $_REQUEST['action']=='bookdetails') {
  770. $bname = req_alnum('name');
  771. if ( empty($bname) ) $bookid = req_int('book');
  772. else {
  773. $sarr = get_idbyname('SELECT id FROM books WHERE','title',$bname);
  774. $bookid = (int) $sarr['id'];
  775. }
  776. if ( isset($sarr) && $sarr['match'] == 'none' ) {
  777. $author = trans('not_available');
  778. goto NoSuchBook;
  779. }
  780. $db->query("SELECT id,name FROM authors WHERE id IN (SELECT author FROM books_authors_link WHERE book=$bookid)");
  781. $author = '';
  782. while ( $db->next_record() ) {
  783. $author .= ', ' . $db->f('name');
  784. $authors[] = array('id'=>$db->f('id'),'name'=>$db->f('name'));
  785. }
  786. $author = substr($author,2);
  787. if ( $cover_mode == 'calibre' ) $db->query("SELECT title,isbn,series_index,strftime('%Y-%m-%dT%H:%M:%S',timestamp) pubdate,'' as uri FROM books WHERE id=".$bookid);
  788. else $db->query("SELECT title,isbn,series_index,strftime('%Y-%m-%dT%H:%M:%S',timestamp) pubdate,uri FROM books WHERE id=".$bookid);
  789. if ( !$db->next_record() ) { // we don't have a book with this ID
  790. NoSuchBook:
  791. header("HTTP/1.0 404 Not Found");
  792. $book = array('title'=>trans('not_available'),'isbn'=>'','tags'=>'','series'=>'','series_index'=>'','uri'=>'','author'=>trans('not_available'),
  793. 'pubdate'=>'1970-01-01T00:00:00','pubdate_human'=>'01-01-1970 00:00','publisher'=>'','comment'=>trans('no_such_book'),'rating'=>0);
  794. $files = $authors = array();
  795. goto Parse;
  796. }
  797. $book = array(
  798. 'title'=>$db->f('title'), 'isbn'=>$db->f('isbn'), 'tags'=>'', 'series_index'=>$db->f('series_index'),
  799. 'uri'=>$db->f('uri'), 'author'=>$author,
  800. 'pubdate'=>$db->f('pubdate').$timezone, 'pubdate_human'=>date("d-m-Y H:i",strtotime($db->f('pubdate')))
  801. );
  802. $db->query("SELECT id,name FROM tags WHERE id IN (SELECT tag FROM books_tags_link WHERE book=$bookid)");
  803. while ( $db->next_record() ) {
  804. $book['tags'] .= ', '.$db->f('name');
  805. }
  806. if (strlen($book['tags'])) $book['tags'] = substr($book['tags'],2);
  807. $db->query("SELECT id,name FROM series WHERE id IN (SELECT series FROM books_series_link WHERE book=$bookid)");
  808. $db->next_record();
  809. $book['series'] = $book['series_name'] = $db->f('name');
  810. $book['series_id'] = $db->f('id');
  811. if (!empty($book['series'])) $book['series'] .= ' (#'.$book['series_index'].')';
  812. $db->query("SELECT name FROM publishers WHERE id IN (SELECT publisher FROM books_publishers_link WHERE book=$bookid)");
  813. $db->next_record();
  814. $book['publisher'] = $db->f('name');
  815. $db->query("SELECT text FROM comments WHERE book=$bookid");
  816. $db->next_record();
  817. $book['comment'] = htmlentities($db->f('text'));
  818. $db->query("SELECT r.rating FROM ratings r, books_ratings_link br WHERE br.book=$bookid AND r.id=br.rating");
  819. if ( $db->next_record() ) {
  820. if ( $cover_mode == 'calibre' ) $book['rating'] = round( $db->f('rating') / 2); // Calibre has 0..10, we have only 0..5
  821. else $book['rating'] = $db->f('rating');
  822. } else $book['rating'] = 0;
  823. $files = get_filenames($db,$bookid);
  824. Parse:
  825. $t->set_file(array("template"=>"book.tpl"));
  826. $t->set_block('template','authorblock','author');
  827. $t->set_block('template','serialblock','serial');
  828. $t->set_block('template','flattrblock','flattr');
  829. $t->set_block('template','flattrstaticblock','flattrstatic');
  830. $t->set_block('template','datablock','data');
  831. $t->set_block('template','itemblock','item');
  832. $t->set_block('template','coverblock','cover');
  833. $t->set_block('template','fakecoverblock','fakecover');
  834. if ($pageformat=='html') {
  835. $t->set_block('template','donationblock','donation');
  836. $t->set_block('donationblock','donaelemblock','donaelem');
  837. }
  838. set_basics($t);
  839. $t->set_var('back_to_authors',trans('back_to_authors'));
  840. $t->set_var('data_name',trans('title'));
  841. $t->set_var('data_data',$book['title']);
  842. $t->parse('data','datablock');
  843. if ( $pageformat=='html' ) {
  844. if ( !empty($GLOBALS['donationInfo']) && file_exists($GLOBALS['donationInfo'])) {
  845. $dona = get_donationInfo($GLOBALS['donationInfo']);
  846. $parsed = false; foreach ( $dona as $don ) {
  847. $t->set_var('donation_url',$don['link']);
  848. $t->set_var('donation_title',$don['alt']);
  849. $t->set_var('donation_img',$don['img']);
  850. $t->parse('donaelem','donaelemblock',$parsed);
  851. $parsed = true;
  852. }
  853. $t->parse('donation','donationblock');
  854. } else {
  855. $t->set_var('donation','');
  856. }
  857. }
  858. // Do the FlattR
  859. if ( empty($GLOBALS['flattrID']) || empty($authors) ) {
  860. $t->set_var('flattr','');
  861. $t->set_var('flattrstatic','');
  862. } else {
  863. $t->set_var('flattrID',$GLOBALS['flattrID']);
  864. $t->set_var('flattred_url',urlencode($GLOBALS['baseurl'].'?lang='.$GLOBALS['use_lang'].'pageformat=html&action=bookdetails&book='.$bookid));
  865. switch ( $flattrMode ) {
  866. case 'dynamic':
  867. $t->parse('flattr','flattrblock');
  868. $t->set_var('flattrstatic','');
  869. break;
  870. case 'static':
  871. default :
  872. $t->set_var('flattr','');
  873. $t->set_var('urlenc_title_by_author',urlencode($book['title']." by $author"));
  874. $t->parse('flattrstatic','flattrstaticblock');
  875. }
  876. }
  877. $more = FALSE;
  878. foreach ($authors as $aut) {
  879. $t->set_var('aid',$aut['id']);
  880. $t->set_var('authors_page',trans('all_books_by_whom',$aut['name']));
  881. $t->parse('author','authorblock',$more);
  882. $more = TRUE;
  883. }
  884. $more = FALSE;
  885. if ( isset($book['series_id']) ) {
  886. $t->set_var('id',$book['series_id']);
  887. $t->set_var('series_page',trans('all_books_in_serie',$book['series_name']));
  888. $t->parse('serial','serialblock',$more);
  889. $more = TRUE;
  890. }
  891. foreach (array('author','isbn','tags','series','publisher','uri','rating') as $field) {
  892. if ($field=='tags' && $pageformat=='html' && !empty($booksearchservices) && !empty($authors)) { // book-search; this should follow the ISBNs
  893. $iurls = get_booksearchurls(str_replace(',',' ',$author),$book['title']);
  894. $text = "<SPAN ID='booksearch'>";
  895. foreach ($iurls as $iurl) $text .= " <A HREF='".$iurl['url']."'>".$iurl['name']."</A>";
  896. $text .= "</SPAN>";
  897. $t->set_var('data_name',trans('title_websearch'));
  898. $t->set_var('data_data',$text);
  899. $t->parse('data','datablock',TRUE);
  900. }
  901. if ( empty($book[$field]) ) continue;
  902. if ($field=='series') $t->set_var('data_name',trans('serie'));
  903. else $t->set_var('data_name',trans($field));
  904. switch ($field) {
  905. case 'uri' :
  906. $t->set_var('data_data',"<A HREF='".$book[$field]."'>".$book[$field]."</A>");
  907. $t->set_var('dataclass'," STYLE='line-height:1.6em;'");
  908. break;
  909. case 'isbn':
  910. $iurls = get_isbnurls($book[$field]);
  911. $text = $book[$field];
  912. if ($pageformat=='html') {
  913. $text .= "<SPAN ID='isbnsearch'>";
  914. foreach ($iurls as $iurl) $text .= " <A HREF='".$iurl['url']."'>".$iurl['name']."</A>";
  915. $text .= "</SPAN>";
  916. }
  917. $t->set_var('data_data',$text);
  918. $t->set_var('dataclass'," STYLE='line-height:1.6em;'");
  919. break;
  920. default :
  921. $t->set_var('data_data',$book[$field]);
  922. $t->set_var('dataclass','');
  923. break;
  924. }
  925. $t->parse('data','datablock',TRUE);
  926. }
  927. if ( !empty($book['rating']) ) {
  928. $t->set_var('data_name',trans('rating'));
  929. $t->set_var('data_data','<IMG SRC="'.$relurl.'tpl/icons/rating_'.$book['rating'].'.gif" ALT="Rating '.$book['rating'].'"/>');
  930. $t->parse('data','datablock',TRUE);
  931. }
  932. $t->set_var("field_download",trans('download'));
  933. $t->set_var('id',$bookid);
  934. $t->set_var('title_by_author',trans('title_by_author',$book['title'],$author));
  935. $t->set_var('booktitle',$book['title']); // used by OPDS only + fakecover
  936. $t->set_var('authorname',$author); // used by OPDS only + fakecover
  937. $t->set_var('field_comment',trans('comment'));
  938. if ( empty($book['comment']) ) {
  939. if ( $pageformat == 'opds' ) $t->set_var('comment',trans('not_available'));
  940. else $t->set_var('comment',trans('comment').' '.trans('not_available'));
  941. } else {
  942. $comm = html_entity_decode($book['comment']);
  943. if ( $pageformat=='opds' ) {
  944. $comm = preg_replace_callback('!(</?\w+)(.*?>)!', function ($word) { return strtolower($word[1]).$word[2]; }, $comm); // OPDS/XML wants tags lower-case!
  945. $comm = preg_replace_callback('! (CLASS|ID|SRC|HREF|ALT)=!', function($word) { return ' ' . strtolower($word[1]); }, $comm); // Same for attributes
  946. $comm = stripslashes($comm);
  947. }
  948. $t->set_var('comment',$comm);
  949. }
  950. $t->set_var('pubdate',$book['pubdate']);
  951. $t->set_var('pubdate_human',$book['pubdate_human']);
  952. $more = FALSE;
  953. $formats = get_formats();
  954. foreach ($files as $file) {
  955. $file['format'] = strtolower($file['format']); // Calibre uses UPPERcase
  956. if ( empty($formats[$file['format']]) ) {
  957. $logger->error('Unsupported format "'.$file['format'].'" for book "'.$file['name'].'"','DETAILS');
  958. continue;
  959. }
  960. $t->set_var('ftype',$formats[$file['format']]['mimetype']);
  961. $t->set_var('ftype_human',$formats[$file['format']]['ftype_human']);
  962. $t->set_var('ftitle',$formats[$file['format']]['ftitle']);
  963. $covername = $file['path'].DIRECTORY_SEPARATOR.$file['name']; // file w/o ext
  964. $t->set_var('flength',$file['size']);
  965. $t->set_var('flength_human',number_format(round($file['size']/1024)).'kB');
  966. $t->set_var('format',$file['format']);
  967. $t->parse('item','itemblock',$more);
  968. $more = TRUE;
  969. }
  970. // book cover
  971. if ($use_lang=='cal') $covername = $cover_base.DIRECTORY_SEPARATOR.$use_lang.DIRECTORY_SEPARATOR.$bookid;
  972. if (!empty($covername)) $coverimg = $filefuncs->getCover($covername);
  973. if ( !empty($coverimg) && file_exists($coverimg) && is_readable($coverimg) ) {
  974. $cover_type = pathinfo($coverimg)['extension']; if ( $cover_type == 'jpg' ) $cover_type = 'jpeg';
  975. $t->set_var('cover_type',$cover_type); // MimeType (opds only)
  976. $t->set_var('cover_src',str_replace(' ','%20',$coverimg));
  977. $t->set_var('cover_width',$cover_width);
  978. $t->parse('cover','coverblock');
  979. } elseif ($cover_fake_fallback) {
  980. $t->parse('fakecover','fakecoverblock');
  981. }
  982. // Ads (if wanted)
  983. if ( $ads_bookdetails && $pageformat=='html' ) {
  984. switch ( strtolower($ads_bookdetails_type) ) {
  985. case 'flash': // Amazon Flash Widget
  986. if ( empty($amazonID) ) $adblock = '';
  987. else {
  988. require_once('./lib/adflash.php');
  989. $adblock = getFlashAd($amazonID,$book['title'],$author,$book['tags']);
  990. }
  991. break;
  992. case 'asap': // Amazon Simple Ads (ASAP)
  993. if ( empty($ads_asap_pubkey) || empty($ads_asap_privkey) || empty($amazonID) ) $adblock = '';
  994. else {
  995. require_once('./lib/asap.php');
  996. if ( $ads_asap_webvertizer && !empty($ads_asap_webvertizer_domain) ) {
  997. foreach( explode(', ',$book['tags']) as $tagname ) setAutoAds($tagname,'genre','regex');
  998. setAutoAds(str_replace("'","",$book['title']),'book','regex');
  999. }
  1000. // tag/genre specific ad string
  1001. $adstring = $ads_asap_default_string;
  1002. if ( !empty($ads_asap_genre_strings) && file_exists($ads_asap_genre_strings) ) {
  1003. $asap_genres = @json_decode(file_get_contents($ads_asap_genre_strings));
  1004. if ( is_object($asap_genres) ) {
  1005. $tagname = explode(', ',$book['tags'])[0];
  1006. if ( isset($asap_genres->$tagname) ) $adstring = $asap_genres->$tagname;
  1007. }
  1008. }
  1009. // book specifica
  1010. $adstring1 = $adstring; // backup
  1011. foreach(explode(', ',$author) as $aut) {
  1012. $sn = preg_replace('!(.*\s+|)(\w+)$!u','$2',$aut); // author's last name
  1013. if ( !empty($sn) ) {
  1014. $adstring = str_replace('keywords::',"keywords::+$sn +",$adstring);
  1015. $adstring = str_replace('++','+',$adstring);
  1016. }
  1017. }
  1018. $asap = @getAds($adstring,$ads_asap_lang); // need to hide error message for some terms
  1019. if ( empty($asap['items']) ) $asap = @getAds($adstring1,$ads_asap_lang); // fallback; sometimes book-specific additions cause empty ads
  1020. $adblock = getAdBlock($asap);
  1021. }
  1022. if ( !empty($adblock) ) $t->set_var('ad_css','<LINK REL="stylesheet" TYPE="text/css" HREF="'.$relurl.'tpl/html/asap.css">');
  1023. break;
  1024. default:
  1025. $adblock = '';
  1026. break;
  1027. }
  1028. $t->set_var('adblock',$adblock);
  1029. } else {
  1030. $t->set_var('adblock','');
  1031. }
  1032. // end ads
  1033. if ( $pageformat=="opds" ) header("Content-type: application/atom+xml");
  1034. $t->pparse("out","template");
  1035. exit;
  1036. // Send the requested book for download
  1037. } elseif (isset($_REQUEST['action']) && $_REQUEST['action']=='getbook') { // bookid=req[book], req[format] = epub/mobi
  1038. $bname = req_alnum('name');
  1039. if ( empty($bname) ) $bookid = req_int('book');
  1040. else {
  1041. $sarr = get_idbyname('SELECT id FROM books WHERE','title',$bname);
  1042. $bookid = (int) $sarr['id'];
  1043. }
  1044. $files = get_filenames($db,$bookid,req_word('format'));
  1045. $book = $files[0]['path'].'/'.$files[0]['name'].'.'.strtolower($files[0]['format']);
  1046. if ($fd = fopen ($book,"rb")) {
  1047. if ( empty($dllogfile) ) { // log DL to default log if no special log is set up
  1048. $logger->info($book,'DOWNLOAD');
  1049. } else {
  1050. $dllogger->info($book,'DOWNLOAD');
  1051. }
  1052. $fformats = get_formats();
  1053. header("Content-type: ".$fformats[strtolower($files[0]['format'])]['mimetype']); // Calibre uses UPPERcase
  1054. header("Content-Disposition: attachment; filename=\"".$files[0]['name'].'.'.strtolower($files[0]['format']."\""));
  1055. header("Content-length: ".filesize($book));
  1056. fpassthru($fd);
  1057. fclose($fd);
  1058. exit;
  1059. }
  1060. exit;
  1061. }
  1062. }
  1063. ?>