diff --git a/README.md b/README.md index 2e52aaac6..afdd7aca5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# Don't use this Repo + +All my changes have been merged into https://github.com/mikespub-org/seblucas-cops along with many other changes and is being actively maintained. Please go check it out! + # COPS COPS stands for Calibre OPDS (and HTML) Php Server. diff --git a/config_default.php b/config_default.php index 84397db17..8e9ea5a5e 100644 --- a/config_default.php +++ b/config_default.php @@ -80,7 +80,7 @@ /* * Height of thumbnail image for HTML */ - $config['cops_html_thumbnail_height'] = '164'; + $config['cops_html_thumbnail_height'] = '225'; /* * Icon for both OPDS and HTML catalog @@ -109,6 +109,11 @@ */ $config['cops_prefered_format'] = array('EPUB', 'PDF', 'AZW3', 'AZW', 'MOBI', 'CBR', 'CBZ'); + /* + * Specify the ignored formats that will never display in COPS + */ + $config['cops_ignored_formats'] = array(); + /* * use URL rewriting for downloading of ebook in HTML catalog * See Github wiki for more information @@ -130,7 +135,7 @@ * Max number of items per page * -1 unlimited */ - $config['cops_max_item_per_page'] = '-1'; + $config['cops_max_item_per_page'] = '48'; /* * split authors by first letter @@ -321,7 +326,7 @@ * 'default' * 'bootstrap' */ - $config['cops_template'] = 'default'; + $config['cops_template'] = 'bootstrap2'; /* * Which style is used by default : diff --git a/config_local.php.example b/config_local.php.example index 78a9e544c..cdb1cd991 100644 --- a/config_local.php.example +++ b/config_local.php.example @@ -28,3 +28,8 @@ * 0 : disable */ $config['cops_use_url_rewriting'] = "0"; + + /* + * Specify the ignored formats that will never display in COPS + */ + $config['cops_ignored_formats'] = array('ORIGINAL_EPUB', 'ORIGINAL_AZW3'); \ No newline at end of file diff --git a/lang/Localization_en.json b/lang/Localization_en.json index 0cd7b3fd1..c67d14dfe 100644 --- a/lang/Localization_en.json +++ b/lang/Localization_en.json @@ -56,6 +56,7 @@ "languages.alphabetical.none": "Alphabetical index of absolutely no languages", "languages.alphabetical.one": "Alphabetical index of the single language", "languages.title": "Languages", + "links.title": "Links", "mail.messagenotsent": "Message could not be sent.", "mail.messagesent": "Message has been sent", "paging.next.alternate": "Next", diff --git a/lib/Author.php b/lib/Author.php index 85e2860ec..f378ebc85 100644 --- a/lib/Author.php +++ b/lib/Author.php @@ -83,7 +83,7 @@ public static function getAuthorById ($authorId) { public static function getAuthorByBookId ($bookId) { $result = parent::getDb ()->prepare('select authors.id as id, authors.name as name, authors.sort as sort from authors, books_authors_link where author = authors.id -and book = ?'); +and book = ? order by books_authors_link.id'); $result->execute (array ($bookId)); $authorArray = array (); while ($post = $result->fetchObject ()) { diff --git a/lib/Book.php b/lib/Book.php index f8d18f40c..61ac36154 100644 --- a/lib/Book.php +++ b/lib/Book.php @@ -58,6 +58,8 @@ define ('SQL_BOOKS_BY_RATING', 'select {0} from books ' . SQL_BOOKS_LEFT_JOIN . ' where books_ratings_link.book = books.id and ratings.id = ? {1} order by sort'); +require('Identifier.php'); + class Book extends Base { const ALL_BOOKS_UUID = 'urn:uuid'; @@ -104,6 +106,7 @@ class Book extends Base public $publisher = NULL; public $serie = NULL; public $tags = NULL; + public $identifiers = NULL; public $languages = NULL; public $format = array (); @@ -228,6 +231,28 @@ public function getTagsName() { return implode(', ', array_map(function ($tag) { return $tag->name; }, $this->getTags())); } + + /** + * @return Identifiers[] + */ + public function getIdentifiers() { + if (is_null ($this->identifiers)) { + $this->identifiers = array(); + + $result = parent::getDb()->prepare('select type, val, id + from identifiers + where book = ? + order by type'); + $result->execute(array($this->id)); + while ($post = $result->fetchObject()) + { + array_push($this->identifiers, new Identifier($post)); + } + + } + return $this->identifiers; + } + /** * @return Data[] */ @@ -314,14 +339,7 @@ public function getComment($withSerie = true) { if (!is_null ($se) && $withSerie) { $addition = $addition . '' . localize('content.series') . '' . str_format(localize('content.series.data'), $this->seriesIndex, htmlspecialchars($se->name)) . "
\n"; } - if (preg_match('/<\/(div|p|a|span)>/', $this->comment)) - { - return $addition . html2xhtml($this->comment); - } - else - { - return $addition . htmlspecialchars($this->comment); - } + return $addition . html2xhtml($this->comment); } public function getDataFormat($format) { diff --git a/lib/Data.php b/lib/Data.php index 3a4ab254d..cae9dc4d3 100644 --- a/lib/Data.php +++ b/lib/Data.php @@ -153,9 +153,20 @@ public function getHtmlLinkWithRewriting ($title = NULL, $view = false) { } public static function getDataByBook ($book) { + global $config; + $out = array (); - $result = parent::getDb ()->prepare('select id, format, name - from data where book = ?'); + + $sql = 'select id, format, name from data where book = ?'; + + $ignored_formats = $config['cops_ignored_formats']; + if (count($ignored_formats) > 0) { + $sql .= " and format not in ('" + . implode("','", $ignored_formats) + . "')"; + } + + $result = parent::getDb ()->prepare($sql); $result->execute (array ($book->id)); while ($post = $result->fetchObject ()) diff --git a/lib/Identifier.php b/lib/Identifier.php new file mode 100644 index 000000000..26682922e --- /dev/null +++ b/lib/Identifier.php @@ -0,0 +1,79 @@ + + */ + +class Identifier +{ + public $id; + public $type; + public $formattedType; + public $val; + + public function __construct($post) + { + $this->id = $post->id; + $this->type = strtolower($post->type);; + $this->val = $post->val; + $this->formatType(); + } + + function formatType() + { + if ($this->type == 'amazon') { + $this->formattedType = "Amazon"; + $this->uri = sprintf("https://amazon.com/dp/%s", $this->val); + } else if ($this->type == "asin") { + $this->formattedType = $this->type; + $this->uri = sprintf("https://amazon.com/dp/%s", $this->val); + } else if (substr($this->type, 0, 7) == "amazon_") { + $this->formattedType = sprintf("Amazon.co.%s", substr($this->type, 7)); + $this->uri = sprintf("https://amazon.co.%s/dp/%s", substr($this->type, 7), $this->val); + } else if ($this->type == "isbn") { + $this->formattedType = "ISBN"; + $this->uri = sprintf("https://www.worldcat.org/isbn/%s", $this->val); + } else if ($this->type == "doi") { + $this->formattedType = "DOI"; + $this->uri = sprintf("https://dx.doi.org/%s", $this->val); + } else if ($this->type == "douban") { + $this->formattedType = "Douban"; + $this->uri = sprintf("https://book.douban.com/subject/%s", $this->val); + } else if ($this->type == "goodreads") { + $this->formattedType = "Goodreads"; + $this->uri = sprintf("https://www.goodreads.com/book/show/%s", $this->val); + } else if ($this->type == "google") { + $this->formattedType = "Google Books"; + $this->uri = sprintf("https://books.google.com/books?id=%s", $this->val); + } else if ($this->type == "kobo") { + $this->formattedType = "Kobo"; + $this->uri = sprintf("https://www.kobo.com/ebook/%s", $this->val); + } else if ($this->type == "litres") { + $this->formattedType = "ЛитРес"; + $this->uri = sprintf("https://www.litres.ru/%s", $this->val); + } else if ($this->type == "issn") { + $this->formattedType = "ISSN"; + $this->uri = sprintf("https://portal.issn.org/resource/ISSN/%s", $this->val); + } else if ($this->type == "isfdb") { + $this->formattedType = "ISFDB"; + $this->uri = sprintf("http://www.isfdb.org/cgi-bin/pl.cgi?%s", $this->val); + } else if ($this->type == "lubimyczytac") { + $this->formattedType = "Lubimyczytac"; + $this->uri = sprintf("https://lubimyczytac.pl/ksiazka/%s/ksiazka", $this->val); + } else if ($this->type == "url") { + $this->formattedType = $this->type; + $this->uri = $this->val; + } else { + $this->formattedType = $this->type; + $this->uri = ''; + } + } + + public function getUri() + { + return $this->uri; + } +} diff --git a/lib/JSON_renderer.php b/lib/JSON_renderer.php index 851ea0487..12fd2a6be 100644 --- a/lib/JSON_renderer.php +++ b/lib/JSON_renderer.php @@ -107,6 +107,12 @@ public static function getFullBookContentArray ($book) { $link = new LinkNavigation ($tag->getUri ()); array_push ($out ["tags"], array ("name" => $tag->name, "url" => $link->hrefXhtml ())); } + + $out ["identifiers"] = array (); + foreach ($book->getIdentifiers () as $ident) { + array_push ($out ["identifiers"], array ("name" => $ident->formattedType, "url" => $ident->getUri ())); + } + $out ["customcolumns_preview"] = $book->getCustomColumnValues($config['cops_calibre_custom_column_preview'], true); return $out; @@ -146,6 +152,7 @@ public static function addCompleteArray ($in) { "authorsTitle" => localize("authors.title"), "bookwordTitle" => localize("bookword.title"), "tagsTitle" => localize("tags.title"), + "linksTitle" => localize("links.title"), "seriesTitle" => localize("series.title"), "customizeTitle" => localize ("customize.title"), "aboutTitle" => localize ("about.title"), diff --git a/lib/PageCustomize.php b/lib/PageCustomize.php index 80fcd14a0..26faf5a71 100644 --- a/lib/PageCustomize.php +++ b/lib/PageCustomize.php @@ -29,6 +29,16 @@ private function isSelected ($key, $value) { return ""; } + private function getTemplateList () { + $result = array (); + foreach (glob ("templates/*") as $filename) { + if (preg_match ('/templates\/(.*)/', $filename, $m)) { + array_push ($result, $m [1]); + } + } + return $result; + } + private function getStyleList () { $result = array (); foreach (glob ("templates/" . getCurrentTemplate () . "/styles/style-*.css") as $filename) { @@ -52,9 +62,23 @@ public function InitializeContent () "language"); $content = ""; + if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) { + $content .= "'; + } else { + foreach ($this-> getTemplateList () as $filename) { + $content .= "isChecked ("template", $filename) . " />"; + } + } array_push ($this->entryArray, new Entry ("Template", "", - "Click to switch to Bootstrap", "text", + $content, "text", array ())); + + $content = ""; if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) { $content .= ' +
+ +
+ + + + + diff --git a/templates/bootstrap2/main.html b/templates/bootstrap2/main.html new file mode 100644 index 000000000..149cf8850 --- /dev/null +++ b/templates/bootstrap2/main.html @@ -0,0 +1,84 @@ +
+{{? it.page == 13 || it.page == 16}} + {{? it.page == 13}} + {{#def.bookdetail}} + {{??}} + {{= it.fullhtml}} + {{?}} +{{??}} +
+ +
+ +{{? it.containsBook == 0}} +
+
+ {{~it.entries:entry:i}} + + {{~}} +
+
+{{??}} +
+ {{~it.entries:entry:i}} +
+
+ {{? entry.book.hasCover == 1}} + + {{=it.c.i18n.coverAlt}} + + {{?}} +
+
+

{{=htmlspecialchars (entry.title)}}

+
{{=htmlspecialchars (entry.book.authorsName)}}
+ {{? entry.book.seriesName != ""}}
{{=htmlspecialchars (entry.book.seriesName)}} ({{=entry.book.seriesIndex}})
{{?}} + + {{~entry.book.customcolumns_list :column:column_index}} +
{{=column.customColumnType.columnTitle}} : {{=column.htmlvalue}}
+ {{~}} +
+
+
+ + {{? entry.book.preferedData.length > 1}}
{{?}} + {{~entry.book.preferedData:data:j}} + {{? j == 0}} + {{=data.name}} + {{? entry.book.preferedData.length > 1}} + + {{? entry.book.preferedData.length > 1}}{{?}} + {{?}} + {{~}} + {{? entry.book.preferedData.length > 1}}
{{?}} +
+
+ {{~}} +
+{{?}} +{{?}} +{{? it.isPaginated == 1}} + +{{?}} +
diff --git a/templates/bootstrap2/page.html b/templates/bootstrap2/page.html new file mode 100644 index 000000000..88139c888 --- /dev/null +++ b/templates/bootstrap2/page.html @@ -0,0 +1,3 @@ +{{#def.header}} +{{#def.main}} +{{#def.footer}} diff --git a/templates/bootstrap2/scripts/cops.js b/templates/bootstrap2/scripts/cops.js new file mode 100644 index 000000000..2647a549f --- /dev/null +++ b/templates/bootstrap2/scripts/cops.js @@ -0,0 +1,7 @@ +function postRefresh() +{ + $('[data-toggle="tooltip"]').tooltip(); + hash = window.location.hash.replace("#", ""); + var elmnt = document.getElementById(hash); + if (elmnt) elmnt.scrollIntoView(); +} \ No newline at end of file diff --git a/templates/bootstrap2/styles/style-base.css b/templates/bootstrap2/styles/style-base.css new file mode 100644 index 000000000..4823668e1 --- /dev/null +++ b/templates/bootstrap2/styles/style-base.css @@ -0,0 +1,3 @@ +.panel-body { padding: 5px; } + +.bottomright {position:absolute; bottom:0; margin-bottom:25px; right: 20px;} \ No newline at end of file diff --git a/templates/bootstrap2/styles/style-default.css b/templates/bootstrap2/styles/style-default.css new file mode 100644 index 000000000..4823668e1 --- /dev/null +++ b/templates/bootstrap2/styles/style-default.css @@ -0,0 +1,3 @@ +.panel-body { padding: 5px; } + +.bottomright {position:absolute; bottom:0; margin-bottom:25px; right: 20px;} \ No newline at end of file diff --git a/templates/bootstrap2/styles/typeahead.css b/templates/bootstrap2/styles/typeahead.css new file mode 100644 index 000000000..23ccbc109 --- /dev/null +++ b/templates/bootstrap2/styles/typeahead.css @@ -0,0 +1,200 @@ +/* + * typehead.js-bootstrap3.less + * @version 0.2.3 + * https://github.com/hyspace/typeahead.js-bootstrap3.less + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ +.has-warning .twitter-typeahead .tt-input, +.has-warning .twitter-typeahead .tt-hint { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-warning .twitter-typeahead .tt-input:focus, +.has-warning .twitter-typeahead .tt-hint:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +} +.has-error .twitter-typeahead .tt-input, +.has-error .twitter-typeahead .tt-hint { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .twitter-typeahead .tt-input:focus, +.has-error .twitter-typeahead .tt-hint:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} +.has-success .twitter-typeahead .tt-input, +.has-success .twitter-typeahead .tt-hint { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-success .twitter-typeahead .tt-input:focus, +.has-success .twitter-typeahead .tt-hint:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +} +.input-group .twitter-typeahead:first-child .tt-input, +.input-group .twitter-typeahead:first-child .tt-hint { + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; + width: 100%; +} +.input-group .twitter-typeahead:last-child .tt-input, +.input-group .twitter-typeahead:last-child .tt-hint { + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; + width: 100%; +} +.input-group.input-group-sm .twitter-typeahead .tt-input, +.input-group.input-group-sm .twitter-typeahead .tt-hint { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group.input-group-sm .twitter-typeahead .tt-input, +select.input-group.input-group-sm .twitter-typeahead .tt-hint { + height: 30px; + line-height: 30px; +} +textarea.input-group.input-group-sm .twitter-typeahead .tt-input, +textarea.input-group.input-group-sm .twitter-typeahead .tt-hint, +select[multiple].input-group.input-group-sm .twitter-typeahead .tt-input, +select[multiple].input-group.input-group-sm .twitter-typeahead .tt-hint { + height: auto; +} +.input-group.input-group-sm .twitter-typeahead:not(:first-child):not(:last-child) .tt-input, +.input-group.input-group-sm .twitter-typeahead:not(:first-child):not(:last-child) .tt-hint { + border-radius: 0; +} +.input-group.input-group-sm .twitter-typeahead:first-child .tt-input, +.input-group.input-group-sm .twitter-typeahead:first-child .tt-hint { + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.input-group.input-group-sm .twitter-typeahead:last-child .tt-input, +.input-group.input-group-sm .twitter-typeahead:last-child .tt-hint { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-bottom-right-radius: 3px; + border-top-right-radius: 3px; +} +.input-group.input-group-lg .twitter-typeahead .tt-input, +.input-group.input-group-lg .twitter-typeahead .tt-hint { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-group.input-group-lg .twitter-typeahead .tt-input, +select.input-group.input-group-lg .twitter-typeahead .tt-hint { + height: 46px; + line-height: 46px; +} +textarea.input-group.input-group-lg .twitter-typeahead .tt-input, +textarea.input-group.input-group-lg .twitter-typeahead .tt-hint, +select[multiple].input-group.input-group-lg .twitter-typeahead .tt-input, +select[multiple].input-group.input-group-lg .twitter-typeahead .tt-hint { + height: auto; +} +.input-group.input-group-lg .twitter-typeahead:not(:first-child):not(:last-child) .tt-input, +.input-group.input-group-lg .twitter-typeahead:not(:first-child):not(:last-child) .tt-hint { + border-radius: 0; +} +.input-group.input-group-lg .twitter-typeahead:first-child .tt-input, +.input-group.input-group-lg .twitter-typeahead:first-child .tt-hint { + border-bottom-left-radius: 6px; + border-top-left-radius: 6px; + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.input-group.input-group-lg .twitter-typeahead:last-child .tt-input, +.input-group.input-group-lg .twitter-typeahead:last-child .tt-hint { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-bottom-right-radius: 6px; + border-top-right-radius: 6px; +} +.twitter-typeahead { + width: 100%; + float: left; +} +.input-group .twitter-typeahead { + display: table-cell !important; +} +.twitter-typeahead .tt-hint { + color: #999999; +} +.twitter-typeahead .tt-input { + z-index: 2; +} +.twitter-typeahead .tt-input[disabled], +.twitter-typeahead .tt-input[readonly], +fieldset[disabled] .twitter-typeahead .tt-input { + cursor: not-allowed; + background-color: #eeeeee !important; +} +.tt-dropdown-menu, +.tt-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + min-width: 160px; + width: 100%; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-clip: padding-box; + *border-right-width: 2px; + *border-bottom-width: 2px; +} +.tt-dropdown-menu .tt-suggestion, +.tt-menu .tt-suggestion { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333333; +} +.tt-dropdown-menu .tt-suggestion.tt-cursor, +.tt-menu .tt-suggestion.tt-cursor, +.tt-dropdown-menu .tt-suggestion:hover, +.tt-menu .tt-suggestion:hover { + cursor: pointer; + text-decoration: none; + outline: 0; + background-color: #f5f5f5; + color: #262626; +} +.tt-dropdown-menu .tt-suggestion.tt-cursor a, +.tt-menu .tt-suggestion.tt-cursor a, +.tt-dropdown-menu .tt-suggestion:hover a, +.tt-menu .tt-suggestion:hover a { + color: #262626; +} +.tt-dropdown-menu .tt-suggestion p, +.tt-menu .tt-suggestion p { + margin: 0; +} diff --git a/templates/bootstrap2/suggestion.html b/templates/bootstrap2/suggestion.html new file mode 100644 index 000000000..1107dadee --- /dev/null +++ b/templates/bootstrap2/suggestion.html @@ -0,0 +1 @@ +

{{=it.title}}

\ No newline at end of file diff --git a/util.js b/util.js index fd2082798..75f07f597 100644 --- a/util.js +++ b/util.js @@ -315,7 +315,6 @@ updatePage = function (data) { } document.title = data.title; currentData = data; - setTimeout( function() { $("input[name=query]").focus(); }, 500 ); debug_log (elapsed ()); @@ -442,7 +441,6 @@ function handleLinks () { $("body").on ("click", ".headright", function(){ if ($("#tool").is(":hidden")) { $("#tool").slideDown("slow"); - $("input[name=query]").focus(); $.cookie('toolbar', '1', { expires: 365 }); } else { $("#tool").slideUp();