1) Исправления в связи со сменой API MySQL
[openlib.git] / www / OPDS_renderer.php
1 <?php
2 /**
3  * COPS (Calibre OPDS PHP Server) class file
4  *
5  * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6  * @author     Sébastien Lucas <sebastien@slucas.fr>
7  */
8
9 require_once ("base.php");
10
11 class OPDSRenderer
12 {
13     const PAGE_OPENSEARCH = "8";
14     const PAGE_OPENSEARCH_QUERY = "9";
15
16     private $xmlStream = NULL;
17     private $updated = NULL;
18
19     private function getUpdatedTime () {
20         if (is_null ($this->updated)) {
21             $this->updated = time();
22         }
23         return date (DATE_ATOM, $this->updated);
24     }
25
26     private function getXmlStream () {
27         if (is_null ($this->xmlStream)) {
28             $this->xmlStream = new XMLWriter();
29             $this->xmlStream->openMemory();
30             $this->xmlStream->setIndent (true);
31         }
32         return $this->xmlStream;
33     }
34
35     public function getOpenSearch () {
36         global $config;
37         $xml = new XMLWriter ();
38         $xml->openMemory ();
39         $xml->setIndent (true);
40         $xml->startDocument('1.0','UTF-8');
41             $xml->startElement ("OpenSearchDescription");
42                 $xml->writeAttribute ("xmlns", "http://a9.com/-/spec/opensearch/1.1/");
43                 $xml->startElement ("ShortName");
44                     $xml->text ("My catalog");
45                 $xml->endElement ();
46                 $xml->startElement ("Description");
47                     $xml->text ("Search for ebooks");
48                 $xml->endElement ();
49                 $xml->startElement ("InputEncoding");
50                     $xml->text ("UTF-8");
51                 $xml->endElement ();
52                 $xml->startElement ("OutputEncoding");
53                     $xml->text ("UTF-8");
54                 $xml->endElement ();
55                 $xml->startElement ("Image");
56                     $xml->writeAttribute ("type", "image/x-icon");
57                     $xml->writeAttribute ("width", "16");
58                     $xml->writeAttribute ("height", "16");
59                     $xml->text ($config['cops_icon']);
60                 $xml->endElement ();
61                 $xml->startElement ("Url");
62                     $xml->writeAttribute ("type", 'application/atom+xml');
63                     $urlparam = "?query={searchTerms}";
64                     if (!is_null (GetUrlParam (DB))) $urlparam = addURLParameter ($urlparam, DB, GetUrlParam (DB));
65                     $urlparam = str_replace ("%7B", "{", $urlparam);
66                     $urlparam = str_replace ("%7D", "}", $urlparam);
67                     $xml->writeAttribute ("template", $config['cops_full_url'] . 'feed.php' . $urlparam);
68                 $xml->endElement ();
69                 $xml->startElement ("Query");
70                     $xml->writeAttribute ("role", "example");
71                     $xml->writeAttribute ("searchTerms", "robot");
72                 $xml->endElement ();
73             $xml->endElement ();
74         $xml->endDocument();
75         return $xml->outputMemory(true);
76     }
77
78     private function startXmlDocument ($page) {
79         global $config;
80         self::getXmlStream ()->startDocument('1.0','UTF-8');
81         self::getXmlStream ()->startElement ("feed");
82             self::getXmlStream ()->writeAttribute ("xmlns", "http://www.w3.org/2005/Atom");
83             self::getXmlStream ()->writeAttribute ("xmlns:xhtml", "http://www.w3.org/1999/xhtml");
84             self::getXmlStream ()->writeAttribute ("xmlns:opds", "http://opds-spec.org/2010/catalog");
85             self::getXmlStream ()->writeAttribute ("xmlns:opensearch", "http://a9.com/-/spec/opensearch/1.1/");
86             self::getXmlStream ()->writeAttribute ("xmlns:dcterms", "http://purl.org/dc/terms/");
87             self::getXmlStream ()->startElement ("title");
88                 self::getXmlStream ()->text ($page->title);
89             self::getXmlStream ()->endElement ();
90             if ($page->subtitle != "")
91             {
92                 self::getXmlStream ()->startElement ("subtitle");
93                     self::getXmlStream ()->text ($page->subtitle);
94                 self::getXmlStream ()->endElement ();
95             }
96             self::getXmlStream ()->startElement ("id");
97                 if ($page->idPage)
98                 {
99                     $idPage = $page->idPage;
100                     if (!is_null (GetUrlParam (DB))) $idPage = str_replace ("cops:", "cops:" . GetUrlParam (DB) . ":", $idPage);
101                     self::getXmlStream ()->text ($idPage);
102                 }
103                 else
104                 {
105                     self::getXmlStream ()->text ($_SERVER['REQUEST_URI']);
106                 }
107             self::getXmlStream ()->endElement ();
108             self::getXmlStream ()->startElement ("updated");
109                 self::getXmlStream ()->text (self::getUpdatedTime ());
110             self::getXmlStream ()->endElement ();
111             self::getXmlStream ()->startElement ("icon");
112                 self::getXmlStream ()->text ($page->favicon);
113             self::getXmlStream ()->endElement ();
114             self::getXmlStream ()->startElement ("author");
115                 self::getXmlStream ()->startElement ("name");
116                     self::getXmlStream ()->text ($page->authorName);
117                 self::getXmlStream ()->endElement ();
118                 self::getXmlStream ()->startElement ("uri");
119                     self::getXmlStream ()->text ($page->authorUri);
120                 self::getXmlStream ()->endElement ();
121                 self::getXmlStream ()->startElement ("email");
122                     self::getXmlStream ()->text ($page->authorEmail);
123                 self::getXmlStream ()->endElement ();
124             self::getXmlStream ()->endElement ();
125             $link = new LinkNavigation ("", "start", "Home");
126             self::renderLink ($link);
127             $link = new LinkNavigation ("?" . $_SERVER['QUERY_STRING'], "self");
128             self::renderLink ($link);
129             $urlparam = "?";
130             if (!is_null (GetUrlParam (DB))) $urlparam = addURLParameter ($urlparam, DB, GetUrlParam (DB));
131             if ($config['cops_generate_invalid_opds_stream'] == 0 || preg_match("/(MantanoReader|FBReader)/", $_SERVER['HTTP_USER_AGENT'])) {
132                 // Good and compliant way of handling search
133                 $urlparam = addURLParameter ($urlparam, "page", self::PAGE_OPENSEARCH);
134                 $link = new Link ("feed.php" . $urlparam, "application/opensearchdescription+xml", "search", "Search here");
135             }
136             else
137             {
138                 // Bad way, will be removed when OPDS client are fixed
139                 $urlparam = addURLParameter ($urlparam, "query", "{searchTerms}");
140                 $urlparam = str_replace ("%7B", "{", $urlparam);
141                 $urlparam = str_replace ("%7D", "}", $urlparam);
142                 $link = new Link ($config['cops_full_url'] . 'feed.php' . $urlparam, "application/atom+xml", "search", "Search here");
143             }
144             self::renderLink ($link);
145             if ($page->containsBook () && !is_null ($config['cops_books_filter']) && count ($config['cops_books_filter']) > 0) {
146                 $Urlfilter = getURLParam ("tag", "");
147                 foreach ($config['cops_books_filter'] as $lib => $filter) {
148                     $link = new LinkFacet ("?" . addURLParameter ($_SERVER['QUERY_STRING'], "tag", $filter), $lib, localize ("tagword.title"), $filter == $Urlfilter);
149                     self::renderLink ($link);
150                 }
151             }
152     }
153
154     private function endXmlDocument () {
155         self::getXmlStream ()->endElement ();
156         self::getXmlStream ()->endDocument ();
157         return self::getXmlStream ()->outputMemory(true);
158     }
159
160     private function renderLink ($link) {
161         self::getXmlStream ()->startElement ("link");
162             self::getXmlStream ()->writeAttribute ("href", $link->href);
163             self::getXmlStream ()->writeAttribute ("type", $link->type);
164             if (!is_null ($link->rel)) {
165                 self::getXmlStream ()->writeAttribute ("rel", $link->rel);
166             }
167             if (!is_null ($link->title)) {
168                 self::getXmlStream ()->writeAttribute ("title", $link->title);
169             }
170             if (!is_null ($link->facetGroup)) {
171                 self::getXmlStream ()->writeAttribute ("opds:facetGroup", $link->facetGroup);
172             }
173             if ($link->activeFacet) {
174                 self::getXmlStream ()->writeAttribute ("opds:activeFacet", "true");
175             }
176         self::getXmlStream ()->endElement ();
177     }
178
179
180     private function renderEntry ($entry) {
181         self::getXmlStream ()->startElement ("title");
182             self::getXmlStream ()->text ($entry->title);
183         self::getXmlStream ()->endElement ();
184         self::getXmlStream ()->startElement ("updated");
185             self::getXmlStream ()->text (self::getUpdatedTime ());
186         self::getXmlStream ()->endElement ();
187         self::getXmlStream ()->startElement ("id");
188             self::getXmlStream ()->text ($entry->id);
189         self::getXmlStream ()->endElement ();
190         self::getXmlStream ()->startElement ("content");
191             self::getXmlStream ()->writeAttribute ("type", $entry->contentType);
192             if ($entry->contentType == "text") {
193                 self::getXmlStream ()->text ($entry->content);
194             } else {
195                 self::getXmlStream ()->writeRaw ($entry->content);
196             }
197         self::getXmlStream ()->endElement ();
198         foreach ($entry->linkArray as $link) {
199             self::renderLink ($link);
200         }
201
202         if (get_class ($entry) != "EntryBook") {
203             return;
204         }
205
206         foreach ($entry->book->getAuthors () as $author) {
207             self::getXmlStream ()->startElement ("author");
208                 self::getXmlStream ()->startElement ("name");
209                     self::getXmlStream ()->text ($author->name);
210                 self::getXmlStream ()->endElement ();
211                 self::getXmlStream ()->startElement ("uri");
212                     self::getXmlStream ()->text ("feed.php" . $author->getUri ());
213                 self::getXmlStream ()->endElement ();
214             self::getXmlStream ()->endElement ();
215         }
216         foreach ($entry->book->getTags () as $category) {
217             self::getXmlStream ()->startElement ("category");
218                 self::getXmlStream ()->writeAttribute ("term", $category->name);
219                 self::getXmlStream ()->writeAttribute ("label", $category->name);
220             self::getXmlStream ()->endElement ();
221         }
222         if ($entry->book->getPubDate () != "") {
223             self::getXmlStream ()->startElement ("dcterms:issued");
224                 self::getXmlStream ()->text (date ("Y-m-d", $entry->book->pubdate));
225             self::getXmlStream ()->endElement ();
226             self::getXmlStream ()->startElement ("published");
227                 self::getXmlStream ()->text (date ("Y-m-d", $entry->book->pubdate) . "T08:08:08Z");
228             self::getXmlStream ()->endElement ();
229         }
230
231         $lang = $entry->book->getLanguages ();
232         if (!empty ($lang)) {
233             self::getXmlStream ()->startElement ("dcterms:language");
234                 self::getXmlStream ()->text ($lang);
235             self::getXmlStream ()->endElement ();
236         }
237
238     }
239
240     public function render ($page) {
241         global $config;
242         self::startXmlDocument ($page);
243         if ($page->isPaginated ())
244         {
245             self::getXmlStream ()->startElement ("opensearch:totalResults");
246                 self::getXmlStream ()->text ($page->totalNumber);
247             self::getXmlStream ()->endElement ();
248             self::getXmlStream ()->startElement ("opensearch:itemsPerPage");
249                 self::getXmlStream ()->text ($config['cops_max_item_per_page']);
250             self::getXmlStream ()->endElement ();
251             self::getXmlStream ()->startElement ("opensearch:startIndex");
252                 self::getXmlStream ()->text (($page->n - 1) * $config['cops_max_item_per_page'] + 1);
253             self::getXmlStream ()->endElement ();
254             $prevLink = $page->getPrevLink ();
255             $nextLink = $page->getNextLink ();
256             if (!is_null ($prevLink)) {
257                 self::renderLink ($prevLink);
258             }
259             if (!is_null ($nextLink)) {
260                 self::renderLink ($nextLink);
261             }
262         }
263         foreach ($page->entryArray as $entry) {
264             self::getXmlStream ()->startElement ("entry");
265                 self::renderEntry ($entry);
266             self::getXmlStream ()->endElement ();
267         }
268         return self::endXmlDocument ();
269     }
270 }
271