Merge almost everything. a lot to test.

This commit is contained in:
Sébastien Lucas 2013-05-15 21:55:33 +02:00
commit 639d1bf8b0
45 changed files with 800 additions and 338 deletions

View file

@ -11,3 +11,6 @@ c5703623704b81dca4228e211830125029cf86a1 0.2.3
5cc3b8ed121d9df57e013e050a75e5602cf2198e 0.3.0
aca483636af460c93f9817e083e85d1976aa1b7d 0.3.1
5888006bc559842de0364ec3e67f641aa1653d0e 0.3.2
2ff58ed42cecf00b24d981426dff507fa1e86c20 0.3.3
3cdee8daedf28e6611203ce90c90bb8906003e22 0.3.4
89ed9654ac9c5de1695f63992aa92d55ef82f2b9 0.4.0

View file

@ -19,8 +19,10 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^download/(.*)/.*\.kepub\.epub$ fetch.php?data=$1&type=epub [L]
RewriteRule ^download/(.*)/.*\.(.*)$ fetch.php?data=$1&type=$2 [L]
RewriteRule ^download/(\d*)/(\d*)/.*\.kepub\.epub$ fetch.php?data=$1&db=$2&type=epub [L]
RewriteRule ^download/(\d*)/(\d*)/.*\.(.*)$ fetch.php?data=$1&db=$2&type=$3 [L]
RewriteRule ^download/(\d*)/.*\.kepub\.epub$ fetch.php?data=$1&type=epub [L]
RewriteRule ^download/(\d*)/.*\.(.*)$ fetch.php?data=$1&type=$2 [L]
</IfModule>
###########################################

View file

