├── .gitignore ├── README.md ├── config.advanced.php ├── config.simple.php ├── doc ├── LICENSE ├── README.txt ├── epubmeta ├── history ├── install_de.txt ├── metadata.txt └── startpage.png ├── index.php ├── install ├── authors.sql ├── books.sql ├── books_authors_link.sql ├── books_publishers_link.sql ├── books_ratings_link.sql ├── books_series_link.sql ├── books_tags_link.sql ├── comments.sql ├── conversion_options.sql ├── data.sql ├── install.sh ├── publishers.sql ├── ratings.sql ├── series.sql └── tags.sql ├── lang ├── ebookterms.de ├── ebookterms.en ├── trans.de └── trans.en ├── lib ├── adflash.php ├── asap.php ├── asap_genres.json.sample ├── booksearch.csv ├── class.csv.php ├── class.db.php ├── class.epub.php ├── class.epubdesc.php ├── class.filefuncs.php ├── class.logging.php ├── class.template.php ├── class.translation.php ├── common.php ├── db_sqlite3.php ├── formats.csv └── isbnsearch.csv ├── scan.php ├── scan_calibre_covers.php ├── scan_de.php ├── scan_en.php └── tpl ├── html ├── asap.css ├── asap3col.tpl ├── author.tpl ├── authors.tpl ├── book.tpl ├── flashads.tpl ├── index.tpl ├── search.tpl ├── serie.tpl ├── series.tpl ├── style.css ├── tag.tpl ├── tags.tpl └── titles.tpl ├── icons ├── 1left.png ├── 1left_grey.png ├── 1right.png ├── 1right_grey.png ├── 2left.png ├── 2left_grey.png ├── 2right.png ├── 2right_grey.png ├── 3left.png ├── 3left_grey.png ├── 3right.png ├── 3right_grey.png ├── alpha.png ├── author.png ├── authors.png ├── book.png ├── bookcase.png ├── bookseries.png ├── date.png ├── find.png ├── flattr-badge.png ├── home.png ├── liberapay-badge.png ├── rating_1.gif ├── rating_2.gif ├── rating_3.gif ├── rating_4.gif ├── rating_5.gif ├── tag.png ├── tags.png └── world.png └── opds ├── author.tpl ├── authors.tpl ├── book.tpl ├── index.tpl ├── serie.tpl ├── series.tpl ├── tag.tpl ├── tags.tpl └── titles.tpl /.gitignore: -------------------------------------------------------------------------------- 1 | # Do not upload the real config file: 2 | config.php 3 | 4 | # Ignore (temporary) backup files: 5 | *[0-9][0-9] 6 | *.bak 7 | *.ph 8 | 9 | # Ignore database files: 10 | db 11 | *.db 12 | 13 | # Ignore logs: 14 | *.log 15 | *.log.* 16 | 17 | # Ignore all scan files but the two examples: 18 | scan_[A-z][A-z].php 19 | !scan_de.php 20 | !scan_en.php 21 | 22 | # Ignore local additions: 23 | books 24 | gutenwork 25 | sync 26 | lib/Michelf 27 | covers 28 | lib/asap_genres.json 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## miniCalOPe 2 | 3 | miniCalOPe is a basic OPDS (and HTML) catalog provider for eBooks. 4 | It allows you to present your collection of eBooks (ePub & Mobi) 5 | on the net (LAN and/or WAN) in order for eBook readers to directly connect and 6 | download books, and also to allow people to access it with their web browsers 7 | to walk through it. 8 | 9 | ![Start Page](doc/startpage.png) 10 | 11 | This little program is (c)opyrighted by Andreas Itzchak Rehberg 12 | (devel AT izzysoft DOT de) and protected by the GNU Public License Version 2 13 | (GPL). For details on the License see the file LICENSE in the `doc/` directory. 14 | The contents of this archive may only be distributed all together. 15 | 16 | 17 | ### Requirements 18 | miniCalOPe requires a web server powered by PHP 5.4+ with SQLite3 to present its 19 | data. For data collection and preparation, you will need the PHP CLI (again with 20 | SQLite3 support). If you want to integrate Amazon ads, *miniCalOPe* supports that 21 | via the [Amazon Simple Api for PHP](https://github.com/IzzySoft/Asap), which then 22 | must be installed in your PHP `include_path`. 23 | 24 | 25 | ### Limitations 26 | No warranties at all :) For more, see also the other documentation files and man 27 | pages. If you think there's something missing here, don't hesitate to notify the 28 | author (me) about it - chances are quite good it will be added if possible. A 29 | good way to do this is to open an issue at the [Github Project 30 | Page](https://github.com/IzzySoft/miniCalOPe). 31 | 32 | 33 | ### Additional Information 34 | For additional information you may want to visit the 35 | [wiki](https://github.com/IzzySoft/miniCalOPe/wiki). To file a bug report, 36 | feature request or simply have a look at the current development, please use 37 | the [issues tab on the project's wiki 38 | page](https://github.com/IzzySoft/miniCalOPe/issues). To see miniCalOPe live in 39 | action, visit [Izzy's eBook Library](https://ebooks.qumran.org/) – where you 40 | can find thousands of eBooks, available at no cost for your personal use. 41 | -------------------------------------------------------------------------------- /config.advanced.php: -------------------------------------------------------------------------------- 1 | # 5 | # http://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 | # Advanced configuration (use language-specific stuff) # 11 | ############################################################################# 12 | # $Id$ 13 | 14 | #================================[ Directories ]=== 15 | // default database file (override in language specific settings) 16 | $dbfile = dirname(__FILE__).'/metadata.db'; 17 | // Where your books are stored. 18 | $bookroot = 'books'; 19 | // Which book formats you want to use (file extensions). supported are epub and mobi 20 | $bookformats = array('epub','mobi'); 21 | // file extension for book descriptions 22 | $bookdesc_ext = array('desc'); 23 | // file extension for Metadata 24 | $bookmeta_ext = 'data'; 25 | 26 | #===============================[ Checking ]=== 27 | // check for possible XML errors in description files and log them? 28 | $check_xml = TRUE; 29 | // as XML errors break OPDS, better skip the broken content. 30 | // Needs above check to be enabled in order to work. 31 | $skip_broken_xml = TRUE; 32 | // how to insert books into the database: 33 | // 'rebuild': drop all data and do a fresh insert (books are likely to get new IDs this way) 34 | // 'merge' : try to figure out what has changed (recommended; keeps the book IDs, but is slower) 35 | $scan_dbmode = 'rebuild'; 36 | 37 | #================================[ Logging ]=== 38 | // Logfile to use. Empty means no logging to file. 39 | $logfile = './minicalope.log'; 40 | // Seperate log file to log downloads to (in addition to the normal log). 41 | // Empty string to disable for now. 42 | $dllogfile = './minical_dl.log'; 43 | // Available log levels: NOLOG, EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG 44 | // Yes - case IS important. Specified level includes all before-mentioned 45 | // File log level 46 | $fileloglevel = INFO; 47 | // Screen output when running in web mode 48 | $screenloglevel = NOLOG; 49 | // Screen output when running from command line 50 | $screenloglevel_cli = INFO; 51 | // Report New/Moved/Deleted on Scan 52 | $scan_report_changes = FALSE; 53 | 54 | #============================[ Book Covers ]=== 55 | // Where to get the covers: calibre, simple, or off (none) 56 | $cover_mode = 'off'; 57 | // maximum width (in px) to display them 58 | $cover_width = '200'; 59 | // where to place the cover img links 60 | $cover_base = 'covers'; 61 | // generate fake-covers when no real img available (HTML only)? 62 | $cover_fake_fallback = TRUE; 63 | 64 | #==========================[ Scan Specials ]=== 65 | // Protect scan scripts against being run by web visitors 66 | $scan_cli_only = TRUE; 67 | // when found in .data files, what shall override definitions collected from the 68 | // directory structure? If not named here, it will be "merged". Currently 69 | // supported: author 70 | $data_overrides = array('author'); 71 | // As it's advisable to use 7-bit ASCII only for directory and file names, 72 | // one might wish to get the "correct spelling" into the Metadata by other means. 73 | // You can place ".name" files to have the directory name "replaced". Keep in 74 | // mind, however, that non-ASCII characters might cause issues with sorting. 75 | // Also remember to keep the .name and the "tag::"/"author::" in sync with .data files :) 76 | $dotname_overrides = array('author','genre'); 77 | // Whether to interprete content of .desc files as Markdown (1) or not (0). 78 | // This is the global switch. If enabled (1), you can override it per-genre 79 | // and/or per author by placing a file named .nomarkdown in their directory. 80 | $use_markdown = 0; 81 | #-------------------------------[ Checking ]--- 82 | // check for possible XML errors in description files and log them? 83 | $check_xml = TRUE; 84 | // as XML errors break OPDS, better skip the broken content. 85 | // Needs above check to be enabled in order to work. 86 | $skip_broken_xml = TRUE; 87 | // how to insert books into the database: 88 | // 'rebuild': drop all data and do a fresh insert (books are likely to get new IDs this way) 89 | // 'merge' : try to figure out what has changed (experimental; keeps the book IDs, but is slower) 90 | $scan_dbmode = 'rebuild'; 91 | #------------------------[ data extraction ]--- 92 | // we can extract some details from EPUB files. Define here whether we shall do 93 | // so, and what to extract. 94 | // Main switch to toggle it on/off: 95 | $autoExtract = TRUE; 96 | // Shall we extract covers? 0=no, 1=extract, 2=extract&resize 97 | // Covers will only be extracted if there is no cover image already. 98 | $extractCover = 1; 99 | // Which details to extract to .data files if autoExtract is enabled. 100 | // Valid values are either 'all' (to extract everything), or one or a 101 | // combination of author,isbn,publisher,rating,series,series_index,tag,title,uri 102 | // note that while 'isbn' and 'uri' are safe to use, there might be 103 | // issues with the others. For details, see issue #4 at Github: 104 | // https://github.com/IzzySoft/miniCalOPe/issues/4 105 | // Empty array switches off .data extraction completely (default) 106 | $extract2data = array(); 107 | // What to extract to .desc files if autoExtract is enabled. 108 | // Valid values are either 'all' (to extract everything), or one or a 109 | // combination of 'desc' (book description only), 'head' (the heading 110 | // part with Metadata), 'toc' (table of contents). Future versions might diverse 111 | // further). For some background, make sure to read issue #4 at Github: 112 | // https://github.com/IzzySoft/miniCalOPe/issues/4 113 | // Empty array switches off .data extraction completely (default) 114 | $extract2desc = array(); 115 | 116 | #============================[ Web Service ]=== 117 | // Timezone 118 | $timezone = '+01:00'; 119 | // Site Title 120 | $sitetitle = 'Book Server'; 121 | // Full URL to miniCalOPe 122 | $baseurl = 'http://'.$_SERVER['SERVER_NAME'].'/opds/'; 123 | // Path relative to the web servers DOCUMENT_ROOT 124 | $relurl = '/opds/'; 125 | // how many items per page 126 | $perpage = 25; 127 | // Full URL to the Wikipedia to use for author info 128 | $wikibase= 'https://de.wikipedia.org/wiki/'; 129 | // Enable some ISBN searches (empty array to disable this feature) 130 | $isbnservices = array('Amazon.DE','Bookzilla.DE','Buchhandel.DE','Google.DE','Buchfreund.DE','ZVAB.COM'); 131 | // Enable some book search services (search by author and title of the selected book; 132 | // empty array to disable this feature) 133 | $booksearchservices = array('Amazon.DE','Bookzilla.DE','Google.DE','Buchfreund.DE','ZVAB.COM'); 134 | 135 | #============================[ Person Info ]=== 136 | // about you: Name, Homepage, Email 137 | $owner = 'John Doe'; 138 | $homepage= 'http://www.johndoe.com/'; 139 | $email = 'john@johndoe.com'; 140 | 141 | #=========================[ Monetarization ]=== 142 | // While our content is served for free, we won't object to any donations :) 143 | // -=[ Donations ]- 144 | // Specifying your Donation page here will enable the corresponding button. 145 | // currently supported: liberapay 146 | $donationType = ''; 147 | $donationURL = ''; 148 | // -=[ Flattr ]=- 149 | // Setting your FlattrID here will enable the FlattR button 150 | $flattrID = ''; 151 | // The "dynamic button" shows FlattRs already received, but exposes your visitors to 3rd party sites 152 | $flattrMode = 'static'; // static|dynamic 153 | // -=[ Amazon ]=- 154 | // Your AmazonID will be used for Amazon ads (see below) as well as for the ISBN 155 | // and book-search services (see above). Simple leave it empty if you have none. 156 | $amazonID=''; 157 | // Amazon ad content for book details page? (needs AmazonID, see Personal Info) 158 | $ads_bookdetails = TRUE; 159 | $ads_bookdetails_type = 'flash'; // flash|asap 160 | $ads_bordercolor = '4D9DFC'; 161 | $ads_logocolor = 'AA4400'; 162 | // --[ Amazon Simple Api (ASAP) ]-- 163 | // your public and private API keys are needed for this. Leave empty if you have none, 164 | // this automatically disables ASAP. Same applies if you have no amazonID (see above). 165 | $ads_asap_pubkey = ''; 166 | $ads_asap_privkey = ''; 167 | $ads_asap_country = 'de'; 168 | // use additional webvertizer class 169 | $ads_asap_webvertizer = FALSE; 170 | $ads_asap_webvertizer_domain = 'ebooks'; 171 | // display ads on "initial pages" (i.e. those with no books, genres, authors) 172 | $ads_asap_initial = FALSE; 173 | // default string for "initial pages" 174 | $ads_asap_default_string = 'keywords::Buch;prodgroup::Books,DVD,Electronics'; 175 | // file with genre specific strings (JSON; see 'lib/asap_genres.json.sample' for 176 | // an example file). Empty string disables this. 177 | $ads_asap_genre_strings = ''; 178 | // disclaimer below the ads. Make sure it contains the '%cachedate%' placeholder. 179 | $ads_asap_disclaimer = 'Stand: %cachedate%
Preis & Verfügbarkeit können sich geändert haben.'; 180 | 181 | #=========================[ Language dependent stuff ]=== 182 | # Here we make use of the language-specific directories. You either can set 183 | # the $use_lang variable directly in your scan scripts for command-line use 184 | # (takes precedence), or pass "?lang=" in the URL. 185 | // check what language shall be used 186 | if ( !isset($use_lang) && isset($_REQUEST['lang']) && !empty($_REQUEST['lang']) ) $use_lang = $_REQUEST['lang']; 187 | elseif ( empty($use_lang) ) $use_lang = 'de'; // define fallback to default lang 188 | // define language-specific settings 189 | switch ($use_lang) { 190 | case 'cal': // not a lang - but use the Calibre DB 191 | $dbfile = '/mnt/data/eBooks/metadata.db'; 192 | break; 193 | case 'en': 194 | $uselangs = array('en'); 195 | $dbfile = dirname(__FILE__).'/metadata_en.db'; 196 | $isbnservices = array('Amazon.COM','Google.COM','BookCrossing.COM','EuroBuch.COM'); 197 | $ads_asap_disclaimer = 'As of %cachedate%
prices & availability might be subject to change.'; 198 | break; 199 | default : 200 | $uselangs = array('de'); 201 | break; 202 | } 203 | 204 | ?> -------------------------------------------------------------------------------- /config.simple.php: -------------------------------------------------------------------------------- 1 | # 5 | # http://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 | # Simple configuration (no language-specific stuff) # 11 | ############################################################################# 12 | # $Id$ 13 | 14 | #================================[ Directories ]=== 15 | // database file 16 | $dbfile = dirname(__FILE__).'/metadata.db'; 17 | // Where your books are stored. 18 | $bookroot = 'books'; 19 | // Which book formats you want to use (file extensions). supported are epub and mobi 20 | $bookformats = array('epub','mobi'); 21 | // file extension for book descriptions 22 | $bookdesc_ext = array('desc'); 23 | // file extension for Metadata 24 | $bookmeta_ext = 'data'; 25 | 26 | #================================[ Logging ]=== 27 | // Logfile to use. Empty means no logging to file. 28 | $logfile = './minicalope.log'; 29 | // Seperate log file to log downloads to (in addition to the normal log). 30 | // Empty string to disable for now. 31 | $dllogfile = './minical_dl.log'; 32 | // Available log levels: NOLOG, EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG 33 | // Yes - case IS important. Specified level includes all before-mentioned 34 | // File log level 35 | $fileloglevel = INFO; 36 | // Screen output when running in web mode 37 | $screenloglevel = NOLOG; 38 | // Screen output when running from command line 39 | $screenloglevel_cli = INFO; 40 | // Report New/Moved/Deleted on Scan 41 | $scan_report_changes = FALSE; 42 | 43 | #============================[ Book Covers ]=== 44 | // Where to get the covers: calibre, simple, or off (none) 45 | $cover_mode = 'off'; 46 | // maximum width (in px) to display them 47 | $cover_width = '200'; 48 | // where to place the cover img links 49 | $cover_base = 'covers'; 50 | // generate fake-covers when no real img available (HTML only)? 51 | $cover_fake_fallback = TRUE; 52 | 53 | #==========================[ Scan Specials ]=== 54 | // Protect scan scripts against being run by web visitors 55 | $scan_cli_only = TRUE; 56 | // when found in .data files, what shall override definitions collected from the 57 | // directory structure? If not named here, it will be "merged". Currently 58 | // supported: author 59 | $data_overrides = array('author'); 60 | // As it's advisable to use 7-bit ASCII only for directory and file names, 61 | // one might wish to get the "correct spelling" into the Metadata by other means. 62 | // You can place ".name" files to have the directory name "replaced". Keep in 63 | // mind, however, that non-ASCII characters might cause issues with sorting. 64 | // Also remember to keep the .name and the "tag::"/"author::" in sync with .data files :) 65 | $dotname_overrides = array('author','genre'); 66 | // Whether to interprete content of .desc files as Markdown (1) or not (0). 67 | // This is the global switch. If enabled (1), you can override it per-genre 68 | // and/or per author by placing a file named .nomarkdown in their directory. 69 | $use_markdown = 0; 70 | #-------------------------------[ Checking ]--- 71 | // check for possible XML errors in description files and log them? 72 | $check_xml = TRUE; 73 | // as XML errors break OPDS, better skip the broken content. 74 | // Needs above check to be enabled in order to work. 75 | $skip_broken_xml = TRUE; 76 | // how to insert books into the database: 77 | // 'rebuild': drop all data and do a fresh insert (books are likely to get new IDs this way) 78 | // 'merge' : try to figure out what has changed (recommended; keeps the book IDs, but is slower) 79 | $scan_dbmode = 'rebuild'; 80 | #------------------------[ data extraction ]--- 81 | // we can extract some details from EPUB files. Define here whether we shall do 82 | // so, and what to extract. 83 | // Main switch to toggle it on/off: 84 | $autoExtract = TRUE; 85 | // Shall we extract covers? 0=no, 1=extract, 2=extract&resize 86 | // Covers will only be extracted if there is no cover image already. 87 | $extractCover = 1; 88 | // Which details to extract to .data files if autoExtract is enabled. 89 | // Valid values are either 'all' (to extract everything), or one or a 90 | // combination of author,isbn,publisher,rating,series,series_index,tag,title,uri 91 | // note that while 'isbn' and 'uri' are safe to use, there might be 92 | // issues with the others. For details, see issue #4 at Github: 93 | // https://github.com/IzzySoft/miniCalOPe/issues/4 94 | // Empty array switches off .data extraction completely (default) 95 | $extract2data = array(); 96 | // What to extract to .desc files if autoExtract is enabled. 97 | // Valid values are either 'all' (to extract everything), or one or a 98 | // combination of 'desc' (book description only), 'head' (the heading 99 | // part with Metadata), 'toc' (table of contents). Future versions might diverse 100 | // further). For some background, make sure to read issue #4 at Github: 101 | // https://github.com/IzzySoft/miniCalOPe/issues/4 102 | // Empty array switches off .data extraction completely (default) 103 | $extract2desc = array(); 104 | 105 | #============================[ Web Service ]=== 106 | // Timezone 107 | $timezone = '+01:00'; 108 | // Site Title 109 | $sitetitle = 'Book Server'; 110 | // Full URL to miniCalOPe 111 | $baseurl = 'http://'.$_SERVER['SERVER_NAME'].'/opds/'; 112 | // Path relative to the web servers DOCUMENT_ROOT 113 | $relurl = '/opds/'; 114 | // how many items per page 115 | $perpage = 25; 116 | // Full URL to the Wikipedia to use for author info 117 | $wikibase= 'https://de.wikipedia.org/wiki/'; 118 | // Enable some ISBN searches (empty array to disable this feature) 119 | $isbnservices = array('Amazon.DE','Bookzilla.DE','Buchhandel.DE','Google.DE','Buchfreund.DE','ZVAB.COM'); 120 | // Enable some book search services (search by author and title of the selected book; 121 | // empty array to disable this feature) 122 | $booksearchservices = array('Amazon.DE','Bookzilla.DE','Google.DE','Buchfreund.DE','ZVAB.COM'); 123 | 124 | #=========================[ Monetarization ]=== 125 | // While our content is served for free, we won't object to any donations :) 126 | // Amazon ad content for book details page? (needs AmazonID, see Personal Info) 127 | $ads_bookdetails = FALSE; 128 | $ads_bookdetails_type = 'flash'; // flash|asap 129 | $ads_bordercolor = '4D9DFC'; 130 | $ads_logocolor = 'AA4400'; 131 | // --[ Amazon Simple Api (ASAP) ]-- 132 | // your public and private API keys are needed for this. Leave empty if you have none, 133 | // this automatically disables ASAP. Same applies if you have no amazonID (see above). 134 | $ads_asap_pubkey = ''; 135 | $ads_asap_privkey = ''; 136 | $ads_asap_country = 'de'; 137 | // use additional webvertizer class 138 | $ads_asap_webvertizer = FALSE; 139 | $ads_asap_webvertizer_domain = 'ebooks'; 140 | // display ads on "initial pages" (i.e. those with no books, genres, authors) 141 | $ads_asap_initial = FALSE; 142 | // default string for "initial pages" 143 | $ads_asap_default_string = 'keywords::Buch;prodgroup::Books,DVD,Electronics'; 144 | // file with genre specific strings (JSON; see 'lib/asap_genres.json.sample' for 145 | // an example file). Empty string disables this. 146 | $ads_asap_genre_strings = ''; 147 | // disclaimer below the ads. Make sure it contains the '%cachedate%' placeholder. 148 | $ads_asap_disclaimer = 'Stand: %cachedate%
Preis & Verfügbarkeit können sich geändert haben.'; 149 | 150 | #============================[ Person Info ]=== 151 | // about you: Name, Homepage, Email, Amazon PartnerID (leave empty if you have none) 152 | $owner = 'John Doe'; 153 | $homepage= 'http://www.johndoe.com/'; 154 | $email = 'john@johndoe.com'; 155 | $amazonID= ''; 156 | 157 | #===========================[ stuff ]=== 158 | $uselangs = array(); // empty = all 159 | 160 | ?> -------------------------------------------------------------------------------- /doc/README.txt: -------------------------------------------------------------------------------- 1 | =============================================================================== 2 | miniCalOPe (c) 2010-2013 by Itzchak Rehberg (devel@izzysoft.de) 3 | ------------------------------------------------------------------------------- 4 | $Id$ 5 | ------------------------------------------------------------------------------- 6 | Basic OPDS (and HTML) catalog provider for eBooks 7 | =============================================================================== 8 | 9 | Contents 10 | -------- 11 | 12 | 1) Copyright and warranty 13 | 2) Requirements 14 | 3) Limitations 15 | 4) What is miniCalOPe, and what does it do? 16 | 5) Installation 17 | 6) Usage 18 | 7) Additional information 19 | 20 | =============================================================================== 21 | 22 | 1) Copyright and Warranty 23 | ------------------------- 24 | 25 | This little program is (c)opyrighted by Andreas Itzchak Rehberg 26 | (devel AT izzysoft DOT de) and protected by the GNU Public License Version 2 27 | (GPL). For details on the License see the file LICENSE in this directory. The 28 | contents of this archive may only be distributed all together. 29 | 30 | =============================================================================== 31 | 32 | 2) Requirements 33 | --------------- 34 | 35 | miniCalOPe requires a web server powered by PHP 5.3+ with SQLite3 to present its data. 36 | For data collection and preparation, you will either need Calibre, or the PHP 37 | CLI (again with SQLite3 support). 38 | 39 | =============================================================================== 40 | 41 | 3) Limitations 42 | --------------- 43 | 44 | No warranties at all :) For more, see also the other documentation files and man 45 | pages. If you think there's something missing here, don't hesitate to notify the 46 | author (me) about it - chances are quite good it will be added if possible. 47 | 48 | =============================================================================== 49 | 50 | 4) What is miniCalOPe, and what does it do? 51 | ------------------------------------------- 52 | 53 | The short description above already said it: It's a basic OPDS catalog provider. 54 | 55 | More detailed: It allows you to present your collection of eBooks (ePub & Mobi) 56 | on the net (LAN and/or WAN) in order for eBook readers to directly connect and 57 | download books, and also to allow people to access it with their web browsers 58 | to walk through it. 59 | 60 | As backend, you can either use Calibre (and have a graphical interface available 61 | to manage your collection), or simply organize your books in a file structure. 62 | 63 | =============================================================================== 64 | 65 | 5) Installation and configuration 66 | --------------------------------- 67 | 68 | See install_de.txt in this directory for German short instructions. 69 | Alternatively, visit the project wiki for the same in either German or English, 70 | including more details -- URL below. 71 | 72 | =============================================================================== 73 | 74 | 6) Usage 75 | -------- 76 | 77 | Again: Please see the project wiki on this topic. 78 | 79 | =============================================================================== 80 | 81 | 7) Additional Information 82 | ------------------------- 83 | 84 | For information on the development as well as availability of new versions and 85 | the project wiki, you may want to visit the project site, i.e. 86 | http://projects.izzysoft.de/trac/minicalope 87 | or the authors website, more precisely: 88 | http://www.izzysoft.de/?topic=software 89 | 90 | To file a bug report, feature request or simply have a look at the current 91 | development, please visit http://projects.izzysoft.de/trac/minicalope 92 | -------------------------------------------------------------------------------- /doc/epubmeta: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | '); 10 | 11 | // Initialize metadata 12 | $epubname = pathinfo($epubfile)['filename']; 13 | $book = new epubdesc($epubfile); 14 | 15 | // Write .desc, .data, and cover files 16 | $book->writeFiles($epubname); 17 | 18 | ?> -------------------------------------------------------------------------------- /doc/history: -------------------------------------------------------------------------------- 1 | History for miniCalOPe 2 | ====================== 3 | 4 | v2.4.0 (03.02.2018) 5 | ------------------- 6 | ! fix epub title extraction with multiple DC entries 7 | ! fix some broken compares and RegEx warnings 8 | * improved cover detection 9 | * some other small improvements / compatibility adjustments 10 | 11 | v2.3.0 (28.12.2016) 12 | ------------------- 13 | + Integration of ASAP (Amazon Simple API for PHP, https://github.com/IzzySoft/Asap) to replace Amazon Flash Ads 14 | * protect scan scripts from being run by web visitors 15 | * MergeMode is no longer experimental (for years already), so remove that hint 16 | * responsive design improvements 17 | * avoiding error message when initializing logging class w/o log file 18 | + adding possibility to use static FlattR button for more privacy (dynamic button loads multiple 3rd party JS sources) 19 | ! fixing content-type in HTTP response for OPDS 20 | 21 | v2.2.0 (21.11.2015) 22 | ------------------- 23 | * scan now cleans up / optimizes database in its final step (VACUUM, REINDEX) for a faster DB 24 | ! (hopefully) restored Calibre compatibility (tested with 0.9.16 on Ubuntu 12.04) 25 | * speedup database processing during scan operation by use of some PRAGMAs 26 | * extracting series/series_index from calibre properties if found 27 | * some minor fixes and improvements, cleanup, etc. 28 | 29 | v2.1.0 (28.05.2015) 30 | ------------------- 31 | + adding possibility to skip pages in lists 32 | ! Scan: check_xml didn't handle tags spread across multiple lines 33 | + adding Markdown support 34 | * re-styling the book description area 35 | + adding classes to deal with metadata in .epub files directly, and a script to extract those 36 | + support for more cover formats added 37 | * some code reorg and cleanup 38 | + cover extraction now uses new epub classes, allow for "thumbnailing" 39 | + added possibility to auto-extract Metadata and book description from epub files. Make sure reading https://github.com/IzzySoft/miniCalOPe/issues/4 before using this! 40 | 41 | v2.0.0 (03.04.2015) 42 | ------------------- 43 | * removing lazyness (replacing "{base}/?args" by full "{base}/index.php?args") 44 | to speed up processing by avoiding unnecessary Apache subrequests 45 | ! last_update in title lists was wrongly set to that of the last book in list 46 | * catch "non-existing book ID exception" to display a clean page pointing this out 47 | ! if bookdetails where requested via "name", and no matching book was found, do not show #0 but a correct error page 48 | ! search results were not paging correctly (search terms were lost on page switch) 49 | * w3c fix: unescaped entities in booksearch/isbnsearch URLs (and some more) 50 | + book page: style improvements for mobile devices 51 | + Let the metadata 'author' tag overwrite the author name taken from the directory name 52 | 53 | v1.9.0 (27.10.2013) 54 | ------------------- 55 | + added optional Amazon ads (decent!) to book details page 56 | + added search in comments (book descriptions) 57 | * when $booksearchservices are disabled (empty array), suppress the entire booksearch-line on HTML book page 58 | ! in global booklist and search results, language was hardcoded ("book von author") 59 | + adding support for Flattr (see http://flattr.com/) 60 | * XML/OPDS requires tags in XHTML content divs to be all lower-case. Taking care for that now. 61 | ! some terms in tpl/opds/index.tpl have been hardcoded instead of using placeholders for translated terms (Mateusz) 62 | ! epub files with an apostroph in filename are generating warnings during scan (scan.php; Mateusz) 63 | * permitting more characters in searches, making them wildcards 64 | 65 | v1.8.0 (16.02.2013) 66 | ------------------- 67 | ! SCAN: Merge crashed when started with empty DB 68 | ! fix some logic in merging series 69 | * make alphabetical ordering default for tags 70 | * added viewport-meta to all (HTML) pages, so they should be readable on mobile devices 71 | without zooming now 72 | * have authors ordered by name per default 73 | ! fixed mime of book download links in OPDS 74 | + added FB2 to formats list 75 | * replacing "PHP short_tags" with "full tags" 76 | * several updates to the db classes (a.o. to make it work with PHP5/SQLite3 again without additional modules) 77 | 78 | v1.7.0 (24.03.2012) 79 | ------------------- 80 | ! last change of get_filenames() broke eBook download when book file was replaced but 81 | database not (yet) updated, as Content-Length (from database) did no longer match. 82 | To prevent this (admittedly rare cases), for Content-Length we revert to filesize() 83 | ! if multiple books had the same filename, scan only got the last one 84 | + SCAN: placing inserts into transactions (speedup inserts by factor 20!) 85 | + SCAN: added 'merge' mode (still experimental). Using this the scan process tries 86 | to figure out what has changed, and merge in these changes - as opposed to the 87 | 'rebuild' (as used up to now) which simply drops all information and builds all 88 | up from the scratch 89 | 90 | v1.6.3 (19.12.2011) 91 | ------------------- 92 | ! the alternate "title" (from *.data) broke the download, as title was used to check the filename 93 | ! last change of get_filenames() broke eBook download (extension was lost) 94 | * in places where lists could be ordered by date/time, inverted the order to show newest entries first 95 | 96 | v1.6.0 (08.12.2011) 97 | ------------------- 98 | + Support for Amazon Partner-ID in ISBN-Links 99 | + added check for unencoded '&' in HREFs (when check_xml enabled) 100 | + optionally generating fake covers if no cover image available (HTML only) 101 | + added "title" attribute to *.data files to specify books title (if differing from file name) 102 | + added title search links to HTML view (to search for the selected book by author + title) 103 | + added check (and "auto-repair") for unescaped & in descriptions (to prevent trouble with XML) 104 | 105 | v1.5.0 106 | ------------------- 107 | + make it easy to add more book formats (definitions kept in lib/formats.csv) 108 | ! if client transmitted "lang=" (w/o specifying any lang), book download sent a different book than requested 109 | ! PDF was using wrong mimetype on download 110 | ! download did not consider the "name" url parameter 111 | * WillaMovius has closed (Amazon.DE ISBN Search), so we had to replace it 112 | 113 | v1.4.0 (06.05.2011) 114 | ------------------- 115 | * some better input handling for DB storage (escaping etc.) 116 | ! book details page was broken for OPDS 117 | * scan no longer aborts when a book title failed (just logs the error and skips the book) 118 | + added simple XML check for description files 119 | - removed $debug variable from sample configs (no longer used) 120 | 121 | v1.3.0 (06.03.2011) 122 | ------------------- 123 | ! search template was not committed to repository 124 | ! pagination was broken in title list and search results 125 | * wildcards ("*") in search terms truncated the whole term being treated as "vulnerable" 126 | + added ISBN-links to HTML version 127 | + more logging details on scan + webif 128 | + offering search-plugin to Firefox (so search can be triggered from its search bar) 129 | 130 | v1.2.0 (21.02.2011) 131 | ------------------- 132 | + added possibility for "permlinks" using the URL param "name" 133 | * changed URL param "default_prefix" to "prefix" (shorter URL) 134 | + added search functionality (search form for now HTML only) 135 | + added OpenSearch support (integrated with OPDS and above search functionality) 136 | + more cover images detected 137 | + added and integrated regular logging class 138 | 139 | v1.1.0 (30.01.2011) 140 | ------------------- 141 | + added metadata file support (*.data): books now can have multiple tags, series, isbn... 142 | + series support: added separate entry point on top-level 143 | + added localization (English, German) 144 | + uri, publisher and rating are now also supported in the metadata file 145 | + added back-links from book details to author and, if available, series 146 | 147 | v1.0.0 (21.01.2011) 148 | ------------------- 149 | * first public version 150 | + OPDS and HTML support 151 | + Calibre backend support 152 | + directory support (for own file base) 153 | -------------------------------------------------------------------------------- /doc/install_de.txt: -------------------------------------------------------------------------------- 1 | $Id$ 2 | ============ 3 | INSTALLATION 4 | ============ 5 | 6 | Schnellstart 7 | ------------ 8 | 9 | Hier wird ein Schnellstart mit einfacher Konfiguration beschrieben. Wer etwas 10 | komplexeres benoetigt, und z.B. verschiedene Sprachen mit eigenen Katalogen 11 | verwenden moechte, findet weiter unten die entsprechenden Informationen. 12 | 13 | 1) Archiv entpacken. Inhalt des Verzeichnisses in den Ordner "opds" 14 | direkt im DOCUMENT_ROOT des Webservers kopieren 15 | 2) config.simple.php nach config.php umbenennen und im Editor anpassen: 16 | - Erster Block (Directories) kann unveraendert bleiben 17 | - Zweiten Block (Book Covers) ebenfalls belassen 18 | - im dritten Block (Web Service) "localhost" durch den 19 | Namen/die IP des Rechners ersetzen (sonst funktioniert das Ganze 20 | am Ende nur auf dem lokalen Rechner, nicht aber vom eBook-Reader aus) 21 | - im vierten Block (Person Info) die Daten anpassen. 22 | 3) Datenbank vorbereiten: 23 | - in den Ordner "install" wechseln 24 | - Linux: install.sh ausfuehren 25 | Windows: alle *.sql Dateien mit sqlite3 ausfuehren (?) 26 | - Datei metadata.db in das uebergeordnete Verzeichnis verschieben 27 | 4) Verzeichnisstruktur fuer Buecher vorbereiten: 28 | - in den Ordner "books" wechseln 29 | - fuer jede gewuenschte Sprache das entsprechende Verzeichnis 30 | anlegen (z.B. "de" fuer Deutsch, "en" fuer Englisch...). 31 | Nur fuer die benoetigten Sprachen. 32 | - in jedem Sprachordner wiederum Ordner fuer die gewuenschten 33 | Genres anlegen (z.B. "Computer", "Science Fiction", ...) 34 | - in den Genre-Ordnern Verzeichnisse mit den Namen der Autoren 35 | anlegen (z.B. "Jules Verne", "Paul Scheerbart", ...) 36 | - die Buecher als . in die Autoren-Ordner kopieren 37 | Eine Beispiel-Datei zur Veranschaulichung: 38 | books/de/Science Fiction/Jules Verne/Reise um den Mond.epub 39 | books/de/Science Fiction/Jules Verne/Reise um den Mond.mobi 40 | Zu jedem Buch (das durchaus in mehreren Formaten vorliegen kann) kann 41 | auch eine Textdatei mit einer Beschreibung angelegt werden, z.B.: 42 | books/de/Science Fiction/Jules Verne/Reise um den Mond.desc 43 | Diese muss dann die Erweiterung ".desc" haben - wie ersichtlich. 44 | 5) Datenbank fuettern (ACHTUNG - NICHT MIT CALIBRE DB!): 45 | - wieder ins Hauptverzeichnis (opds) wechseln 46 | - folgenden Befehl ausfuehren: 47 | php scan.php 48 | 49 | Wenn alles geklappt hat, sollte man jetzt den Katalog benutzen koennen. 50 | Als URL im Browser/eBook-Reader einfach die konfigurierte $baseurl plus 51 | "/index.php" angeben - also z.B. 52 | http://meine_kiste/opds/index.php 53 | 54 | 55 | Erweiterte Konfiguration 56 | ------------------------ 57 | 58 | Mit einer erweiterten Konfiguration lassen sich sprachspezifische Kataloge 59 | verwenden. Im Prinzip ist hier nach obigem Schema zu verfahren - nur der zweite 60 | Schritt wird leicht angepasst: 61 | 62 | Statt config.simple.php verwenden wir hier config.advanced.php als Vorlage - die 63 | Anpassung erfolgt, wie oben beschrieben. Sprach-spezifische Anpassungen erfolgen 64 | nun im Block "Language dependent stuff", und überschreiben jeweils die zuvor 65 | gesetzten Werte. Die Beispiel-Konfiguration gibt sinnvolle Werte für Deutsch 66 | und Englisch bereits an - mit Deutsch als der Default-Sprache, falls (z.B. in 67 | der URL) keine andere Sprache angegeben wurde. Natuerlich lassen sich hier noch 68 | weitere Einstellungen anpassen - etwa der Site-Title. 69 | 70 | Schritt 3 muss nun natuerlich fuer jede zusaetzliche Datenbank angepasst werden. 71 | Dabei kann die einmal erstellte (leere) Datenbank einfach kopiert werden. 72 | 73 | Auch Schritt 5 aendert sich geringfuegig - als Beispiele liegen scan_de.php 74 | sowie scan_en.php mit bei. 75 | 76 | Wird nun die URL wie oben angegeben aufgerufen, greift die Default-Sprache (in 77 | unserem Falle also Deutsch). Besser ist es allemal, generell die Sprache mit 78 | anzugeben. Die URL waere dann z.B. fuer Englisch 79 | http://meine_kiste/opds/index.php?lang=en 80 | 81 | 82 | Calibre-DB nutzen 83 | ----------------- 84 | 85 | Fuer die Nutzung der Calibre-Datenbank wird selbige einfach als $dbfile 86 | eingetragen. ABER AUFPASSEN: DAMIT KEINEN SCAN DURCHFUEHREN - SONST IST SIE 87 | DANACH HINUEBER! 88 | 89 | Stattdessen koennen Anpassungen des zweiten Blocks (Book Covers - ggf. im 90 | sprach-spezifischen Teil) vorgenommen werden, um Cover-Images mit einzubinden. 91 | $cover_mode ist hierzu auf 'calibre' zu setzen. Fuer die Aufbereitung der 92 | Cover-Images werden mittels des Skriptes scan_calibre_covers.php Symlinks 93 | angelegt: Dies setzt ein Linux/Unix System voraus. Unter Windows sollte dies 94 | (Vista, Server 2008 oder neuer vorausgesetzt) mit PHP v5.3 oder hoeher ebenfalls 95 | funktionieren. 96 | -------------------------------------------------------------------------------- /doc/metadata.txt: -------------------------------------------------------------------------------- 1 | Specifying MetaData 2 | =================== 3 | 4 | For each book you can specify some additional MetaData placed in a file named 5 | .data (which must reside within the same directory as the book file 6 | itself). Each line of this file specifies exactly one datum in the format 7 | 8 | name::value 9 | 10 | The following "names" are recognized and used: 11 | 12 | title title of the book (can differ from the file name) 13 | series collection a book belongs to (if there are multiple volumes) 14 | series_index which volume in above collection is this book 15 | isbn ISBN number for this book (may refer to the printed edition) 16 | tag a tag for this book. You can specify multiple tags using 17 | multiple lines (see example below) 18 | author additional author. As with the tags, multiple occurences 19 | are possible. 20 | rating Rating of this book. Must be an integer between 1..5 21 | publisher Publisher of this book 22 | uri Web URL for additional information (usually the publishers) 23 | 24 | A fake example: 25 | 26 | series::Babylon 5 27 | series_index::5 28 | isbn::123-12345-12345-1 29 | tag::Science Fiction 30 | tag::Space 31 | author::JMS 32 | author::Someone Else 33 | rating::5 34 | publisher::Publisher Info 35 | uri::http://publisher.website/ 36 | 37 | -------------------------------------------------------------------------------- /doc/startpage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/doc/startpage.png -------------------------------------------------------------------------------- /install/authors.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Authors 3 | -- 4 | CREATE TABLE authors ( id INTEGER PRIMARY KEY, 5 | name TEXT NOT NULL COLLATE NOCASE, 6 | sort TEXT COLLATE NOCASE, 7 | UNIQUE(name) 8 | ); 9 | 10 | CREATE TRIGGER fkc_delete_on_authors 11 | BEFORE DELETE ON authors 12 | BEGIN 13 | SELECT CASE 14 | WHEN (SELECT COUNT(id) FROM books_authors_link WHERE author=OLD.id) > 0 15 | THEN RAISE(ABORT, 'Foreign key violation: authors is still referenced') 16 | END; 17 | END; 18 | -------------------------------------------------------------------------------- /install/books.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Books 3 | -- 4 | CREATE TABLE books ( id INTEGER PRIMARY KEY AUTOINCREMENT, 5 | title TEXT NOT NULL DEFAULT 'Unknown' COLLATE NOCASE, 6 | sort TEXT COLLATE NOCASE, 7 | timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 8 | uri TEXT, 9 | series_index INTEGER NOT NULL DEFAULT 1, 10 | author_sort TEXT COLLATE NOCASE, 11 | isbn TEXT DEFAULT "" COLLATE NOCASE, 12 | path TEXT NOT NULL DEFAULT "" 13 | ); 14 | CREATE INDEX authors_idx ON books (author_sort COLLATE NOCASE, sort COLLATE NOCASE); 15 | CREATE INDEX books_idx ON books (sort COLLATE NOCASE); 16 | CREATE INDEX series_sort_idx ON books (series_index, id); 17 | 18 | CREATE TRIGGER books_delete_trg 19 | AFTER DELETE ON books 20 | BEGIN 21 | DELETE FROM books_authors_link WHERE book=OLD.id; 22 | DELETE FROM books_publishers_link WHERE book=OLD.id; 23 | DELETE FROM books_ratings_link WHERE book=OLD.id; 24 | DELETE FROM books_series_link WHERE book=OLD.id; 25 | DELETE FROM books_tags_link WHERE book=OLD.id; 26 | DELETE FROM data WHERE book=OLD.id; 27 | DELETE FROM comments WHERE book=OLD.id; 28 | DELETE FROM conversion_options WHERE book=OLD.id; 29 | END; 30 | /* 31 | CREATE TRIGGER books_insert_trg 32 | AFTER INSERT ON books 33 | BEGIN 34 | UPDATE books SET sort=title_sort(NEW.title) WHERE id=NEW.id; 35 | END; 36 | CREATE TRIGGER books_update_trg 37 | AFTER UPDATE ON books 38 | BEGIN 39 | UPDATE books SET sort=title_sort(NEW.title) WHERE id=NEW.id; 40 | END; 41 | */ -------------------------------------------------------------------------------- /install/books_authors_link.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- books_authors_link 3 | -- 4 | 5 | CREATE TABLE books_authors_link ( id INTEGER PRIMARY KEY, 6 | book INTEGER NOT NULL, 7 | author INTEGER NOT NULL, 8 | UNIQUE(book, author) 9 | ); 10 | CREATE INDEX books_authors_link_aidx ON books_authors_link (author); 11 | CREATE INDEX books_authors_link_bidx ON books_authors_link (book); 12 | CREATE TRIGGER fkc_insert_books_authors_link 13 | BEFORE INSERT ON books_authors_link 14 | BEGIN 15 | SELECT CASE 16 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 17 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 18 | WHEN (SELECT id from authors WHERE id=NEW.author) IS NULL 19 | THEN RAISE(ABORT, 'Foreign key violation: author not in authors') 20 | END; 21 | END; 22 | CREATE TRIGGER fkc_update_books_authors_link_a 23 | BEFORE UPDATE OF book ON books_authors_link 24 | BEGIN 25 | SELECT CASE 26 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 27 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 28 | END; 29 | END; 30 | CREATE TRIGGER fkc_update_books_authors_link_b 31 | BEFORE UPDATE OF author ON books_authors_link 32 | BEGIN 33 | SELECT CASE 34 | WHEN (SELECT id from authors WHERE id=NEW.author) IS NULL 35 | THEN RAISE(ABORT, 'Foreign key violation: author not in authors') 36 | END; 37 | END; 38 | -------------------------------------------------------------------------------- /install/books_publishers_link.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- books_publishers_link 3 | -- 4 | CREATE TABLE books_publishers_link ( id INTEGER PRIMARY KEY, 5 | book INTEGER NOT NULL, 6 | publisher INTEGER NOT NULL, 7 | UNIQUE(book) 8 | ); 9 | CREATE INDEX books_publishers_link_aidx ON books_publishers_link (publisher); 10 | CREATE INDEX books_publishers_link_bidx ON books_publishers_link (book); 11 | CREATE TRIGGER fkc_insert_books_publishers_link 12 | BEFORE INSERT ON books_publishers_link 13 | BEGIN 14 | SELECT CASE 15 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 16 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 17 | WHEN (SELECT id from publishers WHERE id=NEW.publisher) IS NULL 18 | THEN RAISE(ABORT, 'Foreign key violation: publisher not in publishers') 19 | END; 20 | END; 21 | CREATE TRIGGER fkc_update_books_publishers_link_a 22 | BEFORE UPDATE OF book ON books_publishers_link 23 | BEGIN 24 | SELECT CASE 25 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 26 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 27 | END; 28 | END; 29 | CREATE TRIGGER fkc_update_books_publishers_link_b 30 | BEFORE UPDATE OF publisher ON books_publishers_link 31 | BEGIN 32 | SELECT CASE 33 | WHEN (SELECT id from publishers WHERE id=NEW.publisher) IS NULL 34 | THEN RAISE(ABORT, 'Foreign key violation: publisher not in publishers') 35 | END; 36 | END; 37 | -------------------------------------------------------------------------------- /install/books_ratings_link.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- books_ratings_link 3 | -- 4 | CREATE TABLE books_ratings_link ( id INTEGER PRIMARY KEY, 5 | book INTEGER NOT NULL, 6 | rating INTEGER NOT NULL, 7 | UNIQUE(book, rating) 8 | ); 9 | CREATE INDEX books_ratings_link_aidx ON books_ratings_link (rating); 10 | CREATE INDEX books_ratings_link_bidx ON books_ratings_link (book); 11 | CREATE TRIGGER fkc_insert_books_ratings_link 12 | BEFORE INSERT ON books_ratings_link 13 | BEGIN 14 | SELECT CASE 15 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 16 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 17 | WHEN (SELECT id from ratings WHERE id=NEW.rating) IS NULL 18 | THEN RAISE(ABORT, 'Foreign key violation: rating not in ratings') 19 | END; 20 | END; 21 | CREATE TRIGGER fkc_update_books_ratings_link_a 22 | BEFORE UPDATE OF book ON books_ratings_link 23 | BEGIN 24 | SELECT CASE 25 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 26 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 27 | END; 28 | END; 29 | CREATE TRIGGER fkc_update_books_ratings_link_b 30 | BEFORE UPDATE OF rating ON books_ratings_link 31 | BEGIN 32 | SELECT CASE 33 | WHEN (SELECT id from ratings WHERE id=NEW.rating) IS NULL 34 | THEN RAISE(ABORT, 'Foreign key violation: rating not in ratings') 35 | END; 36 | END; 37 | -------------------------------------------------------------------------------- /install/books_series_link.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- books_series_link 3 | -- 4 | CREATE TABLE books_series_link ( id INTEGER PRIMARY KEY, 5 | book INTEGER NOT NULL, 6 | series INTEGER NOT NULL, 7 | UNIQUE(book) 8 | ); 9 | CREATE INDEX books_series_link_aidx ON books_series_link (series); 10 | CREATE INDEX books_series_link_bidx ON books_series_link (book); 11 | CREATE TRIGGER fkc_insert_books_series_link 12 | BEFORE INSERT ON books_series_link 13 | BEGIN 14 | SELECT CASE 15 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 16 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 17 | WHEN (SELECT id from series WHERE id=NEW.series) IS NULL 18 | THEN RAISE(ABORT, 'Foreign key violation: series not in series') 19 | END; 20 | END; 21 | CREATE TRIGGER fkc_update_books_series_link_a 22 | BEFORE UPDATE OF book ON books_series_link 23 | BEGIN 24 | SELECT CASE 25 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 26 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 27 | END; 28 | END; 29 | CREATE TRIGGER fkc_update_books_series_link_b 30 | BEFORE UPDATE OF series ON books_series_link 31 | BEGIN 32 | SELECT CASE 33 | WHEN (SELECT id from series WHERE id=NEW.series) IS NULL 34 | THEN RAISE(ABORT, 'Foreign key violation: series not in series') 35 | END; 36 | END; 37 | -------------------------------------------------------------------------------- /install/books_tags_link.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- books_tags_link 3 | -- 4 | CREATE TABLE books_tags_link ( id INTEGER PRIMARY KEY, 5 | book INTEGER NOT NULL, 6 | tag INTEGER NOT NULL, 7 | UNIQUE(book, tag) 8 | ); 9 | CREATE INDEX books_tags_link_aidx ON books_tags_link (tag); 10 | CREATE INDEX books_tags_link_bidx ON books_tags_link (book); 11 | CREATE TRIGGER fkc_insert_books_tags_link 12 | BEFORE INSERT ON books_tags_link 13 | BEGIN 14 | SELECT CASE 15 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 16 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 17 | WHEN (SELECT id from tags WHERE id=NEW.tag) IS NULL 18 | THEN RAISE(ABORT, 'Foreign key violation: tag not in tags') 19 | END; 20 | END; 21 | CREATE TRIGGER fkc_update_books_tags_link_a 22 | BEFORE UPDATE OF book ON books_tags_link 23 | BEGIN 24 | SELECT CASE 25 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 26 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 27 | END; 28 | END; 29 | CREATE TRIGGER fkc_update_books_tags_link_b 30 | BEFORE UPDATE OF tag ON books_tags_link 31 | BEGIN 32 | SELECT CASE 33 | WHEN (SELECT id from tags WHERE id=NEW.tag) IS NULL 34 | THEN RAISE(ABORT, 'Foreign key violation: tag not in tags') 35 | END; 36 | END; 37 | -------------------------------------------------------------------------------- /install/comments.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- comments 3 | -- 4 | CREATE TABLE comments ( id INTEGER PRIMARY KEY, 5 | book INTEGER NON NULL, 6 | text TEXT NON NULL COLLATE NOCASE, 7 | UNIQUE(book) 8 | ); 9 | CREATE INDEX comments_idx ON comments (book); 10 | CREATE TRIGGER fkc_comments_insert 11 | BEFORE INSERT ON comments 12 | BEGIN 13 | SELECT CASE 14 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 15 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 16 | END; 17 | END; 18 | CREATE TRIGGER fkc_comments_update 19 | BEFORE UPDATE OF book ON comments 20 | BEGIN 21 | SELECT CASE 22 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 23 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 24 | END; 25 | END; 26 | -------------------------------------------------------------------------------- /install/conversion_options.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- conversion_options 3 | -- 4 | CREATE TABLE conversion_options ( id INTEGER PRIMARY KEY, 5 | format TEXT NOT NULL COLLATE NOCASE, 6 | book INTEGER, 7 | data BLOB NOT NULL, 8 | UNIQUE(format,book) 9 | ); 10 | CREATE INDEX conversion_options_idx_a ON conversion_options (format COLLATE NOCASE); 11 | CREATE INDEX conversion_options_idx_b ON conversion_options (book); 12 | -------------------------------------------------------------------------------- /install/data.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- data 3 | -- 4 | CREATE TABLE data ( id INTEGER PRIMARY KEY, 5 | book INTEGER NON NULL, 6 | format TEXT NON NULL COLLATE NOCASE, 7 | uncompressed_size INTEGER NON NULL, 8 | name TEXT NON NULL, 9 | UNIQUE(book, format) 10 | ); 11 | CREATE INDEX data_idx ON data (book); 12 | CREATE TRIGGER fkc_data_insert 13 | BEFORE INSERT ON data 14 | BEGIN 15 | SELECT CASE 16 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 17 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 18 | END; 19 | END; 20 | CREATE TRIGGER fkc_data_update 21 | BEFORE UPDATE OF book ON data 22 | BEGIN 23 | SELECT CASE 24 | WHEN (SELECT id from books WHERE id=NEW.book) IS NULL 25 | THEN RAISE(ABORT, 'Foreign key violation: book not in books') 26 | END; 27 | END; 28 | -------------------------------------------------------------------------------- /install/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Create a new database 3 | # 4 | # Simply run this script and copy the created (empty) database to where your 5 | # config points to. Then you can run `php scan.php` anytime you want to refresh 6 | # your database (e.g. when you added new books) 7 | 8 | # remove database file if exists 9 | if [ -e metadata.db ]; then 10 | rm -f metadata.db 11 | fi 12 | 13 | for file in *.sql; do 14 | sqlite3 metadata.db <$file 15 | done 16 | -------------------------------------------------------------------------------- /install/publishers.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- publishers 3 | -- 4 | CREATE TABLE publishers ( id INTEGER PRIMARY KEY, 5 | name TEXT NOT NULL COLLATE NOCASE, 6 | sort TEXT COLLATE NOCASE, 7 | UNIQUE(name) 8 | ); 9 | CREATE INDEX publishers_idx ON publishers (name COLLATE NOCASE); 10 | CREATE TRIGGER fkc_delete_on_publishers 11 | BEFORE DELETE ON publishers 12 | BEGIN 13 | SELECT CASE 14 | WHEN (SELECT COUNT(id) FROM books_publishers_link WHERE publisher=OLD.id) > 0 15 | THEN RAISE(ABORT, 'Foreign key violation: publishers is still referenced') 16 | END; 17 | END; 18 | -------------------------------------------------------------------------------- /install/ratings.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- ratings 3 | -- 4 | CREATE TABLE ratings ( id INTEGER PRIMARY KEY, 5 | rating INTEGER CHECK(rating > -1 AND rating < 6), 6 | UNIQUE (rating) 7 | ); 8 | INSERT INTO ratings (id,rating) VALUES (1,1); 9 | INSERT INTO ratings (id,rating) VALUES (2,2); 10 | INSERT INTO ratings (id,rating) VALUES (3,3); 11 | INSERT INTO ratings (id,rating) VALUES (4,4); 12 | INSERT INTO ratings (id,rating) VALUES (5,5); 13 | -------------------------------------------------------------------------------- /install/series.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- series 3 | -- 4 | CREATE TABLE series ( id INTEGER PRIMARY KEY, 5 | name TEXT NOT NULL COLLATE NOCASE, 6 | sort TEXT COLLATE NOCASE, 7 | UNIQUE (name) 8 | ); 9 | CREATE INDEX series_idx ON series (name COLLATE NOCASE); 10 | CREATE TRIGGER fkc_delete_on_series 11 | BEFORE DELETE ON series 12 | BEGIN 13 | SELECT CASE 14 | WHEN (SELECT COUNT(id) FROM books_series_link WHERE series=OLD.id) > 0 15 | THEN RAISE(ABORT, 'Foreign key violation: series is still referenced') 16 | END; 17 | END; 18 | CREATE TRIGGER series_insert_trg 19 | AFTER INSERT ON series 20 | BEGIN 21 | UPDATE series SET sort=NEW.name WHERE id=NEW.id; 22 | END; 23 | CREATE TRIGGER series_update_trg 24 | AFTER UPDATE ON series 25 | BEGIN 26 | UPDATE series SET sort=NEW.name WHERE id=NEW.id; 27 | END; 28 | -------------------------------------------------------------------------------- /install/tags.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Tags 3 | -- 4 | CREATE TABLE tags ( id INTEGER PRIMARY KEY, 5 | name TEXT NOT NULL COLLATE NOCASE, 6 | UNIQUE (name) 7 | ); 8 | CREATE INDEX tags_idx ON tags (name COLLATE NOCASE); 9 | CREATE TRIGGER fkc_delete_on_tags 10 | BEFORE DELETE ON tags 11 | BEGIN 12 | SELECT CASE 13 | WHEN (SELECT COUNT(id) FROM books_tags_link WHERE tag=OLD.id) > 0 14 | THEN RAISE(ABORT, 'Foreign key violation: tags is still referenced') 15 | END; 16 | END; 17 | -------------------------------------------------------------------------------- /lang/ebookterms.de: -------------------------------------------------------------------------------- 1 | term;"trans" 2 | author;"Autor" 3 | bookproducer;"eBook Ersteller" 4 | collaborator;"Mitarbeiter" 5 | content;"Inhalt" 6 | copyright;"Copyright" 7 | date;"Datum" 8 | editor;"Bearbeitung" 9 | ebookPublished;"eBook veröffentlicht" 10 | ebookCreated;"eBook erstellt" 11 | firstpublished;"Erstveröffentlichung" 12 | illustrator;"Illustrator" 13 | modification;"Änderung" 14 | published;"Veröffentlicht" 15 | publisher;"Herausgeber" 16 | redactor;"Redakteur" 17 | source;"Quelle" 18 | topic;"Thema" 19 | translator;"Übersetzer" 20 | -------------------------------------------------------------------------------- /lang/ebookterms.en: -------------------------------------------------------------------------------- 1 | term;"trans" 2 | author;"Author" 3 | bookproducer;"eBook Creator" 4 | collaborator;"Collaborator" 5 | content;"Table of Contents" 6 | copyright;"Copyright" 7 | date;"Date" 8 | editor;"Editor" 9 | ebookPublished;"eBook published" 10 | ebookCreated;"eBook created" 11 | firstpublished;"First published" 12 | illustrator;"Illustrator" 13 | modification;"Modification" 14 | published;"Published" 15 | publisher;"Publisher" 16 | redactor;"Redactor" 17 | source;"Source" 18 | topic;"Topic" 19 | translator;"Translator" 20 | -------------------------------------------------------------------------------- /lang/trans.de: -------------------------------------------------------------------------------- 1 | ref;"trans" 2 | author;"Autor" 3 | authors;"Autoren" 4 | title;"Titel" 5 | titles;"Titel" 6 | serie;"Buchreihe" 7 | series;"Buchreihen" 8 | last_update;"Letzte Aktualisierung" 9 | created_by;"Erstellt von" 10 | start_page;"Startseite" 11 | sort_alpha;"Alphabetisch sortieren" 12 | sort_author;"Nach Autor sortieren" 13 | sort_bookcount;"Nach Anzahl Büchern sortieren" 14 | sort_date;"Nach Datum sortieren" 15 | sort_index;"Nach Index sortieren" 16 | this_page;"Diese Seite" 17 | first_page;"Erste Seite" 18 | prev_page;"Vorige Seite" 19 | next_page;"Nächste Seite" 20 | last_page;"Letzte Seite" 21 | book;"Buch" 22 | books;"Bücher" 23 | books_by_whom;"Bücher von %1" 24 | all_books_by_whom;"Alle Bücher von %1" 25 | back_to_authors;"Zurück zur Autorenliste" 26 | back_to_series;"Zurück zur Liste der Buchreihen" 27 | title_by_author;"%1 von %2" 28 | publisher;"Herausgeber" 29 | comment;"Kommentar" 30 | books_with_tag;"Bücher mit dem Tag %1" 31 | books_in_serie;"Bücher in der Reihe %1" 32 | all_books_in_serie;"Alle Bücher in der Reihe %1" 33 | not_available;"nicht verfügbar" 34 | rating;"Bewertung" 35 | book_search_title;"Buch-Suche" 36 | search_do;"Suchen" 37 | searchresults;"Suchergebnisse" 38 | search;"Suche" 39 | title_websearch;"Titelsuche" 40 | by;"von" 41 | no_such_book;"Dieses Buch gibt es hier leider nicht." 42 | -------------------------------------------------------------------------------- /lang/trans.en: -------------------------------------------------------------------------------- 1 | ref;"trans" 2 | author;"Author" 3 | authors;"Authors" 4 | title;"Title" 5 | titles;"Titles" 6 | tags;"Tags" 7 | serie;"Serie" 8 | series;"Series" 9 | last_update;"Last Update" 10 | created_by;"Created by" 11 | start_page;"Start Page" 12 | sort_alpha;"Order alphabetically" 13 | sort_author;"Order by Author" 14 | sort_bookcount;"Order by Bookcount" 15 | sort_date;"Order by Date" 16 | sort_index;"Order by Index" 17 | this_page;"This Page" 18 | first_page;"First Page" 19 | prev_page;"Previous Page" 20 | next_page;"Next Page" 21 | last_page;"Last Page" 22 | book;"Book" 23 | books;"Books" 24 | books_by_whom;"Books by %1" 25 | all_books_by_whom;"All books by %1" 26 | back_to_authors;"Back to Authors List" 27 | back_to_series;"Back to Series List" 28 | title_by_author;"%1 by %2" 29 | publisher;"Publisher" 30 | download;"Download" 31 | comment;"Comment" 32 | books_with_tag;"Books tagged %1" 33 | books_in_serie;"Books in Serie %1" 34 | all_books_in_serie;"All books in Serie %1" 35 | not_available;"not available" 36 | uri;"URL" 37 | isbn;"ISBN" 38 | rating;"Rating" 39 | book_search_title;"Book Search" 40 | search_do;"Submit Search" 41 | searchresults;"Search Results" 42 | search;"Search" 43 | title_websearch;"Title-Search" 44 | by;"by" 45 | no_such_book;"Unfortunately, this book does not exist here." 46 | -------------------------------------------------------------------------------- /lib/adflash.php: -------------------------------------------------------------------------------- 1 | set_file(array("template"=>"flashads.tpl")); 7 | $tpl->set_var('amazonID',$amazonID); 8 | $tpl->set_var('amazon_bordercolor',$GLOBALS['ads_bordercolor']); 9 | $tpl->set_var('amazon_logocolor',$GLOBALS['ads_logocolor']); 10 | $tpl->set_var('booktitle_urlenc',urlencode($booktitle)); 11 | $tpl->set_var('authorname_urlenc',urlencode($author)); 12 | $tpl->set_var('booktags_urlenc',urlencode(str_replace(', ',';',$booktags))); 13 | return $tpl->parse('out','template'); 14 | } 15 | ?> -------------------------------------------------------------------------------- /lib/asap.php: -------------------------------------------------------------------------------- 1 | ' for genre, start|authors|search|genre|titles for the corresponding initial page 19 | // pagetype: initial|list|book 20 | // wildcard: regex|none ('none' for "initial pages", else 'regex' – or always use 'regex', permitting for "(genre|genre)" expressions?) 21 | $webvert = new webvertizer(strtolower($GLOBALS['ads_asap_webvertizer_domain'])); // sitename should match, of course ;) 22 | $GLOBALS['localAds'] = array_merge($GLOBALS['localAds'],$webvert->getItemsByAutoMatch($tag,$pagetype,3,$wildcard)); 23 | $GLOBALS['localAutoCrits'] = "latag::${tag};latyp::${pagetype};lamatch::$wildcard"; 24 | $GLOBALS['autoAdsCalled'] = TRUE; 25 | // adjust partnerID to match config 26 | if ( !empty($GLOBALS['localAds']['items']) ) for ($i=0;$i 1 ) shuffle($GLOBALS['localAds']['items']); // randomize item order 67 | if ( $lacount == $limit ) return $GLOBALS['localAds']; // exact count: done. 68 | elseif ( $lacount > $limit ) { // ouch, too much? That hurts :) 69 | trigger_error("getAds: We've got too many paid ads here... (".$GLOBALS['localAutoCrits'].")", E_USER_WARNING); 70 | return $GLOBALS['localAds']; 71 | } 72 | } else { 73 | $lacount = 0; 74 | } 75 | 76 | if ( empty($asin) && empty($keywords) ) { // no ads to retrieve without criteria, sorry 77 | if ($GLOBALS['autoAdsCalled']) { 78 | $dAds = []; //$GLOBALS['page']->getDefaultAds(); 79 | foreach ($dAds as $ad) { 80 | if ( $lacount == $limit ) break; 81 | $GLOBALS['localAds']['items'][] = $ad; 82 | ++$lacount; 83 | } 84 | return $GLOBALS['localAds']; 85 | } 86 | else { 87 | trigger_error("getAds: got neither ASIN nor keywords (from '$criteria')", E_USER_WARNING); 88 | return array(); 89 | } 90 | } 91 | 92 | // Initialize the AmazonAPI 93 | $amazon = new AmazonAds($public, $private, $affiliate, $site); 94 | $res = array(); $asinCount = 0; 95 | 96 | // ASINs have priority 97 | if ( empty($asin) ) { 98 | $res = $GLOBALS['localAds']; 99 | } else { 100 | $res1 = $amazon->getItemByAsin($asin); 101 | $res['cachedate'] = $res1['cachedate']; 102 | if ( !empty($res1) ) { // skip if Amazon-Request failed 103 | $res['items'] = array(); 104 | $asinCount = count($res1['items']); 105 | if ( $asinCount + $lacount == $limit ) { 106 | if ($lacount==0) return $res1; 107 | foreach($GLOBALS['localAds']['items'] as $item) $res['items'][] = $item; 108 | foreach($res1['items'] as $item) $res['items'][] = $item; 109 | return $res; 110 | } 111 | elseif ( $asinCount + $lacount > $limit ) { 112 | $rand = array_rand($res1['items'],$limit - $lacount); 113 | $res2 = array( 'cachedate'=>$res1['cachedate'], 'items'=>array() ); 114 | if ( is_array($rand) ) foreach ($rand as $r) $res2['items'][] = $res1['items'][$r]; 115 | else $res2['items'][] = $res1['items'][$rand]; 116 | if ($lacount==0) return $res2; 117 | foreach($GLOBALS['localAds']['items'] as $item) $res['items'][] = $item; 118 | foreach($res2['items'] as $item) $res['items'][] = $item; 119 | return $res; 120 | } 121 | if ($lacount==0) $res = $res1; 122 | else { 123 | foreach($GLOBALS['localAds']['items'] as $item) $res['items'][] = $item; 124 | foreach($res1['items'] as $item) $res['items'][] = $item; 125 | } 126 | } 127 | } 128 | 129 | // Still here? So we need a search 130 | $needed = $limit - $asinCount - $lacount; 131 | if ( empty($keywords) || empty($prodgroup) ) { // we cannot search without 132 | trigger_error("getAds: We need $needed more items, but have neither keyword nor productgroup (from '$criteria')", E_USER_WARNING); 133 | return $res; 134 | } 135 | // OK, we have something to do with: 136 | $res2 = $amazon->getItemsByKeyword($keywords,$prodgroup,$needed); // just pick the diff number 137 | if ( empty($res2['items']) ) { // nothing found 138 | return $res; 139 | } 140 | foreach ( $res2['items'] as $r ) $res['items'][] = $r; // append our new findings 141 | if ( empty($res['cachedate']) || $res2['cachedate'] < $res['cachedate'] ) { // fix cachedate 142 | $res['cachedate'] = $res2['cachedate']; 143 | } 144 | 145 | // Yo, we're done! 146 | return $res; 147 | } 148 | 149 | function getAdBlock($ads) { 150 | if ( !empty($ads['items']) ) { 151 | $tpl = new Template("tpl/html"); 152 | $tpl->set_file(array("template"=>"asap3col.tpl")); 153 | $tpl->set_block('template','itemblock','item'); 154 | $i = 0; 155 | foreach($ads['items'] as $item) { 156 | if ( !isset($item['title']) || empty($item['title']) ) continue; // no content 157 | if ( strlen($item['title']) > 400 ) $item['title'] = substr($item['title'],0,400) . '…'; 158 | if ( !isset($item['price']) || empty($item['price']) || !preg_match('!(EUR|USD|GBP) [0-9\.\,]+!',$item['price']) ) $item['price'] = ''; 159 | $tpl->set_var('url',str_replace('http:','https:',$item['url'])); 160 | $tpl->set_var('title',str_replace("'","´",strip_tags($item['title']))); 161 | $tpl->set_var('desc',$item['title']); 162 | $tpl->set_var('img',$item['img']); 163 | if ( empty($item['price']) ) $tpl->set_var('price_info',' '); 164 | elseif ( isset($item['source']) && $item['source'] == 'local' ) { 165 | if ( preg_match('! 0[,.]00$!',$item['price']) ) $tpl->set_var('price_info',' '); 166 | else $tpl->set_var('price_info',$item['price']); 167 | } else { 168 | $tpl->set_var('price_info',$item['price']); 169 | } 170 | if (isset($item['is_premium']) && $item['is_premium']) $tpl->set_var('class','premium'); 171 | else $tpl->set_var('class','standard'); 172 | $tpl->parse('item','itemblock',$i); 173 | ++$i; 174 | } 175 | $disclaimer = str_replace('%cachedate%',$ads['cachedate'],$GLOBALS['ads_asap_disclaimer']); 176 | $tpl->set_var('cachedate',$disclaimer); 177 | $adblock = $tpl->parse('out','template'); 178 | } else $adblock = ''; 179 | return $adblock; 180 | } 181 | /* example usage: 182 | $foo = getAds('asin::3645603115,3645602151;keywords::Android Speicherkarte SDKarte;prodgroup::Electronics;limit::3'); 183 | if ( !empty($foo['items']) ) { 184 | foreach($foo['items'] as $item) $page->addAd($item); 185 | $page->setAdCacheDateString('Stand: '.$foo['cachedate'].'; Preis & Verfügbarkeit können sich geändert haben.'); 186 | } 187 | */ 188 | 189 | ?> -------------------------------------------------------------------------------- /lib/asap_genres.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "Abenteuer":"keywords::Abenteuer;prodgroup::Books,DVD", 3 | "Computer":"keywords::Computer;prodgroup::Books,Electronics", 4 | "Heimatroman":"keywords::Heimat;prodgroup::Books,DVD", 5 | "Gesellschaft+Soziales":"keywords::Gesellschaft Soziales;prodgroup::Books,DVD", 6 | "Kinder":"keywords::Kinder;prodgroup::Books,DVD,Toys" 7 | } -------------------------------------------------------------------------------- /lib/booksearch.csv: -------------------------------------------------------------------------------- 1 | "name";"url";"comment" 2 | "Amazon.DE";"http://www.amazon.de/gp/search?ie=UTF8&keywords={fulltext}&tag={amazonID}&index=books";"" 3 | "Amazon.COM";"http://www.amazon.com/s/url=search-alias=aps&field-keywords={fulltext}";"" 4 | "Bookzilla.DE";"http://www.bookzilla.de/shop/action/quickSearch?searchString={fulltext}";"" 5 | "Google.DE";"http://books.google.de/books?as_vt={title}&as_auth={author}";"" 6 | "Google.COM";http://books.google.com/books?as_vt={title}&as_auth={author}";"" 7 | "Hugendubel.DE";"http://www.hugendubel.de/9/?da={author}&dt={titel}";"" 8 | "Buchfreund.DE";"http://www.buchfreund.de/results.php?q={fulltext}";"MetaSearch for used books" 9 | "ZVAB.COM";"http://www.zvab.com/advancedSearch.do?author={author}&title={title}&sortBy=1";"Zentrales Verzeichnis Antiquarischer Bücher (http://de.wikipedia.org/wiki/Zentrales_Verzeichnis_Antiquarischer_B%C3%BCcher)" 10 | "Thalia.AT";"http://www.thalia.at/shop/ama_homestartseite/suche/?sq={fulltext}";"" 11 | "Thalia.CH";"http://www.thalia.ch/shop/ama_homestartseite/suche/?sq={fulltext}";"" 12 | "Thalia.DE";"http://www.thalia.de/shop/ama_homestartseite/suche/?sq={fulltext}";"" 13 | "LovelyBooks.DE";"http://www.lovelybooks.de/suche/{fulltext}/";"Social Stuff" 14 | "abebooks.COM";"http://www.abebooks.com/servlet/SearchResults?an={author}&tn={title}";"Used books / Antiquariat" 15 | "abebooks.DE";"http://www.abebooks.de/servlet/SearchResults?an={author}&tn={title}";"Used books / Antiquariat" 16 | "BookCrossing.COM";"http://www.bookcrossing.com/search/?title={title}&author={author}&status=0";"gratis exchange (see http://de.wikipedia.org/wiki/Bookcrossing)" 17 | "BookLooker.DE";"http://www.booklooker.de/app/result.php?mediaType=0&autor={author}&titel={title}";"market for used books, videos, ..." 18 | -------------------------------------------------------------------------------- /lib/class.csv.php: -------------------------------------------------------------------------------- 1 | # 5 | # http://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 | # CSV File handling (import) # 11 | ############################################################################# 12 | 13 | /* $Id$ */ 14 | 15 | /** CSV file handling 16 | * @package Api 17 | * @class csv 18 | * @author Izzy (izzysoft AT qumran DOT org) 19 | * @copyright (c) 2004-2009 by Itzchak Rehberg and IzzySoft 20 | */ 21 | class csv { 22 | 23 | /** CVS data: array[0..n] of imported records. 24 | * Each Record is an array[0..n] of objects with the properties: name,data 25 | * @class csv 26 | * @attribute array data 27 | */ 28 | /** CVS field separator (defaults to ";") 29 | * @class csv 30 | * @attribute string sep 31 | */ 32 | /** CVS text field encloser (defaults to '"') 33 | * @class csv 34 | * @attribute string txt 35 | */ 36 | /** trim imported values? (Defaults to FALSE) 37 | * @class csv 38 | * @attribute boolean trim 39 | */ 40 | 41 | /** Setting up defaults 42 | * @constructor csv 43 | * @param optional string separator (default: ';') 44 | * @param optional string textmarker 45 | * (by what characters are textfields enclosed; default: '"') 46 | * @param optional boolean trim whether to trim the fields 47 | * @param optional mixed recode either recode definition string 48 | * (e.g. "lat1..utf-8", see method recode) or FALSE for no recoding) 49 | */ 50 | function __construct($sep=";",$txt='"',$trim=FALSE,$recode="lat1..utf-8") { 51 | $this->sep = $sep; 52 | $this->txt = $txt; 53 | $this->trim = $trim; 54 | $this->recode = $recode; 55 | $this->data = array(); 56 | } 57 | 58 | /** Import CSV file 59 | * @class csv 60 | * @method import 61 | * @param string filename file to import 62 | */ 63 | function import($filename) { 64 | $this->read_file($filename); 65 | $pos = strpos($this->text,"\n"); 66 | $line = trim(substr($this->text,0,$pos)); 67 | $this->text = substr($this->text,$pos+1); 68 | $this->read_fields($line); 69 | $this->read_data(); 70 | } 71 | 72 | /** Get the field names 73 | * Reads the field names from the specification line and sets up the 74 | * this::field array 75 | * @class csv 76 | * @method read_fields 77 | * @param string line field specification line from the CSV file to import 78 | */ 79 | function read_fields($line) { 80 | $fields = explode($this->sep,$line); 81 | $fc = count($fields); 82 | for ($i=0;$i<$fc;++$i) { 83 | $this->field[$i] = new stdClass(); 84 | if (substr($fields[$i],0,1)==$this->txt) { 85 | $name = substr($fields[$i],1,strlen($fields[$i])-2); 86 | $this->field[$i]->txt = TRUE; 87 | } else { 88 | $name = $fields[$i]; 89 | $this->field[$i]->txt = FALSE; 90 | } 91 | $this->field[$i]->name = $name; 92 | $this->fieldid[$name] = $i; 93 | if ($this->trim) $this->field[$i]->name = trim($this->field[$i]->name); 94 | $this->field[$i]->name = $this->recode($this->field[$i]->name); 95 | } 96 | if ( $this->field[$fc -1]->txt ) { 97 | $this->rec_end = $this->txt."\n"; 98 | } else { 99 | $this->rec_end = "\n"; 100 | } 101 | } 102 | 103 | /** Get the file content 104 | * Reads the CSV file into the internal string $this->text 105 | * @class csv 106 | * @method read_file 107 | * @param string filename file to read 108 | */ 109 | function read_file($filename) { 110 | if ( !file_exists($filename) || !is_readable($filename) ) trigger_error("Failed to open '$filename' for read",E_USER_NOTICE); 111 | $buffer = file_get_contents($filename); 112 | $this->text = preg_replace("/\r?\n|\r/", "\n", $buffer); 113 | } 114 | 115 | /** Get CSV data 116 | * Reads data from the imported CSV file and stores it into 117 | * the $this->data array (array of ojects with the elements: name, data) 118 | * @class csv 119 | * @method read_data 120 | */ 121 | function read_data() { 122 | $fc = count($this->field); 123 | $rec = 0; 124 | while (!empty($this->text)) { 125 | $pos = strpos($this->text,$this->rec_end); 126 | $line = trim(substr($this->text,0,$pos)); 127 | if (empty($line)) { 128 | $this->text = substr($this->text,$pos+1); 129 | } else { 130 | for ($i=0;$i<$fc;++$i) { // record start 131 | $name = $this->field[$i]->name; 132 | if ($this->field[$i]->txt) { 133 | if ($i+1<$fc) { 134 | $pos = strpos($this->text,$this->txt.$this->sep,1); 135 | } else { 136 | $pos = strpos($this->text,$this->txt."\n"); 137 | } 138 | $data[$name] = substr($this->text,1,$pos-1); 139 | $this->text = substr($this->text,$pos+2); 140 | } else { 141 | if ($i+1<$fc) { 142 | $pos = strpos($this->text,$this->sep); 143 | } else { 144 | $pos = strpos($this->text,"\n"); 145 | } 146 | $data[$name] = substr($this->text,0,$pos); 147 | $this->text = substr($this->text,$pos+1); 148 | } 149 | if ($this->trim) $data[$name] = trim($data[$name]); 150 | $data[$name] = $this->recode($data[$name]); 151 | $this->data[$rec] = $data; 152 | } // record end 153 | } 154 | ++$rec; 155 | } 156 | } 157 | 158 | /** Clear all data 159 | * Remove all internal data, e.g. to import a different file 160 | * @class csv 161 | * @method clear_data 162 | */ 163 | function clear_data() { 164 | unset ($this->data); 165 | unset ($this->field); 166 | } 167 | 168 | /** Recode a string between two character sets 169 | * @class csv 170 | * @method recode 171 | * @param string string to recode 172 | * @return string recoded string 173 | * @version supports only "lat1..utf-8" and vice versa for now; the recode 174 | * definition has to be set in the this::recode property. Usage of an own 175 | * recoding method was due to recode() and iconv() functions of PHP need 176 | * special requirements (such as libraries and modules) which are not 177 | * available on most systems by default. 178 | */ 179 | function recode ($string) { 180 | switch ($this->recode) { 181 | case "lat1..utf-8" : 182 | return preg_replace("/([\x80-\xFF])/e", 183 | "chr(0xC0|ord('\\1')>>6).chr(0x80|ord('\\1')&0x3F)", 184 | $string); break; 185 | case "utf-8..lat1" : 186 | return preg_replace("([\xC2\xC3])([\x80-\xBF])/e", 187 | "chr(ord('\\1')<<6&0xC0|ord('\\2')&0x3F)", 188 | $string); break; 189 | default: return($string); 190 | } 191 | } 192 | 193 | # =========================================================[ data methods ]=== 194 | /** Sort data by field 195 | * @class csv 196 | * @method sort 197 | * @param string column sort by which column? 198 | * @param optional string order ASC or DESC 199 | * @param optional string type "num" or "str" (default: AutoDetect) 200 | */ 201 | function sort($column,$order="",$type="") { 202 | $order = strtoupper($order); 203 | if (empty($order)) $order = "ASC"; 204 | $array=$this->data; 205 | if (sizeof($array)==0) return; 206 | $id = $this->fieldid[$column]; 207 | if (empty($type)||($type!="num" && $type!="str")) { 208 | if ($this->field[$id]->txt) { $type="str"; } else { $type="num"; } 209 | } 210 | if ($order=="ASC") $option=""; else $option="!"; 211 | if ($type=="str") { 212 | $this->array_key_multi_srt($array,$column,$option,"strcmp"); 213 | } else { 214 | $this->array_key_multi_srt($array,$column,$option,"bccomp"); 215 | } 216 | $this->data=$array; 217 | } 218 | 219 | function array_key_multi_srt(&$arr,$l,$option,$f='strcmp') { 220 | if ($f=='bccomp') $func = "return $f(\$a['$l'], \$b['$l'], 2);"; 221 | else $func = "return $f(\$a['$l'], \$b['$l']);"; 222 | if ($option=="!") { 223 | return usort($arr, create_function('$b, $a', $func)); 224 | } else { 225 | return usort($arr, create_function('$a, $b', $func)); 226 | } 227 | } 228 | 229 | /** Restrict data to a subset 230 | * @method where 231 | * @param string column name of the column to restrict by 232 | * @param string compare how to compare. Valid strings are: "lt", "gt", "eq", "ne" 233 | * @param string value to restrict by 234 | * @param optional string type datatype (either "str" (default) or "num") 235 | * @version "le" and "ge" are not yet implemented. 236 | */ 237 | function where ($column,$compare,$value,$type="str") { 238 | if ( !in_array($compare,array('lt','gt','ne','eq','le','ge','~')) ) return; 239 | if ($type=="num") $f = 'bccomp'; 240 | $dc = count($this->data); 241 | for ($i=0;$i<$dc;++$i) { 242 | switch($compare) { 243 | case "eq" : if ($this->data[$i][$column]==$value) $data[] = $this->data[$i]; break; 244 | case "lt" : if ($type=="num") { $val = $this->data[$i][$column]; if (bccomp($value,floatval($val),2)>0) $data[] = $this->data[$i]; } 245 | else { if (strcmp(strtolower($value),strtolower($this->data[$i][$column]))>0) $data[] = $this->data[$i]; } 246 | break; 247 | case "gt" : if ($type=="num") { $val = $this->data[$i][$column]; if (bccomp($value,floatval($val),2)<0) $data[] = $this->data[$i]; } 248 | else { if (strcmp(strtolower($value),strtolower($this->data[$i][$column]))<0) $data[] = $this->data[$i]; } 249 | break; 250 | case "le" : if ($type=="num") { $val = $this->data[$i][$column]; if (bccomp($value,floatval($val),2)>=0) $data[] = $this->data[$i]; } 251 | else { if (strcmp(strtolower($value),strtolower($this->data[$i][$column]))>=0) $data[] = $this->data[$i]; } 252 | break; 253 | case "ge" : if ($type=="num") { $val = $this->data[$i][$column]; if (bccomp($value,floatval($val),2)<=0) $data[] = $this->data[$i]; } 254 | else { if (strcmp(strtolower($value),strtolower($this->data[$i][$column]))<=0) $data[] = $this->data[$i]; } 255 | break; 256 | case "ne" : if ($this->data[$column]!=$value) $data[] = $this->data[$i]; break; 257 | case "~" : if ( preg_match('|'.str_replace("%",".*",$value).'|i',$this->data[$i][$column]) ) $data[] = $this->data[$i]; break; 258 | } 259 | } 260 | unset($this->data); 261 | if (isset($data)) $this->data = $data; 262 | else $this->data = array(); 263 | } 264 | 265 | } // end class csv 266 | ?> -------------------------------------------------------------------------------- /lib/class.logging.php: -------------------------------------------------------------------------------- 1 | # 5 | # http://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 | # Logging (file & screen) # 11 | ############################################################################# 12 | # $Id$ 13 | 14 | #============================================================[ Constants ]=== 15 | /** LogLevel Emergency 16 | * @constant integer EMERGENCY 17 | */ 18 | define('EMERGENCY',0); 19 | /** LogLevel Critical 20 | * @constant integer CRITICAL 21 | */ 22 | define('ALERT',10); 23 | /** LogLevel Alert 24 | * @constant integer ALERT 25 | */ 26 | define('CRITICAL',20); 27 | /** LogLevel Error 28 | * @constant integer ERROR 29 | */ 30 | define('ERROR',30); 31 | /** LogLevel Warning 32 | * @constant integer WARNING 33 | */ 34 | define('WARNING',40); 35 | /** LogLevel Notice 36 | * @constant integer NOTICE 37 | */ 38 | define('NOTICE',50); 39 | /** LogLevel Info 40 | * @constant integer INFO 41 | */ 42 | define('INFO',60); 43 | /** LogLevel Debug 44 | * @constant integer DEBUG 45 | */ 46 | define('DEBUG',70); 47 | /** Logging Disabled Level 48 | * @constant integer NOLOG 49 | */ 50 | define('NOLOG',-1); 51 | /** Are we running in CLI mode (i.e. from command line, not browser)? 52 | * @constant boolean IS_CLI 53 | */ 54 | define ('IS_CLI', !( isset($_SERVER) && isset($_SERVER['REMOTE_ADDR']) )); 55 | 56 | #======================================================[ The Logging Class ]=== 57 | /** Logging to file and screen 58 | * @package Api 59 | * @class logging 60 | * @author Izzy (izzysoft AT qumran DOT org) 61 | * @copyright (c) 2011-2011 by Itzchak Rehberg and IzzySoft 62 | */ 63 | class logging { 64 | 65 | /** Logfile Name 66 | * @class logging 67 | * @attribute private string logfile 68 | */ 69 | private $logfile = ''; 70 | /** Screen Log Level 71 | * @class logging 72 | * @attribute private screenlevel 73 | */ 74 | private $screenlevel = INFO; 75 | /** File Log Level 76 | * @class logging 77 | * @attribute filelevel 78 | */ 79 | private $filelevel = INFO; 80 | /** Error stack 81 | * This is an array [0..n] of int(level), int(msg) 82 | * @class logging 83 | * @attribute array errors 84 | */ 85 | public $errors = array(); 86 | 87 | /** Class initialization 88 | * @constructor logging 89 | * @param optional string logname (default: '' = no file logging) 90 | * @param optional integer fileloglevel (default: INFO) 91 | * @param optional integer screenloglevel (default: INFO) 92 | */ 93 | function __construct($fname='',$flevel=INFO,$slevel=INFO) { 94 | if ( empty($fname) ) { 95 | $this->setloglevel('file',NOLOG); 96 | } else { 97 | $this->setlogfile($fname); 98 | $this->setloglevel('file',$flevel); 99 | } 100 | $this->setloglevel('screen',$slevel); 101 | if (IS_CLI) { 102 | $this->scrnl = "\n"; 103 | } else { 104 | $this->scrnl = "
\n"; 105 | } 106 | } 107 | 108 | /** Store processing errors 109 | * Appends the specified error to the internal errors array 110 | * @function private _error 111 | * @param integer level Severity 112 | * @param string msg Error Message 113 | */ 114 | function _error($level,$msg) { 115 | $this->errors[] = array('level'=>$level,'msg'=>$msg); 116 | } 117 | 118 | /** Setup the logfile 119 | * If an error occurs here, it is logged into the logging::errors array and the method returns FALSE 120 | * @method setlogfile 121 | * @param string filename 122 | * @return boolean success 123 | */ 124 | function setlogfile($fname) { 125 | if ( empty($fname) ) { $this->_error(ERROR,'setlogfile: No filename specified'); return FALSE; } 126 | if ( !is_dir( dirname($fname) ) ) { $this->_error(ERROR,"setlogfile: Directory of specified logfile '$fname' does not exist"); return FALSE; } 127 | if ( file_exists($fname) ) { 128 | if ( !is_writeable($fname) ) { $this->_error(ERROR,"setlogfile: Specified logfile '$fname' is not writeable for us"); return FALSE; } 129 | } else { 130 | if ( !is_writeable(dirname($fname)) ) { $this->_error(ERROR,"setlogfile: Specified logfile '$fname' does not exist and cannot be created due to lacking write permission to the directory"); return FALSE; } 131 | } 132 | $this->logfile = $fname; 133 | return TRUE; 134 | } 135 | 136 | /** Setup loglevels 137 | * @method public setloglevel 138 | * @param string type What log to alter: 'screen' or 'file'? 139 | * @param integer level Loglevel 140 | * @return boolean success 141 | */ 142 | public function setloglevel($ltype='file',$llevel=INFO) { 143 | $type = strtolower($ltype); 144 | if ( !in_array($type,array('screen','file')) ) { 145 | $this->_error(ERROR,"setloglevel: log type must be one of 'screen','file' - you specified '$ltype'"); 146 | return FALSE; 147 | } 148 | if ( !is_numeric($llevel) ) { 149 | $this->_error(ERROR,"setloglevel: log level must be integer - you specified '$llevel'"); 150 | return FALSE; 151 | } 152 | switch ($type) { 153 | case 'screen' : $this->screenlevel = $llevel; break; 154 | case 'file' : $this->filelevel = $llevel; break; 155 | default: $this->_error(ERROR,'setloglevel: default in switch statement triggered???'); return FALSE; break; 156 | } 157 | return TRUE; 158 | } 159 | 160 | /** Log a message (generic) 161 | * @method public log 162 | * @param integer level Level for this message 163 | * @param string msg Log message 164 | * @param optional string module Calling modul 165 | */ 166 | public function log($level,$msg,$mod='') { 167 | if ( !is_numeric($level) ) { 168 | $this->_error(ERROR,"log: level must be integer, got '$level'"); 169 | return FALSE; 170 | } 171 | if ( !is_string($mod) ) { 172 | $this->_error(WARNING,"log: Specified module name is not a string"); 173 | $mod = ''; 174 | } 175 | switch ($level) { 176 | case EMERGENCY: $llevel = 'EMERGENCY'; break; 177 | case ALERT : $llevel = 'ALERT'; break; 178 | case CRITICAL : $llevel = 'CRITICAL'; break; 179 | case ERROR : $llevel = 'ERROR'; break; 180 | case WARNING : $llevel = 'WARNING'; break; 181 | case NOTICE : $llevel = 'NOTICE'; break; 182 | case INFO : $llevel = 'INFO'; break; 183 | case DEBUG : $llevel = 'DEBUG'; break; 184 | default : $llevel = $level; break; 185 | } 186 | if ( isset($_SERVER) && isset($_SERVER['REMOTE_ADDR']) ) $who = $_SERVER['REMOTE_ADDR']; 187 | else $who = 'local'; 188 | if ( !empty($this->logfile) && $level <= $this->filelevel ) { 189 | error_log(date('Y-m-d H:i:s')." $who $llevel $mod $msg\n", 3, $this->logfile); 190 | } 191 | if ( $level <= $this->screenlevel ) { 192 | echo "$msg" . $this->scrnl; 193 | } 194 | } 195 | 196 | /** Log an emergency error 197 | * (shortcut to logging::log) 198 | * @method public emergency 199 | * @param string msg Log message 200 | * @param optional string module Calling modul 201 | */ 202 | public function emergency($msg,$mod='') { 203 | $this->log(EMERGENCY,$msg,$mod); 204 | } 205 | 206 | /** Log an alert 207 | * (shortcut to logging::log) 208 | * @method public alert 209 | * @param string msg Log message 210 | * @param optional string module Calling modul 211 | */ 212 | public function alert($msg,$mod='') { 213 | $this->log(ALERT,$msg,$mod); 214 | } 215 | 216 | /** Log a critical error 217 | * (shortcut to logging::log) 218 | * @method public critical 219 | * @param string msg Log message 220 | * @param optional string module Calling modul 221 | */ 222 | public function critical($msg,$mod='') { 223 | $this->log(CRITICAL,$msg,$mod); 224 | } 225 | 226 | /** Log a "normal" error 227 | * (shortcut to logging::log) 228 | * @method public error 229 | * @param string msg Log message 230 | * @param optional string module Calling modul 231 | */ 232 | public function error($msg,$mod='') { 233 | $this->log(ERROR,$msg,$mod); 234 | } 235 | 236 | /** Log a warning 237 | * (shortcut to logging::log) 238 | * @method public warning 239 | * @param string msg Log message 240 | * @param optional string module Calling modul 241 | */ 242 | public function warning($msg,$mod='') { 243 | $this->log(WARNING,$msg,$mod); 244 | } 245 | 246 | /** Log a warning (alias to logging::warning) 247 | * @method public warn 248 | * @param string msg Log message 249 | * @param optional string module Calling modul 250 | */ 251 | public function warn($msg,$mod='') { 252 | $this->warning($msg,$mod); 253 | } 254 | 255 | /** Log a notice 256 | * (shortcut to logging::log) 257 | * @method public notice 258 | * @param string msg Log message 259 | * @param optional string module Calling modul 260 | */ 261 | public function notice($msg,$mod='') { 262 | $this->log(NOTICE,$msg,$mod); 263 | } 264 | 265 | /** Log an informal message 266 | * (shortcut to logging::log) 267 | * @method public info 268 | * @param string msg Log message 269 | * @param optional string module Calling modul 270 | */ 271 | public function info($msg,$mod='') { 272 | $this->log(INFO,$msg,$mod); 273 | } 274 | 275 | /** Log a debug message 276 | * (shortcut to logging::log) 277 | * @method public debug 278 | * @param string msg Log message 279 | * @param optional string module Calling modul 280 | */ 281 | public function debug($msg,$mod='') { 282 | $this->log(DEBUG,$msg,$mod); 283 | } 284 | 285 | } 286 | 287 | ?> -------------------------------------------------------------------------------- /lib/class.translation.php: -------------------------------------------------------------------------------- 1 | # 5 | # http://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 | # Translation handling methods # 11 | ############################################################################# 12 | 13 | require_once(dirname(__FILE__).'/class.csv.php'); 14 | 15 | /** Translation handling methods 16 | * @package Api 17 | * @class translation 18 | * @author Izzy (izzysoft AT qumran DOT org) 19 | * @copyright (c) 2004-2009 by Itzchak Rehberg and IzzySoft 20 | */ 21 | class translation { 22 | 23 | /** Initialize 24 | * @constructor translation 25 | * @param string langpath where do the translation files reside 26 | */ 27 | function __construct($langpath,$fallback) { 28 | $this->langpath = $langpath; 29 | $this->avail = $this->avail(); 30 | $this->browserpref = $this->get_browserlang(); 31 | if ( empty($this->browserpref) ) $this->browserpref = $fallback; 32 | $this->translations = array(); 33 | } 34 | 35 | /** Read in all available translations for a given language 36 | * @class translation 37 | * @method get_translations 38 | * @param string lang_id ISO code of the prefered language 39 | */ 40 | function get_translations() { 41 | $trans = array(); 42 | $lang_id = $this->browserpref; 43 | if ( $lang_id!="en" ) { 44 | $filename = $this->langpath."/trans.en"; 45 | if ( !file_exists($filename) ) return $trans; 46 | $trans = $this->read_translations($filename,$trans); 47 | } 48 | $filename = $this->langpath."/trans.$lang_id"; 49 | if ( !file_exists($filename) ) return $trans; 50 | $this->translations = $this->read_translations($filename,$trans); 51 | } 52 | 53 | /** Read translations from CSV file 54 | * (helper for get_translations) 55 | * @method private read_translations 56 | * @param string filename name of the file to read from 57 | * @param array trans array of translations to append to 58 | */ 59 | function read_translations($filename,$trans) { 60 | $csv = new csv(";",'"',TRUE,FALSE); 61 | $csv->import($filename); 62 | $dc = count($csv->data); 63 | for ($i=0;$i<$dc;++$i) { 64 | $trans[$csv->data[$i]["ref"]] = $csv->data[$i]["trans"]; 65 | } 66 | return $trans; 67 | } 68 | 69 | 70 | /** Get list of available language files 71 | * @class translation 72 | * @method favail 73 | * @return array lang_keys ( n=>key ) 74 | */ 75 | function favail() { 76 | $prefix = "trans"; 77 | $dir = dir($this->langpath); 78 | while ( $file=$dir->read() ) { 79 | if ( strpos($file,$prefix)===0 ) { 80 | $pos = strpos($file,"."); 81 | $lang[] = substr($file,$pos+1); 82 | } 83 | } 84 | return $lang; 85 | } 86 | 87 | /** Get list of available languages 88 | * @class translation 89 | * @method avail 90 | * @return array lang_keys ( n=>key ) 91 | */ 92 | function avail() { 93 | return $this->favail(); 94 | } 95 | 96 | /** translate a given reference 97 | * if no translation is found, the content of the passed param $key is 98 | * returned instead 99 | * @class translation 100 | * @method trans 101 | * @param string key translation key 102 | * @param optional array m1 replace placeholder (%1..%10 in translations) 103 | * @param optional string m2 instead of $m1 being an array, all 10 replacements may be passed separately as strings 104 | */ 105 | function transl($key,$m1="",$m2="",$m3="",$m4="",$m5="",$m6="",$m7="",$m8="",$m9="",$m10="") { 106 | if (is_array($m1)){ 107 | $vars = $m1; 108 | } else { 109 | $vars = array($m1,$m2,$m3,$m4,$m5,$m6,$m7,$m8,$m9,$m10); 110 | } 111 | return $this->translate("$key",$vars); 112 | } 113 | 114 | /** build the translations (helper func to method trans) 115 | * @class translation 116 | * @method translate 117 | * @param string key translation key 118 | * @param optional array vars array of replacement strings (see function lang) 119 | */ 120 | public function translate($key,$vars=FALSE) { 121 | $trans = $this->translations[strtolower($key)]; 122 | if (!$trans) $trans = $key; 123 | if (!$vars) $vars=array(); 124 | $ndx = 1; 125 | while ( list($k,$v)=each($vars) ) { 126 | $trans = preg_replace("/%$ndx/",$v,$trans); 127 | $ndx++; 128 | } 129 | return $trans; 130 | } 131 | 132 | /** retrieve users prefered languages from browser info 133 | * @class translation 134 | * @method get_browserlang 135 | * @return string language (ISO language code) - empty if we can't support any 136 | */ 137 | function get_browserlang() { 138 | if (!isset($_SERVER["HTTP_ACCEPT_LANGUAGE"])) return ""; // prevent notice 139 | $langs = explode(",",$_SERVER["HTTP_ACCEPT_LANGUAGE"]); 140 | $lc = count($langs); 141 | for ($i=0;$i<$lc;++$i) { // check if we have a suitable file 142 | $want = substr($langs[$i],0,2); 143 | if (in_array($want,$this->avail)) { 144 | $this->browserpref = $want; 145 | return $want; 146 | } 147 | } 148 | return ""; 149 | } 150 | 151 | /** Visitors prefered language according to browser info 152 | * @class translation 153 | * @attribute string browserpref 154 | */ 155 | /** Translations 156 | * @class translation 157 | * @attribute array translations 158 | */ 159 | /** Available languages 160 | * @class translation 161 | * @attribute array avail 162 | */ 163 | 164 | } // end class trans 165 | 166 | ?> -------------------------------------------------------------------------------- /lib/common.php: -------------------------------------------------------------------------------- 1 | # 5 | # http://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 | # $Id$ 11 | 12 | require_once(dirname(__FILE__).'/class.logging.php'); // just in case 13 | 14 | // Central Logger instance 15 | if (IS_CLI) { 16 | $logger = new logging($logfile, $fileloglevel,$screenloglevel_cli); 17 | $dllogger = new logging($dllogfile,$fileloglevel,$screenloglevel_cli); 18 | } else { 19 | $logger = new logging($logfile, $fileloglevel,$screenloglevel); 20 | $dllogger = new logging($dllogfile,$fileloglevel,$screenloglevel); 21 | } 22 | 23 | #=====================================================[ Checking URL input ]=== 24 | /** Verifying integers 25 | * @function req_int 26 | * @param string in varname Name of the _REQUEST variable 27 | * @param optional integer default Default value to return in case of mismatch (0) 28 | * @return integer value 29 | */ 30 | function req_int($name,$default=0) { 31 | if ( !isset($_REQUEST[$name]) ) { 32 | $GLOBALS['logger']->debug("URLParam '$name' was expected but is not set",'WEBIF'); 33 | return $default; 34 | } 35 | $val = (int) $_REQUEST[$name]; 36 | if ( $val!=$_REQUEST[$name] ) { 37 | $GLOBALS['logger']->warn("URLParam '$name' expected to be INT - got '".$_REQUEST[$name]."'",'WEBIF'); 38 | } 39 | return $val; 40 | } 41 | /** Verifying words 42 | * @function req_word 43 | * @param string in varname Name of the _REQUEST variable 44 | * @param optional string default Default value to return in case of mismatch ('') 45 | * @return string value 46 | */ 47 | function req_word($name,$default='') { 48 | if ( !isset($_REQUEST[$name]) ) { 49 | $GLOBALS['logger']->debug("URLParam '$name' was expected but is not set",'WEBIF'); 50 | return $default; 51 | } 52 | if (!preg_match('!^\w*$!',$_REQUEST[$name],$match)) { 53 | $GLOBALS['logger']->warn("URLParam '$name' expected to be WORD - got '".$_REQUEST[$name]."'",'WEBIF'); 54 | return $default; 55 | } 56 | return $_REQUEST[$name]; 57 | } 58 | /** Verifying alpha-numerical input, e.g. names 59 | * @function rel_alnum 60 | * @param string in varname Name of the _REQUEST variable 61 | * @param optional string default Default value to return in case of mismatch ('') 62 | */ 63 | function req_alnum($name,$default='') { 64 | if ( !isset($_REQUEST[$name]) ) { 65 | $GLOBALS['logger']->debug("URLParam '$name' was expected but is not set",'WEBIF'); 66 | return $default; 67 | } 68 | if ( preg_match('![^\w\s_\-\pL]!u',$_REQUEST[$name],$match) ) { 69 | $GLOBALS['logger']->warn("URLParam '$name' expected to be ALNUM - got '".$_REQUEST[$name]."'",'WEBIF'); 70 | return $default; 71 | } 72 | return $_REQUEST[$name]; 73 | } 74 | /** Verifying alpha-numerical input with wildcards (e.g. user-input from search 75 | * mask), replacing all occurences of ",.:*?" by "%" for SQL wildcard (LIKE) search 76 | * @function rel_alnumwild 77 | * @param string in varname Name of the _REQUEST variable 78 | * @param optional string default Default value to return in case of mismatch ('') 79 | */ 80 | function req_alnumwild($name,$default='') { 81 | if ( !isset($_REQUEST[$name]) ) { 82 | $GLOBALS['logger']->debug("URLParam '$name' was expected but is not set",'WEBIF'); 83 | return $default; 84 | } 85 | $output = preg_replace('![,.:*?]!ims','%',$_REQUEST[$name]); 86 | if ( preg_match('![^\w\s\%_\-\p{L}]!u',$output,$match) ) { 87 | $GLOBALS['logger']->warn("Rejecting input '".$_REQUEST[$name]."' for '$name'",'VERIFY'); 88 | return $default; 89 | } 90 | return $output; 91 | } 92 | /** Verifying array of integer (HTML multi-select form element) 93 | * @function req_intarr 94 | * @param string in varname Name of the _REQUEST variable 95 | * @param optional mixed default Default value to return in case of mismatch (array()) 96 | */ 97 | function req_intarr($name,$default=array()) { 98 | if ( !isset($_REQUEST[$name]) ) { 99 | $GLOBALS['logger']->debug("URLParam '$name' was expected but is not set",'WEBIF'); 100 | return $default; 101 | } 102 | if ( !is_array($_REQUEST[$name]) ) { 103 | $GLOBALS['logger']->warn("URLParam '$name' expected to be ARRAY - got '".$_REQUEST[$name]."'",'WEBIF'); 104 | return $default; 105 | } 106 | $vals = array(); 107 | foreach ($_REQUEST[$name] as $val) { 108 | if ( is_numeric($val) ) $vals[] = $val; 109 | else { 110 | $globals['logger']->warn("Skipping non-numeric value '$val' in expected INTARR '$name'",'WEBIF'); 111 | } 112 | } 113 | return $vals; 114 | } 115 | ?> -------------------------------------------------------------------------------- /lib/db_sqlite3.php: -------------------------------------------------------------------------------- 1 | query($query); 44 | } 45 | } 46 | 47 | /** Escape a string for safe insert 48 | * @function escape 49 | * @param string input 50 | * @return string escaped 51 | */ 52 | function escape($str) { 53 | return $this->escapeString($str); 54 | } 55 | 56 | /** Connect to database 57 | * @class db_sqlite 58 | * @method connect 59 | * @return integer Link_ID on success, 0 otherwise 60 | */ 61 | function connect() { 62 | if ( $this->Connected ) return; 63 | if ( empty($this->Database) ) { 64 | $this->halt("connect failed: no database specified"); 65 | } 66 | $this->open($this->Database); 67 | $this->Connected = TRUE; 68 | } 69 | 70 | /** Disconnect from database 71 | * @class db_sqlite 72 | * @method disconnect 73 | */ 74 | function disconnect() { 75 | if ( !$this->Connected ) return; 76 | $this->close(); 77 | $this->Connected = FALSE; 78 | } 79 | 80 | /** Perform a query using the LIMIT clause 81 | * @method lim_query 82 | * @param string Query_String Query without limit clause 83 | * @param int start record to start at 84 | * @param int limit max number of records wanted 85 | * @return int allover records (w/o LIMIT used) 86 | */ 87 | function lim_query($Query_String,$start,$limit) { 88 | $this->query($Query_String); 89 | $totals = $this->num_rows(); 90 | $this->query($Query_String . ' LIMIT ' . $limit . ' OFFSET ' . $start); 91 | return $totals; 92 | } 93 | 94 | /** Perform a query 95 | * @class db_sqlite 96 | * @method query 97 | * @param string Query_String SQL Query 98 | * @return integer Query_ID on success, 0 otherwise 99 | */ 100 | function query($Query_String) { 101 | /* No empty queries, please, since PHP4 chokes on them. */ 102 | if ($Query_String == "") 103 | /* The empty query string is passed on from the constructor, 104 | * when calling the class without a query, e.g. in situations 105 | * like these: '$db = new db_sqlite_Subclass;' 106 | */ 107 | return 0; 108 | $this->connect(); 109 | //$this->qlog .= "$Query_String;\n"; 110 | if ($this->AdjustQuotes) $Query_String = str_replace("\\'","''",str_replace('\\"','""',$Query_String)); 111 | $this->Result = parent::query($Query_String); 112 | $this->Row = 0; 113 | return $this->Result; 114 | } 115 | 116 | /** Execute a query (SELECT only!) and return all results as array[0..n] of assoc_array 117 | * This emulates sqlite_array_query(dblink,query,SQLITE_ASSOC) 118 | * @method query_array 119 | * @param string query The SQL-Query to run 120 | * @return array results 121 | */ 122 | function query_array($Query_String) { 123 | $ret = array(); 124 | if ( $this->query($Query_String) ) { 125 | while ( $this->next_record() ) $ret[] = $this->Record; 126 | } 127 | return $ret; 128 | } 129 | 130 | /** Execute a query (SELECT only!) and return the first row of the result as an associative array. 131 | * This emulates sqlite_single_query(dblink,query,TRUE) 132 | * @method query_single_row 133 | * @param string query The SQL-Query to run 134 | * @return array results 135 | */ 136 | function query_single_row($Query_String) { 137 | if ( $this->query($Query_String) ) { 138 | $this->next_record(); 139 | return $this->Record; 140 | } else { 141 | return array(); 142 | } 143 | } 144 | 145 | /** Execute a query (SELECT only!) and return an array[0..n] of the first affected column. 146 | * This emulates sqlite_single_query(dblink,query,FALSE) 147 | * @method query_single_column 148 | * @param string query The SQL-Query to run 149 | * @return array results (empty array if no results or if an error occures) 150 | */ 151 | function query_single_column($Query_String) { 152 | if ( $this->query($Query_String) ) { 153 | $ret = array(); 154 | while ( $this->next_record() ) { 155 | foreach ( $this->Record as $rec ) $ret[] = $rec; 156 | } 157 | return $ret; 158 | } else { 159 | return array(); 160 | } 161 | } 162 | 163 | /** Retrieve a single value with one call. 164 | * Intended for things like 'SELECT count(*)' or 'SELECT name .. WHERE id=' 165 | * @method query_single_value 166 | * @param string query The SQL-Query to run 167 | * @return mixed result either the retrieved value or FALSE on error 168 | */ 169 | function query_single_value($Query_String) { 170 | return $this->querySingle($Query_String); 171 | } 172 | 173 | /** Walk result set 174 | * @class db_sqlite 175 | * @method next_record 176 | * @return boolean success 177 | */ 178 | function next_record() { 179 | $this->Record = $this->Result->fetchArray(); 180 | $this->Row += 1; 181 | $stat = is_array($this->Record); 182 | return $stat; 183 | } 184 | 185 | /** Position in result set 186 | * @class db_sqlite 187 | * @method seek 188 | * @param optional integer pos Position to set the pointer to (result/row number) 189 | * @return boolean success 190 | * @version not supported by current PHP API 191 | */ 192 | function seek($pos) { 193 | return FALSE; 194 | } 195 | 196 | /** Obtain table metadata 197 | * @method metadata 198 | * @param string table_name 199 | * @return array 0..n of array(table,name,type,len,position,flags) 200 | * @version the field position is only kept for compatibility and will always 201 | * be empty. Flags may contain attributes such as "PRIMARY KEY", if they 202 | * immediately followd the column description. This code is still experimental 203 | * and may fail, listing constraints as columns. Please let me know the 204 | * details if you encounter this, so the code can be updated and fixed. 205 | */ 206 | function metadata($table) { 207 | $count = 0; 208 | $i = 0; 209 | $res = array(); 210 | 211 | $this->connect(); 212 | $this->query("SELECT sql FROM sqlite_master WHERE name='$table' AND type='table'"); 213 | if (!$this->next_record()) return FALSE; // no data found 214 | 215 | $sql = $this->f('sql'); 216 | $pos_s = strpos($sql,"(") +1; 217 | $pos_e = strrpos($sql,")"); 218 | $sql = substr($sql,$pos_s,$pos_e - $pos_s); 219 | $arr = explode(",",$sql); 220 | foreach($arr as $var) { 221 | if (strpos($sql,'UNIQUE')===0) continue; // constraint 222 | $var = str_replace("("," ",str_replace(")","",$var)); 223 | $col = explode(" ",$var); 224 | $res[$i]["table"] = $table; 225 | $res[$i]["name"] = $col[0]; 226 | $res[$i]["type"] = $col[1]; 227 | $res[$i]["flags"] = ""; 228 | $res[$i]["position"] = ""; 229 | if (is_numeric($col[2])) { 230 | $res[$i]["len"] = $col[2]; 231 | $flag_pos = 3; 232 | } else { 233 | $flag_pos = 2; 234 | $res[$i]["len"] = ""; // constraints like 'PRIMARY KEY' may follow the column name 235 | } 236 | while (!empty($col[$flag_pos])) { 237 | $res[$i]["flags"] .= " ".$col[$flag_pos]; 238 | ++$flag_pos; 239 | } 240 | $res[$i]["flags"] = trim($res[$i]["flags"]); 241 | ++$i; 242 | } 243 | $this->free(); 244 | return $res; 245 | } 246 | 247 | /** Free result set 248 | * @method free 249 | */ 250 | function free() { 251 | $this->Result->finalize(); 252 | } 253 | 254 | /** Evaluate the result for DML operation 255 | * @class db_sqlite 256 | * @method affected_rows 257 | * @return integer affected rows 258 | */ 259 | function affected_rows() { 260 | return $this->changes(); 261 | } 262 | 263 | /** Evaluate the result for SELECT operation (row count) 264 | * @class db_sqlite 265 | * @method num_rows 266 | * @return integer number of rows in result set 267 | * @version not available in PHP API, so this always returns FALSE 268 | */ 269 | function num_rows() { 270 | if (!$this->NumRowsEmulate) return FALSE; 271 | $i = 0; 272 | while ($this->next_record()) ++$i; 273 | $this->Result->reset(); 274 | return $i; 275 | } 276 | 277 | /** Evaluate the result for SELECT operation (fieldset count) 278 | * @class db_sqlite 279 | * @method num_fields 280 | * @return integer number of columns in result set 281 | */ 282 | function num_fields() { 283 | return $this->Result->numColumns(); 284 | } 285 | 286 | function nf() { 287 | return $this->num_rows(); 288 | } 289 | 290 | function np() { 291 | print $this->num_rows(); 292 | } 293 | 294 | /** Retrieve the content of a field in the current record of the result set 295 | * @class db_sqlite 296 | * @method f 297 | * @return string content of field 298 | */ 299 | function f($Name) { 300 | return $this->Record[$Name]; 301 | } 302 | 303 | function p($Name) { 304 | print $this->Record[$Name]; 305 | } 306 | 307 | function halt($msg) { 308 | if ("no" == $this->Halt_On_Error) 309 | return; 310 | 311 | $this->haltmsg($msg); 312 | 313 | if ("report" != $this->Halt_On_Error) 314 | die("Session halted."); 315 | } 316 | 317 | function haltmsg($msg) { 318 | //file_put_contents('qlog.err',$this->qlog); 319 | printf("

Database error: %s
\n", $msg); 320 | printf("SQLite3 Error %s

\n", $this->Error); 321 | $bt = debug_backtrace(); 322 | $btl = count($bt); 323 | echo("

Backtrace:

");
324 |     for ($i=2;$i<=$btl;++$i) { // 0=here, 1=query which is already reported above
325 |       echo "  [".$bt[$i]['file'].":".$bt[$i]['line']."] in function '".$bt[$i]['function']."()'\n";
326 |     }
327 |     echo "
\n"; 328 | } 329 | 330 | /** Retrieve all table names 331 | * @class db_sqlite 332 | * @method table_names 333 | * @return array table_name, tablespace_name (=Database), database 334 | */ 335 | function table_names() { 336 | $this->query("SELECT name FROM SQLITE_MASTER WHERE type='table' ORDER BY name"); 337 | $i=0; 338 | while ($info=sqlite3_fetch_array($this->Query_ID)) 339 | { 340 | $return[$i]["table_name"]= $info[0]; 341 | $return[$i]["tablespace_name"]=$this->Database; 342 | $return[$i]["database"]=$this->Database; 343 | $i++; 344 | } 345 | return $return; 346 | } 347 | 348 | /** Table locking 349 | * @class db_sqlite 350 | * @method lock 351 | * @param mixed table table to lock (either string or array of strings) 352 | * @param optional string mode locking mode (defaults to "write") 353 | * @return boolean success 354 | */ 355 | function lock($table, $mode="write") { 356 | $this->connect(); 357 | 358 | $query="lock tables "; 359 | if (is_array($table)) { 360 | while (list($key,$value)=each($table)) { 361 | if ($key=="read" && $key!=0) { 362 | $query.="$value read, "; 363 | } else { 364 | $query.="$value $mode, "; 365 | } 366 | } 367 | $query=substr($query,0,-2); 368 | } else { 369 | $query.="$table $mode"; 370 | } 371 | $res = @sqlite3_query($this->Link_ID,$query); 372 | if (!$res) { 373 | $this->halt("lock($table, $mode) failed."); 374 | return 0; 375 | } 376 | return $res; 377 | } 378 | 379 | /** Unlock all tables 380 | * @class db_sqlite 381 | * @method unlock 382 | * @return boolean success 383 | */ 384 | function unlock() { 385 | $this->connect(); 386 | $res = @sqlite3_query($this->Link_ID,"unlock tables"); 387 | if (!$res) { 388 | $this->halt("unlock() failed."); 389 | return 0; 390 | } 391 | return $res; 392 | } 393 | 394 | } 395 | ?> 396 | -------------------------------------------------------------------------------- /lib/formats.csv: -------------------------------------------------------------------------------- 1 | "name";"mimetype";"ftype_human";"ftitle" 2 | "epub";"application/epub+zip";"ePub";"EPUB" 3 | "mobi";"application/x-mobipocket-ebook";"MobiPocket";"Kindle" 4 | "pdf";"application/pdf";"PDF";"PDF" 5 | "fb2";"application/fb2+zip";"FB2";"FictionBook" 6 | -------------------------------------------------------------------------------- /lib/isbnsearch.csv: -------------------------------------------------------------------------------- 1 | "name";"url";"comment" 2 | "Amazon.DE";"http://www.amazon.de/gp/search?ie=UTF8&keywords={isbn}&tag={amazonID}&index=books";"" 3 | "Amazon.COM";"http://www.amazon.com/s/url=search-alias=aps&field-keywords={isbn}";"" 4 | "Bookzilla.DE";"http://www.bookzilla.de/shop/action/quickSearch?searchString={isbn}";"" 5 | "Buchhandel.DE";"http://vlb-public.bvdep.com/?caller=vlbPublic&strFrame=titelsuche&isbn={isbn}";"All books available in German book trade" 6 | "Google.DE";"http://books.google.de/books?isbn={isbn}";"" 7 | "Google.COM";http://books.google.com/books?isbn={isbn}";"" 8 | "Hugendubel.DE","http://www.hugendubel.de/suche/index.html?com={isbn}&f=search.getsearch";"" 9 | "Thalia.AT","http://www.thalia.at/shop/ama_homestartseite/suche/?sq={isbn}";"" 10 | "Thalia.CH","http://www.thalia.ch/shop/ama_homestartseite/suche/?sq={isbn}";"" 11 | "Thalia.DE","http://www.thalia.de/shop/ama_homestartseite/suche/?sq={isbn}";"" 12 | "abebooks.DE";"http://www.abebooks.de/servlet/BookSearchPL?ph=2&isbn={isbn}";"Used books / Antiquariat" 13 | "BookCrossing.COM";"http://www.bookcrossing.com/search/?title=&author=&category=&isbn={isbn}&bcid=&status=0";"gratis exchange (see http://de.wikipedia.org/wiki/Bookcrossing)" 14 | "BookLooker.DE";"http://www.booklooker.de/app/result.php?mediaType=0&isbn={isbn}";"market for used books, videos, ..." 15 | "Buchfreund.DE";"http://www.buchfreund.de/results.php?q={isbn}";"MetaSearch for used books" 16 | "EuroBuch.COM";"http://suche.eurobuch.com/?isbn={isbn}";"MetaSearch for used books" 17 | "ZVAB.COM";"http://www.zvab.com/?axx=wiki_ISBNde&isbn={isbn}";"Zentrales Verzeichnis Antiquarischer Bücher (http://de.wikipedia.org/wiki/Zentrales_Verzeichnis_Antiquarischer_B%C3%BCcher)" 18 | "LibraryThing";"http://www.librarything.de/search_works.php?q={isbn}";"Social Stuff" 19 | "LovelyBooks.DE";"http://www.lovelybooks.de/suche/{isbn}/";"Social Stuff" 20 | -------------------------------------------------------------------------------- /scan.php: -------------------------------------------------------------------------------- 1 | # 5 | # http://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 | # Scan for books and feed database # 11 | ############################################################################# 12 | # $Id$ 13 | 14 | require_once('./lib/class.logging.php'); // must come first as it also defines some CONST 15 | require_once('./config.php'); 16 | if ( $scan_cli_only && php_sapi_name() != 'cli' ) { // protect agains access by visitors 17 | header('HTTP/1.0 403 Forbidden'); 18 | echo "

You are not allowed to be here.

\n

The gatekeeper won't let you see this page. Please point your browser to a different one.

\n"; 19 | trigger_error('Web access to scan scripts denied by config', E_USER_ERROR); 20 | exit; 21 | } 22 | require_once('./lib/common.php'); 23 | require_once('./lib/class.filefuncs.php'); 24 | $filefuncs = new filefuncs($logger,$use_markdown,$bookformats,$bookdesc_ext,$bookmeta_ext,$check_xml,$skip_broken_xml); 25 | require_once('./lib/db_sqlite3.php'); 26 | require_once('./lib/class.db.php'); 27 | if ( $autoExtract ) { 28 | require_once('./lib/class.epubdesc.php'); 29 | if ( in_array('all',$extract2desc) || in_array('desc',$extract2desc) ) { // for trans of terms in bookdesc 30 | require_once('./lib/class.csv.php'); 31 | $csv = new csv(";",'"',TRUE,FALSE); 32 | } else { 33 | $csv = NULL; 34 | } 35 | } else { 36 | $csv = NULL; 37 | } 38 | 39 | $db = new db($dbfile); 40 | 41 | $pubdate = date('c'); 42 | $books = array(); 43 | $genres = array(); $allGenres = array(); 44 | $authors = array(); 45 | $series = array(); 46 | $publisher = array(); 47 | 48 | // directory structure for books directory: 49 | // /// 50 | 51 | #===========================================================[ Collect data ]=== 52 | // Go for the languages available 53 | $logger->info("Scanning $bookroot [MODE=$scan_dbmode]",'SCAN'); 54 | $logger->debug("use_lang: $use_lang",'SCAN'); 55 | $logger->debug("Languages: ".implode(', ',$uselangs),'SCAN'); 56 | $logger->debug("DBFile: $dbfile",'SCAN'); 57 | $langs = $filefuncs->scanFolder($bookroot); 58 | 59 | // Now collect the genres 60 | foreach($langs as $lang) { 61 | GLOBAL $use_markdown; 62 | if ( !empty($uselangs) && !in_array($lang,$uselangs) ) { 63 | $logger->debug("* Skipping langDir '$lang'",'SCAN'); 64 | continue; 65 | } 66 | $logger->info("* Scanning langDir '$lang'",'SCAN'); 67 | if ( $csv !== NULL && file_exists("./lang/ebookterms.${lang}") ) { 68 | $csv = new csv(";",'"',TRUE,FALSE); 69 | $logger->debug('# Loaded terms for ebook desc from '.__DIR__."./lang/ebookterms.${lang}",'SCAN'); 70 | $csv->import("./lang/ebookterms.${lang}"); 71 | } 72 | $genres[$lang] = $filefuncs->scanFolder($bookroot . DIRECTORY_SEPARATOR . $lang); 73 | 74 | // Now come the authors 75 | foreach($genres[$lang] as $gidx => $genre) { 76 | $logger->info(" + Scanning genre dir '$genre'",'SCAN'); 77 | $gdir = $genre; 78 | if ( $use_markdown && !file_exists($bookroot . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $genre . DIRECTORY_SEPARATOR . '.nomarkdown') ) $gmarkdown = 1; 79 | else $gmarkdown = 0; 80 | if ( in_array('genre',$dotname_overrides) && file_exists($bookroot . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $genre . DIRECTORY_SEPARATOR . '.name') ) { 81 | $genre = trim(file_get_contents($bookroot . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $genre . DIRECTORY_SEPARATOR . '.name')); 82 | $genres[$lang][$gidx] = $genre; 83 | } 84 | $allGenres = array_merge($allGenres,array($genre)); 85 | $tauthors = $filefuncs->scanFolder($bookroot . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $gdir); 86 | 87 | // Guess what - they wrote books! 88 | foreach($tauthors as $aidx => $author) { 89 | $logger->debug(" - Scanning author dir '$author'",'SCAN'); 90 | $adir = $author; 91 | if ( in_array('author',$dotname_overrides) && file_exists($bookroot . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $gdir . DIRECTORY_SEPARATOR . $adir . DIRECTORY_SEPARATOR . '.name') ) { 92 | $author = trim(file_get_contents($bookroot . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $gdir . DIRECTORY_SEPARATOR . $adir . DIRECTORY_SEPARATOR . '.name')); 93 | $tauthors[$aidx] = $author; 94 | } 95 | if ( $gmarkdown && !file_exists($bookroot . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $gdir . DIRECTORY_SEPARATOR . $adir . DIRECTORY_SEPARATOR . '.nomarkdown') ) { 96 | $tbooks = $filefuncs->scanFolder($bookroot . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $gdir . DIRECTORY_SEPARATOR . $adir, 'files', $gmarkdown); 97 | } else { 98 | $tbooks = $filefuncs->scanFolder($bookroot . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $gdir . DIRECTORY_SEPARATOR . $adir, 'files', 0); 99 | } 100 | //array[name] with [files][ext], [desc] ([series],[series_index],[rating],[publisher],[isbn], [author],[tag] 101 | foreach($tbooks as $book=>$dummy) { 102 | $tbooks[$book]['lang'] = $lang; 103 | $tbooks[$book]['genre'] = $genre; 104 | if ( empty($tbooks[$book]['author']) ) { // no author defined in .data 105 | $authors = array_merge($authors,array($author)); 106 | $tbooks[$book]['author'][] = $author; 107 | } else { 108 | $authors = array_merge($authors,$tbooks[$book]['author']); 109 | if ( !in_array('author',$data_overrides) ) { // in no-override mode, merge in author from dirname 110 | $authors = array_merge($authors,$author); 111 | if ( !(is_array($tbooks[$book]['author']) && in_array($author,$tbooks[$book]['author'])) ) $tbooks[$book]['author'][] = $author; 112 | } 113 | } 114 | if ( $GLOBALS['autoExtract'] && isset($tbooks[$book]['files']['epub']) ) { 115 | $epub = new epubdesc($tbooks[$book]['files']['epub']); 116 | $pathinfo = pathinfo($tbooks[$book]['files']['epub']); 117 | $cover = $filefuncs->getCover($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename']); 118 | if ( $extractCover > 0 && $GLOBALS['cover_mode']!='off' && empty($cover) ) { 119 | if ( $rc = $epub->writeCover($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename']) ) { 120 | $cover = $filefuncs->getCover($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename']); 121 | $logger->info(" - extracted cover: '${cover}'",'SCAN'); 122 | } 123 | if ( $extractCover > 1 && $rc ) { 124 | $filefuncs->resizeCover($cover,$cover_width); 125 | } 126 | } 127 | if ( !empty($extract2data) && !file_exists($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename'].'.'.$bookmeta_ext) ) { 128 | $logger->info(" - extracting Metadata: '".$pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename'].'.'.$bookmeta_ext."'",'SCAN'); 129 | $epub->setExtract2data($extract2data); 130 | $epub->setDataExt($bookmeta_ext); 131 | $epub->writeData($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename']); 132 | if ( file_exists($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename'].'.'.$bookmeta_ext) ) 133 | $filefuncs->readData($tbooks[$book],$pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename'].'.'.$bookmeta_ext); 134 | } 135 | if ( !empty($extract2desc) && !$filefuncs->file_exists_glob($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename'],$bookdesc_ext) ) { 136 | $logger->info(" - extracting Descdata: '".$pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename'].'.'.$bookdesc_ext[0]."'",'SCAN'); 137 | if ( !empty($csv->data) ) $epub->setTerms($csv->data); 138 | $epub->setExtract2desc($extract2desc); 139 | $epub->setDescExt($bookdesc_ext[0]); 140 | $epub->writeDesc($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename']); 141 | if ( file_exists($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename'].'.'.$bookdesc_ext[0]) ) { 142 | $tbooks[$book]['desc'] = trim(file_get_contents($pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename'].'.'.$bookdesc_ext[0])); 143 | $filefuncs->formatDesc($tbooks[$book]['desc'],$gmarkdown); 144 | if ( $check_xml && !empty($tbooks[$book]['desc']) ) $filefuncs->validateXML($tbooks[$book]['desc'], $pathinfo['dirname'].DIRECTORY_SEPARATOR.$pathinfo['filename'].'.'.$bookdesc_ext[0]); 145 | } 146 | } 147 | } 148 | if ( !empty($tbooks[$book]['tag']) ) $allGenres = array_merge($allGenres,$tbooks[$book]['tag']); // from *.data file 149 | if ( !empty($tbooks[$book]['series']) ) $series[] = $tbooks[$book]['series']; // from *.data file 150 | if ( !empty($tbooks[$book]['publisher']) ) $publisher[] = $tbooks[$book]['publisher']; // from *.data file 151 | } 152 | $books = array_merge($books,$tbooks); 153 | } 154 | } 155 | } 156 | sort($allGenres); 157 | 158 | #======================================================[ Feed the database ]=== 159 | $logger->info('* Updating database','SCAN'); 160 | $db->truncAll(); 161 | $db->make_genres($allGenres); 162 | if (!empty($publisher)) $db->make_publisher($publisher); 163 | if (!empty($series)) $db->make_series($series); 164 | $db->make_authors($authors); 165 | $db->make_books($books); 166 | 167 | $logger->info('* Cleaning up database','SCAN'); 168 | $db->query('VACUUM'); 169 | $db->query('REINDEX'); 170 | 171 | $logger->info("* Done",'SCAN'); 172 | 173 | exit; 174 | 175 | ?> -------------------------------------------------------------------------------- /scan_calibre_covers.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | # 6 | # http://www.izzysoft.de/ # 7 | # ------------------------------------------------------------------------- # 8 | # This program is free software; you can redistribute and/or modify it # 9 | # under the terms of the GNU General Public License (see doc/LICENSE) # 10 | # ------------------------------------------------------------------------- # 11 | # Scan for covers and create symlinks # 12 | ############################################################################# 13 | # $Id$ 14 | 15 | $use_lang = 'cal'; 16 | 17 | require_once('./lib/class.logging.php'); // must come first as it also defines some CONST 18 | require_once('./config.php'); 19 | require_once('./lib/common.php'); 20 | require_once('./lib/class.filefuncs.php'); 21 | $filefuncs = new filefuncs($logger,$use_markdown,$bookformats,$bookdesc_ext,$bookmeta_ext,$check_xml,$skip_broken_xml); 22 | require_once('./lib/db_sqlite3.php'); 23 | require_once('./lib/class.db.php'); 24 | 25 | $coverdir = $cover_base.DIRECTORY_SEPARATOR.$use_lang; 26 | if ( !is_dir($cover_base) ) mkdir($cover_base); 27 | if ( !is_dir($coverdir) ) mkdir($coverdir); 28 | $filefuncs->prepareCalibreCovers($coverdir,$bookroot,$cover_mode,$dbfile); 29 | ?> -------------------------------------------------------------------------------- /scan_de.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | -------------------------------------------------------------------------------- /scan_en.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | -------------------------------------------------------------------------------- /tpl/html/asap.css: -------------------------------------------------------------------------------- 1 | /* Styles for ASAP (Amazon Simple ADs) */ 2 | 3 | /* 3 horizontal elements */ 4 | .amazon3col { 5 | width:550px; 6 | max-height:100px; 7 | background-color:transparent; 8 | margin-bottom:.3em; 9 | } 10 | .amazon3col .amarow div { 11 | background-color:#ffffff; 12 | margin:1em; 13 | border-radius:4px; 14 | border-spacing:1em; 15 | border:2px solid gray; 16 | box-shadow: 4px 4px 2px #8a8; 17 | font-family: "DejaVu Sans", Tahoma, Verdana, sans-serif; 18 | font-size:10px !important; 19 | } 20 | .amazon3col div.premium { 21 | border-color: rgb(255, 153, 102); /* #f96 */ 22 | background-color: rgb(255, 238, 170); 23 | } 24 | .amazon3col img { 25 | border:0; 26 | vertical-align:middle; 27 | float:left; 28 | margin-right:0.5em; 29 | max-height:75px; 30 | max-width:50px; 31 | } 32 | .amazon3col a, .amazon3col a:visited { 33 | text-decoration:none; 34 | color:#00f; 35 | } 36 | #amazon .priceinfo { color:#009000; } 37 | #amazon { 38 | text-align:center; 39 | margin-top:2em; 40 | } 41 | #amazon .ama_cachedate { 42 | font-size:9px !important; 43 | color:#555555; 44 | margin-bottom:1em; 45 | } 46 | 47 | .amazon3col { display:table; margin-left:auto;margin-right:auto; border:0; } 48 | .amazon3col .amarow { display:table-row; } 49 | .amazon3col .amarow div { display:table-cell; min-width:150px; margin-right:.5em; } 50 | @media (min-width: 601px) { .amazon3col { border-spacing:.1em; } } 51 | @media (max-width: 600px) { 52 | .amazon3col, .amazon3col div { display:inline-table; } 53 | .amazon3col .amarow div { display:inline-table; margin:.2em; border-spacing:.2em; max-width:400px; } 54 | .amazon3col { width:auto; max-width: 250px; } 55 | } 56 | -------------------------------------------------------------------------------- /tpl/html/asap3col.tpl: -------------------------------------------------------------------------------- 1 |
2 |
3 | 11 |
12 |
{cachedate}
13 |
14 | -------------------------------------------------------------------------------- /tpl/html/author.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} - {books_by_whom} 4 | 5 | 6 | 7 | {ad_css} 8 | 9 | 10 | 11 | 12 | 13 |

{site_title} - {books_by_whom}

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 36 |
home {start_page}
home {back_to_authors}
home {sort_alpha}
home {sort_date}
home Wikipedia
home {title}
 
26 | 27 | {link1_open}{first_page}{link_close} 28 | {link2_open}{prev_page}{link_close} 29 | 30 | 31 | 32 | {link1_open}{next_page}{link_close} 33 | {link2_open}{last_page}{link_close} 34 | 35 |
37 | 38 |
{last_update}: {pubdate_human}
39 | 40 | {adblock} 41 | 42 |
{created_by} miniCalOPe
43 | 44 | -------------------------------------------------------------------------------- /tpl/html/authors.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} - {author_list} 4 | 5 | 6 | 7 | {ad_css} 8 | 9 | 10 | 11 | 12 | 13 |

