Merge pull request #2 from seblucas/master

update to latest official COPS
This commit is contained in:
At-Libitum 2013-11-22 12:02:35 -08:00
commit 9d4e358738
8 changed files with 141 additions and 89 deletions

View file

@ -1,3 +1,5 @@
DirectoryIndex index.php
<IfModule mod_xsendfile.c>
<Files fetch.php>
XSendFile on

View file

@ -126,15 +126,19 @@ class OPDSRenderer
self::renderLink ($link);
$link = new LinkNavigation ("?" . $_SERVER['QUERY_STRING'], "self");
self::renderLink ($link);
$urlparam = "?page=" . self::PAGE_OPENSEARCH;
$urlparam = "?";
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
$urlparam = addURLParameter ($urlparam, "page", self::PAGE_OPENSEARCH);
$link = new Link ("feed.php" . $urlparam, "application/opensearchdescription+xml", "search", "Search here");
}
else
{
// Bad way, will be removed when OPDS client are fixed
$urlparam = addURLParameter ($urlparam, "query", "{searchTerms}");
$urlparam = str_replace ("%7B", "{", $urlparam);
$urlparam = str_replace ("%7D", "}", $urlparam);
$link = new Link ($config['cops_full_url'] . 'feed.php' . $urlparam, "application/atom+xml", "search", "Search here");
}
self::renderLink ($link);

View file