@ -1,3 +1,26 @@
0.4.0 - 20130507
* Add multiple database support. Check the documentation of $config['calibre_directory'] in config-default.php to see how ot enable it.
* Include jquery library in COPS's repository to be sure that COPS will work on LAN (without Internet access).
* Prepare the switch to HTML5. Thanks to Thomas Severinsen for most of the code.
* Update the locale strings to be more strict with plurals. Thanks to Tobias Ausländer for the code.
* If Fancybox is not enabled ($config['cops_use_fancyapps'] = "0") then it's not used at all (even in the about box).
* Fix book comments if it contains UTF8 characters. Reported by Alain.
* Link to the book permalink was not working correctly in some cases. Reported by celta.
* Moved some external resources to a resources directory.
* Add chinese translation. Thanks to wogong for the pull request.
0.3.4 - 20130327
* Hopefully fix metadata update. Beware you should remove the directory php-epub-meta if you have one. Thanks to Mario for his time.
* Fix two warnings. Reported by Goner and Mario.
0.3.3 - 20130323
* Fix catalog if book summary contains bad HTML again :(.
* Upgrade to Fancybox 2.4.0 and JQuery 1.9.1.
* Search is now dependant on the page you're in. For now if you're on author page it'll look for author name.
* Update checkconfig to check if the database provided comes from Calibre.
* Update to latest php-epub-meta should fix the metadata update with Epub.
* Fix OPDS catalog with Ibis Reader. It didn't like empty language.
0.3.2 - 20130303
* Add dutch translation. Provided by Northguy.
* Fix an ugly bug introduced in 0.3.1. Reported by mariosipad.

View file

@ -60,7 +60,11 @@ class OPDSRenderer
$xml->endElement ();
$xml->startElement ("Url");
$xml->writeAttribute ("type", 'application/atom+xml');
$xml->writeAttribute ("template", $config['cops_full_url'] . 'feed.php?query={searchTerms}');
$urlparam = "?query={searchTerms}";
if (!is_null (GetUrlParam (DB))) $urlparam = addURLParameter ($urlparam, DB, GetUrlParam (DB));
$urlparam = str_replace ("%7B", "{", $urlparam);
$urlparam = str_replace ("%7D", "}", $urlparam);
$xml->writeAttribute ("template", $config['cops_full_url'] . 'feed.php' . $urlparam);
$xml->endElement ();
$xml->startElement ("Query");
$xml->writeAttribute ("role", "example");
@ -92,7 +96,9 @@ class OPDSRenderer
self::getXmlStream ()->startElement ("id");
if ($page->idPage)
{
self::getXmlStream ()->text ($page->idPage);
$idPage = $page->idPage;
if (!is_null (GetUrlParam (DB))) $idPage = GetUrlParam (DB) . ":" . $idPage;
self::getXmlStream ()->text ($idPage);
}
else
{
@ -120,14 +126,16 @@ class OPDSRenderer
self::renderLink ($link);
$link = new LinkNavigation ("?" . $_SERVER['QUERY_STRING'], "self");
self::renderLink ($link);
$urlparam = "?page=" . self::PAGE_OPENSEARCH;
if (!is_null (GetUrlParam (DB))) $urlparam = addURLParameter ($urlparam, DB, GetUrlParam (DB));
if ($config['cops_generate_invalid_opds_stream'] == 0 || preg_match("/(MantanoReader|FBReader)/", $_SERVER['HTTP_USER_AGENT'])) {
// Good and compliant way of handling search
$link = new Link ("feed.php?page=" . self::PAGE_OPENSEARCH, "application/opensearchdescription+xml", "search", "Search here");
$link = new Link ("feed.php" . $urlparam, "application/opensearchdescription+xml", "search", "Search here");
}
else
{
// Bad way, will be removed when OPDS client are fixed
$link = new Link ($config['cops_full_url'] . 'feed.php?query={searchTerms}', "application/atom+xml", "search", "Search here");
$link = new Link ($config['cops_full_url'] . 'feed.php' . $urlparam, "application/atom+xml", "search", "Search here");
}
self::renderLink ($link);
if ($page->containsBook () && !is_null ($config['cops_books_filter']) && count ($config['cops_books_filter']) > 0) {
@ -217,7 +225,7 @@ class OPDSRenderer
}
$lang = $entry->book->getLanguages ();
if (!is_null ($lang)) {
if (!empty ($lang)) {
self::getXmlStream ()->startElement ("dcterms:language");
self::getXmlStream ()->text ($lang);
self::getXmlStream ()->endElement ();

29
about.xml Normal file
View file

@ -0,0 +1,29 @@
<div class="bookdetail">
<div class="entryTitle">Authors</div>
<div class="content" style="max-width:700px;">
<p>COPS is developped and maintained by Sébastien Lucas.</p>
<p>See full history on <a href="https://github.com/seblucas">Github</a> to check all authors.</p>
<p>COPS use some external librairies, check README for the details.</p>
</div>
<div class="entryTitle">Copyright</div>
<div class="content" style="max-width:700px;">
<p>This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation.</p>
<p>The complete content of license is provided in file COPYING within distribution and also available <a href="http://www.gnu.org/licenses/gpl-2.0.html">online</a>.</p>
</div>
<div class="entryTitle">Contact</div>
<div class="content" style="max-width:700px;">
<p>For more info please visit <a href="http://blog.slucas.fr/en/oss/calibre-opds-php-server">COPS Home Page</a></p>
<p>You can also check <a href="http://www.mobileread.com/forums/showthread.php?t=170903">COPS's topic on MobileRead forum</a>.</p>
</div>
<div class="entryTitle">Thanks</div>
<div class="content" style="max-width:700px;">
<p>Thanks a lot to Kovid Goyal for <a href="http://calibre-ebook.com">Calibre</a>.</p>
<p>And many thanks to all those who helped test COPS.</p>
</div>
</div>

View file

@ -39,7 +39,7 @@ class Author extends Base {
public static function getCount() {
$nAuthors = parent::getDb ()->query('select count(*) from authors')->fetchColumn();
$entry = new Entry (localize("authors.title"), self::ALL_AUTHORS_ID,
str_format (localize("authors.alphabetical"), $nAuthors), "text",
str_format (localize("authors.alphabetical", $nAuthors), $nAuthors), "text",
array ( new LinkNavigation ("?page=".parent::PAGE_ALL_AUTHORS)));
return $entry;
}

201
base.php
View file

@ -6,7 +6,8 @@
* @author Sébastien Lucas <sebastien@slucas.fr>
*/
define ("VERSION", "0.3.2");
define ("VERSION", "0.4.0");
define ("DB", "db");
date_default_timezone_set($config['default_timezone']);
function getURLParam ($name, $default = NULL) {
@ -27,14 +28,66 @@ function xml2xhtml($xml) {
'), $xml);
}
function display_xml_error($error)
{
$return .= str_repeat('-', $error->column) . "^\n";
switch ($error->level) {
case LIBXML_ERR_WARNING:
$return .= "Warning $error->code: ";
break;
case LIBXML_ERR_ERROR:
$return .= "Error $error->code: ";
break;
case LIBXML_ERR_FATAL:
$return .= "Fatal Error $error->code: ";
break;
}
$return .= trim($error->message) .
"\n Line: $error->line" .
"\n Column: $error->column";
if ($error->file) {
$return .= "\n File: $error->file";
}
return "$return\n\n--------------------------------------------\n\n";
}
function are_libxml_errors_ok ()
{
$errors = libxml_get_errors();
foreach ($errors as $error) {
if ($error->code == 801) return false;
}
return true;
}
function html2xhtml ($html) {
$doc = new DOMDocument();
$doc->loadHTML($html); // Load the HTML
$output = utf8_decode($doc->saveXML($doc->documentElement)); // Transform to an Ansi xml stream
$output = xml2xhtml($output); // Fix the br / hr ...
if (preg_match ("/<html><body>(.*)<\/body><\/html>/", $output, $matches)) {
libxml_use_internal_errors(true);
$doc->loadHTML('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' .
$html . '</body></html>'); // Load the HTML
$output = $doc->saveXML($doc->documentElement); // Transform to an Ansi xml stream
$output = xml2xhtml($output);
if (preg_match ('#<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></meta></head><body>(.*)</body></html>#ms', $output, $matches)) {
$output = $matches [1]; // Remove <html><body>
}
/*
// In case of error with summary, use it to debug
$errors = libxml_get_errors();
foreach ($errors as $error) {
$output .= display_xml_error($error);
}
*/
if (!are_libxml_errors_ok ()) $output = "HTML code not valid.";
libxml_use_internal_errors(false);
return $output;
}
@ -100,14 +153,19 @@ function localize($phrase, $count=-1) {
}
function addURLParameter($urlParams, $paramName, $paramValue) {
$start = "";
if (preg_match ("#^\?(.*)#", $urlParams, $matches)) {
$start = "?";
$urlParams = $matches[1];
}
$params = array();
parse_str($urlParams, $params);
if (empty ($paramValue)) {
if (empty ($paramValue) && $paramValue != 0) {
unset ($params[$paramName]);
} else {
$params[$paramName] = $paramValue;
}
return http_build_query($params);
return $start . http_build_query($params);
}
class Link
@ -143,7 +201,13 @@ class LinkNavigation extends Link
{
public function __construct($phref, $prel = NULL, $ptitle = NULL) {
parent::__construct ($phref, Link::OPDS_NAVIGATION_TYPE, $prel, $ptitle);
$this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
if (!is_null (GetUrlParam (DB))) $this->href = addURLParameter ($this->href, DB, GetUrlParam (DB));
if (!preg_match ("#^\?(.*)#", $this->href) && !empty ($this->href)) $this->href = "?" . $this->href;
if (preg_match ("/bookdetail.php/", $_SERVER["SCRIPT_NAME"])) {
$this->href = "index.php" . $this->href;
} else {
$this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
}
}
}
@ -151,6 +215,7 @@ class LinkFacet extends Link
{
public function __construct($phref, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
parent::__construct ($phref, Link::OPDS_PAGING_TYPE, "http://opds-spec.org/facet", $ptitle, $pfacetGroup, $pactiveFacet);
if (!is_null (GetUrlParam (DB))) $this->href = addURLParameter ($this->href, DB, GetUrlParam (DB));
$this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
}
}
@ -203,6 +268,8 @@ class Entry
}
}
}
if (!is_null (GetUrlParam (DB))) $this->id = GetUrlParam (DB) . ":" . $this->id;
}
}
@ -276,6 +343,8 @@ class Page
return new PageQueryResult ($id, $query, $n);
case Base::PAGE_BOOK_DETAIL :
return new PageBookDetail ($id, $query, $n);
case Base::PAGE_ABOUT :
return new PageAbout ($id, $query, $n);
default:
$page = new Page ($id, $query, $n);
$page->idPage = "cops:catalog";
@ -297,16 +366,29 @@ class Page
global $config;
$this->title = $config['cops_title_default'];
$this->subtitle = $config['cops_subtitle_default'];
array_push ($this->entryArray, Author::getCount());
array_push ($this->entryArray, Serie::getCount());
array_push ($this->entryArray, Tag::getCount());
foreach ($config['cops_calibre_custom_column'] as $lookup) {
$customId = CustomColumn::getCustomId ($lookup);
if (!is_null ($customId)) {
array_push ($this->entryArray, CustomColumn::getCount($customId));
$database = GetUrlParam (DB);
if (is_array ($config['calibre_directory']) && is_null ($database)) {
$i = 0;
foreach ($config['calibre_directory'] as $key => $value) {
array_push ($this->entryArray, new Entry ($key, "{$i}:cops:catalog",
"", "text",
array ( new LinkNavigation ("?" . DB . "={$i}"))));
$i++;
}
} else {
array_push ($this->entryArray, Author::getCount());
array_push ($this->entryArray, Serie::getCount());
array_push ($this->entryArray, Tag::getCount());
foreach ($config['cops_calibre_custom_column'] as $lookup) {
$customId = CustomColumn::getCustomId ($lookup);
if (!is_null ($customId)) {
array_push ($this->entryArray, CustomColumn::getCount($customId));
}
}
$this->entryArray = array_merge ($this->entryArray, Book::getCount());
if (!is_null ($database)) $this->title = Base::getDbName ();
}
$this->entryArray = array_merge ($this->entryArray, Book::getCount());
}
public function isPaginated ()
@ -498,8 +580,32 @@ class PageQueryResult extends Page
{
public function InitializeContent ()
{
$this->title = "Search result for query *" . $this->query . "*"; // TODO I18N
list ($this->entryArray, $this->totalNumber) = Book::getBooksByQuery ($this->query, $this->n);
global $config;
$this->title = str_format (localize ("search.result"), $this->query);
$currentPage = getURLParam ("current", NULL);
// Special case when we are doing a search and no database is selected
if (is_array ($config['calibre_directory']) && is_null (GetUrlParam (DB))) {
$i = 0;
foreach ($config['calibre_directory'] as $key => $value) {
Base::clearDb ();
list ($array, $totalNumber) = Book::getBooksByQuery ($this->query, $this->n, $i);
array_push ($this->entryArray, new Entry ($key, DB . ":query:{$i}",
str_format (localize ("bookword", count($array)), count($array)), "text",
array ( new LinkNavigation ("?" . DB . "={$i}&page=9&query=" . $this->query))));
$i++;
}
return;
}
switch ($currentPage) {
case Base::PAGE_ALL_AUTHORS :
case Base::PAGE_AUTHORS_FIRST_LETTER :
$this->entryArray = Author::getAuthorsByStartingLetter ('%' . $this->query);
break;
default:
list ($this->entryArray, $this->totalNumber) = Book::getBooksByQuery ($this->query, $this->n);
}
}
}
@ -512,6 +618,15 @@ class PageBookDetail extends Page
}
}
class PageAbout extends Page
{
public function InitializeContent ()
{
$this->title = localize ("about.title");
}
}
abstract class Base
{
const PAGE_INDEX = "index";
@ -530,21 +645,51 @@ abstract class Base
const PAGE_BOOK_DETAIL = "13";
const PAGE_ALL_CUSTOMS = "14";
const PAGE_CUSTOM_DETAIL = "15";
const PAGE_ABOUT = "16";
const COMPATIBILITY_XML_ALDIKO = "aldiko";
private static $db = NULL;
public static function getDbFileName () {
public static function getDbList () {
global $config;
return $config['calibre_directory'] .'metadata.db';
if (is_array ($config['calibre_directory'])) {
return $config['calibre_directory'];
} else {
return array ("" => $config['calibre_directory']);
}
}
public static function getDbName ($database = NULL) {
global $config;
if (is_array ($config['calibre_directory'])) {
if (is_null ($database)) $database = GetUrlParam (DB, 0);
$array = array_keys ($config['calibre_directory']);
return $array[$database];
}
return "";
}
public static function getDbDirectory ($database = NULL) {
global $config;
if (is_array ($config['calibre_directory'])) {
if (is_null ($database)) $database = GetUrlParam (DB, 0);
$array = array_values ($config['calibre_directory']);
return $array[$database];
}
return $config['calibre_directory'];
}
public static function getDbFileName ($database = NULL) {
return self::getDbDirectory ($database) .'metadata.db';
}
public static function getDb () {
public static function getDb ($database = NULL) {
global $config;
if (is_null (self::$db)) {
try {
self::$db = new PDO('sqlite:'. self::getDbFileName ());
self::$db = new PDO('sqlite:'. self::getDbFileName ($database));
} catch (Exception $e) {
header("location: checkconfig.php?err=1");
exit();
@ -553,14 +698,18 @@ abstract class Base
return self::$db;
}
public static function executeQuery($query, $columns, $filter, $params, $n) {
public static function clearDb () {
self::$db = NULL;
}
public static function executeQuery($query, $columns, $filter, $params, $n, $database = NULL) {
global $config;
$totalResult = -1;
if ($config['cops_max_item_per_page'] != -1 && $n != -1)
{
// First check total number of results
$result = self::getDb ()->prepare (str_format ($query, "count(*)", $filter));
$result = self::getDb ($database)->prepare (str_format ($query, "count(*)", $filter));
$result->execute ($params);
$totalResult = $result->fetchColumn ();
@ -569,10 +718,10 @@ abstract class Base
array_push ($params, ($n - 1) * $config['cops_max_item_per_page'], $config['cops_max_item_per_page']);
}
$result = self::getDb ()->prepare(str_format ($query, $columns, $filter));
$result = self::getDb ($database)->prepare(str_format ($query, $columns, $filter));
$result->execute ($params);
return array ($totalResult, $result);
}
}
?>
?>

View file

@ -12,7 +12,7 @@ require_once('author.php');
require_once('tag.php');
require_once ("customcolumn.php");
require_once('data.php');
require_once('php-epub-meta/epub.php');
require_once('resources/php-epub-meta/epub.php');
// Silly thing because PHP forbid string concatenation in class const
define ('SQL_BOOKS_LEFT_JOIN', "left outer join comments on comments.book = books.id
@ -72,7 +72,7 @@ class Book extends Base {
$this->title = $line->title;
$this->timestamp = strtotime ($line->timestamp);
$this->pubdate = strtotime ($line->pubdate);
$this->path = $config['calibre_directory'] . $line->path;
$this->path = Base::getDbDirectory () . $line->path;
$this->relativePath = $line->path;
$this->seriesIndex = $line->series_index;
$this->comment = $line->comment;
@ -94,15 +94,18 @@ class Book extends Base {
}
public function getUri () {
return "?page=".parent::PAGE_BOOK_DETAIL."&amp;id=$this->id";
return "?page=".parent::PAGE_BOOK_DETAIL."&id=$this->id";
}
public function getDetailUrl () {
public function getDetailUrl ($permalink = false) {
global $config;
if ($config['cops_use_fancyapps'] == 0) {
return 'index.php' . $this->getUri ();
$urlParam = $this->getUri ();
if (!is_null (GetUrlParam (DB))) $urlParam = addURLParameter ($urlParam, DB, GetUrlParam (DB));
$urlParam = str_replace ("&", "&amp;", $urlParam);
if ($permalink || $config['cops_use_fancyapps'] == 0) {
return 'index.php' . $urlParam;
} else {
return 'bookdetail.php?id=' . $this->id;
return 'bookdetail.php' . $urlParam;
}
}
@ -117,7 +120,7 @@ class Book extends Base {
return $this->authors;
}
public function getFilterString () {
public static function getFilterString () {
$filter = getURLParam ("tag", NULL);
if (empty ($filter)) return "";
@ -137,17 +140,7 @@ class Book extends Base {
}
public function getAuthorsName () {
$authorList = null;
foreach ($this->getAuthors () as $author) {
if ($authorList) {
$authorList = $authorList . ", " . $author->name;
}
else
{
$authorList = $author->name;
}
}
return $authorList;
return implode (", ", array_map (function ($author) { return $author->name; }, $this->getAuthors ()));
}
public function getSerie () {
@ -219,17 +212,7 @@ class Book extends Base {
public function getTagsName () {
$tagList = null;
foreach ($this->getTags () as $tag) {
if ($tagList) {
$tagList = $tagList . ", " . $tag->name;
}
else
{
$tagList = $tag->name;
}
}
return $tagList;
return implode (", ", array_map (function ($tag) { return $tag->name; }, $this->getTags ()));
}
public function getRating () {
@ -349,15 +332,8 @@ class Book extends Base {
if ($this->hasCover)
{
array_push ($linkArray, Data::getLink ($this, "jpg", "image/jpeg", Link::OPDS_IMAGE_TYPE, "cover.jpg", NULL));
$height = "50";
if (preg_match ('/feed.php/', $_SERVER["SCRIPT_NAME"])) {
$height = $config['cops_opds_thumbnail_height'];
}
else
{
$height = $config['cops_html_thumbnail_height'];
}
array_push ($linkArray, new Link ("fetch.php?id=$this->id&height=" . $height, "image/jpeg", Link::OPDS_THUMBNAIL_TYPE));
array_push ($linkArray, Data::getLink ($this, "jpg", "image/jpeg", Link::OPDS_THUMBNAIL_TYPE, "cover.jpg", NULL));
}
foreach ($this->getDatas () as $data)
@ -393,7 +369,7 @@ class Book extends Base {
$result = array();
$entry = new Entry (localize ("allbooks.title"),
self::ALL_BOOKS_ID,
str_format (localize ("allbooks.alphabetical"), $nBooks), "text",
str_format (localize ("allbooks.alphabetical", $nBooks), $nBooks), "text",
array ( new LinkNavigation ("?page=".parent::PAGE_ALL_BOOKS)));
array_push ($result, $entry);
$entry = new Entry (localize ("recent.title"),
@ -451,8 +427,8 @@ where data.book = books.id and data.id = ?');
return NULL;
}
public static function getBooksByQuery($query, $n) {
return self::getEntryArray (self::SQL_BOOKS_QUERY, array ("%" . $query . "%", "%" . $query . "%"), $n);
public static function getBooksByQuery($query, $n, $database = NULL) {
return self::getEntryArray (self::SQL_BOOKS_QUERY, array ("%" . $query . "%", "%" . $query . "%"), $n, $database);
}
public static function getAllBooks() {
@ -474,8 +450,8 @@ order by substr (upper (sort), 1, 1)");
return self::getEntryArray (self::SQL_BOOKS_BY_FIRST_LETTER, array ($letter . "%"), $n);
}
public static function getEntryArray ($query, $params, $n) {
list ($totalNumber, $result) = parent::executeQuery ($query, self::BOOK_COLUMNS, self::getFilterString (), $params, $n);
public static function getEntryArray ($query, $params, $n, $database = NULL) {
list ($totalNumber, $result) = parent::executeQuery ($query, self::BOOK_COLUMNS, self::getFilterString (), $params, $n, $database);
$entryArray = array();
while ($post = $result->fetchObject ())
{

View file

@ -32,7 +32,9 @@ $book->getLinkArray ();
<?php
if ($book->hasCover) {
?>
<a href="fetch.php?id=<?php echo $book->id ?>"><img src="fetch.php?id=<?php echo $book->id ?>&amp;height=150" alt="<?php echo localize("i18n.coversection") ?>" /></a>
<a href="<?php echo Data::getLink ($book, "jpg", "image/jpeg", Link::OPDS_IMAGE_TYPE, "cover.jpg", NULL)->hrefXhtml () ?>">
<img src="<?php echo Data::getLink ($book, "jpg", "image/jpeg", Link::OPDS_THUMBNAIL_TYPE, "cover.jpg", NULL, NULL, 150)->hrefXhtml () ?>" alt="<?php echo localize("i18n.coversection") ?>" />
</a>
<?php
}
?>
@ -54,7 +56,7 @@ $book->getLinkArray ();
foreach ($authors as $author) {
if ($i > 0) echo ", ";
?>
<a href="index.php<?php echo str_replace ("&", "&amp;", $author->getUri ()) ?>"><?php echo htmlspecialchars ($author->name) ?></a>
<a href="<?php $link = new LinkNavigation ($author->getUri ()); echo $link->hrefXhtml () ?>"><?php echo htmlspecialchars ($author->name) ?></a>
<?php
}
?>
@ -69,7 +71,7 @@ $book->getLinkArray ();
foreach ($tags as $tag) {
if ($i > 0) echo ", ";
?>
<a href="index.php<?php echo str_replace ("&", "&amp;", $tag->getUri ()) ?>"><?php echo htmlspecialchars ($tag->name) ?></a>
<a href="<?php $link = new LinkNavigation ($tag->getUri ()); echo $link->hrefXhtml () ?>"><?php echo htmlspecialchars ($tag->name) ?></a>
<?php
}
?>
@ -79,7 +81,7 @@ $book->getLinkArray ();
if (!is_null ($serie))
{
?>
<h3><a href="index.php<?php echo str_replace ("&", "&amp;", $serie->getUri ()) ?>"><?php echo localize("series.title") ?></a>: </h3>
<h3><a href="index.php<?php $link = new LinkNavigation ($serie->getUri ()); echo $link->hrefXhtml () ?>"><?php echo localize("series.title") ?></a>: </h3>
<?php echo str_format (localize ("content.series.data"), $book->seriesIndex, htmlspecialchars ($serie->name)) ?>
<br />
<?php

View file

@ -74,15 +74,30 @@
?>
</div>
</div>
<div class="entry">
<div class="entryTitle">Check if libxml is properly installed and loaded</div>
<div class="entryContent">
<?php
if (extension_loaded('libxml')) {
echo "OK";
} else {
echo "Please make sure libxml is enabled";
}
?>
</div>
</div>
<?php
$i = 0;
foreach (Base::getDbList () as $name => $database) {
?>
<div class="entry">
<div class="entryTitle">Check if Calibre database file exists and is readable</div>
<div class="entryContent">
<?php
if (is_readable (Base::getDbFileName ())) {
echo "OK";
if (is_readable (Base::getDbFileName ($i))) {
echo "{$name} OK";
} else {
echo "File " . Base::getDbFileName () . " not found,
echo "{$name} File " . Base::getDbFileName ($i) . " not found,
Please check
<ul>
<li>Value of \$config['calibre_directory'] in config_local.php</li>
@ -99,14 +114,33 @@ Please check
<div class="entryContent">
<?php
try {
$db = new PDO('sqlite:'. Base::getDbFileName ());
echo "OK";
$db = new PDO('sqlite:'. Base::getDbFileName ($i));
echo "{$name} OK";
} catch (Exception $e) {
echo "If the file is readable, check your php configuration. Exception detail : " . $e;
echo "{$name} If the file is readable, check your php configuration. Exception detail : " . $e;
}
?>
</div>
</div>
<div class="entry">
<div class="entryTitle">Check if Calibre database file contains at least some of the needed tables</div>
<div class="entryContent">
<?php
try {
$db = new PDO('sqlite:'. Base::getDbFileName ($i));
$count = $db->query("select count(*) FROM sqlite_master WHERE type='table' AND name in ('books', 'authors', 'tags', 'series')")->fetchColumn();
if ($count == 4) {
echo "{$name} OK";
} else {
echo "{$name} Not all Calibre tables were found. Are you you're using the correct database.";
}
} catch (Exception $e) {
echo "{$name} If the file is readable, check your php configuration. Exception detail : " . $e;
}
?>
</div>
</div>
<?php $i++; } ?>
</div>
</div>
</body>

View file

@ -13,6 +13,8 @@
* The directory containing calibre's metadata.db file, with sub-directories
* containing all the formats.
* BEWARE : it has to end with a /
* You can enable multiple database with this notation instead of a simple string :
* $config['calibre_directory'] = array ("My database name" => "/home/directory/calibre1/", "My other database name" => "/home/directory/calibre2/");
*/
$config['calibre_directory'] = './feedbook/';
@ -93,7 +95,7 @@
/*
* use URL rewriting for downloading of ebook in HTML catalog
* See README for more information
* See Github wiki for more information
* 1 : enable
* 0 : disable
*/
@ -164,4 +166,5 @@
* 0 : No
*/
$config['cops_provide_kepub'] = "0";
?>

View file

@ -68,13 +68,12 @@ class CustomColumn extends Base {
public static function getCount($customId) {
$nCustoms = parent::getDb ()->query('select count(*) from ' . self::getTableName ($customId))->fetchColumn();
$entry = new Entry (self::getAllTitle ($customId), self::getAllCustomsId ($customId),
str_format (localize("tags.alphabetical"), $nCustoms), "text",
str_format (localize("tags.alphabetical", $nCustoms), $nCustoms), "text",
array ( new LinkNavigation (self::getUriAllCustoms ($customId))));
return $entry;
}
public static function getCustomById ($customId, $id) {
$test = 'select id, value as name from ' . self::getTableName ($customId) . ' where id = ?';
$result = parent::getDb ()->prepare('select id, value as name from ' . self::getTableName ($customId) . ' where id = ?');
$result->execute (array ($id));
if ($post = $result->fetchObject ()) {
@ -84,12 +83,6 @@ class CustomColumn extends Base {
}
public static function getAllCustoms($customId) {
$test = str_format ("{0} - {1} - {2}", self::getTableName ($customId), self::getTableLinkName ($customId), self::getTableLinkColumn ($customId));
$test = str_format ('select {0}.id as id, {0}.value as name, count(*) as count
from {0}, {1}
where {0}.id = {1}.{2}
group by {0}.id, {0}.value
order by {0}.value', self::getTableName ($customId), self::getTableLinkName ($customId), self::getTableLinkColumn ($customId));
$result = parent::getDb ()->query(str_format ('select {0}.id as id, {0}.value as name, count(*) as count
from {0}, {1}
where {0}.id = {1}.{2}

View file

@ -95,10 +95,12 @@ class Data extends Base {
if ($config['cops_use_url_rewriting'] == "1")
{
$database = "";
if (!is_null (GetUrlParam (DB))) $database = GetUrlParam (DB) . "/";
if ($config['cops_provide_kepub'] == "1" && preg_match("/Kobo/", $_SERVER['HTTP_USER_AGENT'])) {
return "download/" . $this->id . "/" . urlencode ($this->getUpdatedFilenameKepub ());
return "download/" . $this->id . "/" . $database . urlencode ($this->getUpdatedFilenameKepub ());
} else {
return "download/" . $this->id . "/" . urlencode ($this->getFilename ());
return "download/" . $this->id . "/" . $database . urlencode ($this->getFilename ());
}
}
else
@ -107,22 +109,33 @@ class Data extends Base {
}
}
public static function getLink ($book, $type, $mime, $rel, $filename, $idData, $title = NULL)
public static function getLink ($book, $type, $mime, $rel, $filename, $idData, $title = NULL, $height = NULL)
{
global $config;
$textData = "";
if (!is_null ($idData))
{
$textData = "&data=" . $idData;
}
$urlParam = addURLParameter("", "data", $idData);
if (preg_match ('/^\//', $config['calibre_directory']) || // Linux /
preg_match ('/^\w\:/', $config['calibre_directory']) || // Windows X:
if (preg_match ('/^\//', Base::getDbDirectory ()) || // Linux /
preg_match ('/^\w\:/', Base::getDbDirectory ()) || // Windows X:
$rel == Link::OPDS_THUMBNAIL_TYPE ||
($type == "epub" && $config['cops_update_epub-metadata']))
{
if ($type != "jpg") $textData .= "&type=" . $type;
return new Link ("fetch.php?id=$book->id" . $textData, $mime, $rel, $title);
if ($type != "jpg") $urlParam = addURLParameter($urlParam, "type", $type);
if ($rel == Link::OPDS_THUMBNAIL_TYPE) {
if (is_null ($height)) {
if (preg_match ('/feed.php/', $_SERVER["SCRIPT_NAME"])) {
$height = $config['cops_opds_thumbnail_height'];
}
else
{
$height = $config['cops_html_thumbnail_height'];
}
}
$urlParam = addURLParameter($urlParam, "height", $height);
}
$urlParam = addURLParameter($urlParam, "id", $book->id);
if (!is_null (GetUrlParam (DB))) $urlParam = addURLParameter ($urlParam, DB, GetUrlParam (DB));
return new Link ("fetch.php?" . $urlParam, $mime, $rel, $title);
}
else
{

View file

@ -1,45 +0,0 @@
/*! fancyBox v2.1.3 fancyapps.com | fancyapps.com/fancybox/#license */
(function(B,x,f,q){var r=f(B),m=f(x),b=f.fancybox=function(){b.open.apply(this,arguments)},u=null,n=x.createTouch!==q,s=function(a){return a&&a.hasOwnProperty&&a instanceof f},p=function(a){return a&&"string"===f.type(a)},E=function(a){return p(a)&&0<a.indexOf("%")},k=function(a,d){var e=parseInt(a,10)||0;d&&E(a)&&(e*=b.getViewport()[d]/100);return Math.ceil(e)},v=function(a,b){return k(a,b)+"px"};f.extend(b,{version:"2.1.3",defaults:{padding:15,margin:20,width:800,height:600,minWidth:100,minHeight:100,
maxWidth:9999,maxHeight:9999,autoSize:!0,autoHeight:!1,autoWidth:!1,autoResize:!0,autoCenter:!n,fitToView:!0,aspectRatio:!1,topRatio:0.5,leftRatio:0.5,scrolling:"auto",wrapCSS:"",arrows:!0,closeBtn:!0,closeClick:!1,nextClick:!1,mouseWheel:!0,autoPlay:!1,playSpeed:3E3,preload:3,modal:!1,loop:!0,ajax:{dataType:"html",headers:{"X-fancyBox":!0}},iframe:{scrolling:"auto",preload:!0},swf:{wmode:"transparent",allowfullscreen:"true",allowscriptaccess:"always"},keys:{next:{13:"left",34:"up",39:"left",40:"up"},
prev:{8:"right",33:"down",37:"right",38:"down"},close:[27],play:[32],toggle:[70]},direction:{next:"left",prev:"right"},scrollOutside:!0,index:0,type:null,href:null,content:null,title:null,tpl:{wrap:'<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-skin"><div class="fancybox-outer"><div class="fancybox-inner"></div></div></div></div>',image:'<img class="fancybox-image" src="{href}" alt="" />',iframe:'<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen'+
(f.browser.msie?' allowtransparency="true"':"")+"></iframe>",error:'<p class="fancybox-error">The requested content cannot be loaded.<br/>Please try again later.</p>',closeBtn:'<a title="Close" class="fancybox-item fancybox-close" href="javascript:;"></a>',next:'<a title="Next" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>',prev:'<a title="Previous" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>'},openEffect:"fade",openSpeed:250,openEasing:"swing",
openOpacity:!0,openMethod:"zoomIn",closeEffect:"fade",closeSpeed:250,closeEasing:"swing",closeOpacity:!0,closeMethod:"zoomOut",nextEffect:"elastic",nextSpeed:250,nextEasing:"swing",nextMethod:"changeIn",prevEffect:"elastic",prevSpeed:250,prevEasing:"swing",prevMethod:"changeOut",helpers:{overlay:!0,title:!0},onCancel:f.noop,beforeLoad:f.noop,afterLoad:f.noop,beforeShow:f.noop,afterShow:f.noop,beforeChange:f.noop,beforeClose:f.noop,afterClose:f.noop},group:{},opts:{},previous:null,coming:null,current:null,
isActive:!1,isOpen:!1,isOpened:!1,wrap:null,skin:null,outer:null,inner:null,player:{timer:null,isActive:!1},ajaxLoad:null,imgPreload:null,transitions:{},helpers:{},open:function(a,d){if(a&&(f.isPlainObject(d)||(d={}),!1!==b.close(!0)))return f.isArray(a)||(a=s(a)?f(a).get():[a]),f.each(a,function(e,c){var j={},g,h,i,l,k;"object"===f.type(c)&&(c.nodeType&&(c=f(c)),s(c)?(j={href:c.data("fancybox-href")||c.attr("href"),title:c.data("fancybox-title")||c.attr("title"),isDom:!0,element:c},f.metadata&&f.extend(!0,
j,c.metadata())):j=c);g=d.href||j.href||(p(c)?c:null);h=d.title!==q?d.title:j.title||"";l=(i=d.content||j.content)?"html":d.type||j.type;!l&&j.isDom&&(l=c.data("fancybox-type"),l||(l=(l=c.prop("class").match(/fancybox\.(\w+)/))?l[1]:null));p(g)&&(l||(b.isImage(g)?l="image":b.isSWF(g)?l="swf":"#"===g.charAt(0)?l="inline":p(c)&&(l="html",i=c)),"ajax"===l&&(k=g.split(/\s+/,2),g=k.shift(),k=k.shift()));i||("inline"===l?g?i=f(p(g)?g.replace(/.*(?=#[^\s]+$)/,""):g):j.isDom&&(i=c):"html"===l?i=g:!l&&(!g&&
j.isDom)&&(l="inline",i=c));f.extend(j,{href:g,type:l,content:i,title:h,selector:k});a[e]=j}),b.opts=f.extend(!0,{},b.defaults,d),d.keys!==q&&(b.opts.keys=d.keys?f.extend({},b.defaults.keys,d.keys):!1),b.group=a,b._start(b.opts.index)},cancel:function(){var a=b.coming;a&&!1!==b.trigger("onCancel")&&(b.hideLoading(),b.ajaxLoad&&b.ajaxLoad.abort(),b.ajaxLoad=null,b.imgPreload&&(b.imgPreload.onload=b.imgPreload.onerror=null),a.wrap&&a.wrap.stop(!0,!0).trigger("onReset").remove(),b.coming=null,b.current||
b._afterZoomOut(a))},close:function(a){b.cancel();!1!==b.trigger("beforeClose")&&(b.unbindEvents(),b.isActive&&(!b.isOpen||!0===a?(f(".fancybox-wrap").stop(!0).trigger("onReset").remove(),b._afterZoomOut()):(b.isOpen=b.isOpened=!1,b.isClosing=!0,f(".fancybox-item, .fancybox-nav").remove(),b.wrap.stop(!0,!0).removeClass("fancybox-opened"),b.transitions[b.current.closeMethod]())))},play:function(a){var d=function(){clearTimeout(b.player.timer)},e=function(){d();b.current&&b.player.isActive&&(b.player.timer=
setTimeout(b.next,b.current.playSpeed))},c=function(){d();f("body").unbind(".player");b.player.isActive=!1;b.trigger("onPlayEnd")};if(!0===a||!b.player.isActive&&!1!==a){if(b.current&&(b.current.loop||b.current.index<b.group.length-1))b.player.isActive=!0,f("body").bind({"afterShow.player onUpdate.player":e,"onCancel.player beforeClose.player":c,"beforeLoad.player":d}),e(),b.trigger("onPlayStart")}else c()},next:function(a){var d=b.current;d&&(p(a)||(a=d.direction.next),b.jumpto(d.index+1,a,"next"))},
prev:function(a){var d=b.current;d&&(p(a)||(a=d.direction.prev),b.jumpto(d.index-1,a,"prev"))},jumpto:function(a,d,e){var c=b.current;c&&(a=k(a),b.direction=d||c.direction[a>=c.index?"next":"prev"],b.router=e||"jumpto",c.loop&&(0>a&&(a=c.group.length+a%c.group.length),a%=c.group.length),c.group[a]!==q&&(b.cancel(),b._start(a)))},reposition:function(a,d){var e=b.current,c=e?e.wrap:null,j;c&&(j=b._getPosition(d),a&&"scroll"===a.type?(delete j.position,c.stop(!0,!0).animate(j,200)):(c.css(j),e.pos=f.extend({},
e.dim,j)))},update:function(a){var d=a&&a.type,e=!d||"orientationchange"===d;e&&(clearTimeout(u),u=null);b.isOpen&&!u&&(u=setTimeout(function(){var c=b.current;c&&!b.isClosing&&(b.wrap.removeClass("fancybox-tmp"),(e||"load"===d||"resize"===d&&c.autoResize)&&b._setDimension(),"scroll"===d&&c.canShrink||b.reposition(a),b.trigger("onUpdate"),u=null)},e&&!n?0:300))},toggle:function(a){b.isOpen&&(b.current.fitToView="boolean"===f.type(a)?a:!b.current.fitToView,n&&(b.wrap.removeAttr("style").addClass("fancybox-tmp"),
b.trigger("onUpdate")),b.update())},hideLoading:function(){m.unbind(".loading");f("#fancybox-loading").remove()},showLoading:function(){var a,d;b.hideLoading();a=f('<div id="fancybox-loading"><div></div></div>').click(b.cancel).appendTo("body");m.bind("keydown.loading",function(a){if(27===(a.which||a.keyCode))a.preventDefault(),b.cancel()});b.defaults.fixed||(d=b.getViewport(),a.css({position:"absolute",top:0.5*d.h+d.y,left:0.5*d.w+d.x}))},getViewport:function(){var a=b.current&&b.current.locked||
!1,d={x:r.scrollLeft(),y:r.scrollTop()};a?(d.w=a[0].clientWidth,d.h=a[0].clientHeight):(d.w=n&&B.innerWidth?B.innerWidth:r.width(),d.h=n&&B.innerHeight?B.innerHeight:r.height());return d},unbindEvents:function(){b.wrap&&s(b.wrap)&&b.wrap.unbind(".fb");m.unbind(".fb");r.unbind(".fb")},bindEvents:function(){var a=b.current,d;a&&(r.bind("orientationchange.fb"+(n?"":" resize.fb")+(a.autoCenter&&!a.locked?" scroll.fb":""),b.update),(d=a.keys)&&m.bind("keydown.fb",function(e){var c=e.which||e.keyCode,j=
e.target||e.srcElement;if(27===c&&b.coming)return!1;!e.ctrlKey&&(!e.altKey&&!e.shiftKey&&!e.metaKey&&(!j||!j.type&&!f(j).is("[contenteditable]")))&&f.each(d,function(d,j){if(1<a.group.length&&j[c]!==q)return b[d](j[c]),e.preventDefault(),!1;if(-1<f.inArray(c,j))return b[d](),e.preventDefault(),!1})}),f.fn.mousewheel&&a.mouseWheel&&b.wrap.bind("mousewheel.fb",function(d,c,j,g){for(var h=f(d.target||null),i=!1;h.length&&!i&&!h.is(".fancybox-skin")&&!h.is(".fancybox-wrap");)i=h[0]&&!(h[0].style.overflow&&
"hidden"===h[0].style.overflow)&&(h[0].clientWidth&&h[0].scrollWidth>h[0].clientWidth||h[0].clientHeight&&h[0].scrollHeight>h[0].clientHeight),h=f(h).parent();if(0!==c&&!i&&1<b.group.length&&!a.canShrink){if(0<g||0<j)b.prev(0<g?"down":"left");else if(0>g||0>j)b.next(0>g?"up":"right");d.preventDefault()}}))},trigger:function(a,d){var e,c=d||b.coming||b.current;if(c){f.isFunction(c[a])&&(e=c[a].apply(c,Array.prototype.slice.call(arguments,1)));if(!1===e)return!1;c.helpers&&f.each(c.helpers,function(d,
e){e&&(b.helpers[d]&&f.isFunction(b.helpers[d][a]))&&(e=f.extend(!0,{},b.helpers[d].defaults,e),b.helpers[d][a](e,c))});f.event.trigger(a+".fb")}},isImage:function(a){return p(a)&&a.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i)},isSWF:function(a){return p(a)&&a.match(/\.(swf)((\?|#).*)?$/i)},_start:function(a){var d={},e,c,a=k(a);e=b.group[a]||null;if(!e)return!1;d=f.extend(!0,{},b.opts,e);e=d.margin;c=d.padding;"number"===f.type(e)&&(d.margin=[e,e,e,e]);"number"===f.type(c)&&
(d.padding=[c,c,c,c]);d.modal&&f.extend(!0,d,{closeBtn:!1,closeClick:!1,nextClick:!1,arrows:!1,mouseWheel:!1,keys:null,helpers:{overlay:{closeClick:!1}}});d.autoSize&&(d.autoWidth=d.autoHeight=!0);"auto"===d.width&&(d.autoWidth=!0);"auto"===d.height&&(d.autoHeight=!0);d.group=b.group;d.index=a;b.coming=d;if(!1===b.trigger("beforeLoad"))b.coming=null;else{c=d.type;e=d.href;if(!c)return b.coming=null,b.current&&b.router&&"jumpto"!==b.router?(b.current.index=a,b[b.router](b.direction)):!1;b.isActive=
!0;if("image"===c||"swf"===c)d.autoHeight=d.autoWidth=!1,d.scrolling="visible";"image"===c&&(d.aspectRatio=!0);"iframe"===c&&n&&(d.scrolling="scroll");d.wrap=f(d.tpl.wrap).addClass("fancybox-"+(n?"mobile":"desktop")+" fancybox-type-"+c+" fancybox-tmp "+d.wrapCSS).appendTo(d.parent||"body");f.extend(d,{skin:f(".fancybox-skin",d.wrap),outer:f(".fancybox-outer",d.wrap),inner:f(".fancybox-inner",d.wrap)});f.each(["Top","Right","Bottom","Left"],function(a,b){d.skin.css("padding"+b,v(d.padding[a]))});b.trigger("onReady");
if("inline"===c||"html"===c){if(!d.content||!d.content.length)return b._error("content")}else if(!e)return b._error("href");"image"===c?b._loadImage():"ajax"===c?b._loadAjax():"iframe"===c?b._loadIframe():b._afterLoad()}},_error:function(a){f.extend(b.coming,{type:"html",autoWidth:!0,autoHeight:!0,minWidth:0,minHeight:0,scrolling:"no",hasError:a,content:b.coming.tpl.error});b._afterLoad()},_loadImage:function(){var a=b.imgPreload=new Image;a.onload=function(){this.onload=this.onerror=null;b.coming.width=
this.width;b.coming.height=this.height;b._afterLoad()};a.onerror=function(){this.onload=this.onerror=null;b._error("image")};a.src=b.coming.href;!0!==a.complete&&b.showLoading()},_loadAjax:function(){var a=b.coming;b.showLoading();b.ajaxLoad=f.ajax(f.extend({},a.ajax,{url:a.href,error:function(a,e){b.coming&&"abort"!==e?b._error("ajax",a):b.hideLoading()},success:function(d,e){"success"===e&&(a.content=d,b._afterLoad())}}))},_loadIframe:function(){var a=b.coming,d=f(a.tpl.iframe.replace(/\{rnd\}/g,
(new Date).getTime())).attr("scrolling",n?"auto":a.iframe.scrolling).attr("src",a.href);f(a.wrap).bind("onReset",function(){try{f(this).find("iframe").hide().attr("src","//about:blank").end().empty()}catch(a){}});a.iframe.preload&&(b.showLoading(),d.one("load",function(){f(this).data("ready",1);n||f(this).bind("load.fb",b.update);f(this).parents(".fancybox-wrap").width("100%").removeClass("fancybox-tmp").show();b._afterLoad()}));a.content=d.appendTo(a.inner);a.iframe.preload||b._afterLoad()},_preloadImages:function(){var a=
b.group,d=b.current,e=a.length,c=d.preload?Math.min(d.preload,e-1):0,f,g;for(g=1;g<=c;g+=1)f=a[(d.index+g)%e],"image"===f.type&&f.href&&((new Image).src=f.href)},_afterLoad:function(){var a=b.coming,d=b.current,e,c,j,g,h;b.hideLoading();if(a&&!1!==b.isActive)if(!1===b.trigger("afterLoad",a,d))a.wrap.stop(!0).trigger("onReset").remove(),b.coming=null;else{d&&(b.trigger("beforeChange",d),d.wrap.stop(!0).removeClass("fancybox-opened").find(".fancybox-item, .fancybox-nav").remove());b.unbindEvents();
e=a.content;c=a.type;j=a.scrolling;f.extend(b,{wrap:a.wrap,skin:a.skin,outer:a.outer,inner:a.inner,current:a,previous:d});g=a.href;switch(c){case "inline":case "ajax":case "html":a.selector?e=f("<div>").html(e).find(a.selector):s(e)&&(e.data("fancybox-placeholder")||e.data("fancybox-placeholder",f('<div class="fancybox-placeholder"></div>').insertAfter(e).hide()),e=e.show().detach(),a.wrap.bind("onReset",function(){f(this).find(e).length&&e.hide().replaceAll(e.data("fancybox-placeholder")).data("fancybox-placeholder",
!1)}));break;case "image":e=a.tpl.image.replace("{href}",g);break;case "swf":e='<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="'+g+'"></param>',h="",f.each(a.swf,function(a,b){e+='<param name="'+a+'" value="'+b+'"></param>';h+=" "+a+'="'+b+'"'}),e+='<embed src="'+g+'" type="application/x-shockwave-flash" width="100%" height="100%"'+h+"></embed></object>"}(!s(e)||!e.parent().is(a.inner))&&a.inner.append(e);b.trigger("beforeShow");
a.inner.css("overflow","yes"===j?"scroll":"no"===j?"hidden":j);b._setDimension();b.reposition();b.isOpen=!1;b.coming=null;b.bindEvents();if(b.isOpened){if(d.prevMethod)b.transitions[d.prevMethod]()}else f(".fancybox-wrap").not(a.wrap).stop(!0).trigger("onReset").remove();b.transitions[b.isOpened?a.nextMethod:a.openMethod]();b._preloadImages()}},_setDimension:function(){var a=b.getViewport(),d=0,e=!1,c=!1,e=b.wrap,j=b.skin,g=b.inner,h=b.current,c=h.width,i=h.height,l=h.minWidth,t=h.minHeight,m=h.maxWidth,
n=h.maxHeight,r=h.scrolling,p=h.scrollOutside?h.scrollbarWidth:0,w=h.margin,y=k(w[1]+w[3]),q=k(w[0]+w[2]),x,z,s,C,A,F,B,D,u;e.add(j).add(g).width("auto").height("auto").removeClass("fancybox-tmp");w=k(j.outerWidth(!0)-j.width());x=k(j.outerHeight(!0)-j.height());z=y+w;s=q+x;C=E(c)?(a.w-z)*k(c)/100:c;A=E(i)?(a.h-s)*k(i)/100:i;if("iframe"===h.type){if(u=h.content,h.autoHeight&&1===u.data("ready"))try{u[0].contentWindow.document.location&&(g.width(C).height(9999),F=u.contents().find("body"),p&&F.css("overflow-x",
"hidden"),A=F.height())}catch(G){}}else if(h.autoWidth||h.autoHeight)g.addClass("fancybox-tmp"),h.autoWidth||g.width(C),h.autoHeight||g.height(A),h.autoWidth&&(C=g.width()),h.autoHeight&&(A=g.height()),g.removeClass("fancybox-tmp");c=k(C);i=k(A);D=C/A;l=k(E(l)?k(l,"w")-z:l);m=k(E(m)?k(m,"w")-z:m);t=k(E(t)?k(t,"h")-s:t);n=k(E(n)?k(n,"h")-s:n);F=m;B=n;h.fitToView&&(m=Math.min(a.w-z,m),n=Math.min(a.h-s,n));z=a.w-y;q=a.h-q;h.aspectRatio?(c>m&&(c=m,i=k(c/D)),i>n&&(i=n,c=k(i*D)),c<l&&(c=l,i=k(c/D)),i<t&&
(i=t,c=k(i*D))):(c=Math.max(l,Math.min(c,m)),h.autoHeight&&"iframe"!==h.type&&(g.width(c),i=g.height()),i=Math.max(t,Math.min(i,n)));if(h.fitToView)if(g.width(c).height(i),e.width(c+w),a=e.width(),y=e.height(),h.aspectRatio)for(;(a>z||y>q)&&(c>l&&i>t)&&!(19<d++);)i=Math.max(t,Math.min(n,i-10)),c=k(i*D),c<l&&(c=l,i=k(c/D)),c>m&&(c=m,i=k(c/D)),g.width(c).height(i),e.width(c+w),a=e.width(),y=e.height();else c=Math.max(l,Math.min(c,c-(a-z))),i=Math.max(t,Math.min(i,i-(y-q)));p&&("auto"===r&&i<A&&c+w+
p<z)&&(c+=p);g.width(c).height(i);e.width(c+w);a=e.width();y=e.height();e=(a>z||y>q)&&c>l&&i>t;c=h.aspectRatio?c<F&&i<B&&c<C&&i<A:(c<F||i<B)&&(c<C||i<A);f.extend(h,{dim:{width:v(a),height:v(y)},origWidth:C,origHeight:A,canShrink:e,canExpand:c,wPadding:w,hPadding:x,wrapSpace:y-j.outerHeight(!0),skinSpace:j.height()-i});!u&&(h.autoHeight&&i>t&&i<n&&!c)&&g.height("auto")},_getPosition:function(a){var d=b.current,e=b.getViewport(),c=d.margin,f=b.wrap.width()+c[1]+c[3],g=b.wrap.height()+c[0]+c[2],c={position:"absolute",
top:c[0],left:c[3]};d.autoCenter&&d.fixed&&!a&&g<=e.h&&f<=e.w?c.position="fixed":d.locked||(c.top+=e.y,c.left+=e.x);c.top=v(Math.max(c.top,c.top+(e.h-g)*d.topRatio));c.left=v(Math.max(c.left,c.left+(e.w-f)*d.leftRatio));return c},_afterZoomIn:function(){var a=b.current;a&&(b.isOpen=b.isOpened=!0,b.wrap.css("overflow","visible").addClass("fancybox-opened"),b.update(),(a.closeClick||a.nextClick&&1<b.group.length)&&b.inner.css("cursor","pointer").bind("click.fb",function(d){!f(d.target).is("a")&&!f(d.target).parent().is("a")&&
(d.preventDefault(),b[a.closeClick?"close":"next"]())}),a.closeBtn&&f(a.tpl.closeBtn).appendTo(b.skin).bind(n?"touchstart.fb":"click.fb",function(a){a.preventDefault();b.close()}),a.arrows&&1<b.group.length&&((a.loop||0<a.index)&&f(a.tpl.prev).appendTo(b.outer).bind("click.fb",b.prev),(a.loop||a.index<b.group.length-1)&&f(a.tpl.next).appendTo(b.outer).bind("click.fb",b.next)),b.trigger("afterShow"),!a.loop&&a.index===a.group.length-1?b.play(!1):b.opts.autoPlay&&!b.player.isActive&&(b.opts.autoPlay=
!1,b.play()))},_afterZoomOut:function(a){a=a||b.current;f(".fancybox-wrap").trigger("onReset").remove();f.extend(b,{group:{},opts:{},router:!1,current:null,isActive:!1,isOpened:!1,isOpen:!1,isClosing:!1,wrap:null,skin:null,outer:null,inner:null});b.trigger("afterClose",a)}});b.transitions={getOrigPosition:function(){var a=b.current,d=a.element,e=a.orig,c={},f=50,g=50,h=a.hPadding,i=a.wPadding,l=b.getViewport();!e&&(a.isDom&&d.is(":visible"))&&(e=d.find("img:first"),e.length||(e=d));s(e)?(c=e.offset(),
e.is("img")&&(f=e.outerWidth(),g=e.outerHeight())):(c.top=l.y+(l.h-g)*a.topRatio,c.left=l.x+(l.w-f)*a.leftRatio);if("fixed"===b.wrap.css("position")||a.locked)c.top-=l.y,c.left-=l.x;return c={top:v(c.top-h*a.topRatio),left:v(c.left-i*a.leftRatio),width:v(f+i),height:v(g+h)}},step:function(a,d){var e,c,f=d.prop;c=b.current;var g=c.wrapSpace,h=c.skinSpace;if("width"===f||"height"===f)e=d.end===d.start?1:(a-d.start)/(d.end-d.start),b.isClosing&&(e=1-e),c="width"===f?c.wPadding:c.hPadding,c=a-c,b.skin[f](k("width"===
f?c:c-g*e)),b.inner[f](k("width"===f?c:c-g*e-h*e))},zoomIn:function(){var a=b.current,d=a.pos,e=a.openEffect,c="elastic"===e,j=f.extend({opacity:1},d);delete j.position;c?(d=this.getOrigPosition(),a.openOpacity&&(d.opacity=0.1)):"fade"===e&&(d.opacity=0.1);b.wrap.css(d).animate(j,{duration:"none"===e?0:a.openSpeed,easing:a.openEasing,step:c?this.step:null,complete:b._afterZoomIn})},zoomOut:function(){var a=b.current,d=a.closeEffect,e="elastic"===d,c={opacity:0.1};e&&(c=this.getOrigPosition(),a.closeOpacity&&
(c.opacity=0.1));b.wrap.animate(c,{duration:"none"===d?0:a.closeSpeed,easing:a.closeEasing,step:e?this.step:null,complete:b._afterZoomOut})},changeIn:function(){var a=b.current,d=a.nextEffect,e=a.pos,c={opacity:1},f=b.direction,g;e.opacity=0.1;"elastic"===d&&(g="down"===f||"up"===f?"top":"left","down"===f||"right"===f?(e[g]=v(k(e[g])-200),c[g]="+=200px"):(e[g]=v(k(e[g])+200),c[g]="-=200px"));"none"===d?b._afterZoomIn():b.wrap.css(e).animate(c,{duration:a.nextSpeed,easing:a.nextEasing,complete:function(){setTimeout(b._afterZoomIn,
20)}})},changeOut:function(){var a=b.previous,d=a.prevEffect,e={opacity:0.1},c=b.direction;"elastic"===d&&(e["down"===c||"up"===c?"top":"left"]=("up"===c||"left"===c?"-":"+")+"=200px");a.wrap.animate(e,{duration:"none"===d?0:a.prevSpeed,easing:a.prevEasing,complete:function(){f(this).trigger("onReset").remove()}})}};b.helpers.overlay={defaults:{closeClick:!0,speedOut:200,showEarly:!0,css:{},locked:!n,fixed:!0},overlay:null,fixed:!1,create:function(a){a=f.extend({},this.defaults,a);this.overlay&&this.close();
this.overlay=f('<div class="fancybox-overlay"></div>').appendTo("body");this.fixed=!1;a.fixed&&b.defaults.fixed&&(this.overlay.addClass("fancybox-overlay-fixed"),this.fixed=!0)},open:function(a){var d=this,a=f.extend({},this.defaults,a);this.overlay?this.overlay.unbind(".overlay").width("auto").height("auto"):this.create(a);this.fixed||(r.bind("resize.overlay",f.proxy(this.update,this)),this.update());a.closeClick&&this.overlay.bind("click.overlay",function(a){f(a.target).hasClass("fancybox-overlay")&&
(b.isActive?b.close():d.close())});this.overlay.css(a.css).show()},close:function(){f(".fancybox-overlay").remove();r.unbind("resize.overlay");this.overlay=null;!1!==this.margin&&(f("body").css("margin-right",this.margin),this.margin=!1);this.el&&this.el.removeClass("fancybox-lock")},update:function(){var a="100%",b;this.overlay.width(a).height("100%");f.browser.msie?(b=Math.max(x.documentElement.offsetWidth,x.body.offsetWidth),m.width()>b&&(a=m.width())):m.width()>r.width()&&(a=m.width());this.overlay.width(a).height(m.height())},
onReady:function(a,b){f(".fancybox-overlay").stop(!0,!0);this.overlay||(this.margin=m.height()>r.height()||"scroll"===f("body").css("overflow-y")?f("body").css("margin-right"):!1,this.el=x.all&&!x.querySelector?f("html"):f("body"),this.create(a));a.locked&&this.fixed&&(b.locked=this.overlay.append(b.wrap),b.fixed=!1);!0===a.showEarly&&this.beforeShow.apply(this,arguments)},beforeShow:function(a,b){b.locked&&(this.el.addClass("fancybox-lock"),!1!==this.margin&&f("body").css("margin-right",k(this.margin)+
b.scrollbarWidth));this.open(a)},onUpdate:function(){this.fixed||this.update()},afterClose:function(a){this.overlay&&!b.isActive&&this.overlay.fadeOut(a.speedOut,f.proxy(this.close,this))}};b.helpers.title={defaults:{type:"float",position:"bottom"},beforeShow:function(a){var d=b.current,e=d.title,c=a.type;f.isFunction(e)&&(e=e.call(d.element,d));if(p(e)&&""!==f.trim(e)){d=f('<div class="fancybox-title fancybox-title-'+c+'-wrap">'+e+"</div>");switch(c){case "inside":c=b.skin;break;case "outside":c=
b.wrap;break;case "over":c=b.inner;break;default:c=b.skin,d.appendTo("body"),f.browser.msie&&d.width(d.width()),d.wrapInner('<span class="child"></span>'),b.current.margin[2]+=Math.abs(k(d.css("margin-bottom")))}d["top"===a.position?"prependTo":"appendTo"](c)}}};f.fn.fancybox=function(a){var d,e=f(this),c=this.selector||"",j=function(g){var h=f(this).blur(),i=d,j,k;!g.ctrlKey&&(!g.altKey&&!g.shiftKey&&!g.metaKey)&&!h.is(".fancybox-wrap")&&(j=a.groupAttr||"data-fancybox-group",k=h.attr(j),k||(j="rel",
k=h.get(0)[j]),k&&(""!==k&&"nofollow"!==k)&&(h=c.length?f(c):e,h=h.filter("["+j+'="'+k+'"]'),i=h.index(this)),a.index=i,!1!==b.open(h,a)&&g.preventDefault())},a=a||{};d=a.index||0;!c||!1===a.live?e.unbind("click.fb-start").bind("click.fb-start",j):m.undelegate(c,"click.fb-start").delegate(c+":not('.fancybox-item, .fancybox-nav')","click.fb-start",j);this.filter("[data-fancybox-start=1]").trigger("click");return this};m.ready(function(){f.scrollbarWidth===q&&(f.scrollbarWidth=function(){var a=f('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo("body"),
b=a.children(),b=b.innerWidth()-b.height(99).innerWidth();a.remove();return b});if(f.support.fixedPosition===q){var a=f.support,d=f('<div style="position:fixed;top:20px;"></div>').appendTo("body"),e=20===d[0].offsetTop||15===d[0].offsetTop;d.remove();a.fixedPosition=e}f.extend(b.defaults,{scrollbarWidth:f.scrollbarWidth(),fixed:f.support.fixedPosition,parent:f("body")})})})(window,document,jQuery);

View file

@ -100,7 +100,7 @@
$dir = $config['calibre_internal_directory'];
if (empty ($config['calibre_internal_directory'])) {
$dir = $config['calibre_directory'];
$dir = Base::getDbDirectory ();
}
if (empty ($config['cops_x_accel_redirect'])) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -24,11 +24,12 @@
$withToolbar = false;
if (!isset($_COOKIE['toolbar'])) $withToolbar = true;
header ("Content-Type:application/xhtml+xml");
header ("Content-Type:application/xhtml+xml;charset=utf-8");
$page = getURLParam ("page", Base::PAGE_INDEX);
$query = getURLParam ("query");
$qid = getURLParam ("id");
$n = getURLParam ("n", "1");
$database = GetUrlParam (DB);
$currentPage = Page::getPage ($page, $qid, $query, $n);
$currentPage->InitializeContent ();
@ -52,13 +53,15 @@
<meta http-equiv="imagetoolbar" content="no" />
<meta name="viewport" content="width=device-width, height=device-height, user-scalable=no" />
<title><?php echo htmlspecialchars ($currentPage->title) ?></title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script type="text/javascript" src="fancybox/jquery.fancybox.pack.js?v=2.1.3"></script>
<script type="text/javascript" src="<?php echo getUrlWithVersion("js/jquery.sortElements.js") ?>"></script>
<script type="text/javascript" src="<?php echo getUrlWithVersion("js/jquery-1.9.1.min.js") ?>"></script>
<script type="text/javascript" src="<?php echo getUrlWithVersion("js/jquery.cookies.js") ?>"></script>
<link rel="related" href="feed.php" type="application/atom+xml;profile=opds-catalog" title="<?php echo $config['cops_title_default']; ?>" />
<?php if ($config['cops_use_fancyapps'] == 1) { ?>
<script type="text/javascript" src="<?php echo getUrlWithVersion("resources/fancybox/jquery.fancybox.pack.js") ?>"></script>
<link rel="stylesheet" type="text/css" href="<?php echo getUrlWithVersion("resources/fancybox/jquery.fancybox.css") ?>" media="screen" />
<?php } ?>
<script type="text/javascript" src="<?php echo getUrlWithVersion("js/jquery.sortElements.js") ?>"></script>
<link rel="related" href="<?php echo $config['cops_full_url'] ?>feed.php" type="application/atom+xml;profile=opds-catalog" title="<?php echo $config['cops_title_default']; ?>" />
<link rel="icon" type="image/vnd.microsoft.icon" href="<?php echo $currentPage->favicon ?>" />
<link rel="stylesheet" type="text/css" href="fancybox/jquery.fancybox.css?v=2.1.3" media="screen" />
<link rel="stylesheet" type="text/css" href="<?php echo getUrlWithVersion("style.css") ?>" media="screen" />
<link rel="stylesheet" href="//normalize-css.googlecode.com/svn/trunk/normalize.css" />
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,300italic,800,300,400italic,600,600italic,700,700italic,800italic' rel='stylesheet' type='text/css' />
@ -89,7 +92,6 @@
nextEffect : 'none'
<?php if ($isEink) echo ", openEffect : 'none', closeEffect : 'none', helpers : {overlay : null}"; ?>
});
<?php } ?>
$(".fancyabout").fancybox({
'type' : 'ajax',
@ -98,6 +100,7 @@
nextEffect : 'none'
<?php if ($isEink) echo ", openEffect : 'none', closeEffect : 'none', helpers : {overlay : null}"; ?>
});
<?php } ?>
$(".headright").click(function(){
if ($("#tool").is(":hidden")) {
@ -109,6 +112,7 @@
}
});
<?php if ($page != Base::PAGE_BOOK_DETAIL) { ?>
$(".bookdetail").click(function(){
var url = $(this).find("a").attr("href");
<?php if ($config['cops_use_fancyapps'] == 0) { ?>
@ -124,6 +128,7 @@
<?php } ?>
return false;
});
<?php } ?>
});
<?php
@ -148,25 +153,26 @@
</script>
</head>
<body>
<div id="loading">
<p><img src="images/ajax-loader.gif" alt="waiting" /> Please Wait</p>
</div>
<div class="container">
<header>
<a class="headleft" href="<?php echo $_SERVER["SCRIPT_NAME"] ?>">
<img src="<?php echo getUrlWithVersion("images/home.png") ?>" alt="Home" />
<a class="headleft" href="<?php echo $_SERVER["SCRIPT_NAME"]; if ($page != Base::PAGE_INDEX && !is_null ($database)) echo "?" . addURLParameter ("", DB, $database); ?>">
<img src="<?php echo getUrlWithVersion("images/home.png") ?>" alt="<?php echo localize ("home.alternate") ?>" />
</a>
<img class="headright" id="searchImage" src="<?php echo getUrlWithVersion("images/setting64.png") ?>" alt="Settings and menu" />
<h1><?php echo htmlspecialchars ($currentPage->title) ?></h1>
</header>
<aside id="tool" <?php if ($withToolbar) echo 'style="display: none"' ?>>
<div style="float: left; width: 60%">
<form action="index.php?page=9" method="get">
<form action="index.php" method="get">
<div style="float: right">
<input type="image" src="images/search32.png" alt="Search" />
<input type="image" src="images/search32.png" alt="<?php echo localize ("search.alternate") ?>" />
</div>
<div class="stop">
<input type="hidden" name="current" value="<?php echo $page ?>" />
<input type="hidden" name="page" value="9" />
<?php if (!is_null ($database)) { ?>
<input type="hidden" name="<?php echo DB ?>" value="<?php echo $database ?>" />
<?php } ?>
<input type="text" name="query" />
</div>
</form>
@ -174,7 +180,7 @@
<?php if ($currentPage->containsBook ()) { ?>
<div style="float: right; width: 35%">
<div style="float: right">
<img id="sort" src="images/sort32.png" alt="Sort" />
<img id="sort" src="images/sort32.png" alt="<?php echo localize ("sort.alternate") ?>" />
</div>
<div class="stop">
<select id="sortchoice">
@ -184,8 +190,8 @@
<option value="sp"><?php echo localize("content.published") ?></option>
</select>
<select id="sortorder">
<option value="asc">Asc</option>
<option value="desc">Desc</option>
<option value="asc"><?php echo localize("search.sortorder.asc") ?></option>
<option value="desc"><?php echo localize("search.sortorder.desc") ?></option>
</select>
</div>
</div>
@ -193,14 +199,15 @@
</aside>
<div id="content" style="display: none;"></div>
<section>
<?php
if ($page == Base::PAGE_BOOK_DETAIL)
{
<?php
if ($page == Base::PAGE_BOOK_DETAIL) {
include ("bookdetail.php");
} else if ($page == Base::PAGE_ABOUT) {
readfile ("about.xml");
}
foreach ($currentPage->entryArray as $entry) {
if (get_class ($entry) != "EntryBook") {
?>
?>
<article>
<div class="frontpage">
<?php foreach ($entry->linkArray as $link) { if ($link->type != Link::OPDS_NAVIGATION_TYPE) { continue; } ?> <a href="<?php echo $link->hrefXhtml () ?>">
@ -270,7 +277,9 @@
?>
</section>
<footer>
<a class="fancyabout" href="about.html"><img src="<?php echo getUrlWithVersion("images/info.png") ?>" alt="Home" /></a>
<a class="fancyabout" href="<?php if ($config['cops_use_fancyapps'] == 1) { echo "about.xml"; } else { echo $_SERVER["SCRIPT_NAME"] . str_replace ("&", "&amp;", addURLParameter ("?page=16", DB, $database)); } "><img src="<?php echo getUrlWithVersion("images/info.png") ?>" alt="<?php echo localize ("about.title") ?>" /></a>
</a>
<?php
if ($currentPage->isPaginated ()) {
?>
@ -279,7 +288,7 @@
<?php
if (!is_null ($prevLink)) {
?>
<a href="<?php echo $prevLink->hrefXhtml () ?>" ><img src="<?php echo getUrlWithVersion("images/previous.png") ?>" alt="Previous" /></a>
<a href="<?php echo $prevLink->hrefXhtml () ?>" ><img src="<?php echo getUrlWithVersion("images/previous.png") ?>" alt="<?php echo localize ("paging.previous.alternate") ?>" /></a>
<?php
}
?>
@ -287,7 +296,7 @@
<?php
if (!is_null ($nextLink)) {
?>
<a href="<?php echo $nextLink->hrefXhtml () ?>" ><img src="<?php echo getUrlWithVersion("images/next.png") ?>" alt="Next" /></a>
<a href="<?php echo $nextLink->hrefXhtml () ?>" ><img src="<?php echo getUrlWithVersion("images/next.png") ?>" alt="<?php echo localize ("paging.next.alternate") ?>" /></a>
<?php
}
?>

5
js/jquery-1.9.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -50,17 +50,17 @@
"tags.title":"Etiquetes",
"tags.categorized":"Llistat per categories de les {0} etiquetes",
"tags.categorized.single":"Llistat per categories de la única etiqueta - molt útil ;)",
"tags.alphabetical":"{0} etiquetes ordenades alfabèticament",
"tags.alphabetical.single":"Llistat alfabètic de la única etiqueta ;)",
"tags.alphabetical.many":"{0} etiquetes ordenades alfabèticament",
"tags.alphabetical.one":"Llistat alfabètic de la única etiqueta ;)",
"splitByLetter.tag.other":"Altres etiquetes",
"authors.series.title":"Sèries",
"authors.title":"Autors",
"authors.alphabetical":"{0} autors ordenats alfabèticament",
"authors.alphabetical.single":"Índex s'un sol autor",
"authors.alphabetical.many":"{0} autors ordenats alfabèticament",
"authors.alphabetical.one":"Índex s'un sol autor",
"splitByLetter.author.other":"Altres autors",
"series.title":"Sèries",
"series.alphabetical":"{0} sèries ordenades alfabèticament",
"series.alphabetical.single":"Índex alfabètic d'una sola sèrie",
"series.alphabetical.many":"{0} sèries ordenades alfabèticament",
"series.alphabetical.one":"Índex alfabètic d'una sola sèrie",
"splitByLetter.series.other":"Altres sèries",
"recent.title":"Els més recents",
"recent.list":"{0} darrers títols incorporats",
@ -68,8 +68,8 @@
"rating.title":"Valoració",
"rating.summary":"{0}, agrupats per valoració",
"allbooks.title":"Tots els llibres",
"allbooks.alphabetical":"{0} llibres ordenats alfabèticament",
"allbooks.alphabetical.single":"Índex d'un sol llibre",
"allbooks.alphabetical.many":"{0} llibres ordenats alfabèticament",
"allbooks.alphabetical.one":"Índex d'un sol llibre",
"splitByLetter.book.other":"Altres llibres",
"main.title":"Biblioteca generada per Calibre",
"main.summary":"{0} ha catalogat {1}",
@ -83,7 +83,6 @@
"i18n.summarysection":"Resum",
"i18n.dateGenerated":"Catàleg creat el {0}",
"deeplevel.summary":"{0} per autors, etiquetes, etc.",
"about.title":"Documentació de Calibre2Opds",
"about.summary":"Notes d'ús",
"usage.intro":"Les opcions provenen de l'arxiu de configuració present a: {0}",
"config.Language.label":"Idioma",

View file

@ -54,17 +54,17 @@
"tags.title":"Schlagwörter",
"tags.categorized":"Index der {0} Schlagwörter nach Kategorien",
"tags.categorized.single":"Index des Schlagworts nach Kategorien - sehr nützlich ;)",
"tags.alphabetical":"Alphabetischer Index der {0} Schlagwörter",
"tags.alphabetical.single":"Alphabetischer Index des Schlagworts ;)",
"tags.alphabetical.many":"Alphabetischer Index der {0} Schlagwörter",
"tags.alphabetical.one":"Alphabetischer Index des Schlagworts ;)",
"splitByLetter.tag.other":"Andere Schlagwörter",
"authors.series.title":"Serien: {0}",
"authors.title":"Autoren",
"authors.alphabetical":"Alphabetischer Index der {0} Autoren",
"authors.alphabetical.single":"Alphabetischer Index des Autors - unglaublich nützlich ;)",
"authors.alphabetical.many":"Alphabetischer Index der {0} Autoren",
"authors.alphabetical.one":"Alphabetischer Index des Autors - unglaublich nützlich ;)",
"splitByLetter.author.other":"Andere Autoren",
"series.title":"Serien",
"series.alphabetical":"Alphabetischer Index der {0} Serien",
"series.alphabetical.single":"Alphabetischer Index der Serie - äußerst nützlich ;)",
"series.alphabetical.many":"Alphabetischer Index der {0} Serien",
"series.alphabetical.one":"Alphabetischer Index der Serie - äußerst nützlich ;)",
"splitByLetter.series.other":"Andere Serien",
"recent.title":"Neuzugänge",
"recent.list":"{0} neue Bücher",
@ -72,8 +72,8 @@
"rating.title":"Bewertung",
"rating.summary":"{0}, gruppiert nach Bewertung",
"allbooks.title":"Alle Bücher",
"allbooks.alphabetical":"Alphabetischer Index der {0} Bücher",
"allbooks.alphabetical.single":"Alphabetischer Index des einzigen Buchs - unverzichtbar ;)",
"allbooks.alphabetical.many":"Alphabetischer Index der {0} Bücher",
"allbooks.alphabetical.one":"Alphabetischer Index des einzigen Buchs - unverzichtbar ;)",
"splitByLetter.book.other":"Andere Bücher",
"main.title":"Calibre Bibliothek",
"main.summary":"{0} hat {1} katalogisierte Bücher",
@ -100,7 +100,6 @@
"i18n.summarysection":"Zusammenfassung",
"i18n.dateGenerated":"Katalog erstellt am {0}",
"deeplevel.summary":"{0} aufgeschlüsselt nach Autoren, Schlagwörter, etc.",
"about.title":"Calibre2Opds Dokumentation",
"about.summary":"Anmerkungen zur Verwendung von Calibre2Opds",
"usage.intro":"Die Optionen werden der Konfigurationsdatei in {0} entnommen",
"config.Language.label":"Sprache",
@ -117,5 +116,8 @@
"intro.team.list5":"Jane Litte - Beta Tests und moralische Unterstützung",
"intro.thanks.1":"Speziellen Dank an Kb Sriram, der nicht nur Trook, eine exzellente und OPDS kompatible ",
"intro.thanks.2":"Bibliotheksverwaltung programmierte, sondern auch einen Nook spendete!",
"search.result":"Suchergebnis für",
"search.sortorder.asc":"Auf",
"search.sortorder.desc":"Ab",
"fin":"fin"
}

View file

@ -57,17 +57,20 @@
"tags.title":"Tags",
"tags.categorized":"Categorized index of the {0} tags",
"tags.categorized.single":"Categorized index of the single tag - very useful indeed ;)",
"tags.alphabetical":"Alphabetical index of the {0} tags",
"tags.alphabetical.single":"Alphabetical index of the single tag ;)",
"tags.alphabetical.many":"Alphabetical index of the {0} tags",
"tags.alphabetical.one":"Alphabetical index of the single tag - very useful indeed ;)",
"tags.alphabetical.none":"Alphabetical index of absolutely no tag - very useful indeed ;)",
"splitByLetter.tag.other":"Other tags",
"authors.series.title":"Series: {0}",
"authors.title":"Authors",
"authors.alphabetical":"Alphabetical index of the {0} authors",
"authors.alphabetical.single":"Alphabetical index of the single author - very useful indeed ;)",
"authors.alphabetical.many":"Alphabetical index of the {0} authors",
"authors.alphabetical.one":"Alphabetical index of the single author - very useful indeed ;)",
"authors.alphabetical.none":"Alphabetical index of absolutely no author - very useful indeed ;)",
"splitByLetter.author.other":"Other authors",
"series.title":"Series",
"series.alphabetical":"Alphabetical index of the {0} series",
"series.alphabetical.single":"Alphabetical index of the single series - very useful indeed ;)",
"series.alphabetical.many":"Alphabetical index of the {0} series",
"series.alphabetical.one":"Alphabetical index of the single series - very useful indeed ;)",
"series.alphabetical.none":"Alphabetical index of absolutely no series - very useful indeed ;)",
"splitByLetter.series.other":"Other series",
"recent.title":"Recent additions",
"recent.list":"{0} most recent books",
@ -75,8 +78,9 @@
"rating.title":"Rating",
"rating.summary":"{0}, grouped by rating",
"allbooks.title":"All books",
"allbooks.alphabetical":"Alphabetical index of the {0} books",
"allbooks.alphabetical.single":"Alphabetical index of the single book - very useful indeed ;)",
"allbooks.alphabetical.many":"Alphabetical index of the {0} books",
"allbooks.alphabetical.one":"Alphabetical index of the single book - very useful indeed ;)",
"allbooks.alphabetical.none":"Alphabetical index of absolutely no book - very useful indeed ;)",
"splitByLetter.book.other":"Other books",
"main.title":"Calibre library",
"main.summary":"{0} has catalogued {1}",
@ -103,7 +107,7 @@
"i18n.summarysection":"Description",
"i18n.dateGenerated":"Catalog generated on {0}",
"deeplevel.summary":"{0} broken up by authors, tags, etc. ",
"about.title":"Calibre2Opds Documentation",
"about.title":"About COPS",
"about.summary":"Notes on using Calibre2Opds",
"usage.intro":"The options are taken from the configuration file located at {0}",
"config.Language.label":"Language",
@ -120,5 +124,14 @@
"intro.team.list5":"Jane Litte - beta tester and moral support",
"intro.thanks.1":"Special thanks to Kb Sriram, who not only programmed Trook, an excellent and OPDS compatible ",
"intro.thanks.2":"library manager for the Nook, but also was kind enough to donate a Nook !",
"search.result":"Search result for *{0}*",
"search.sortorder.asc":"Asc",
"search.sortorder.desc":"Desc",
"permalink.alternate":"Permalink",
"home.alternate":"Home",
"search.alternate":"Search",
"sort.alternate":"Sort",
"paging.next.alternate":"Next",
"paging.previous.alternate":"Previous",
"fin":"fin"
}

View file

@ -50,17 +50,17 @@
"tags.title":"etiquetas",
"tags.categorized":"Listado por categorias de las {0} etiquetas",
"tags.categorized.single":"Listado por categorias de la unica etiqueta - muy util ;)",
"tags.alphabetical":"Listado alfabético de las {0} etiquetas",
"tags.alphabetical.single":"Listado Alfabético de la unica etiqueta ;)",
"tags.alphabetical.many":"Listado alfabético de las {0} etiquetas",
"tags.alphabetical.one":"Listado Alfabético de la unica etiqueta ;)",
"splitByLetter.tag.other":"Otras etiquetas",
"authors.series.title":"Series",
"authors.title":"Autores",
"authors.alphabetical":"Indice alfabético de {0}",
"authors.alphabetical.single":"Indice de un solo autor",
"authors.alphabetical.many":"Indice alfabético de {0}",
"authors.alphabetical.one":"Indice de un solo autor",
"splitByLetter.author.other":"Otros Autores",
"series.title":"Series:",
"series.alphabetical":"Indice alfabético de {0}",
"series.alphabetical.single":"Indice de una sola serie",
"series.alphabetical.many":"Indice alfabético de {0}",
"series.alphabetical.one":"Indice de una sola serie",
"splitByLetter.series.other":"Otras series",
"recent.title":"Añadidos recientemente",
"recent.list":"{0} libros más recientes",
@ -68,8 +68,8 @@
"rating.title":"Valoración",
"rating.summary":"{0}, agrupados por valoración",
"allbooks.title":"Todos los libros",
"allbooks.alphabetical":"Indice alfabético de {0} libros",
"allbooks.alphabetical.single":"Indice de un solo ",
"allbooks.alphabetical.many":"Indice alfabético de {0} libros",
"allbooks.alphabetical.one":"Indice de un solo ",
"splitByLetter.book.other":"Otros libros",
"main.title":"Biblioteca generada de calibre",
"main.summary":"{0} ha catalogado {1}",
@ -83,7 +83,6 @@
"i18n.summarysection":"Resumen",
"i18n.dateGenerated":"Catálogo creado el {0}",
"deeplevel.summary":"{0} por autores, etiquetas, etc.",
"about.title":"Documentación de Calibre2Opds",
"about.summary":"Notas de Uso",
"usage.intro":"Las opciones provienen del fichero de configuración presente en: {0}",
"config.Language.label":"Idioma",

View file

@ -55,17 +55,17 @@
"tags.title":"Étiquettes",
"tags.categorized":"Index ordonné des {0} étiquettes",
"tags.categorized.single":"Index ordonné de la seule étiquette disponible",
"tags.alphabetical":"Index alphabétique des {0} étiquettes",
"tags.alphabetical.single":"Index alphabétique de la seule étiquette ;)",
"tags.alphabetical.many":"Index alphabétique des {0} étiquettes",
"tags.alphabetical.one":"Index alphabétique de la seule étiquette ;)",
"splitByLetter.tag.other":"Autres étiquettes",
"authors.series.title":"Collection: {0}",
"authors.title":"Auteurs",
"authors.alphabetical":"Index alphabétique des {0} auteurs",
"authors.alphabetical.single":"Index alphabétique du seul auteur ;)",
"authors.alphabetical.many":"Index alphabétique des {0} auteurs",
"authors.alphabetical.one":"Index alphabétique du seul auteur ;)",
"splitByLetter.author.other":"Autres auteurs",
"series.title":"Collections",
"series.alphabetical":"Index alphabétique de {0} collections",
"series.alphabetical.single":"Index alphabétique de la seule collection ;)",
"series.alphabetical.many":"Index alphabétique de {0} collections",
"series.alphabetical.one":"Index alphabétique de la seule collection ;)",
"splitByLetter.series.other":"Autres collections",
"recent.title":"Ajouts récents",
"recent.list":"{0} livres les plus récents",
@ -73,8 +73,8 @@
"rating.title":"Appréciations",
"rating.summary":"{0}, par appréciation",
"allbooks.title":"Tous les livres",
"allbooks.alphabetical":"Index alphabétique des {0} livres",
"allbooks.alphabetical.single":"Index alphabétique du seul livre ;)",
"allbooks.alphabetical.many":"Index alphabétique des {0} livres",
"allbooks.alphabetical.one":"Index alphabétique du seul livre ;)",
"splitByLetter.book.other":"Autres livres",
"main.title":"Bibliothèque gérée par Calibre",
"main.summary":"{0} a catalogué {1}",
@ -89,7 +89,7 @@
"i18n.summarysection":"Résumé",
"i18n.dateGenerated":"Catalogue généré le {0}",
"deeplevel.summary":"{0} par auteur, étiquette, etc.",
"about.title":"Documentation de Calibre2Opds",
"about.title":"A propos de COPS",
"about.summary":"Notes d'utilisation",
"usage.intro":"Les options sont tirées du fichier de configuration qui se trouve ici : {0}",
"config.Language.label":"Langue",
@ -105,5 +105,14 @@
"intro.team.list5":"Jane Litte - beta testeuse et soutien moral",
"intro.thanks.1":"Remerciements particuliers à Kb Sriram, qui est l'auteur de Trook, un excellent",
"intro.thanks.2":"gestionnaire de bibliothèque pour Nook, et a m'a fait cadeau d'un Nook !",
"search.result":"Résultats pour *{0}*",
"search.sortorder.asc":"Crois.",
"search.sortorder.desc":"Décrois.",
"permalink.alternate":"Permalien",
"home.alternate":"Accueil",
"search.alternate":"Rechercher",
"sort.alternate":"Trier",
"paging.next.alternate":"Suivant",
"paging.previous.alternate":"Précédent",
"fin":"fin"
}

View file

@ -51,17 +51,17 @@
"tags.title":"Argomenti",
"tags.categorized":"Indice ordinato di {0} argomenti",
"tags.categorized.single":"Indice ordinato del solo argomento disponibile",
"tags.alphabetical":"Indice alfabetico di {0} argomenti",
"tags.alphabetical.single":"Indice alfabetico del solo argomento ;)",
"tags.alphabetical.many":"Indice alfabetico di {0} argomenti",
"tags.alphabetical.one":"Indice alfabetico del solo argomento ;)",
"splitByLetter.tag.other":"Altri argomenti",
"authors.series.title":"Collane: {0}",
"authors.title":"Autori",
"authors.alphabetical":"Indice alfabetico di {0} autori",
"authors.alphabetical.single":"Indice alfabetico di un solo autore ;)",
"authors.alphabetical.many":"Indice alfabetico di {0} autori",
"authors.alphabetical.one":"Indice alfabetico di un solo autore ;)",
"splitByLetter.author.other":"Altri autori",
"series.title":"Collane",
"series.alphabetical":"Indice alfabetico di {0} collane",
"series.alphabetical.single":"Indice alfabetico di una sola collana ;)",
"series.alphabetical.many":"Indice alfabetico di {0} collane",
"series.alphabetical.one":"Indice alfabetico di una sola collana ;)",
"splitByLetter.series.other":"Altre collane",
"recent.title":"Ultime aggiunte",
"recent.list":" I {0} libri più recenti",
@ -69,8 +69,8 @@
"rating.title":"Valutazioni",
"rating.summary":"{0}, per valutazioni",
"allbooks.title":"Tutti i libri",
"allbooks.alphabetical":"Indice alfabetico di {0} libri",
"allbooks.alphabetical.single":"Indice alfabetico di un solo libro ;)",
"allbooks.alphabetical.many":"Indice alfabetico di {0} libri",
"allbooks.alphabetical.one":"Indice alfabetico di un solo libro ;)",
"splitByLetter.book.other":"Altri libri",
"main.title":"Biblioteca generata da Calibre",
"main.summary":"{0} ha catalogato {1}",
@ -84,7 +84,6 @@
"i18n.summarysection":"Riassunto",
"i18n.dateGenerated":"Catalogo generato il {0}",
"deeplevel.summary":"{0} par autore, etichetta, ecc.",
"about.title":"Documentazione di Calibre2Opds",
"about.summary":"Note d'utilizzo",
"usage.intro":"Le opzioni sono prese dal file di configuraziones che si trova qui: {0}",
"config.Language.label":"Lingua",

View file

@ -56,17 +56,17 @@
"tags.title":"Tags",
"tags.categorized":"Gecategorizeerde index van {0} tags",
"tags.categorized.single":"Gecategorizeerde index van 1 tag - Erg handig ;)",
"tags.alphabetical":"Alfabetische index van {0} tags",
"tags.alphabetical.single":"Alfabetische index van 1 tag ;)",
"tags.alphabetical.many":"Alfabetische index van {0} tags",
"tags.alphabetical.one":"Alfabetische index van 1 tag ;)",
"splitByLetter.tag.other":"Andere tags",
"authors.series.title":"Series: {0}",
"authors.title":"Auteurs",
"authors.alphabetical":"Alfabetische index van {0} auteurs",
"authors.alphabetical.single":"Alfabetische index van 1 auteur - Erg handig ;)",
"authors.alphabetical.many":"Alfabetische index van {0} auteurs",
"authors.alphabetical.one":"Alfabetische index van 1 auteur - Erg handig ;)",
"splitByLetter.author.other":"Andere auteurs",
"series.title":"Series",
"series.alphabetical":"Alfabetische index van {0} series",
"series.alphabetical.single":"Alfabetische index van 1 serie - Erg handig ;)",
"series.alphabetical.many":"Alfabetische index van {0} series",
"series.alphabetical.one":"Alfabetische index van 1 serie - Erg handig ;)",
"splitByLetter.series.other":"Andere series",
"recent.title":"Recent toegevoegd",
"recent.list":"{0} meest recente boeken",
@ -74,8 +74,8 @@
"rating.title":"Rating",
"rating.summary":"{0}, gegroepeerd per rating",
"allbooks.title":"Alle boeken",
"allbooks.alphabetical":"Alfabetische index van {0} boeken",
"allbooks.alphabetical.single":"Alfabetische index van 1 boek",
"allbooks.alphabetical.many":"Alfabetische index van {0} boeken",
"allbooks.alphabetical.one":"Alfabetische index van 1 boek",
"splitByLetter.book.other":"Andere boeken",
"main.title":"Calibre bibliotheek",
"main.summary":"{0} heeft gecatalogiseerd {1}",
@ -102,7 +102,6 @@
"i18n.summarysection":"Omschrijving",
"i18n.dateGenerated":"Catalogus gegenereerd op {0}",
"deeplevel.summary":"{0} uitgesplitst naar auteurs, tags, etc. ",
"about.title":"Calibre2Opds Documentatie",
"about.summary":"Notities hoe Calibre2Opds te gebruiken",
"usage.intro":"De opties worden uit het configuratie bestand {0} gebruikt",
"config.Language.label":"Taal",

View file

@ -53,17 +53,17 @@
"tags.title":"Тэги",
"tags.categorized":"Категорированный указатель для {0} тэгов",
"tags.categorized.single":"Категорированный указатель для одного тэга - действительно очень полезно ;)",
"tags.alphabetical":"Алфавитный указатель для {0} тэгов",
"tags.alphabetical.single":"Алфавитный указатель для одного тэга ;)",
"tags.alphabetical.many":"Алфавитный указатель для {0} тэгов",
"tags.alphabetical.one":"Алфавитный указатель для одного тэга ;)",
"splitByLetter.tag.other":"Другие тэги",
"authors.series.title":"Серии: {0}",
"authors.title":"Авторы",
"authors.alphabetical":"Алфавитный указатель для {0} авторов",
"authors.alphabetical.single":"Алфавитный указатель для одного автора - действительно очень полезно ;)",
"authors.alphabetical.many":"Алфавитный указатель для {0} авторов",
"authors.alphabetical.one":"Алфавитный указатель для одного автора - действительно очень полезно ;)",
"splitByLetter.author.other":"Другие авторы",
"series.title":"Серии",
"series.alphabetical":"Алфавитный указатель для {0} серий",
"series.alphabetical.single":"Алфавитный указатель для одной серии - действительно очень полезно ;)",
"series.alphabetical.many":"Алфавитный указатель для {0} серий",
"series.alphabetical.one":"Алфавитный указатель для одной серии - действительно очень полезно ;)",
"splitByLetter.series.other":"Другие серии",
"recent.title":"Недавние поступления",
"recent.list":"{0} недавно поступивших(ие) книг(и)",
@ -71,8 +71,8 @@
"rating.title":"Рейтинг",
"rating.summary":"{0}, сгруппировано по рейтингу",
"allbooks.title":"Все книги",
"allbooks.alphabetical":"Алфавитный указатель всех {0} книг",
"allbooks.alphabetical.single":"Алфавитный указатель одной книги - действительно очень полезно ;)",
"allbooks.alphabetical.many":"Алфавитный указатель всех {0} книг",
"allbooks.alphabetical.one":"Алфавитный указатель одной книги - действительно очень полезно ;)",
"splitByLetter.book.other":"Другие книги",
"main.title":"Библиотека Calibre",
"main.summary":"{0} каталогизировано {1}",
@ -86,7 +86,6 @@
"i18n.summarysection":"Краткое содержание",
"i18n.dateGenerated":"Каталог создан {0}",
"deeplevel.summary":"{0} разбито по авторам, тэгам и т.д.",
"about.title":"Документация Calibre2Opds",
"about.summary":"Примечания по использованию Calibre2Opds",
"usage.intro":"Эта опция взята из файла конфигурации, расположенного в {0}",
"config.Language.label":"Язык",

137
lang/Localization_zh.json Normal file
View file

@ -0,0 +1,137 @@
{
"boolean.no":"否",
"boolean.yes":"是",
"splitByLetter.letter":"{0} 以 {1} 开头",
"home.title":"目录",
"link.fullentry":"全部条目",
"title.nextpage":"下一页 ({0} of {1})",
"title.lastpage":"最后一页",
"title.numberOfPages":"{0} (共 {1} 页)",
"pubdate.title":"出版时间",
"bookword.title":"书名",
"bookword.none":"没有书籍",
"bookword.one":"1 本书籍",
"bookword.many":"{0} 本书籍",
"authorword.title":"作者",
"authorword.none":"未知",
"authorword.one":"1 位作者",
"authorword.many":"{0} 位作者",
"taglevelword.title":"Tag levels",
"taglevelword.none":"No tag level",
"taglevelword.one":"1 tag level",
"taglevelword.many":"{0} tag levels",
"seriesword.title":"系列",
"seriesword.none":"没有系列",
"seriesword.one":"1 个系列",
"seriesword.many":"{0} 系列",
"tagword.title":"标签",
"tagword.none":"没有标签",
"tagword.one":"1 个标签",
"tagword.many":"{0} 标签",
"content.tags":"标签:",
"content.series":"系列:",
"content.series.data":"{0} 系列的第 {0} 本",
"content.publisher":"出版社:",
"content.published":"出版时间",
"content.added":"添加了: ",
"content.modified":"修改了:",
"content.publisher.data":"由 {0} 出版于 {1}",
"content.summary":"概要",
"bookentry.series":"Book {0} in the {1} series",
"bookentry.author":"{0} 由作者 {1}",
"bookentry.tags":"{0} 在标签 {1}",
"bookentry.ratings":"{0} 评分 {1}",
"bookentry.goodreads":"Goodreads 上的这本书",
"bookentry.goodreads.review":"在 Goodreads上评论这本书",
"bookentry.goodreads.author":"{0} 在 Goodreads",
"bookentry.wikipedia":"维基百科上的这本书",
"bookentry.wikipedia.author":"{0} 在维基百科上",
"bookentry.librarything":"LibraryThing 上的这本书",
"bookentry.librarything.author":"{0} 在 LibraryThing",
"bookentry.amazon":"Amazon上的这本书",
"bookentry.amazon.author":"{0} 在 Amazon",
"bookentry.isfdb.author":"{0} 在 ISFDB",
"bookentry.download":"下载这本书 {0}",
"bookentry.rated":"{0} {1}",
"bookentry.fullentrylink":"全部条目",
"tags.title":"标签",
"tags.categorized":"Categorized index of the {0} tags",
"tags.categorized.single":"Categorized index of the single tag - very useful indeed ;)",
"tags.alphabetical.many":"{0} 个标签的字母索引",
"tags.alphabetical.one":"Alphabetical index of the single tag - very useful indeed ;)",
"tags.alphabetical.none":"Alphabetical index of absolutely no tag - very useful indeed ;)",
"splitByLetter.tag.other":"其他标签",
"authors.series.title":"系列: {0}",
"authors.title":"作者",
"authors.alphabetical.many":"{0} 位作者的字母索引",
"authors.alphabetical.one":"Alphabetical index of the single author - very useful indeed ;)",
"authors.alphabetical.none":"Alphabetical index of absolutely no author - very useful indeed ;)",
"splitByLetter.author.other":"其他作者",
"series.title":"系列",
"series.alphabetical.many":"{0} 个系列的字母索引",
"series.alphabetical.one":"Alphabetical index of the single series - very useful indeed ;)",
"series.alphabetical.none":"Alphabetical index of absolutely no series - very useful indeed ;)",
"splitByLetter.series.other":"其他系列",
"recent.title":"最近添加",
"recent.list":"{0} 本最近添加的书",
"recent.list.single":"Most recent single book - very useful indeed ;)",
"rating.title":"Rating",
"rating.summary":"{0}, 根据评分分组",
"allbooks.title":"所有书籍",
"allbooks.alphabetical.many":"{0} 本书籍的字母索引",
"allbooks.alphabetical.one":"Alphabetical index of the single book - very useful indeed ;)",
"allbooks.alphabetical.none":"Alphabetical index of absolutely no book - very useful indeed ;)",
"splitByLetter.book.other":"其他书籍",
"main.title":"Calibre 书架",
"main.summary":"{0} has catalogued {1}",
"startup.newhome":"Default configuration folder home redirected to {0}",
"startup.redirectfound":".redirect file found in {0}",
"startup.redirectreadfail":"... failure reading .redirect file",
"startup.redirectnotfound":"... unable to find redirect folder {0}",
"startup.redirectabandoned":"... so redirect abandoned",
"startup.redirecting":"redirecting home folder to {0}",
"startup.configusing":"Using configuration folder {0}",
"startup.folderuserhome":"Try configuration folder in user home folder {0}",
"startup.foldertilde":"Try configuration folder from tilde folder {0}",
"startup.folderjar":"Try configuration folder from .jar location {0}",
"startup.foldernotexist":"... but specified folder does not exist",
"i18n.and":"和",
"i18n.downloads":"下载,链接和其他目录 ",
"i18n.links":"链接和其他目录",
"i18n.coversection":"封面",
"i18n.downloadfile":"下载文件",
"i18n.downloadsection":"下载",
"i18n.relatedsection":"相关目录",
"i18n.linksection":"外部链接",
"i18n.backToMain":"返回目录主页",
"i18n.summarysection":"描述",
"i18n.dateGenerated":"目录生成于 {0}",
"deeplevel.summary":"{0} broken up by authors, tags, etc. ",
"about.title":"关于COPS",
"about.summary":"关于Calibre2Opds的说明",
"usage.intro":"选项从此配置文件生成 {0}",
"config.Language.label":"语言",
"config.Language.description":"此项设置将会改变程序使用的语言 ; 请使用ISO标准语言代码 (例如 EN, FR, DE...)",
"config.Language.possible":"可能的选项 : {0}",
"intro.goal":"从你的 Calibre 数据库生成 OPDS 和 HTML 目录",
"intro.wiki.title":"项目主页 : ",
"intro.wiki.url":"http://calibre2opds.com",
"intro.team.title":"项目人员 :",
"intro.team.list1":"David Pierron - main programmer",
"intro.team.list2":"Dave Walker - guru, features manager and tester extraordinaire",
"intro.team.list3":"Farid Soussi - css and html guru",
"intro.team.list4":"Douglas Steele - programmer",
"intro.team.list5":"Jane Litte - beta tester and moral support",
"intro.thanks.1":"Special thanks to Kb Sriram, who not only programmed Trook, an excellent and OPDS compatible ",
"intro.thanks.2":"library manager for the Nook, but also was kind enough to donate a Nook !",
"search.result":"*{0}* 的搜索结果",
"search.sortorder.asc":"升序",
"search.sortorder.desc":"降序",
"permalink.alternate":"永久链接",
"home.alternate":"首页",
"search.alternate":"搜索",
"sort.alternate":"排序",
"paging.next.alternate":"下一页",
"paging.previous.alternate":"上一页",
"fin":"fin"
}

View file

Before

Width:  |  Height:  |  Size: 43 B

After

Width:  |  Height:  |  Size: 43 B

View file

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

Before

Width:  |  Height:  |  Size: 1,003 B

After

Width:  |  Height:  |  Size: 1,003 B

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,4 +1,4 @@
/*! fancyBox v2.1.3 fancyapps.com | fancyapps.com/fancybox/#license */
/*! fancyBox v2.1.4 fancyapps.com | fancyapps.com/fancybox/#license */
.fancybox-wrap,
.fancybox-skin,
.fancybox-outer,

File diff suppressed because one or more lines are too long

View file

@ -17,3 +17,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
All code created or modified by Sébastien Lucas <sebastien@slucas.fr> is licensed
under GPL 2 (http://www.gnu.org/licenses/gpl.html).

View file

@ -3,15 +3,18 @@
* PHP EPub Meta library
*
* @author Andreas Gohr <andi@splitbrain.org>
* @author Sébastien Lucas <sebastien@slucas.fr>
*/
require_once('tbszip.php');
require_once(realpath( dirname( __FILE__ ) ) . '/tbszip.php');
define ("METADATA_FILE", "META-INF/container.xml");
class EPub {
public $xml; //FIXME change to protected, later
public $toc;
protected $xpath;
protected $toc_xpath;
protected $file;
protected $meta;
protected $zip;
@ -64,6 +67,26 @@ class EPub {
$this->xml->formatOutput = true;
$this->xpath = new EPubDOMXPath($this->xml);
}
public function initSpineComponent ()
{
$spine = $this->xpath->query('//opf:spine')->item(0);
$tocid = $spine->getAttribute('toc');
$tochref = $this->xpath->query("//opf:manifest/opf:item[@id='$tocid']")->item(0)->attr('href');
$tocpath = dirname($this->meta).'/'.$tochref;
// read epub toc
if (!$this->zip->FileExists($tocpath)) {
throw new Exception ("Unable to find " . $tocpath);
}
$data = $this->zip->FileRead($tocpath);
$this->toc = new DOMDocument();
$this->toc->registerNodeClass('DOMElement','EPubDOMElement');
$this->toc->loadXML($data);
$this->toc_xpath = new EPubDOMXPath($this->toc);
$rootNamespace = $this->toc->lookupNamespaceUri($this->toc->namespaceURI);
$this->toc_xpath->registerNamespace('x', $rootNamespace);
}
/**
* file name getter
@ -81,13 +104,24 @@ class EPub {
$this->zip->Close ();
}
/**
* Remove iTunes files
*/
public function cleanITunesCrap () {
if ($this->zip->FileExists("iTunesMetadata.plist")) {
$this->zip->FileReplace ("iTunesMetadata.plist", false);
}
if ($this->zip->FileExists("iTunesArtwork")) {
$this->zip->FileReplace ("iTunesArtwork", false);
}
}
/**
* Writes back all meta data changes
* TODO update
*/
public function save(){
$this->download ();
$zip->close();
$this->zip->close();
}
/**
@ -102,7 +136,57 @@ class EPub {
}
if ($file) $this->zip->Flush(TBSZIP_DOWNLOAD, $file);
}
/**
* Get the components list as an array
*/
public function components(){
$spine = array();
$nodes = $this->xpath->query('//opf:spine/opf:itemref');
foreach($nodes as $node){
$idref = $node->getAttribute('idref');
$spine[] = $this->xpath->query("//opf:manifest/opf:item[@id='$idref']")->item(0)->getAttribute('href');
}
return $spine;
}
/**
* Get the component content
*/
public function component($comp) {
$path = dirname($this->meta).'/'.$comp;
if (!$this->zip->FileExists($path)) {
throw new Exception ("Unable to find " . $path);
}
$data = $this->zip->FileRead($path);
$data = preg_replace ("/src=[\"']([\w\/\.]*?)[\"']/", "src='epubfs.php?comp=$1'", $data);
$data = preg_replace ("/href=[\"']([\w\/\.]*?)[\"']/", "href='epubfs.php?comp=$1'", $data);
return $data;
}
/**
* Get the component content type
*/
public function componentContentType($comp) {
return $this->xpath->query("//opf:manifest/opf:item[@href='$comp']")->item(0)->getAttribute('media-type');
}
/**
* Get the Epub content (TOC) as an array
*
* For each chapter there is a "title" and a "src"
*/
public function contents(){
$contents = array();
$nodes = $this->toc_xpath->query('//x:ncx/x:navMap/x:navPoint');
foreach($nodes as $node){
$title = $this->toc_xpath->query('x:navLabel/x:text', $node)->item(0)->nodeValue;
$src = $this->toc_xpath->query('x:content', $node)->item(0)->attr('src');
$contents[] = array("title" => $title, "src" => $src);
}
return $contents;
}
/**
@ -413,7 +497,6 @@ class EPub {
}
}
public function Cover2($path=false, $mime=false){
$hascover = true;
$item = $this->getCoverItem ();
@ -423,6 +506,7 @@ class EPub {
$mime = $item->attr('opf:media-type');
$this->coverpath = $item->attr('opf:href');
$this->coverpath = dirname('/'.$this->meta).'/'.$this->coverpath; // image path is relative to meta file
$this->coverpath = ltrim($this->coverpath,'\\');
$this->coverpath = ltrim($this->coverpath,'/');
}
@ -441,18 +525,6 @@ class EPub {
}
if (!$hascover) return $this->no_cover();
$zip = new ZipArchive();
if(!@$zip->open($this->file)){
throw new Exception('Failed to read epub file');
}
$data = $zip->getFromName($this->coverpath);
return array(
'mime' => $mime,
'data' => $data,
'found' => $this->coverpath
);
}
/**

View file

@ -1,7 +1,8 @@
<?php
/*
TbsZip version 2.11 (2012-02-14)
TbsZip version 2.12
Date : 2013-03-16
Author : Skrol29 (email: http://www.tinybutstrong.com/onlyyou.html)
Licence : LGPL
This class is independent from any other classes and has been originally created for the OpenTbs plug-in
@ -36,7 +37,7 @@ class clsTbsZip {
$this->CdPos = $this->CdInfo['p_cd'];
}
function Open($ArchFile) {
function Open($ArchFile, $UseIncludePath=false) {
// Open the zip archive
if (!isset($this->Meth8Ok)) $this->__construct(); // for PHP 4 compatibility
$this->Close(); // close handle and init info
@ -44,7 +45,7 @@ class clsTbsZip {
$this->ArchFile = $ArchFile;
$this->ArchIsNew = false;
// open the file
$this->ArchHnd = fopen($ArchFile, 'rb');
$this->ArchHnd = fopen($ArchFile, 'rb', $UseIncludePath);
$ok = !($this->ArchHnd===false);
if ($ok) $ok = $this->CentralDirRead();
return $ok;
@ -89,7 +90,7 @@ class clsTbsZip {
$cd_pos = -22;
$this->_MoveTo($cd_pos, SEEK_END);
$b = $this->_ReadData(4);
if ($b!==$cd_info) return $this->RaiseError('The footer of the Central Directory is not found.');
if ($b!==$cd_info) return $this->RaiseError('The End of Central Rirectory Record is not found.');
$this->CdEndPos = ftell($this->ArchHnd) - 4;
$this->CdInfo = $this->CentralDirRead_End($cd_info);
@ -97,8 +98,8 @@ class clsTbsZip {
$this->CdFileNbr = $this->CdInfo['file_nbr_curr'];
$this->CdPos = $this->CdInfo['p_cd'];
if ($this->CdFileNbr<=0) return $this->RaiseError('No file found in the Central Directory.');
if ($this->CdPos<=0) return $this->RaiseError('No position found for the Central Directory listing.');
if ($this->CdFileNbr<=0) return $this->RaiseError('No header found in the Central Directory.');
if ($this->CdPos<=0) return $this->RaiseError('No position found for the Central Directory.');
$this->_MoveTo($this->CdPos);
for ($i=0;$i<$this->CdFileNbr;$i++) {
@ -114,13 +115,13 @@ class clsTbsZip {
function CentralDirRead_End($cd_info) {
$b = $cd_info.$this->_ReadData(18);
$x = array();
$x['disk_num_curr'] = $this->_GetDec($b,4,2); // number of this disk
$x['disk_num_cd'] = $this->_GetDec($b,6,2); // number of the disk with the start of the central directory
$x['file_nbr_curr'] = $this->_GetDec($b,8,2); // total number of entries in the central directory on this disk
$x['disk_num_curr'] = $this->_GetDec($b,4,2); // number of this disk
$x['disk_num_cd'] = $this->_GetDec($b,6,2); // number of the disk with the start of the central directory
$x['file_nbr_curr'] = $this->_GetDec($b,8,2); // total number of entries in the central directory on this disk
$x['file_nbr_tot'] = $this->_GetDec($b,10,2); // total number of entries in the central directory
$x['l_cd'] = $this->_GetDec($b,12,4); // size of the central directory
$x['p_cd'] = $this->_GetDec($b,16,4); // offset of start of central directory with respect to the starting disk number
$x['l_comm'] = $this->_GetDec($b,20,2); // .ZIP file comment length
$x['p_cd'] = $this->_GetDec($b,16,4); // position of start of central directory with respect to the starting disk number
$x['l_comm'] = $this->_GetDec($b,20,2); // .ZIP file comment length
$x['v_comm'] = $this->_ReadData($x['l_comm']); // .ZIP file comment
$x['bin'] = $b.$x['v_comm'];
return $x;
@ -131,7 +132,7 @@ class clsTbsZip {
$b = $this->_ReadData(46);
$x = $this->_GetHex($b,0,4);
if ($x!=='h:02014b50') return $this->RaiseError('Signature of file information not found in the Central Directory in position '.(ftell($this->ArchHnd)-46).' for file #'.$idx.'.');
if ($x!=='h:02014b50') return $this->RaiseError("Signature of Central Directory Header #".$idx." (file information) expected but not found at position ".$this->_TxtPos(ftell($this->ArchHnd) - 46).".");
$x = array();
$x['vers_used'] = $this->_GetDec($b,4,2);
@ -169,36 +170,56 @@ class clsTbsZip {
$this->DisplayError = true;
echo "<br />\r\n";
echo "------------------<br/>\r\n";
echo "Central Directory:<br/>\r\n";
echo "------------------<br/>\r\n";
print_r($this->CdInfo);
echo "<br />\r\n";
echo "-----------------------------------<br/>\r\n";
echo "File List in the Central Directory:<br/>\r\n";
echo "-----------------------------------<br/>\r\n";
print_r($this->CdFileLst);
if ($FileHeaders) {
echo "<br/>\r\n";
echo "------------------------------<br/>\r\n";
echo "File List in the Data Section:<br/>\r\n";
echo "------------------------------<br/>\r\n";
// Calculations first in order to have error messages before other information
$idx = 0;
$pos = 0;
$pos_stop = $this->CdInfo['p_cd'];
$this->_MoveTo($pos);
while ($ok = $this->_ReadFile($idx,false)) {
$this->VisFileLst[$idx]['debug_pos'] = $pos;
while ( ($pos<$pos_stop) && ($ok = $this->_ReadFile($idx,false)) ) {
$this->VisFileLst[$idx]['p_this_header (debug_mode only)'] = $pos;
$pos = ftell($this->ArchHnd);
$idx++;
}
print_r($this->VisFileLst);
}
$nl = "\r\n";
echo "<pre>";
echo "-------------------------------".$nl;
echo "End of Central Directory record".$nl;
echo "-------------------------------".$nl;
print_r($this->DebugArray($this->CdInfo));
echo $nl;
echo "-------------------------".$nl;
echo "Central Directory headers".$nl;
echo "-------------------------".$nl;
print_r($this->DebugArray($this->CdFileLst));
if ($FileHeaders) {
echo $nl;
echo "------------------".$nl;
echo "Local File headers".$nl;
echo "------------------".$nl;
print_r($this->DebugArray($this->VisFileLst));
}
echo "</pre>";
}
function DebugArray($arr) {
foreach ($arr as $k=>$v) {
if (is_array($v)) {
$arr[$k] = $this->DebugArray($v);
} elseif (substr($k,0,2)=='p_') {
$arr[$k] = $this->_TxtPos($v);
}
}
return $arr;
}
function FileExists($NameOrIdx) {
return ($this->FileGetIdx($NameOrIdx)!==false);
}
@ -272,9 +293,9 @@ class clsTbsZip {
// read the file header (and maybe the data ) in the archive, assuming the cursor in at a new file position
$b = $this->_ReadData(30);
$x = $this->_GetHex($b,0,4);
if ($x!=='h:04034b50') return $this->RaiseError('Signature of file information not found in the Data Section in position '.(ftell($this->ArchHnd)-30).' for file #'.$idx.'.');
if ($x!=='h:04034b50') return $this->RaiseError("Signature of Local File Header #".$idx." (data section) expected but not found at position ".$this->_TxtPos(ftell($this->ArchHnd)-30).".");
$x = array();
$x['vers'] = $this->_GetDec($b,4,2);
@ -293,15 +314,20 @@ class clsTbsZip {
$x['bin'] = $b.$x['v_name'].$x['v_fields'];
// Read Data
$len_cd = $this->CdFileLst[$idx]['l_data_c'];
if ($x['l_data_c']==0) {
// Sometimes, the size is not specified in the local information.
$len = $len_cd;
if (isset($this->CdFileLst[$idx])) {
$len_cd = $this->CdFileLst[$idx]['l_data_c'];
if ($x['l_data_c']==0) {
// Sometimes, the size is not specified in the local information.
$len = $len_cd;
} else {
$len = $x['l_data_c'];
if ($len!=$len_cd) {
//echo "TbsZip Warning: Local information for file #".$idx." says len=".$len.", while Central Directory says len=".$len_cd.".";
}
}
} else {
$len = $x['l_data_c'];
if ($len!=$len_cd) {
//echo "TbsZip Warning: Local information for file #".$idx." says len=".$len.", while Central Directory says len=".$len_cd.".";
}
if ($len==0) $this->RaiseError("File Data #".$idx." cannt be read because no length is specified in the Local File Header and its Central Directory information has not been found.");
}
if ($ReadData) {
@ -309,16 +335,25 @@ class clsTbsZip {
} else {
$this->_MoveTo($len, SEEK_CUR);
}
// Description information
$desc_ok = ($x['purp'][2+3]=='1');
if ($desc_ok) {
$b = $this->_ReadData(16);
$x['desc_bin'] = $b;
$x['desc_sign'] = $this->_GetHex($b,0,4); // not specified in the documentation sign=h:08074b50
$x['desc_crc32'] = $this->_GetDec($b,4,4);
$x['desc_l_data_c'] = $this->_GetDec($b,8,4);
$x['desc_l_data_u'] = $this->_GetDec($b,12,4);
$b = $this->_ReadData(12);
$s = $this->_GetHex($b,0,4);
$d = 0;
// the specification says the signature may or may not be present
if ($s=='h:08074b50') {
$b .= $this->_ReadData(4);
$d = 4;
$x['desc_bin'] = $b;
$x['desc_sign'] = $s;
} else {
$x['desc_bin'] = $b;
}
$x['desc_crc32'] = $this->_GetDec($b,0+$d,4);
$x['desc_l_data_c'] = $this->_GetDec($b,4+$d,4);
$x['desc_l_data_u'] = $this->_GetDec($b,8+$d,4);
}
// Save file info without the data
@ -441,9 +476,10 @@ class clsTbsZip {
if ($ReplInfo['meth']!==false) $this->_PutDec($b1, $ReplInfo['meth'], 8, 2); // meth
// prepare the bottom description if the zipped file, if any
if ($b2!=='') {
$this->_PutDec($b2, $ReplInfo['crc32'], 4, 4); // crc32
$this->_PutDec($b2, $ReplInfo['len_c'], 8, 4); // l_data_c
$this->_PutDec($b2, $ReplInfo['len_u'], 12, 4); // l_data_u
$d = (strlen($b2)==16) ? 4 : 0; // offset because of the signature if any
$this->_PutDec($b2, $ReplInfo['crc32'], $d+0, 4); // crc32
$this->_PutDec($b2, $ReplInfo['len_c'], $d+4, 4); // l_data_c
$this->_PutDec($b2, $ReplInfo['len_u'], $d+8, 4); // l_data_u
}
// output data
$this->OutputFromString($b1.$ReplInfo['data'].$b2);
@ -501,7 +537,7 @@ class clsTbsZip {
$ArchPos += $old_cd_len;
$DeltaCdLen = $DeltaCdLen + strlen($b2) - $old_cd_len;
// Output until Central Directory footer
// Output until "end of central directory record"
if ($this->ArchHnd!==false) $this->OutputFromArch($ArchPos, $this->CdEndPos); // ArchHnd is false if CreateNew() has been called
// Output file information of the Central Directory for added files
@ -514,7 +550,7 @@ class clsTbsZip {
$DeltaCdLen += strlen($b2);
}
// Output Central Directory footer
// Output "end of central directory record"
$b2 = $this->CdInfo['bin'];
$DelNbr = count($DelLst);
if ( ($AddNbr>0) or ($DelNbr>0) ) {
@ -708,6 +744,11 @@ class clsTbsZip {
return $y.'-'.str_pad($m,2,'0',STR_PAD_LEFT).'-'.str_pad($d,2,'0',STR_PAD_LEFT).' '.str_pad($h,2,'0',STR_PAD_LEFT).':'.str_pad($i,2,'0',STR_PAD_LEFT).':'.str_pad($s,2,'0',STR_PAD_LEFT);
}
function _TxtPos($pos) {
// Return the human readable position in both decimal and hexa
return $pos." (h:".dechex($pos).")";
}
function _DataOuputAddedFile($Idx, $PosLoc) {
$Ref =& $this->AddInfo[$Idx];

View file

@ -30,7 +30,7 @@ class Serie extends Base {
public static function getCount() {
$nSeries = parent::getDb ()->query('select count(*) from series')->fetchColumn();
$entry = new Entry (localize("series.title"), self::ALL_SERIES_ID,
str_format (localize("series.alphabetical"), $nSeries), "text",
str_format (localize("series.alphabetical", $nSeries), $nSeries), "text",
array ( new LinkNavigation ("?page=".parent::PAGE_ALL_SERIES)));
return $entry;
}

View file

@ -224,21 +224,6 @@ display: block;
overflow: hidden;
}
#loading
{
display:none;
position:fixed;
left:0;
top:0;
width:100%;
height:100%;
background-color: #ccc;
filter: alpha(opacity=70);
opacity: 0.70;
text-align: center;
padding-top: 120px;
}
/* =============================================================================
Mediaquerie stuff goes here
========================================================================== */

View file

@ -30,7 +30,7 @@ class tag extends Base {
public static function getCount() {
$nTags = parent::getDb ()->query('select count(*) from tags')->fetchColumn();
$entry = new Entry (localize("tags.title"), self::ALL_TAGS_ID,
str_format (localize("tags.alphabetical"), $nTags), "text",
str_format (localize("tags.alphabetical", $nTags), $nTags), "text",
array ( new LinkNavigation ("?page=".parent::PAGE_ALL_TAGS)));
return $entry;
}