{site_title} - {author_list}

14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 37 |
home {start_page}
sort {sort_alpha}
sort {sort_bookcount}
authors {name} 21 | ({num_books} {books})
 
25 | 26 | {link1_open}{first_page}{link_close} 27 | {linkx_open}{skip_page}{link_close} 28 | {link2_open}{prev_page}{link_close} 29 | 30 | 31 | 32 | {link1_open}{next_page}{link_close} 33 | {linkx_open}{skip_page}{link_close} 34 | {link2_open}{last_page}{link_close} 35 | 36 |
38 | 39 |
{last_update}: {pubdate_human}
40 | 41 | {adblock} 42 | 43 |
{created_by} miniCalOPe
44 | 45 | -------------------------------------------------------------------------------- /tpl/html/book.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} - {title_by_author} 4 | 5 | 6 | 7 | {ad_css} 8 | 9 | 10 | 11 | 12 | 13 |

{site_title} - {title_by_author}

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
home {start_page}
authors {authors_page}
home {series_page}
30 | 31 | 51 |
32 | 33 | cover 34 | 35 | 36 |
37 | {booktitle} 38 |
{authorname}
39 |
40 | 41 |
42 | 43 | {data_name}: {data_data}

44 | 45 |
{comment}
46 |

{field_download}: 47 | 48 | {ftype_human} ({flength_human})  49 | 50 |