@ -173,7 +173,7 @@ function localize($phrase, $count=-1, $reset=false) {
$translations = json_decode($lang_file_content, true);
/* Clean the array of all unfinished translations */
foreach ($translations as $key => $val) {
foreach (array_keys ($translations) as $key) {
if (preg_match ("/^##TODO##/", $key)) {
unset ($translations [$key]);
}
@ -441,7 +441,7 @@ class Page
$database = GetUrlParam (DB);
if (is_array ($config['calibre_directory']) && is_null ($database)) {
$i = 0;
foreach ($config['calibre_directory'] as $key => $value) {
foreach (array_keys ($config['calibre_directory']) as $key) {
$nBooks = Book::getBookCount ($i);
array_push ($this->entryArray, new Entry ($key, "cops:{$i}:catalog",
str_format (localize ("bookword", $nBooks), $nBooks), "text",
@ -471,7 +471,6 @@ class Page
public function isPaginated ()
{
global $config;
return (getCurrentOption ("max_item_per_page") != -1 &&
$this->totalNumber != -1 &&
$this->totalNumber > getCurrentOption ("max_item_per_page"));
@ -479,7 +478,6 @@ class Page
public function getNextLink ()
{
global $config;
$currentUrl = $_SERVER['QUERY_STRING'];
$currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
if (($this->n) * getCurrentOption ("max_item_per_page") < $this->totalNumber) {
@ -490,7 +488,6 @@ class Page
public function getPrevLink ()
{
global $config;
$currentUrl = $_SERVER['QUERY_STRING'];
$currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
if ($this->n > 1) {
@ -501,7 +498,6 @@ class Page
public function getMaxPage ()
{
global $config;
return ceil ($this->totalNumber / getCurrentOption ("max_item_per_page"));
}
@ -535,8 +531,6 @@ class PageAllAuthorsLetter extends Page
{
public function InitializeContent ()
{
global $config;
$this->idPage = Author::getEntryIdByLetter ($this->idGet);
$this->entryArray = Author::getAuthorsByStartingLetter ($this->idGet);
$this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("authorword", count ($this->entryArray)), count ($this->entryArray)), $this->idGet);
@ -729,7 +723,7 @@ class PageQueryResult extends Page
$this->entryArray = Author::getAuthorsByStartingLetter ('%' . $this->query);
break;
case self::SCOPE_TAG :
$this->entryArray = Tag::getAllTagsByQuery ($this->query);
list ($this->entryArray, $this->totalNumber) = Tag::getAllTagsByQuery ($this->query, -1);
break;
case self::SCOPE_SERIES :
$this->entryArray = Serie::getAllSeriesByQuery ($this->query);
@ -856,6 +850,11 @@ abstract class Base
const COMPATIBILITY_XML_ALDIKO = "aldiko";
private static $db = NULL;
public static function isMultipleDatabaseEnabled () {
global $config;
return is_array ($config['calibre_directory']);
}
public static function getDbList () {
global $config;
@ -908,11 +907,15 @@ abstract class Base
self::$db = NULL;
}
public static function executeQuery($query, $columns, $filter, $params, $n, $database = NULL) {
public static function executeQuery($query, $columns, $filter, $params, $n, $database = NULL, $numberPerPage = NULL) {
global $config;
$totalResult = -1;
if (is_null ($numberPerPage)) {
$numberPerPage = getCurrentOption ("max_item_per_page");
}
if (getCurrentOption ("max_item_per_page") != -1 && $n != -1)
if ($numberPerPage != -1 && $n != -1)
{
// First check total number of results
$result = self::getDb ($database)->prepare (str_format ($query, "count(*)", $filter));
@ -921,7 +924,8 @@ abstract class Base
// Next modify the query and params
$query .= " limit ?, ?";
array_push ($params, ($n - 1) * getCurrentOption ("max_item_per_page"), getCurrentOption ("max_item_per_page"));
array_push ($params, ($n - 1) * $numberPerPage, $numberPerPage);
}
$result = self::getDb ($database)->prepare(str_format ($query, $columns, $filter));

View file

@ -78,7 +78,6 @@ class Book extends Base {
public function __construct($line) {
global $config;
$this->id = $line->id;
$this->title = $line->title;
$this->timestamp = strtotime ($line->timestamp);
@ -177,7 +176,6 @@ class Book extends Base {
}
public function getDetailUrl ($permalink = false) {
global $config;
$urlParam = $this->getUri ();
if (!is_null (GetUrlParam (DB))) $urlParam = addURLParameter ($urlParam, DB, GetUrlParam (DB));
return 'index.php' . $urlParam;
@ -416,7 +414,6 @@ class Book extends Base {
public function getLinkArray ()
{
global $config;
$linkArray = array();
if ($this->hasCover)
@ -454,7 +451,6 @@ class Book extends Base {
}
public static function getBookCount($database = NULL) {
global $config;
$nBooks = parent::getDb ($database)->query('select count(*) from books')->fetchColumn();
return $nBooks;
}
@ -553,12 +549,12 @@ order by substr (upper (sort), 1, 1)");
return $entryArray;
}
public static function getBooksByStartingLetter($letter, $n) {
return self::getEntryArray (self::SQL_BOOKS_BY_FIRST_LETTER, array ($letter . "%"), $n);
public static function getBooksByStartingLetter($letter, $n, $database = NULL, $numberPerPage = NULL) {
return self::getEntryArray (self::SQL_BOOKS_BY_FIRST_LETTER, array ($letter . "%"), $n, $database, $numberPerPage);
}
public static function getEntryArray ($query, $params, $n, $database = NULL) {
list ($totalNumber, $result) = parent::executeQuery ($query, self::BOOK_COLUMNS, self::getFilterString (), $params, $n, $database);
public static function getEntryArray ($query, $params, $n, $database = NULL, $numberPerPage = NULL) {
list ($totalNumber, $result) = parent::executeQuery ($query, self::BOOK_COLUMNS, self::getFilterString (), $params, $n, $database, $numberPerPage);
$entryArray = array();
while ($post = $result->fetchObject ())
{
@ -571,7 +567,8 @@ order by substr (upper (sort), 1, 1)");
public static function getAllRecentBooks() {
global $config;
list ($entryArray, $totalNumber) = self::getEntryArray (self::SQL_BOOKS_RECENT . $config['cops_recentbooks_limit'], array (), -1);
$entryArray = self::getEntryArray (self::SQL_BOOKS_RECENT . $config['cops_recentbooks_limit'], array (), -1);
$entryArray = $entryArray [0];
return $entryArray;
}
@ -582,30 +579,60 @@ function getJson ($complete = false) {
$page = getURLParam ("page", Base::PAGE_INDEX);
$query = getURLParam ("query");
$search = getURLParam ("search");
$multi = getURLParam ("multi");
$qid = getURLParam ("id");
$n = getURLParam ("n", "1");
$database = GetUrlParam (DB);
if ($search) {
$out = array ();
$arrayTag = Tag::getAllTagsByQuery ($query);
$arraySeries = Serie::getAllSeriesByQuery ($query);
$arrayAuthor = Author::getAuthorsByStartingLetter ('%' . $query);
list ($arrayBook, $totalNumber) = Book::getBooksByStartingLetter ('%' . $query, -1);
$pagequery = Base::PAGE_OPENSEARCH_QUERY;
// Special case when no databases were chosen, we search on all databases
if (is_array ($config['calibre_directory']) && $multi === "1") {
$i = 0;
foreach (array_keys ($config['calibre_directory']) as $key) {
Base::clearDb ();
array_push ($out, array ("title" => $key,
"class" => "tt-header",
"navlink" => "index.php?db={$i}"));
list ($array, $total) = Book::getBooksByStartingLetter ('%' . $query, 1, $i, 5);
array_push ($out, array ("title" => str_format (localize("bookword", $total), $total),
"class" => "",
"navlink" => "index.php?page={$pagequery}&query={$query}&db={$i}&scope=book"));
$i++;
}
return $out;
}
$arrayTag = Tag::getAllTagsByQuery ($query, 1, NULL, 5);
$arraySeries = Serie::getAllSeriesByQuery ($query);
$arrayAuthor = Author::getAuthorsByStartingLetter ('%' . $query);
$arrayBook = Book::getBooksByStartingLetter ('%' . $query, 1, NULL, 5);
foreach (array ("book" => $arrayBook,
"author" => $arrayAuthor,
"series" => $arraySeries,
"tag" => $arrayTag) as $key => $array) {
$i = 0;
$pagequery = Base::PAGE_OPENSEARCH_QUERY;
if (count($array) > 0) {
if (count ($array) == 2 && is_array ($array [0])) {
$total = $array [1];
$array = $array [0];
} else {
$total = count($array);
}
if ($total > 0) {
// Comment to help the perl i18n script
// str_format (localize("bookword", count($array))
// str_format (localize("authorword", count($array)
// str_format (localize("seriesword", count($array)
// str_format (localize("tagword", count($array)
array_push ($out, array ("title" => str_format (localize("{$key}word", count($array)), count($array)),
array_push ($out, array ("title" => str_format (localize("{$key}word", $total), $total),
"class" => "tt-header",
"navlink" => "index.php?page={$pagequery}&query={$query}&db={$database}&scope={$key}"));
}
@ -636,6 +663,7 @@ function getJson ($complete = false) {
$out ["databaseId"] = GetUrlParam (DB, "");
$out ["databaseName"] = Base::getDbName ();
$out ["page"] = $page;
$out ["multipleDatabase"] = Base::isMultipleDatabaseEnabled () ? 1 : 0;
$out ["entries"] = $entries;
$out ["isPaginated"] = 0;
if ($currentPage->isPaginated ()) {

View file

@ -1,73 +1,73 @@
{
"##TODO##about.title":"About COPS",
"allbooks.alphabetical.many":"Алфавитный указатель всех {0} книг",
"##TODO##allbooks.alphabetical.none":"Alphabetical index of absolutely no book",
"##TODO##about.title":"О Программе COPS",
"allbooks.alphabetical.many":"Алфавитный указатель {0} книг",
"##TODO##allbooks.alphabetical.none":"Алфавитный указатель книг без названия",
"allbooks.alphabetical.one":"Алфавитный указатель одной книги",
"allbooks.title":"Все книги",
"authors.alphabetical.many":"Алфавитный указатель для {0} авторов",
"##TODO##authors.alphabetical.none":"Alphabetical index of absolutely no author",
"##TODO##authors.alphabetical.none":"Алфавитный указатель книг без автора",
"authors.alphabetical.one":"Алфавитный указатель для одного автора",
"authors.title":"Авторы",
"authorword.many":"{0} авторов(а)",
"authorword.none":"Нет авторов",
"authorword.none":"Нет автора",
"authorword.one":"1 автор",
"bookentry.author":"{0} из {1}",
"bookword.many":"{0} книг(и)",
"bookword.none":"Нет книг",
"bookword.one":"1 книга",
"bookword.title":"Книги",
"##TODO##cog.alternate":"Search, sort and filters",
"##TODO##cog.alternate":"Поиск, сортировка и фильтры",
"content.series":"Серии:",
"content.series.data":"Книга {0} в {1} серии",
"content.summary":"Краткое содержание:",
"##TODO##customize.email":"Set your email (to allow book emailing)",
"##TODO##customize.fancybox":"Use a Lightbox",
"##TODO##customize.filter":"Enable tag filtering",
"##TODO##customize.paging":"Max number of books per page (-1 to disable)",
"##TODO##customize.style":"Theme",
"##TODO##customize.title":"Customize COPS UI",
"##TODO##home.alternate":"Home",
"##TODO##i18n.coversection":"Cover",
"##TODO##customize.email":"Укажите Ваш email (для отправки книг по электронной почте)",
"##TODO##customize.fancybox":"Использовать Lightbox",
"##TODO##customize.filter":"Включить фильтрацию по меткам",
"##TODO##customize.paging":"Макс. число книг на странице (-1 - не ограничивать)",
"##TODO##customize.style":"Тема",
"##TODO##customize.title":"Изменить Интерфейс COPS",
"##TODO##home.alternate":"Домой",
"##TODO##i18n.coversection":"Обложка",
"language.title":"Язык",
"##TODO##languages.alphabetical.many":"Alphabetical index of the {0} languages",
"##TODO##languages.alphabetical.none":"Alphabetical index of absolutely no language",
"##TODO##languages.alphabetical.one":"Alphabetical index of the single language",
"##TODO##languages.title":"Languages",
"##TODO##mail.messagenotsent":"Message could not be sent.",
"##TODO##mail.messagesent":"Message has been sent",
"##TODO##paging.next.alternate":"Next",
"##TODO##paging.previous.alternate":"Previous",
"##TODO##permalink.alternate":"Permalink",
"##TODO##pubdate.title":"Publication year",
"##TODO##languages.alphabetical.many":"Алфавитный указатель {0} языков",
"##TODO##languages.alphabetical.none":"Алфавитный указатель {0} книг без языка",
"##TODO##languages.alphabetical.one":"Алфавитный указатель для одного языка",
"##TODO##languages.title":"Языки",
"##TODO##mail.messagenotsent":"Сообщение не может быть отправлено",
"##TODO##mail.messagesent":"Сообщение было отправлено",
"##TODO##paging.next.alternate":"След.",
"##TODO##paging.previous.alternate":"Предыд.",
"##TODO##permalink.alternate":"Постоянная Ссылка",
"##TODO##pubdate.title":"Дата публикации",
"recent.list":"{0} недавно поступивших(ие) книг(и)",
"recent.title":"Недавние поступления",
"##TODO##search.alternate":"Search",
"##TODO##search.result":"Search result for *{0}*",
"##TODO##search.result.author":"Search result for *{0}* in authors",
"##TODO##search.result.book":"Search result for *{0}* in books",
"##TODO##search.result.series":"Search result for *{0}* in series",
"##TODO##search.result.tag":"Search result for *{0}* in tags",
"##TODO##search.sortorder.asc":"Asc",
"##TODO##search.sortorder.desc":"Desc",
"##TODO##search.alternate":"Поиск",
"##TODO##search.result":"Результаты поиска для *{0}*",
"##TODO##search.result.author":"Результаты поиска для *{0}* авторов",
"##TODO##search.result.book":"Результаты поиска для *{0}* книг",
"##TODO##search.result.series":"Результаты поиска для *{0}* серий",
"##TODO##search.result.tag":"Результаты поиска для *{0}* меток",
"##TODO##search.sortorder.asc":"Возр.",
"##TODO##search.sortorder.desc":"Убыв.",
"series.alphabetical.many":"Алфавитный указатель для {0} серий",
"##TODO##series.alphabetical.none":"Alphabetical index of absolutely no series",
"##TODO##series.alphabetical.none":"Алфавитный указатель для книг без серии",
"series.alphabetical.one":"Алфавитный указатель для одной серии",
"series.title":"Серии",
"seriesword.many":"{0} серий(и)",
"seriesword.none":"Нет серий",
"seriesword.one":"1 серия",
"##TODO##sort.alternate":"Sort",
"##TODO##sort.alternate":"Сортировка",
"splitByLetter.book.other":"Другие книги",
"splitByLetter.letter":"{0} начать с {1}",
"tags.alphabetical.many":"Алфавитный указатель для {0} тэгов",
"##TODO##tags.alphabetical.none":"Alphabetical index of absolutely no tag",
"tags.alphabetical.one":"Алфавитный указатель для одного тэга",
"tags.title":"Тэги",
"tagword.many":"{0} тэгов(а)",
"tagword.none":"Нет тэгов",
"tagword.one":"1 тэг",
"tagword.title":"Тэги",
"##TODO##languages.abk":"Abkhaz",
"tags.alphabetical.many":"Алфавитный указатель для {0} меток",
"##TODO##tags.alphabetical.none":"Алфавитный указатель книг без меток",
"tags.alphabetical.one":"Алфавитный указатель для одной метки",
"tags.title":"Метки",
"tagword.many":"{0} метки(ок)",
"tagword.none":"Нет меток",
"tagword.one":"1 метка",
"tagword.title":"Метки",
"##TODO##languages.abk":"Абхазский",
"##TODO##languages.aaf":"Afar",
"##TODO##languages.afr":"Afrikaans",
"##TODO##languages.aka":"Akan",
@ -84,7 +84,7 @@
"##TODO##languages.bam":"Bambara",
"##TODO##languages.bak":"Bashkir",
"##TODO##languages.eus":"Basque",
"##TODO##languages.bel":"Belarusian",
"##TODO##languages.bel":"Белорусский",
"##TODO##languages.ben":"Bengali",
"##TODO##languages.bih":"Bihari",
"##TODO##languages.bis":"Bislama",
@ -94,10 +94,10 @@
"##TODO##languages.mya":"Burmese",
"##TODO##languages.cat":"Catalan",
"##TODO##languages.cha":"Chamorro",
"##TODO##languages.che":"Chechen",
"##TODO##languages.che":"Чеченский",
"##TODO##languages.nya":"Chichewa",
"##TODO##languages.zho":"Chinese",
"##TODO##languages.chv":"Chuvash",
"##TODO##languages.chv":"Чувашский",
"##TODO##languages.cor":"Cornish",
"##TODO##languages.cos":"Corsican",
"##TODO##languages.cre":"Cree",
@ -189,7 +189,7 @@
"##TODO##languages.chu":"Old Church Slavonic",
"##TODO##languages.orm":"Oromo",
"##TODO##languages.ori":"Oriya",
"##TODO##languages.oss":"Ossetian",
"##TODO##languages.oss":"Осетинский",
"##TODO##languages.pan":"Panjabi",
"##TODO##languages.pli":"Pāli",
"##TODO##languages.fas":"Persian",
@ -200,7 +200,7 @@
"##TODO##languages.roh":"Romansh",
"##TODO##languages.run":"Kirundi",
"##TODO##languages.ron":"Romanian",
"##TODO##languages.rus":"Russian",
"##TODO##languages.rus":"Русский",
"##TODO##languages.san":"Sanskrit",
"##TODO##languages.srd":"Sardinian",
"##TODO##languages.snd":"Sindhi",
@ -232,7 +232,7 @@
"##TODO##languages.ton":"Tonga",
"##TODO##languages.tur":"Turkish",
"##TODO##languages.tso":"Tsonga",
"##TODO##languages.tat":"Tatar",
"##TODO##languages.tat":"Татарский",
"##TODO##languages.twi":"Twi",
"##TODO##languages.tah":"Tahitian",
"##TODO##languages.uig":"Uighur",

13
tag.php
View file

@ -62,14 +62,11 @@ order by tags.name');
return $entryArray;
}
public static function getAllTagsByQuery($query) {
$result = parent::getDb ()->prepare('select tags.id as id, tags.name as name, count(*) as count
from tags, books_tags_link
where tags.id = tag and tags.name like ?
group by tags.id, tags.name
order by tags.name');
public static function getAllTagsByQuery($query, $n, $database = NULL, $numberPerPage = NULL) {
$columns = "tags.id as id, tags.name as name, (select count(*) from books_tags_link where tags.id = tag) as count";
$sql = 'select {0} from tags where tags.name like ? {1} order by tags.name';
list ($totalNumber, $result) = parent::executeQuery ($sql, $columns, "", array ('%' . $query . '%'), $n, $database, $numberPerPage);
$entryArray = array();
$result->execute (array ('%' . $query . '%'));
while ($post = $result->fetchObject ())
{
$tag = new Tag ($post->id, $post->name);
@ -77,6 +74,6 @@ order by tags.name');
str_format (localize("bookword", $post->count), $post->count), "text",
array ( new LinkNavigation ($tag->getUri ()))));
}
return $entryArray;
return array ($entryArray, $totalNumber);
}
}

View file

@ -286,9 +286,22 @@ class PageTest extends PHPUnit_Framework_TestCase
$n = "1";
$database = NULL;
$config['cops_titles_split_first_letter'] = 0;
$currentPage = Page::getPage ($page, $qid, $query, $n);
$currentPage->InitializeContent ();
$this->assertEquals ("All books", $currentPage->title);
$this->assertCount (14, $currentPage->entryArray);
$this->assertEquals ("The Adventures of Sherlock Holmes", $currentPage->entryArray [0]->title);
$this->assertEquals ("Alice's Adventures in Wonderland", $currentPage->entryArray [1]->title);
$this->assertTrue ($currentPage->ContainsBook ());
$config['cops_titles_split_first_letter'] = 1;
$currentPage = Page::getPage ($page, $qid, $query, $n);
$currentPage->InitializeContent ();
$this->assertEquals ("All books", $currentPage->title);
$this->assertCount (9, $currentPage->entryArray);
$this->assertEquals ("A", $currentPage->entryArray [0]->title);

10
util.js
View file

@ -258,7 +258,7 @@ updatePage = function (data) {
} else {
$("#sortForm").hide ();
}
$('input[name=query]').typeahead([
{
name: 'search',
@ -268,9 +268,13 @@ updatePage = function (data) {
limit: 24,
template: templateSuggestion,
remote: {
url: 'getJSON.php?search=1&db=%DB&query=%QUERY',
url: 'getJSON.php?search=1&db=%DB&query=%QUERY&multi=%MULTI',
replace: function (url, query) {
return url.replace('%QUERY', query).replace('%DB', currentData.databaseId);
var multi = 0;
if (currentData.multipleDatabase === 1 && currentData.databaseId === "") {
multi = 1;
}
return url.replace('%QUERY', query).replace('%DB', currentData.databaseId).replace ("%MULTI", multi);
}
}
}