├── .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 | 
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("