52 | 53 |
{last_update}: {pubdate_human}
54 | 55 | {adblock} 56 | 57 | 58 |
59 | 60 | 61 |
{created_by} miniCalOPe
62 | 63 | 64 | -------------------------------------------------------------------------------- /tpl/html/flashads.tpl: -------------------------------------------------------------------------------- 1 |
2 | 12 | 13 |
14 | -------------------------------------------------------------------------------- /tpl/html/index.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} 4 | 5 | 6 | 7 | {ad_css} 8 | 9 | 10 | 11 | 12 | 13 |

{site_title}

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
author {author_list}
books {title_list}
topics {tags_list}
series {series_list}
search {search_form}
22 | 23 |
{last_update}: {pubdate_human}
({num_allbooks} {allbooks})
24 | 25 | {adblock} 26 | 27 |
{created_by} miniCalOPe
28 | 29 | -------------------------------------------------------------------------------- /tpl/html/search.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} 4 | 5 | 6 | 7 | {ad_css} 8 | 9 | 10 | 11 | 18 | 19 | 20 |

{site_title}: {bsearch}

21 | 22 |
23 | 24 | 35 | 36 |
25 | 26 | 27 | 28 | 29 |
{author_title}:
{book_title}:
{series_title}:
{desc_title}:
{tags_title}: 34 |
37 |
38 | 39 |
{last_update}: {pubdate_human}
({num_allbooks} {allbooks})
40 | 41 | {adblock} 42 | 43 |
{created_by} miniCalOPe
44 | 45 | -------------------------------------------------------------------------------- /tpl/html/serie.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} - {books_in_serie} 4 | 5 | 6 | 7 | {ad_css} 8 | 9 | 10 | 11 | 12 | 13 |

