diff --git a/CHANGELOG b/CHANGELOG index b0f6ffe..37e8eaa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,11 @@ +0.0.4 - 20120523 + * More code refactoring to simplify code. + * Changed OPDS Page id to match Calibre2Opds + * Add icons to author, serie, tags and recent items (there is config item to disable it) + * Fixed author URL + * Added publishing date (works on Mantano) + * Added Tags support + 0.0.3 - 20120507 * Fixed many things blocking opensearch from working * There was a bug introduced in 0.0.2 diff --git a/OPDS_renderer.php b/OPDS_renderer.php index 1657308..0769971 100644 --- a/OPDS_renderer.php +++ b/OPDS_renderer.php @@ -68,7 +68,7 @@ class OPDSRenderer return $xml->outputMemory(true); } - private function startXmlDocument ($title) { + private function startXmlDocument ($title, $idPage) { self::getXmlStream ()->startDocument('1.0','UTF-8'); self::getXmlStream ()->startElement ("feed"); self::getXmlStream ()->writeAttribute ("xmlns", "http://www.w3.org/2005/Atom"); @@ -80,7 +80,14 @@ class OPDSRenderer self::getXmlStream ()->text ($title); self::getXmlStream ()->endElement (); self::getXmlStream ()->startElement ("id"); - self::getXmlStream ()->text ($_SERVER['REQUEST_URI']); + if ($idPage) + { + self::getXmlStream ()->text ($idPage); + } + else + { + self::getXmlStream ()->text ($_SERVER['REQUEST_URI']); + } self::getXmlStream ()->endElement (); self::getXmlStream ()->startElement ("updated"); self::getXmlStream ()->text (self::getUpdatedTime ()); @@ -159,7 +166,7 @@ class OPDSRenderer self::getXmlStream ()->text ($author->name); self::getXmlStream ()->endElement (); self::getXmlStream ()->startElement ("uri"); - self::getXmlStream ()->text ($author->getUri ()); + self::getXmlStream ()->text ("feed.php" . $author->getUri ()); self::getXmlStream ()->endElement (); self::getXmlStream ()->endElement (); } @@ -173,12 +180,15 @@ class OPDSRenderer self::getXmlStream ()->startElement ("dcterms:issued"); self::getXmlStream ()->text (date ("Y-m-d", $entry->book->pubdate)); self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("published"); + self::getXmlStream ()->text (date ("Y-m-d", $entry->book->pubdate) . "T08:08:08Z"); + self::getXmlStream ()->endElement (); } } public function render ($page) { - self::startXmlDocument ($page->title); + self::startXmlDocument ($page->title, $page->idPage); if ($page->query) { self::getXmlStream ()->startElement ("opensearch:totalResults"); diff --git a/author.php b/author.php index 3727a8e..7412486 100644 --- a/author.php +++ b/author.php @@ -54,10 +54,10 @@ order by sort'); return $entryArray; } - public static function getAuthorName ($authorId) { + public static function getAuthorById ($authorId) { $result = parent::getDb ()->prepare('select sort from authors where id = ?'); $result->execute (array ($authorId)); - return $result->fetchColumn (); + return new Author ($authorId, $result->fetchColumn ()); } public static function getAuthorByBookId ($bookId) { diff --git a/base.php b/base.php index 878967a..bc485ca 100644 --- a/base.php +++ b/base.php @@ -15,6 +15,11 @@ function getURLParam ($name, $default = NULL) { class Link { + const OPDS_THUMBNAIL_TYPE = "http://opds-spec.org/image/thumbnail"; + const OPDS_IMAGE_TYPE = "http://opds-spec.org/image"; + const OPDS_ACQUISITION_TYPE = "http://opds-spec.org/acquisition"; + const OPDS_NAVIGATION_TYPE = "application/atom+xml;profile=opds-catalog;kind=navigation"; + public $href; public $type; public $rel; @@ -34,10 +39,8 @@ class Link class LinkNavigation extends Link { - const OPDS_NAVIGATION_TYPE = "application/atom+xml;profile=opds-catalog;kind=navigation"; - public function __construct($phref, $prel = NULL, $ptitle = NULL) { - parent::__construct ($phref, self::OPDS_NAVIGATION_TYPE, $prel, $ptitle); + parent::__construct ($phref, Link::OPDS_NAVIGATION_TYPE, $prel, $ptitle); $this->href = $_SERVER["SCRIPT_NAME"] . $this->href; } } @@ -53,6 +56,15 @@ class Entry public $localUpdated; private static $updated = NULL; + public static $icons = array( + Author::ALL_AUTHORS_ID => 'images/author.png', + Serie::ALL_SERIES_ID => 'images/serie.png', + Book::ALL_RECENT_BOOKS_ID => 'images/recent.png', + Tag::ALL_TAGS_ID => 'images/tag.png', + "calibre:books$" => 'images/allbook.png', + "calibre:books:letter" => 'images/allbook.png' + ); + public function getUpdatedTime () { if (!is_null ($this->localUpdated)) { return date (DATE_ATOM, $this->localUpdated); @@ -64,11 +76,23 @@ class Entry } public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray) { + global $config; $this->title = $ptitle; $this->id = $pid; $this->content = $pcontent; $this->contentType = $pcontentType; $this->linkArray = $plinkArray; + + if ($config['cops_show_icons'] == 1) + { + foreach (self::$icons as $reg => $image) + { + if (preg_match ("/" . $reg . "/", $pid)) { + array_push ($this->linkArray, new Link ($image, "image/png", Link::OPDS_THUMBNAIL_TYPE)); + break; + } + } + } } } @@ -84,7 +108,15 @@ class EntryBook extends Entry public function getCoverThumbnail () { foreach ($this->linkArray as $link) { - if ($link->rel == "http://opds-spec.org/image/thumbnail") + if ($link->rel == Link::OPDS_THUMBNAIL_TYPE) + return $link->hrefXhtml (); + } + return null; + } + + public function getCover () { + foreach ($this->linkArray as $link) { + if ($link->rel == Link::OPDS_IMAGE_TYPE) return $link->hrefXhtml (); } return null; @@ -106,6 +138,10 @@ class Page return new PageAllAuthors ($id, $query); case Base::PAGE_AUTHOR_DETAIL : return new PageAuthorDetail ($id, $query); + case Base::PAGE_ALL_TAGS : + return new PageAllTags ($id, $query); + case Base::PAGE_TAG_DETAIL : + return new PageTagDetail ($id, $query); case Base::PAGE_ALL_SERIES : return new PageAllSeries ($id, $query); case Base::PAGE_ALL_BOOKS : @@ -120,7 +156,9 @@ class Page return new PageQueryResult ($id, $query); break; default: - return new Page ($id, $query); + $page = new Page ($id, $query); + $page->idPage = "cops:catalog"; + return $page; } } @@ -135,6 +173,7 @@ class Page $this->title = $config['cops_title_default']; array_push ($this->entryArray, Author::getCount()); array_push ($this->entryArray, Serie::getCount()); + array_push ($this->entryArray, Tag::getCount()); $this->entryArray = array_merge ($this->entryArray, Book::getCount()); } @@ -146,6 +185,7 @@ class PageAllAuthors extends Page { $this->title = "All authors"; $this->entryArray = Author::getAllAuthors(); + $this->idPage = Author::ALL_AUTHORS_ID; } } @@ -153,17 +193,41 @@ class PageAuthorDetail extends Page { public function InitializeContent () { - $this->title = Author::getAuthorName ($this->idGet); + $author = Author::getAuthorById ($this->idGet); + $this->idPage = $author->getEntryId (); + $this->title = $author->name; $this->entryArray = Book::getBooksByAuthor ($this->idGet); } } +class PageAllTags extends Page +{ + public function InitializeContent () + { + $this->title = "All tags"; + $this->entryArray = Tag::getAllTags(); + $this->idPage = Tag::ALL_TAGS_ID; + } +} + +class PageTagDetail extends Page +{ + public function InitializeContent () + { + $tag = Tag::getTagById ($this->idGet); + $this->idPage = $tag->getEntryId (); + $this->title = $tag->name; + $this->entryArray = Book::getBooksByTag ($this->idGet); + } +} + class PageAllSeries extends Page { public function InitializeContent () { $this->title = "All series"; $this->entryArray = Serie::getAllSeries(); + $this->idPage = Serie::ALL_SERIES_ID; } } @@ -171,8 +235,10 @@ class PageSerieDetail extends Page { public function InitializeContent () { - $this->title = "Series : " . Serie::getSerieById ($this->idGet)->name; + $serie = Serie::getSerieById ($this->idGet); + $this->title = "Series : " . $serie->name; $this->entryArray = Book::getBooksBySeries ($this->idGet); + $this->idPage = $serie->getEntryId (); } } @@ -182,6 +248,7 @@ class PageAllBooks extends Page { $this->title = "All books by starting letter"; $this->entryArray = Book::getAllBooks (); + $this->idPage = Book::ALL_BOOKS_ID; } } @@ -191,6 +258,7 @@ class PageAllBooksLetter extends Page { $this->title = "All books starting by " . $this->idGet; $this->entryArray = Book::getBooksByStartingLetter ($this->idGet); + $this->idPage = Book::getEntryIdByLetter ($this->idGet); } } @@ -200,6 +268,7 @@ class PageRecentBooks extends Page { $this->title = "Most recent books"; $this->entryArray = Book::getAllRecentBooks (); + $this->idPage = Book::ALL_RECENT_BOOKS_ID; } } @@ -207,7 +276,7 @@ class PageQueryResult extends Page { public function InitializeContent () { - $this->title = "Search result for query <" . $this->query . ">"; + $this->title = "Search result for query *" . $this->query . "*"; $this->entryArray = Book::getBooksByQuery ($this->query); } } @@ -225,6 +294,9 @@ abstract class Base const PAGE_OPENSEARCH = "8"; const PAGE_OPENSEARCH_QUERY = "9"; const PAGE_ALL_RECENT_BOOKS = "10"; + const PAGE_ALL_TAGS = "11"; + const PAGE_TAG_DETAIL = "12"; + const COMPATIBILITY_XML_ALDIKO = "aldiko"; private static $db = NULL; diff --git a/book.php b/book.php index 808f11f..2725807 100644 --- a/book.php +++ b/book.php @@ -13,6 +13,7 @@ require_once('author.php'); class Book extends Base { const ALL_BOOKS_ID = "calibre:books"; const ALL_RECENT_BOOKS_ID = "calibre:recentbooks"; + const BOOK_COLUMNS = "books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index"; public $id; public $title; @@ -31,24 +32,27 @@ class Book extends Base { 'mobi' => 'application/x-mobipocket-ebook', 'pdf' => 'application/pdf' ); - - - public function __construct($pid, $ptitle, $ptimestamp, $ppubdate, $ppath, $pseriesIndex, $pcomment) { + + public function __construct($line) { global $config; - $this->id = $pid; - $this->title = $ptitle; - $this->timestamp = strtotime ($ptimestamp); - $this->pubdate = strtotime ($ppubdate); - $this->path = $config['calibre_directory'] . $ppath; - $this->relativePath = $ppath; - $this->seriesIndex = $pseriesIndex; - $this->comment = $pcomment; + $this->id = $line->id; + $this->title = $line->title; + $this->timestamp = strtotime ($line->timestamp); + $this->pubdate = strtotime ($line->pubdate); + $this->path = $config['calibre_directory'] . $line->path; + $this->relativePath = $line->path; + $this->seriesIndex = $line->series_index; + $this->comment = $line->comment; } public function getEntryId () { return self::ALL_BOOKS_ID.":".$this->id; } + public static function getEntryIdByLetter ($startingLetter) { + return self::ALL_BOOKS_ID.":letter:".$startingLetter; + } + public function getTitle () { return $this->title; } @@ -150,13 +154,21 @@ class Book extends Base { if (preg_match ('/jpg$/', $file)) { if (preg_match ('/^\//', $config['calibre_directory'])) { - array_push ($linkArray, new Link ("fetch.php?id=$this->id", "image/jpeg", "http://opds-spec.org/image")); + array_push ($linkArray, new Link ("fetch.php?id=$this->id", "image/jpeg", Link::OPDS_IMAGE_TYPE)); } else { - array_push ($linkArray, new Link (str_replace('%2F','/',rawurlencode ($this->path."/".$file)), "image/jpeg", "http://opds-spec.org/image")); + array_push ($linkArray, new Link (str_replace('%2F','/',rawurlencode ($this->path."/".$file)), "image/jpeg", Link::OPDS_IMAGE_TYPE)); } - array_push ($linkArray, new Link ("fetch.php?id=$this->id&height=70", "image/jpeg", "http://opds-spec.org/image/thumbnail")); + $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)); } foreach (self::$mimetypes as $ext => $mime) { @@ -164,11 +176,11 @@ class Book extends Base { $this->format [$ext] = $file; if (preg_match ('/^\//', $config['calibre_directory'])) { - array_push ($linkArray, new Link ("fetch.php?id=$this->id&type=" . $ext, $mime, "http://opds-spec.org/acquisition", "Download")); + array_push ($linkArray, new Link ("fetch.php?id=$this->id&type=" . $ext, $mime, Link::OPDS_ACQUISITION_TYPE, "Download")); } else { - array_push ($linkArray, new Link (str_replace('%2F','/',rawurlencode ($this->path."/".$file)), $mime, "http://opds-spec.org/acquisition", "Download")); + array_push ($linkArray, new Link (str_replace('%2F','/',rawurlencode ($this->path."/".$file)), $mime, Link::OPDS_ACQUISITION_TYPE, "Download")); } } } @@ -212,7 +224,7 @@ class Book extends Base { } public static function getBooksByAuthor($authorId) { - $result = parent::getDb ()->prepare('select books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index + $result = parent::getDb ()->prepare('select ' . self::BOOK_COLUMNS . ' from books_authors_link, books left outer join comments on comments.book = books.id where books_authors_link.book = books.id and author = ? @@ -221,7 +233,7 @@ order by pubdate'); $result->execute (array ($authorId)); while ($post = $result->fetchObject ()) { - $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); + $book = new Book ($post); array_push ($entryArray, $book->getEntry ()); } return $entryArray; @@ -229,7 +241,7 @@ order by pubdate'); public static function getBooksBySeries($serieId) { - $result = parent::getDb ()->prepare('select books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index + $result = parent::getDb ()->prepare('select ' . self::BOOK_COLUMNS . ' from books_series_link, books left outer join comments on comments.book = books.id where books_series_link.book = books.id and series = ? order by series_index'); @@ -237,28 +249,43 @@ order by series_index'); $result->execute (array ($serieId)); while ($post = $result->fetchObject ()) { - $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); + $book = new Book ($post); + array_push ($entryArray, $book->getEntry ()); + } + return $entryArray; + } + + public static function getBooksByTag($tagId) { + $result = parent::getDb ()->prepare('select ' . self::BOOK_COLUMNS . ' +from books_tags_link, books left outer join comments on comments.book = books.id +where books_tags_link.book = books.id and tag = ? +order by sort'); + $entryArray = array(); + $result->execute (array ($tagId)); + while ($post = $result->fetchObject ()) + { + $book = new Book ($post); array_push ($entryArray, $book->getEntry ()); } return $entryArray; } public static function getBookById($bookId) { - $result = parent::getDb ()->prepare('select books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index + $result = parent::getDb ()->prepare('select ' . self::BOOK_COLUMNS . ' from books left outer join comments on book = books.id where books.id = ?'); $entryArray = array(); $result->execute (array ($bookId)); while ($post = $result->fetchObject ()) { - $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); + $book = new Book ($post); return $book; } return NULL; } public static function getBooksByQuery($query) { - $result = parent::getDb ()->prepare("select books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index + $result = parent::getDb ()->prepare("select " . self::BOOK_COLUMNS . " from books left outer join comments on book = books.id where exists (select null from authors, books_authors_link where book = books.id and author = authors.id and authors.name like ?) or title like ?"); @@ -267,7 +294,7 @@ or title like ?"); $result->execute (array ($queryLike, $queryLike)); while ($post = $result->fetchObject ()) { - $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); + $book = new Book ($post); array_push ($entryArray, $book->getEntry ()); } return $entryArray; @@ -281,7 +308,7 @@ order by substr (upper (sort), 1, 1)"); $entryArray = array(); while ($post = $result->fetchObject ()) { - array_push ($entryArray, new Entry ($post->title, "allbooks_" . $post->title, + array_push ($entryArray, new Entry ($post->title, Book::getEntryIdByLetter ($post->title), "$post->count books", "text", array ( new LinkNavigation ("?page=".parent::PAGE_ALL_BOOKS_LETTER."&id=".$post->title)))); } @@ -289,7 +316,7 @@ order by substr (upper (sort), 1, 1)"); } public static function getBooksByStartingLetter($letter) { - $result = parent::getDb ()->prepare('select books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index + $result = parent::getDb ()->prepare('select ' . self::BOOK_COLUMNS . ' from books left outer join comments on book = books.id where upper (books.sort) like ?'); $entryArray = array(); @@ -297,7 +324,7 @@ where upper (books.sort) like ?'); $result->execute (array ($queryLike)); while ($post = $result->fetchObject ()) { - $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); + $book = new Book ($post); array_push ($entryArray, $book->getEntry ()); } return $entryArray; @@ -306,13 +333,13 @@ where upper (books.sort) like ?'); public static function getAllRecentBooks() { global $config; - $result = parent::getDb ()->query("select books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index + $result = parent::getDb ()->query("select " . self::BOOK_COLUMNS . " from books left outer join comments on book = books.id order by timestamp desc limit " . $config['cops_recentbooks_limit']); $entryArray = array(); while ($post = $result->fetchObject ()) { - $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); + $book = new Book ($post); array_push ($entryArray, $book->getEntry ()); } return $entryArray; diff --git a/config_default.php b/config_default.php index 88f762a..d0fb5a5 100644 --- a/config_default.php +++ b/config_default.php @@ -49,4 +49,21 @@ * X-Sendfile : For Lightttpd or Apache (with mod_xsendfile) */ $config['cops_x_accel_redirect'] = "X-Accel-Redirect"; + + /* + * Height of thumbnail image for OPDS + */ + $config['cops_opds_thumbnail_height'] = "40"; + + /* + * Height of thumbnail image for HTML + */ + $config['cops_html_thumbnail_height'] = "70"; + + /* + * Show icon for authors, series, tags and books on OPDS feed + * 1 : enable + * 0 : disable + */ + $config['cops_show_icons'] = "1"; ?> \ No newline at end of file diff --git a/feed.php b/feed.php index f2348c7..28f1317 100644 --- a/feed.php +++ b/feed.php @@ -11,6 +11,7 @@ require_once ("base.php"); require_once ("author.php"); require_once ("serie.php"); + require_once ("tag.php"); require_once ("book.php"); require_once ("OPDS_renderer.php"); diff --git a/images/ajax-loader.gif b/images/ajax-loader.gif new file mode 100644 index 0000000..1560b64 Binary files /dev/null and b/images/ajax-loader.gif differ diff --git a/images/allbook.png b/images/allbook.png new file mode 100644 index 0000000..7d863f9 Binary files /dev/null and b/images/allbook.png differ diff --git a/images/author.png b/images/author.png new file mode 100644 index 0000000..79f35cc Binary files /dev/null and b/images/author.png differ diff --git a/images/home.png b/images/home.png new file mode 100644 index 0000000..2b36f75 Binary files /dev/null and b/images/home.png differ diff --git a/images/recent.png b/images/recent.png new file mode 100644 index 0000000..783c833 Binary files /dev/null and b/images/recent.png differ diff --git a/images/search.png b/images/search.png new file mode 100644 index 0000000..aacf805 Binary files /dev/null and b/images/search.png differ diff --git a/images/serie.png b/images/serie.png new file mode 100644 index 0000000..0f9ed4d Binary files /dev/null and b/images/serie.png differ diff --git a/images/tag.png b/images/tag.png new file mode 100644 index 0000000..9757fc6 Binary files /dev/null and b/images/tag.png differ diff --git a/serie.php b/serie.php index be03a91..1de7c3a 100644 --- a/serie.php +++ b/serie.php @@ -22,6 +22,10 @@ class Serie extends Base { public function getUri () { return "?page=".parent::PAGE_SERIE_DETAIL."&id=$this->id"; } + + public function getEntryId () { + return self::ALL_SERIES_ID.":".$this->id; + } public static function getCount() { $nSeries = parent::getDb ()->query('select count(*) from series')->fetchColumn(); @@ -60,9 +64,10 @@ order by series.sort'); $entryArray = array(); while ($post = $result->fetchObject ()) { - array_push ($entryArray, new Entry ($post->sort, self::ALL_SERIES_ID.":".$post->id, + $serie = new Serie ($post->id, $post->sort); + array_push ($entryArray, new Entry ($serie->name, $serie->getEntryId (), "$post->count books", "text", - array ( new LinkNavigation ("?page=".parent::PAGE_SERIE_DETAIL."&id=$post->id")))); + array ( new LinkNavigation ($serie->getUri ())))); } return $entryArray; } diff --git a/tag.php b/tag.php new file mode 100644 index 0000000..066934a --- /dev/null +++ b/tag.php @@ -0,0 +1,64 @@ + + */ + +require_once('base.php'); + +class tag extends Base { + const ALL_TAGS_ID = "calibre:tags"; + + public $id; + public $name; + + public function __construct($pid, $pname) { + $this->id = $pid; + $this->name = $pname; + } + + public function getUri () { + return "?page=".parent::PAGE_TAG_DETAIL."&id=$this->id"; + } + + public function getEntryId () { + return self::ALL_TAGS_ID.":".$this->id; + } + + public static function getCount() { + $nTags = parent::getDb ()->query('select count(*) from tags')->fetchColumn(); + $entry = new Entry ("Tags", self::ALL_TAGS_ID, + "Alphabetical index of the $nTags tags", "text", + array ( new LinkNavigation ("?page=".parent::PAGE_ALL_TAGS))); + return $entry; + } + + public static function getTagById ($tagId) { + $result = parent::getDb ()->prepare('select id, name from tags where id = ?'); + $result->execute (array ($tagId)); + if ($post = $result->fetchObject ()) { + return new Tag ($post->id, $post->name); + } + return NULL; + } + + public static function getAllTags() { + $result = parent::getDb ()->query('select tags.id as id, tags.name as name, count(*) as count +from tags, books_tags_link +where tags.id = tag +group by tags.id, tags.name +order by tags.name'); + $entryArray = array(); + while ($post = $result->fetchObject ()) + { + $tag = new Tag ($post->id, $post->name); + array_push ($entryArray, new Entry ($tag->name, $tag->getEntryId (), + "$post->count books", "text", + array ( new LinkNavigation ($tag->getUri ())))); + } + return $entryArray; + } +} +?> \ No newline at end of file