3 * COPS (Calibre OPDS PHP Server) class file
5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author Sébastien Lucas <sebastien@slucas.fr>
9 define ("VERSION", "0.6.1");
10 date_default_timezone_set($config['default_timezone']);
12 function getURLParam ($name, $default = NULL) {
13 if (!empty ($_GET) && isset($_GET[$name]) && $_GET[$name] != "") {
19 function getCurrentOption ($option) {
21 if (isset($_COOKIE[$option])) {
22 return $_COOKIE[$option];
24 if ($option == "style") {
28 if (isset($config ["cops_" . $option])) {
29 return $config ["cops_" . $option];
35 function getCurrentCss () {
36 return "styles/style-" . getCurrentOption ("style") . ".css";
39 function getUrlWithVersion ($url) {
40 return $url . "?v=" . VERSION;
43 function xml2xhtml($xml) {
44 return preg_replace_callback('#<(\w+)([^>]*)\s*/>#s', create_function('$m', '
45 $xhtml_tags = array("br", "hr", "input", "frame", "img", "area", "link", "col", "base", "basefont", "param");
46 return in_array($m[1], $xhtml_tags) ? "<$m[1]$m[2] />" : "<$m[1]$m[2]></$m[1]>";
50 function display_xml_error($error)
52 $return .= str_repeat('-', $error->column) . "^\n";
54 switch ($error->level) {
55 case LIBXML_ERR_WARNING:
56 $return .= "Warning $error->code: ";
58 case LIBXML_ERR_ERROR:
59 $return .= "Error $error->code: ";
61 case LIBXML_ERR_FATAL:
62 $return .= "Fatal Error $error->code: ";
66 $return .= trim($error->message) .
67 "\n Line: $error->line" .
68 "\n Column: $error->column";
71 $return .= "\n File: $error->file";
74 return "$return\n\n--------------------------------------------\n\n";
77 function are_libxml_errors_ok ()
79 $errors = libxml_get_errors();
81 foreach ($errors as $error) {
82 if ($error->code == 801) return false;
87 function html2xhtml ($html) {
88 $doc = new DOMDocument();
89 libxml_use_internal_errors(true);
91 $doc->loadHTML('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' .
92 $html . '</body></html>'); // Load the HTML
93 $output = $doc->saveXML($doc->documentElement); // Transform to an Ansi xml stream
94 $output = xml2xhtml($output);
95 if (preg_match ('#<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></meta></head><body>(.*)</body></html>#ms', $output, $matches)) {
96 $output = $matches [1]; // Remove <html><body>
99 // In case of error with summary, use it to debug
100 $errors = libxml_get_errors();
102 foreach ($errors as $error) {
103 $output .= display_xml_error($error);
107 if (!are_libxml_errors_ok ()) $output = "HTML code not valid.";
109 libxml_use_internal_errors(false);
114 * This method is a direct copy-paste from
115 * http://tmont.com/blargh/2010/1/string-format-in-php
117 function str_format($format) {
118 $args = func_get_args();
119 $format = array_shift($args);
121 preg_match_all('/(?=\{)\{(\d+)\}(?!\})/', $format, $matches, PREG_OFFSET_CAPTURE);
123 foreach ($matches[1] as $data) {
125 $format = substr_replace($format, @$args[$i], $offset + $data[1] - 1, 2 + strlen($i));
126 $offset += strlen(@$args[$i]) - 2 - strlen($i);
133 * This method is based on this page
134 * http://www.mind-it.info/2010/02/22/a-simple-approach-to-localization-in-php/
136 function localize($phrase, $count=-1) {
145 /* Static keyword is used to ensure the file is loaded only once */
146 static $translations = NULL;
147 /* If no instance of $translations has occured load the language file */
148 if (is_null($translations)) {
150 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
152 $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
154 $lang_file_en = NULL;
155 $lang_file = 'lang/Localization_' . $lang . '.json';
156 if (!file_exists($lang_file)) {
157 $lang_file = 'lang/' . 'Localization_en.json';
159 elseif ($lang != "en") {
160 $lang_file_en = 'lang/' . 'Localization_en.json';
162 $lang_file_content = file_get_contents($lang_file);
163 /* Load the language file as a JSON object and transform it into an associative array */
164 $translations = json_decode($lang_file_content, true);
167 $lang_file_content = file_get_contents($lang_file_en);
168 $translations_en = json_decode($lang_file_content, true);
169 $translations = array_merge ($translations_en, $translations);
172 if (array_key_exists ($phrase, $translations)) {
173 return $translations[$phrase];
178 function addURLParameter($urlParams, $paramName, $paramValue) {
180 if (preg_match ("#^\?(.*)#", $urlParams, $matches)) {
182 $urlParams = $matches[1];
185 parse_str($urlParams, $params);
186 if (empty ($paramValue) && $paramValue != 0) {
187 unset ($params[$paramName]);
189 $params[$paramName] = $paramValue;
191 return $start . http_build_query($params);
196 const OPDS_THUMBNAIL_TYPE = "http://opds-spec.org/image/thumbnail";
197 const OPDS_IMAGE_TYPE = "http://opds-spec.org/image";
198 const OPDS_ACQUISITION_TYPE = "http://opds-spec.org/acquisition/open-access";
199 const OPDS_NAVIGATION_TYPE = "application/atom+xml;profile=opds-catalog;kind=navigation";
200 const OPDS_PAGING_TYPE = "application/atom+xml;profile=opds-catalog;kind=acquisition";
209 public function __construct($phref, $ptype, $prel = NULL, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
210 $this->href = $phref;
211 $this->type = $ptype;
213 $this->title = $ptitle;
214 $this->facetGroup = $pfacetGroup;
215 $this->activeFacet = $pactiveFacet;
218 public function hrefXhtml () {
223 class LinkNavigation extends Link
225 public function __construct($phref, $prel = NULL, $ptitle = NULL) {
226 parent::__construct ($phref, Link::OPDS_NAVIGATION_TYPE, $prel, $ptitle);
227 if (!preg_match ("#^\?(.*)#", $this->href) && !empty ($this->href)) $this->href = "?" . $this->href;
228 if (preg_match ("/(bookdetail|getJSON).php/", $_SERVER["SCRIPT_NAME"])) {
229 $this->href = "index.php" . $this->href;
231 $this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
236 class LinkFacet extends Link
238 public function __construct($phref, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
239 parent::__construct ($phref, Link::OPDS_PAGING_TYPE, "http://opds-spec.org/facet", $ptitle, $pfacetGroup, $pactiveFacet);
240 $this->href = $_SERVER["SCRIPT_NAME"] . $this->href;
251 public $localUpdated;
252 private static $updated = NULL;
254 public static $icons = array(
255 Author::ALL_AUTHORS_ID => 'images/author.png',
256 Serie::ALL_SERIES_ID => 'images/serie.png',
257 Book::ALL_RECENT_BOOKS_ID => 'images/recent.png',
258 Tag::ALL_TAGS_ID => 'images/tag.png',
259 Language::ALL_LANGUAGES_ID => 'images/language.png',
260 "calibre:books$" => 'images/allbook.png',
261 "calibre:books:letter" => 'images/allbook.png'
264 public function getUpdatedTime () {
265 if (!is_null ($this->localUpdated)) {
266 return date (DATE_ATOM, $this->localUpdated);
268 if (is_null (self::$updated)) {
269 self::$updated = time();
271 return date (DATE_ATOM, self::$updated);
274 public function getContentArray () {
276 foreach ($this->linkArray as $link) {
277 if ($link->type != Link::OPDS_NAVIGATION_TYPE) { continue; }
279 $navlink = $link->hrefXhtml ();
281 return array ( "title" => $this->title, "content" => $this->content, "navlink" => $navlink );
284 public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray) {
286 $this->title = $ptitle;
288 $this->content = $pcontent;
289 $this->contentType = $pcontentType;
290 $this->linkArray = $plinkArray;
292 if ($config['cops_show_icons'] == 1)
294 foreach (self::$icons as $reg => $image)
296 if (preg_match ("/" . $reg . "/", $pid)) {
297 array_push ($this->linkArray, new Link (getUrlWithVersion ($image), "image/png", Link::OPDS_THUMBNAIL_TYPE));
306 class EntryBook extends Entry
310 public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $pbook) {
311 parent::__construct ($ptitle, $pid, $pcontent, $pcontentType, $plinkArray);
312 $this->book = $pbook;
313 $this->localUpdated = $pbook->timestamp;
316 public function getContentArray () {
317 $entry = array ( "title" => $this->title);
318 $entry ["book"] = $this->book->getContentArray ();
322 public function getCoverThumbnail () {
323 foreach ($this->linkArray as $link) {
324 if ($link->rel == Link::OPDS_THUMBNAIL_TYPE)
325 return $link->hrefXhtml ();
330 public function getCover () {
331 foreach ($this->linkArray as $link) {
332 if ($link->rel == Link::OPDS_IMAGE_TYPE)
333 return $link->hrefXhtml ();
342 public $subtitle = "";
349 public $totalNumber = -1;
350 public $entryArray = array();
352 public static function getPage ($pageId, $id, $query, $n)
355 case Base::PAGE_AUTHORS_FIRST_LETTER :
356 return new PageAllAuthorsLetter ($id, $query, $n);
357 case Base::PAGE_AUTHORS_STARTING_LETTERS :
358 return new PageAuthorsLetter ($id, $query, $n);
359 case Base::PAGE_AUTHOR_DETAIL :
360 return new PageAuthorDetail ($id, $query, $n);
361 case Base::PAGE_ALL_TAGS :
362 return new PageAllTags ($id, $query, $n);
363 case Base::PAGE_TAG_DETAIL :
364 return new PageTagDetail ($id, $query, $n);
365 case Base::PAGE_ALL_LANGUAGES :
366 return new PageAllLanguages ($id, $query, $n);
367 case Base::PAGE_LANGUAGE_DETAIL :
368 return new PageLanguageDetail ($id, $query, $n);
369 case Base::PAGE_ALL_SERIES :
370 return new PageAllSeries ($id, $query, $n);
371 case Base::PAGE_SERIES_STARTING_LETTERS :
372 return new PageAllSeriesStartingLetters ($id, $query, $n);
373 case Base::PAGE_ALL_BOOKS :
374 return new PageAllBooks ($id, $query, $n);
375 case Base::PAGE_ALL_BOOKS_LETTER:
376 return new PageAllBooksLetter ($id, $query, $n);
377 case Base::PAGE_ALL_RECENT_BOOKS :
378 return new PageRecentBooks ($id, $query, $n);
379 case Base::PAGE_SERIE_DETAIL :
380 return new PageSerieDetail ($id, $query, $n);
381 case Base::PAGE_OPENSEARCH_QUERY :
382 return new PageQueryResult ($id, $query, $n);
383 case Base::PAGE_BOOK_DETAIL :
384 return new PageBookDetail ($id, $query, $n);
385 case Base::PAGE_ABOUT :
386 return new PageAbout ($id, $query, $n);
387 case Base::PAGE_CUSTOMIZE :
388 return new PageCustomize ($id, $query, $n);
390 $page = new Page ($id, $query, $n);
391 $page->idPage = "cops:catalog";
396 public function __construct($pid, $pquery, $pn) {
400 $this->query = $pquery;
402 $this->favicon = $config['cops_icon'];
405 public function InitializeContent ()
408 $this->title = $config['cops_title_default'];
409 $this->subtitle = $config['cops_subtitle_default'];
410 array_push ($this->entryArray, Author::getCount());
411 $series = Serie::getCount();
412 if (!is_null ($series)) array_push ($this->entryArray, $series);
413 $tags = Tag::getCount();
414 if (!is_null ($tags)) array_push ($this->entryArray, $tags);
415 $languages = Language::getCount();
416 if (!is_null ($languages)) array_push ($this->entryArray, $languages);
417 $this->entryArray = array_merge ($this->entryArray, Book::getCount());
420 public function isPaginated ()
423 return (getCurrentOption ("max_item_per_page") != -1 &&
424 $this->totalNumber != -1 &&
425 $this->totalNumber > getCurrentOption ("max_item_per_page"));
428 public function getNextLink ()
431 $currentUrl = $_SERVER['QUERY_STRING'];
432 $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
433 if (($this->n) * getCurrentOption ("max_item_per_page") < $this->totalNumber) {
434 return new LinkNavigation ($currentUrl . "&n=" . ($this->n + 1), "next", "Page suivante");
439 public function getPrevLink ()
442 $currentUrl = $_SERVER['QUERY_STRING'];
443 $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
445 return new LinkNavigation ($currentUrl . "&n=" . ($this->n - 1), "previous", "Page precedente");
450 public function getMaxPage ()
453 return ceil ($this->totalNumber / getCurrentOption ("max_item_per_page"));
456 public function containsBook ()
458 if (count ($this->entryArray) == 0) return false;
459 if (get_class ($this->entryArray [0]) == "EntryBook") return true;
465 class PageAllAuthorsLetter extends Page
467 public function InitializeContent ()
471 $this->idPage = Author::getEntryIdByLetter ($this->idGet);
472 $this->entryArray = Author::getAuthorsByFirstLetter ($this->idGet);
474 $this->title = localize("authors.starting").' '.$this->idGet;
476 $this->title = localize("authors.title");
481 class PageAuthorsLetter extends Page
483 public function InitializeContent ()
487 $this->idPage = Author::getEntryIdByLetter ($this->idGet);
488 $this->entryArray = Author::getAuthorsByStartingLetter ($this->idGet);
489 $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("authorword", count ($this->entryArray)), count ($this->entryArray)), $this->idGet);
493 class PageAuthorDetail extends Page
495 public function InitializeContent ()
497 $author = Author::getAuthorById ($this->idGet);
498 $this->idPage = $author->getEntryId ();
499 $this->title = $author->name;
500 list ($this->entryArray, $this->totalNumber) = Book::getBooksByAuthor ($this->idGet, $this->n);
504 class PageAllTags extends Page
506 public function InitializeContent ()
508 $this->title = localize("tags.title");
509 $array = Tag::getAllTags();
511 $this->entryArray = $array;
512 $this->idPage = Tag::ALL_TAGS_ID;
516 class PageAllLanguages extends Page
518 public function InitializeContent ()
520 $this->title = localize("languages.title");
521 $array = Language::getAllLanguages();
523 $this->entryArray = $array;
524 $this->idPage = Language::ALL_LANGUAGES_ID;
528 class PageTagDetail extends Page
530 public function InitializeContent ()
532 $tag = Tag::getTagById ($this->idGet);
533 $this->idPage = $tag->getEntryId ();
534 $this->title = $tag->name;
535 list ($this->entryArray, $this->totalNumber) = Book::getBooksByTag ($this->idGet, $this->n);
539 class PageLanguageDetail extends Page
541 public function InitializeContent ()
543 $language = Language::getLanguageById ($this->idGet);
544 $this->idPage = $language->getEntryId ();
545 $this->title = $language->lang_code;
546 list ($this->entryArray, $this->totalNumber) = Book::getBooksByLanguage ($this->idGet, $this->n);
550 class PageAllSeries extends Page
552 public function InitializeContent ()
555 $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("series.title")), $this->idGet);
557 $this->title = localize("series.title");
559 $this->entryArray = Serie::getAllSeries($this->idGet);
560 $this->idPage = Serie::getEntryIdByLetters($this->idGet);
564 class PageAllSeriesStartingLetters extends Page
566 public function InitializeContent ()
568 $this->entryArray = Serie::getAllSeriesStartingLetters($this->idGet);
569 $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("series.title")), $this->idGet);
570 $this->idPage = Serie::getEntryIdByLetters($this->idGet);
574 class PageSerieDetail extends Page
576 public function InitializeContent ()
578 $serie = Serie::getSerieById ($this->idGet);
579 $this->title = $serie->name;
580 list ($this->entryArray, $this->totalNumber) = Book::getBooksBySeries ($this->idGet, $this->n);
581 $this->idPage = $serie->getEntryId ();
585 class PageAllBooks extends Page
587 public function InitializeContent ()
589 $this->entryArray = Book::getAllBooks ($this->idGet);
590 if ($this->entryArray->count==1) {
591 $this->title = $this->idGet;
592 } elseif ($this->idGet) {
593 $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("bookword.title")), $this->idGet);
595 $this->title = localize ("allbooks.title");
597 $this->idPage = Book::ALL_BOOKS_ID;
601 class PageAllBooksLetter extends Page
603 public function InitializeContent ()
605 list ($this->entryArray, $this->totalNumber) = Book::getBooksByStartingLetter ($this->idGet, $this->n);
606 $this->idPage = Book::getEntryIdByLetter ($this->idGet);
608 $count = $this->totalNumber;
610 $count = count ($this->entryArray);
613 $this->title = $this->idGet;
615 $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("bookword", $count), $count), $this->idGet);
620 class PageRecentBooks extends Page
622 public function InitializeContent ()
624 $this->title = localize ("recent.title");
625 $this->entryArray = Book::getAllRecentBooks ();
626 $this->idPage = Book::ALL_RECENT_BOOKS_ID;
630 class PageQueryResult extends Page
632 public function InitializeContent ()
635 $this->title = str_format (localize ("search.result"), $this->query);
636 $currentPage = getURLParam ("current", NULL);
638 switch ($currentPage) {
639 case Base::PAGE_AUTHORS_FIRST_LETTER :
640 $this->entryArray = Author::getAuthorsByStartingLetter ('%' . $this->query);
643 list ($this->entryArray, $this->totalNumber) = Book::getBooksByQuery ($this->query, $this->n);
648 class PageBookDetail extends Page
650 public function InitializeContent ()
652 $this->book = Book::getBookById ($this->idGet);
653 $this->title = $this->book->title;
657 class PageAbout extends Page
659 public function InitializeContent ()
661 $this->title = localize ("about.title");
665 class PageCustomize extends Page
667 public function InitializeContent ()
670 $this->title = localize ("customize.title");
671 $this->entryArray = array ();
674 if (getCurrentOption ("use_fancyapps") == 1) {
675 $use_fancybox = "checked='checked'";
677 $html_tag_filter = "";
678 if (getCurrentOption ("html_tag_filter") == 1) {
679 $html_tag_filter = "checked='checked'";
684 if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
685 $content .= '<select id="style" onchange="updateCookie (this);">';
687 foreach (glob ("styles/style-*.css") as $filename) {
688 if (preg_match ('/styles\/style-(.*?)\.css/', $filename, $m)) {
692 if (getCurrentOption ("style") == $filename) {
693 if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
694 $selected = "selected='selected'";
696 $selected = "checked='checked'";
699 if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
700 $content .= "<option value='{$filename}' {$selected}>{$filename}</option>";
702 $content .= "<input type='radio' onchange='updateCookieFromCheckbox (this);' id='style-{$filename}' name='style' value='{$filename}' {$selected} /><label for='style-{$filename}'> {$filename} </label>";
705 if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
706 $content .= '</select>';
708 array_push ($this->entryArray, new Entry (localize ("customize.style"), "",
712 $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="use_fancyapps" ' . $use_fancybox . ' />';
713 array_push ($this->entryArray, new Entry (localize ("customize.fancybox"), "",
716 $content = '<input type="number" onchange="updateCookie (this);" id="max_item_per_page" value="' . getCurrentOption ("max_item_per_page") . '" min="-1" max="1200" pattern="^[-+]?[0-9]+$" />';
717 array_push ($this->entryArray, new Entry (localize ("customize.paging"), "",
720 $content = '<input type="number" onchange="updateCookie (this);" id="max_books_pages" value="' . getCurrentOption ("max_books_pages") . '" min="-1" max="50" pattern="^[-+]?[0-9]+$" />';
721 array_push ($this->entryArray, new Entry (localize ("customize.pages"), "",
724 $content = '<input type="text" onchange="updateCookie (this);" id="email" value="' . getCurrentOption ("email") . '" />';
725 array_push ($this->entryArray, new Entry (localize ("customize.email"), "",
728 $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="html_tag_filter" ' . $html_tag_filter . ' />';
729 array_push ($this->entryArray, new Entry (localize ("customize.filter"), "",
738 const PAGE_INDEX = "index";
739 const PAGE_AUTHORS_FIRST_LETTER = "AUTH";
740 const PAGE_AUTHORS_STARTING_LETTERS = "AUTHST";
741 const PAGE_AUTHOR_DETAIL = "AUTHDET";
742 const PAGE_ALL_BOOKS = "BOOK";
743 const PAGE_ALL_BOOKS_LETTER = "BOOKST";
744 const PAGE_ALL_SERIES = "SER";
745 const PAGE_SERIES_STARTING_LETTERS = "SERST";
746 const PAGE_SERIE_DETAIL = "SERDET";
747 const PAGE_OPENSEARCH = "OPEN";
748 const PAGE_OPENSEARCH_QUERY = "OPENQ";
749 const PAGE_ALL_RECENT_BOOKS = "RECENT";
750 const PAGE_ALL_TAGS = "GENR";
751 const PAGE_TAG_DETAIL = "GENRDET";
752 const PAGE_BOOK_DETAIL = "BOOKDET";
753 const PAGE_ABOUT = "ABOUT";
754 const PAGE_ALL_LANGUAGES = "LANG";
755 const PAGE_LANGUAGE_DETAIL = "LANGDET";
756 const PAGE_CUSTOMIZE = "OPTS";
758 const COMPATIBILITY_XML_ALDIKO = "aldiko";
760 private static $db = NULL;
762 public static function getFileDirectory () {
764 return $config['file_directory'];
767 public static function getDbHost () {
769 return $config['db_host'];
772 public static function getDbUser () {
774 return $config['db_user'];
777 public static function getDbPasswd () {
779 return $config['db_passwd'];
782 public static function getDb () {
784 if (is_null (self::$db)) {
786 self::$db = new PDO('mysql:host='. self::getDbHost (), self::getDbUser(), self::getDbPasswd());
787 self::$db -> exec("set names utf8");
788 } catch (Exception $e) {
789 header("location: checkconfig.php?err=1");
796 public static function clearDb () {
800 public static function executeQuery($query, $columns, $filter, $params, $n) {
804 if (getCurrentOption ("max_item_per_page") != -1 && $n != -1)
806 // First check total number of results
807 $query_str = str_format ($query, "count(*)", $filter);
808 $result = self::getDb ()->prepare ($query_str);
809 $result->execute ($params);
810 $totalResult = $result->fetchColumn (0);
812 // Next modify the query and params
813 $query .= " limit ".(($n - 1) * getCurrentOption ("max_item_per_page")).", ".getCurrentOption ("max_item_per_page");
816 $query_str = str_format($query, $columns, $filter);
817 $result = self::getDb ()->prepare($query_str);
818 $result->execute ($params);
819 return array ($totalResult, $result);
822 public static function booksPages ()
825 $result = getCurrentOption("max_books_pages");
826 if ($result=="") { $result = 10; }
830 public static function maxItemsPerPage()
833 $result = getCurrentOption("max_item_per_page");
834 if ($result=="") { $result = 25; }