{site_title} - {books_in_serie}

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 36 |
home {start_page}
tags {back_to_series}
alpha {sort_alpha}
authors {sort_author}
date {sort_index}
book {title_by_author}
 
26 | 27 | {link1_open}{first_page}{link_close} 28 | {link2_open}{prev_page}{link_close} 29 | 30 | 31 | 32 | {link1_open}{next_page}{link_close} 33 | {link2_open}{last_page}{link_close} 34 | 35 |
37 | 38 |
{last_update}: {pubdate_human}
39 | 40 | {adblock} 41 | 42 |
{created_by} miniCalOPe
43 | 44 | 45 | -------------------------------------------------------------------------------- /tpl/html/series.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} - {series_list} 4 | 5 | 6 | 7 | {ad_css} 8 | 9 | 10 | 11 | 12 | 13 |

{site_title} - {series_list}

14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 37 |
home {start_page}
home {sort_alpha}
home {sort_bookcount}
home {name} 21 | ({num_books} {books})
 
25 | 26 | {link1_open}{first_page}{link_close} 27 | {linkx_open}skip{link_close} 28 | {link2_open}{prev_page}{link_close} 29 | 30 | 31 | 32 | {link1_open}{next_page}{link_close} 33 | {linkx_open}skip{link_close} 34 | {link2_open}{last_page}{link_close} 35 | 36 |
38 | 39 |
{last_update}: {pubdate_human}
({num_allbooks} {allbooks})
40 | 41 | {adblock} 42 | 43 |
{created_by} miniCalOPe
44 | 45 | 46 | -------------------------------------------------------------------------------- /tpl/html/style.css: -------------------------------------------------------------------------------- 1 | body,p,td,th,div { 2 | font-family: "DejaVu Sans", Verdana, sans-serif; 3 | font-size:10pt; 4 | } 5 | body { 6 | margin-top: 10px; 7 | margin-right: 0px; 8 | margin-left: 0px; 9 | background-color: #fff; 10 | color: #00e; 11 | } 12 | 13 | p { text-align:justify; } 14 | 15 | a { text-decoration:none; } 16 | a:link { color: #b00; } 17 | a:visited { color: #f50; } 18 | .list td a:link, .list td a:visited, #isbnsearch a:link, #isbnsearch a:visited, #booksearch a:link, #booksearch a:visited { color: #00e; } 19 | .list td:hover a, #isbnsearch a:hover { color:#ffffff; } 20 | .list td:hover, #isbnsearch a:hover { background-color:#4d9dfc; } 21 | #isbnsearch a, #booksearch a { 22 | border: 1px solid #4d9dfc; 23 | padding-left: 2px; 24 | padding-right: 2px; 25 | } 26 | #booksearch a { background-color: #dff; } 27 | #isbnsearch a { background-color: #ddd; } 28 | #isbnsearch a:hover, #booksearch a:hover { border-color: yellow; } 29 | 30 | .error { color: #f00; } 31 | 32 | h1, div.updated, div.appsig, div.offers { text-align:center; } 33 | h1 { font-size:110%; } 34 | div.appsig { font-size:70%; } 35 | div.updated, div.appsig { margin-top: 10px; } 36 | div.offers { margin:1em; } 37 | span.count { font-size:85%; } 38 | img { border:0; vertical-align:middle; } 39 | 40 | /* Fake-Cover */ 41 | .CoverBlank { 42 | position:relative; 43 | width:180px; 44 | height:280px; 45 | padding:10px; 46 | margin-right:15px; 47 | margin-left:5px; 48 | text-align:center; 49 | float:left; 50 | background:#dedede; 51 | /* cursor:pointer;display:block; */ 52 | } 53 | .CoverBlank .innerBorder { 54 | float:left; 55 | width:176px; 56 | height:276px; 57 | border:1px solid #fff; 58 | } 59 | .CoverBlank a {text-decoration: none;} 60 | .CoverBlank .BookTitle { 61 | position:absolute; 62 | top:50%;left:50%; 63 | width:160px;margin-left:-85px; 64 | height:200px; 65 | margin-top:-100px; 66 | font-size: 14px; 67 | font-weight: bold; 68 | color:#333; 69 | } 70 | .CoverBlank .Author { 71 | color:#666; 72 | font-style:italic; 73 | padding:8px 4px 0; 74 | font-size:12px; 75 | font-weight: normal; 76 | position:absolute; 77 | bottom: 0; 78 | width:100%; 79 | } 80 | 81 | #bookdesc { 82 | background-color:#ffc; 83 | color:#00a; 84 | padding:.3em 1em; 85 | margin-right:1em; 86 | box-shadow: 0 0 20px rgba(222,222,222,.95); 87 | } 88 | 89 | /* Adjustments for mobile devices in portait mode */ 90 | @media (max-width: 800px) { 91 | #booktable tr { display:inline; } 92 | #booktable .bookcover { display:table; margin-left:auto; margin-right:auto; } 93 | #booktable td p.buttons { line-height: 1.7em; } 94 | #bookdesc { padding:.15em .5em; margin-right:.5em; } 95 | #isbnsearch a, #booksearch a { padding-left:4px; padding-right:4px; } 96 | } 97 | -------------------------------------------------------------------------------- /tpl/html/tag.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} - {books_with_tag} 4 | 5 | 6 | 7 | {ad_css} 8 | 9 | 10 | 11 | 12 | 13 |

{site_title} - {books_with_tag}

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 36 |
home {start_page}
tags {tags_list}
alpha {sort_alpha}
authors {sort_author}
book {title_by_author}
 
26 | 27 | {link1_open}{first_page}{link_close} 28 | {link2_open}{prev_page}{link_close} 29 | 30 | 31 | 32 | {link1_open}{next_page}{link_close} 33 | {link2_open}{last_page}{link_close} 34 | 35 |
37 | 38 |
{last_update}: {pubdate_human}
39 | 40 | {adblock} 41 | 42 |
{created_by} miniCalOPe
43 | 44 | 45 | -------------------------------------------------------------------------------- /tpl/html/tags.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} - {tags_list} 4 | 5 | 6 | 7 | {ad_css} 8 | 9 | 10 | 11 | 12 | 13 |

{site_title} - {tags_list}

14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 37 |
home {start_page}
alpha {sort_alpha}
numsort {sort_bookcount}
tag {name} 21 | ({num_books} {books})
 
25 | 26 | {link1_open}{first_page}{link_close} 27 | {linkx_open}skip{link_close} 28 | {link2_open}{prev_page}{link_close} 29 | 30 | 31 | 32 | {link1_open}{next_page}{link_close} 33 | {linkx_open}skip{link_close} 34 | {link2_open}{last_page}{link_close} 35 | 36 |
38 | 39 |
{last_update}: {pubdate_human}
({num_allbooks} {allbooks})
40 | 41 | {adblock} 42 | 43 |
{created_by} miniCalOPe
44 | 45 | 46 | -------------------------------------------------------------------------------- /tpl/html/titles.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | {site_title} - {title_list} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

{site_title} - {title_list}

13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 36 |
home {start_page}
alpha {sort_alpha}
alpha {sort_author}
alpha {sort_date}
book {title}
 
24 | 25 | {link1_open}{first_page}{link_close} 26 | {linkx_open}skip{link_close} 27 | {link2_open}{prev_page}{link_close} 28 | 29 | 30 | 31 | {link1_open}{next_page}{link_close} 32 | {linkx_open}skip{link_close} 33 | {link2_open}{last_page}{link_close} 34 | 35 |
37 | 38 |
{last_update}: {pubdate_human}
({num_allbooks} {allbooks})
39 | 40 |
{created_by} miniCalOPe
41 | 42 | 43 | -------------------------------------------------------------------------------- /tpl/icons/1left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/1left.png -------------------------------------------------------------------------------- /tpl/icons/1left_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/1left_grey.png -------------------------------------------------------------------------------- /tpl/icons/1right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/1right.png -------------------------------------------------------------------------------- /tpl/icons/1right_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/1right_grey.png -------------------------------------------------------------------------------- /tpl/icons/2left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/2left.png -------------------------------------------------------------------------------- /tpl/icons/2left_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/2left_grey.png -------------------------------------------------------------------------------- /tpl/icons/2right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/2right.png -------------------------------------------------------------------------------- /tpl/icons/2right_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/2right_grey.png -------------------------------------------------------------------------------- /tpl/icons/3left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/3left.png -------------------------------------------------------------------------------- /tpl/icons/3left_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/3left_grey.png -------------------------------------------------------------------------------- /tpl/icons/3right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/3right.png -------------------------------------------------------------------------------- /tpl/icons/3right_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/3right_grey.png -------------------------------------------------------------------------------- /tpl/icons/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/alpha.png -------------------------------------------------------------------------------- /tpl/icons/author.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/author.png -------------------------------------------------------------------------------- /tpl/icons/authors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/authors.png -------------------------------------------------------------------------------- /tpl/icons/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/book.png -------------------------------------------------------------------------------- /tpl/icons/bookcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/bookcase.png -------------------------------------------------------------------------------- /tpl/icons/bookseries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/bookseries.png -------------------------------------------------------------------------------- /tpl/icons/date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/date.png -------------------------------------------------------------------------------- /tpl/icons/find.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/find.png -------------------------------------------------------------------------------- /tpl/icons/flattr-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/flattr-badge.png -------------------------------------------------------------------------------- /tpl/icons/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/home.png -------------------------------------------------------------------------------- /tpl/icons/liberapay-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/liberapay-badge.png -------------------------------------------------------------------------------- /tpl/icons/rating_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/rating_1.gif -------------------------------------------------------------------------------- /tpl/icons/rating_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/rating_2.gif -------------------------------------------------------------------------------- /tpl/icons/rating_3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/rating_3.gif -------------------------------------------------------------------------------- /tpl/icons/rating_4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/rating_4.gif -------------------------------------------------------------------------------- /tpl/icons/rating_5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/rating_5.gif -------------------------------------------------------------------------------- /tpl/icons/tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/tag.png -------------------------------------------------------------------------------- /tpl/icons/tags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/tags.png -------------------------------------------------------------------------------- /tpl/icons/world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IzzySoft/miniCalOPe/b1000fbb1921ee417a91867cbc6f604f222f1f81/tpl/icons/world.png -------------------------------------------------------------------------------- /tpl/opds/author.tpl: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | {baseurl}index.php?prefix=author_id&sort_order={sortorder}&query={aid}&offset={offset}&lang={lang} 8 | {books_by_whom} 9 | {pubdate} 10 | miniCalOPe. 11 | 12 | 13 | {owner} 14 | {homepage} 15 | {email} 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 37 | 40 | 43 | 46 | 49 | 50 | {total} 51 | {start} 52 | {per_page} 53 | 54 | 55 | {start_page} 56 | {baseurl}index.php?lang={lang} 57 | 58 | 59 | 60 | {pubdate} 61 | 62 | 63 | 64 | {back_to_authors} 65 | {baseurl}index.php?prefix=authors&lang={lang} 66 | 67 | 68 | 69 | {pubdate} 70 | 71 | 72 | 73 | {sort_alpha} 74 | baseurl}?prefix=author_id&sort_order=title&query={aid}&lang={lang} 75 | 76 | 77 | 78 | {pubdate} 79 | 80 | 81 | 82 | {sort_date} 83 | {baseurl}index.php?prefix=author_id&sort_order=date&query={aid}&lang={lang} 84 | 85 | 86 | 87 | {pubdate} 88 | 89 | 90 | 91 | Wikipedia 92 | {wikibase}{wikiauthor} 93 | 94 | 95 | 96 | {pubdate} 97 | 98 | 99 | 100 | 101 | {title} 102 | {baseurl}{bid}.opds 103 | ISBN: {isbn} 104 | 105 | 106 | {pubdate} 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /tpl/opds/authors.tpl: -------------------------------------------------------------------------------- 1 | 2 | 6 | {baseurl}index.php?prefix=authors&sort_order={sortorder}&lang={lang} 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 25 | 28 | 31 | 34 | 35 | 36 | {author_list} 37 | miniCalOPe 38 | {pubdate} 39 | 40 | 41 | {owner} 42 | {homepage} 43 | {email} 44 | 45 | 46 | {total} 47 | {start} 48 | {per_page} 49 | 50 | 51 | {start_page} 52 | {baseurl}index.php?lang={lang} 53 | 54 | 55 | 56 | {pubdate} 57 | 58 | 59 | 60 | 61 | {sort_alpha} 62 | {baseurl}index.php?prefix=authors&sort_order=title&lang={lang} 63 | 64 | 65 | 66 | {pubdate} 67 | 68 | 69 | 70 | {sort_bookcount} 71 | {baseurl}index.php?prefix=authors&sort_order=books&lang={lang} 72 | 73 | 74 | 75 | {pubdate} 76 | 77 | 78 | 79 | 80 | {name} 81 | {baseurl}index.php?prefix=author_id&sort_order=downloads&query={id}&lang={lang} 82 | {num_books} {books} 83 | 84 | 85 | {pubdate} 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /tpl/opds/book.tpl: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | {baseurl}{id}.opds 7 | {title_by_author} 8 | miniCalOPe. 9 | {pubdate} 10 | 11 | 12 | {owner} 13 | {homepage} 14 | {email} 15 | 16 | 17 | 20 | 23 | 26 | 29 | 32 | 33 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | {start_page} 45 | {baseurl}index.php?lang={lang} 46 | 47 | 48 | 49 | {pubdate} 50 | 51 | 52 | 53 | 54 | {authors_page} 55 | {baseurl}index.php?prefix=author_id&query={aid}&lang={lang} 56 | 57 | 58 | 59 | {pubdate} 60 | 61 | 62 | 63 | 64 | 65 | {series_page} 66 | {baseurl}index.php?lang={lang}&prefix=series_id&query={id} 67 | 68 | 69 | 70 | {pubdate} 71 | 72 | 73 | 74 | 75 | {booktitle} 76 | urn:calibre:{id} 77 | {pubdate} 78 | {pubdate} 79 | 80 | 81 | {authorname} 82 | 83 | 84 |
85 | 86 |

{data_name}: {data_data}

87 | 88 |
{field_comment}:
{comment}
89 |
90 |
91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 |
104 | 105 |
-------------------------------------------------------------------------------- /tpl/opds/index.tpl: -------------------------------------------------------------------------------- 1 | 2 | 5 | {baseurl}index.php?lang={lang} 6 | 9 | 12 | 15 | 18 | {site_title} 19 | {pubdate} 20 | 21 | {owner} 22 | {homepage} 23 | {email} 24 | 25 | 26 | 27 | {author_list} 28 | {baseurl}index.php?prefix=authors&lang={lang} 29 | {author_list} 30 | 31 | 32 | {pubdate} 33 | 34 | 35 | 36 | {title_list} 37 | {baseurl}index.php?prefix=titles&lang={lang} 38 | {title_list} 39 | 40 | 41 | {pubdate} 42 | 43 | 44 | 45 | {tags_list} 46 | {baseurl}index.php?prefix=subjects&lang={lang} 47 | {tags_list} 48 | 49 | 50 | {pubdate} 51 | 52 | 53 | 54 | {series_list} 55 | {baseurl}index.php?prefix=series&lang={lang} 56 | {series_list} 57 | 58 | 59 | {pubdate} 60 | 61 | 62 | -------------------------------------------------------------------------------- /tpl/opds/serie.tpl: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | {books_in_serie} 8 | miniCalOPe. 9 | {baseurl}index.php?prefix=series_id&sort_order={sortorder}&query={aid}&offset={offset}&lang={lang} 10 | {pubdate} 11 | 12 | 13 | {owner} 14 | {homepage} 15 | {email} 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 35 | 38 | 41 | 44 | 47 | 50 | 51 | {total} 52 | {start} 53 | {per_page} 54 | 55 | 56 | {start_page} 57 | {baseurl}index.php?lang={lang} 58 | 59 | 60 | 61 | {pubdate} 62 | 63 | 64 | 65 | {back_to_series} 66 | {baseurl}index.php?prefix=authors&lang={lang} 67 | 68 | 69 | 70 | {pubdate} 71 | 72 | 73 | 74 | {sort_alpha} 75 | baseurl}?prefix=series_id&sort_order=title&query={aid}&lang={lang} 76 | 77 | 78 | 79 | {pubdate} 80 | 81 | 82 | 83 | {sort_author} 84 | {baseurl}index.php?prefix=series_id&sort_order=author&query={aid}&lang={lang} 85 | 86 | 87 | 88 | {pubdate} 89 | 90 | 91 | 92 | {sort_index} 93 | {baseurl}index.php?prefix=series_id&sort_order=index&query={aid}&lang={lang} 94 | 95 | 96 | 97 | {pubdate} 98 | 99 | 100 | 101 | 102 | {title_by_author} 103 | {baseurl}{bid}.opds 104 | ISBN: {isbn} 105 | 106 | 107 | {pubdate} 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /tpl/opds/series.tpl: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | {series_list} 8 | miniCalOPe 9 | {baseurl}index.php?prefix=series&sort_order={sortorder}&offset={offset}&lang={lang} 10 | {pubdate} 11 | 12 | 13 | {owner} 14 | {homepage} 15 | {email} 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 37 | 40 | 41 | 42 | 43 | {total} 44 | {start} 45 | {per_page} 46 | 47 | 48 | {start_page} 49 | {baseurl}index.php?lang={lang} 50 | 51 | 52 | 53 | {pubdate} 54 | 55 | 56 | 57 | {sort_alpha} 58 | {baseurl}index.php?prefix=series&sort_order=title&lang={lang} 59 | 60 | 61 | 62 | {pubdate} 63 | 64 | 65 | 66 | {sort_bookcount} 67 | {baseurl}index.php?prefix=series&sort_order=books&lang={lang} 68 | 69 | 70 | 71 | {pubdate} 72 | 73 | 74 | 75 | 76 | {name} 77 | {baseurl}index.php?prefix=series_id&sort_order={sortorder}&query={id}&lang={lang} 78 | {num_books} {books} 79 | 80 | 81 | {pubdate} 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /tpl/opds/tag.tpl: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | {books_with_tag} 8 | miniCalOPe. 9 | {baseurl}index.php?prefix=tag_id&sort_order={sortorder}&query={aid}&offset={offset}&lang={lang} 10 | {pubdate} 11 | 12 | 13 | {owner} 14 | {homepage} 15 | {email} 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 35 | 38 | 41 | 44 | 47 | 50 | 51 | {total} 52 | {start} 53 | {per_page} 54 | 55 | 56 | {start_page} 57 | {baseurl}index.php?lang={lang} 58 | 59 | 60 | 61 | {pubdate} 62 | 63 | 64 | 65 | {tags_list} 66 | {baseurl}index.php?prefix=authors&lang={lang} 67 | 68 | 69 | 70 | {pubdate} 71 | 72 | 73 | 74 | {sort_alpha} 75 | baseurl}?prefix=tag_id&sort_order=title&query={aid}&lang={lang} 76 | 77 | 78 | 79 | {pubdate} 80 | 81 | 82 | 83 | {sort_author} 84 | {baseurl}index.php?prefix=tag_id&sort_order=author&query={aid}&lang={lang} 85 | 86 | 87 | 88 | {pubdate} 89 | 90 | 91 | 99 | 100 | 101 | 102 | {title_by_author} 103 | {baseurl}{bid}.opds 104 | ISBN: {isbn} 105 | 106 | 107 | {pubdate} 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /tpl/opds/tags.tpl: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | {tags_list} 8 | miniCalOPe 9 | {baseurl}index.php?prefix=tags&sort_order={sortorder}&offset={offset}&lang={lang} 10 | {pubdate} 11 | 12 | 13 | {owner} 14 | {homepage} 15 | {email} 16 | 17 | 18 | 21 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | {total} 44 | {start} 45 | {per_page} 46 | 47 | 48 | {start_page} 49 | {baseurl}index.php?lang={lang} 50 | 51 | 52 | 53 | {pubdate} 54 | 55 | 56 | 57 | {sort_alpha} 58 | {baseurl}index.php?prefix=tags&sort_order=title&lang={lang} 59 | 60 | 61 | 62 | {pubdate} 63 | 64 | 65 | 66 | {sort_bookcount} 67 | {baseurl}index.php?prefix=tags&sort_order=books&lang={lang} 68 | 69 | 70 | 71 | {pubdate} 72 | 73 | 74 | 75 | 76 | {name} 77 | {baseurl}index.php?prefix=tag_id&sort_order={sortorder}&query={id}&lang={lang} 78 | {num_books} {books} 79 | 80 | 81 | {pubdate} 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /tpl/opds/titles.tpl: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | {title_list} 8 | miniCalOPe 9 | {baseurl}index.php?prefix=titels&sort_order={sortorder}&lang={lang}&offset={offset} 10 | {pubdate} 11 | 12 | 13 | {owner} 14 | {homepage} 15 | {email} 16 | 17 | 18 | 21 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 41 | 44 | 47 | 48 | {total} 49 | {start} 50 | {per_page} 51 | 52 | 53 | {start_page} 54 | {baseurl}index.php?lang={lang} 55 | 56 | 57 | 58 | {pubdate} 59 | 60 | 61 | 62 | {sort_alpha} 63 | {baseurl}index.php?prefix={prefix}{searchvals}&sort_order=title&lang={lang} 64 | 65 | 66 | 67 | {pubdate} 68 | 69 | 70 | 71 | {sort_author} 72 | {baseurl}index.php?prefix={prefix}{searchvals}&sort_order=name&lang={lang} 73 | 74 | 75 | 76 | {pubdate} 77 | 78 | 79 | 80 | {sort_date} 81 | {baseurl}index.php?prefix={prefix}{searchvals}&sort_order=time&lang={lang} 82 | 83 | 84 | 85 | {pubdate} 86 | 87 | 88 | 89 | 90 | {title} 91 | {baseurl}{bid}.opds 92 | ISBN: {isbn} 93 | 94 | 95 | {pubdate} 96 | 97 | 98 | 99 | --------------------------------------------------------------------------------