diff --git a/.htaccess b/.htaccess
index d20a79d..80f0908 100644
--- a/.htaccess
+++ b/.htaccess
@@ -2,7 +2,7 @@ DirectoryIndex index.php
- XSendFile on
+ XSendFile on
@@ -61,6 +61,12 @@ ExpiresByType text/javascript "access plus 1 year"
###########################################
# Uncomment if you wish to protect access with a password
###########################################
+# If your covers and books are not available as soon as you protect it
+# You can try replacing the FilesMatch directive by this one
+#
+# If helps for Sony PRS-TX and Aldiko, beware fetch.php can be accessed
+# with authentication
+###########################################
#
#AuthUserFile /path/to/file
#AuthGroupFile /dev/null
diff --git a/.travis.yml b/.travis.yml
index 86c8cda..7d208da 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,4 +10,13 @@ script:
- phpunit
- jshint --verbose --show-non-errors util.js
- php test/coverage-checker.php clover.xml 45
-
\ No newline at end of file
+after_success:
+ - chmod +x test/prepareSauceTest.sh
+ - test/prepareSauceTest.sh
+env:
+ global:
+ - SAUCE_USERNAME=seblucas
+ - secure: VVxocvmz6WYr3tZSTA42M/LUhaHoBWw5onh85hnquoMaxspd3tDCyfQIowTTmEXikRh2T0CkTH7X3dhVwRTd/Ha9isja1qDo9Lc2flGCoWICF7WFZuom084+d+O+EWx4WZMAw4Lz4w6a5xflpPKnzNs9B0+de0BdTlQ5qSXVrcA=
+addons:
+ hosts:
+ - cops-travis
diff --git a/CHANGELOG b/CHANGELOG
index 1632745..940f8aa 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,11 +4,14 @@
* Updated the way locales are handled. Should be easier to add new languages.
* Fixed display of Cyrillic characters.
* Upgraded doT to version 1.0.1, Magnific-Popup to 0.9.8, Normalize.css to 2.1.3, Jquery-cookie to 1.4.0.
- * Upgraded
* Fixed OPDS stream validity. Reported by Didier.
* Added a new check in checkconfig.php to detect case problem between the actual path and the path stored in Calibre database. Try checkconfig.php?full=1. Reported by Ruud.
* Fixed the display of the rating stars with Chrome. Thanks to At_Libitum.
- * Added a new parameter to avoid splitting the books by first letter. Thanks to At_Libitum.
+ * Added a new parameter ($config['cops_titles_split_first_letter']) to avoid splitting the books by first letter. Thanks to At_Libitum.
+ * Fixed non compliant OPDS search (for Stanza, Moon+ Reader, ...). Reported by At_Libitum.
+ * Fixed the redirection in case the Calibre database is not found. Reported by At_Libitum
+ * Changed .htaccess to allow the use of password protected catalogs with Sony's eReader (PRS-TX). Thanks to Ruud for the beta testing.
+ * Updated Chinese, German, Norwegian, Portuguese, Russian translations. Thanks to all the translators.
0.6.2 - 20130913
* Added server side rendering for devices like PRS-TX / Kindle / Cybook. Thanks to all the testers.
diff --git a/README.md b/README.md
index 8f30b32..b0e5b4d 100644
--- a/README.md
+++ b/README.md
@@ -4,3 +4,9 @@ cops
Calibre OPDS (and HTML) PHP Server : light alternative to Calibre content server / Calibre2OPDS
See : http://blog.slucas.fr/en/oss/calibre-opds-php-server
+
+[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/seblucas/cops/badges/quality-score.png?s=e1c87a92ef90b8d666cd9bd4f3612bd10db84364)](https://scrutinizer-ci.com/g/seblucas/cops/)
+
+[![Code Coverage](https://scrutinizer-ci.com/g/seblucas/cops/badges/coverage.png?s=1e21d8c3bf96d7b0b7cc0e54429fa897ddea1506)](https://scrutinizer-ci.com/g/seblucas/cops/)
+
+[![Build Status](https://travis-ci.org/seblucas/cops.png)](https://travis-ci.org/seblucas/cops)
diff --git a/base.php b/base.php
index e450f3d..c23e100 100644
--- a/base.php
+++ b/base.php
@@ -16,6 +16,13 @@ function useServerSideRendering () {
return preg_match("/" . $config['cops_server_side_render'] . "/", $_SERVER['HTTP_USER_AGENT']);
}
+function getQueryString () {
+ if ( isset($_SERVER['QUERY_STRING']) ) {
+ return $_SERVER['QUERY_STRING'];
+ }
+ return "";
+}
+
function getURLParam ($name, $default = NULL) {
if (!empty ($_GET) && isset($_GET[$name]) && $_GET[$name] != "") {
return $_GET[$name];
@@ -192,6 +199,9 @@ function localize($phrase, $count=-1, $reset=false) {
}
function addURLParameter($urlParams, $paramName, $paramValue) {
+ if (empty ($urlParams)) {
+ $urlParams = "";
+ }
$start = "";
if (preg_match ("#^\?(.*)#", $urlParams, $matches)) {
$start = "?";
@@ -464,7 +474,7 @@ class Page
$tags = Tag::getCount();
if (!is_null ($tags)) array_push ($this->entryArray, $tags);
- $languages = Language::getCount();
+ $languages = Language::getCount();
if (!is_null ($languages)) array_push ($this->entryArray, $languages);
foreach ($config['cops_calibre_custom_column'] as $lookup) {
$customId = CustomColumn::getCustomId ($lookup);
@@ -487,8 +497,8 @@ class Page
public function getNextLink ()
{
- $currentUrl = $_SERVER['QUERY_STRING'];
- $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
+ $currentUrl = getQueryString ();
+ $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . getQueryString ());
if (($this->n) * getCurrentOption ("max_item_per_page") < $this->totalNumber) {
return new LinkNavigation ($currentUrl . "&n=" . ($this->n + 1), "next", localize ("paging.next.alternate"));
}
@@ -497,8 +507,8 @@ class Page
public function getPrevLink ()
{
- $currentUrl = $_SERVER['QUERY_STRING'];
- $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . $_SERVER['QUERY_STRING']);
+ $currentUrl = getQueryString ();
+ $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . getQueryString ());
if ($this->n > 1) {
return new LinkNavigation ($currentUrl . "&n=" . ($this->n - 1), "previous", localize ("paging.previous.alternate"));
}
@@ -795,7 +805,6 @@ class PageCustomize extends Page
{
public function InitializeContent ()
{
- global $config;
$this->title = localize ("customize.title");
$this->entryArray = array ();
@@ -926,15 +935,22 @@ abstract class Base
public static function getDbFileName ($database = NULL) {
return self::getDbDirectory ($database) .'metadata.db';
}
+
+ private static function error () {
+ header("location: checkconfig.php?err=1");
+ exit();
+ }
public static function getDb ($database = NULL) {
- global $config;
if (is_null (self::$db)) {
try {
- self::$db = new PDO('sqlite:'. self::getDbFileName ($database));
+ if (is_readable (self::getDbFileName ($database))) {
+ self::$db = new PDO('sqlite:'. self::getDbFileName ($database));
+ } else {
+ self::error ();
+ }
} catch (Exception $e) {
- header("location: checkconfig.php?err=1");
- exit();
+ self::error ();
}
}
return self::$db;
@@ -945,7 +961,6 @@ abstract class Base
}
public static function executeQuery($query, $columns, $filter, $params, $n, $database = NULL, $numberPerPage = NULL) {
- global $config;
$totalResult = -1;
if (is_null ($numberPerPage)) {
diff --git a/book.php b/book.php
index 7eb1ab6..e539920 100644
--- a/book.php
+++ b/book.php
@@ -452,7 +452,7 @@ class Book extends Base {
{
if ($data->isKnownType ())
{
- array_push ($linkArray, $data->getDataLink (Link::OPDS_ACQUISITION_TYPE, "Download"));
+ array_push ($linkArray, $data->getDataLink (Link::OPDS_ACQUISITION_TYPE, $data->format));
}
}
diff --git a/checkconfig.php b/checkconfig.php
index 57f97a7..aac84bc 100644
--- a/checkconfig.php
+++ b/checkconfig.php
@@ -129,6 +129,7 @@ Please check
}
?>
+
Check if Calibre database file can be opened with PHP
@@ -182,6 +183,7 @@ Please check
+
diff --git a/index.php b/index.php
index d2a7a04..b123250 100644
--- a/index.php
+++ b/index.php
@@ -24,12 +24,27 @@
exit ();
}
- header ("Content-Type:text/html;charset=utf-8");
$page = getURLParam ("page", Base::PAGE_INDEX);
$query = getURLParam ("query");
$qid = getURLParam ("id");
$n = getURLParam ("n", "1");
$database = GetUrlParam (DB);
+
+
+ // Access the database ASAP to be sure it's readable, redirect if that's not the case.
+ // It has to be done before any header is sent.
+ if (is_array ($config['calibre_directory']) && is_null ($database)) {
+ $i = 0;
+ foreach (array_keys ($config['calibre_directory']) as $key) {
+ $test = Base::getDb ($i);
+ Base::clearDb ();
+ $i++;
+ }
+ } else {
+ $test = Base::getDb ();
+ }
+
+ header ("Content-Type:text/html;charset=utf-8");
?>
@@ -66,7 +81,7 @@
$(document).ready(function() {
// Handler for .ready() called.
- var url = "";
+ var url = "";
$.when($.get('templates/default/header.html'),
$.get('templates/default/footer.html'),
@@ -98,7 +113,9 @@
updatePage (data [0]);
cache.put (url, data [0]);
- history.replaceState(url, "", window.location);
+ if (isPushStateEnabled) {
+ history.replaceState(url, "", window.location);
+ }
handleLinks ();
});
diff --git a/test/OPDSTest.php b/test/OPDSTest.php
index edaa245..9d6aafc 100644
--- a/test/OPDSTest.php
+++ b/test/OPDSTest.php
@@ -38,10 +38,8 @@ class OpdsTest extends PHPUnit_Framework_TestCase
global $config;
$page = Base::PAGE_INDEX;
$query = NULL;
- $search = NULL;
$qid = NULL;
$n = "1";
- $database = NULL;
$_SERVER['QUERY_STRING'] = "";
$config['cops_subtitle_default'] = "My subtitle";
@@ -66,10 +64,8 @@ class OpdsTest extends PHPUnit_Framework_TestCase
"One book" => dirname(__FILE__) . "/BaseWithOneBook/");
$page = Base::PAGE_AUTHOR_DETAIL;
$query = NULL;
- $search = NULL;
$qid = "1";
$n = "1";
- $database = NULL;
$_SERVER['QUERY_STRING'] = "page=" . Base::PAGE_AUTHOR_DETAIL . "&id=1";
$_GET ["db"] = "0";
@@ -87,10 +83,8 @@ class OpdsTest extends PHPUnit_Framework_TestCase
global $config;
$page = Base::PAGE_AUTHOR_DETAIL;
$query = NULL;
- $search = NULL;
$qid = "1";
$n = "1";
- $database = NULL;
$_SERVER['QUERY_STRING'] = "page=" . Base::PAGE_AUTHOR_DETAIL . "&id=1&n=1";
$config['cops_max_item_per_page'] = 2;
diff --git a/test/Sauce.php b/test/Sauce.php
new file mode 100644
index 0000000..db778eb
--- /dev/null
+++ b/test/Sauce.php
@@ -0,0 +1,140 @@
+ 'firefox',
+ 'desiredCapabilities' => array(
+ 'version' => '15',
+ 'platform' => 'Windows 2012',
+ )
+ ),
+ // run IE9 on Windows 7 on Sauce
+ array(
+ 'browserName' => 'internet explorer',
+ 'desiredCapabilities' => array(
+ 'version' => '9',
+ 'platform' => 'Windows 7',
+ )
+ ),
+ // run IE10 on Windows 8 on Sauce
+ array(
+ 'browserName' => 'internet explorer',
+ 'desiredCapabilities' => array(
+ 'version' => '10',
+ 'platform' => 'Windows 8',
+ )
+ ),
+ // run Opera 12 on Windows 7 on Sauce
+ array(
+ 'browserName' => 'opera',
+ 'desiredCapabilities' => array(
+ 'version' => '12',
+ 'platform' => 'Windows 7',
+ )
+ ),
+ // run Mobile Safari on iOS
+ array(
+ 'browserName' => '',
+ 'desiredCapabilities' => array(
+ 'app' => 'safari',
+ 'device' => 'iPhone Simulator',
+ 'version' => '6.1',
+ 'platform' => 'Mac 10.8',
+ )
+ ),
+ // run Chrome on Linux on Sauce
+ array(
+ 'browserName' => 'chrome',
+ 'desiredCapabilities' => array(
+ 'version' => '30',
+ 'platform' => 'Linux'
+ )
+ )
+ // run Mobile Browser on Android
+ // array(
+ // 'browserName' => 'Android',
+ // 'desiredCapabilities' => array(
+ // 'version' => '4.0',
+ // 'platform' => 'Linux',
+ // )
+ // )
+
+ // run Chrome locally
+ //array(
+ //'browserName' => 'chrome',
+ //'local' => true,
+ //'sessionStrategy' => 'shared'
+ //)
+ );
+
+ public function setUp()
+ {
+ if (isset ($_SERVER["TRAVIS_JOB_NUMBER"])) {
+ $caps = $this->getDesiredCapabilities();
+ $caps['build'] = getenv ("TRAVIS_JOB_NUMBER");
+ $this->setDesiredCapabilities($caps);
+ }
+ parent::setUp ();
+ }
+
+ public function setUpPage()
+ {
+ if (isset ($_SERVER["TRAVIS_JOB_NUMBER"])) {
+ $this->url('http://127.0.0.1:8888/index.php');
+ } else {
+ $this->url('http://cops-demo.slucas.fr/index.php');
+ }
+
+ $driver = $this;
+ $title_test = function($value) use ($driver) {
+ $text = $driver->byXPath('//h1')->text ();
+ return $text == $value;
+ };
+
+ $this->spinAssert("Home Title", $title_test, [ "COPS DEMO" ]);
+ }
+
+ public function string_to_ascii($string)
+ {
+ $ascii = NULL;
+
+ for ($i = 0; $i < strlen($string); $i++)
+ {
+ $ascii += ord($string[$i]);
+ }
+
+ return mb_detect_encoding($string) . "X" . $ascii;
+ }
+
+ public function testTitle()
+ {
+ $driver = $this;
+ $title_test = function($value) use ($driver) {
+ $text = $driver->byXPath('//h1')->text ();
+ return $text == $value;
+ };
+
+ $author = $this->byXPath ('//h2[contains(text(), "Authors")]');
+ $author->click ();
+
+ $this->spinAssert("Author Title", $title_test, [ "AUTHORS" ]);
+ }
+
+ public function testCog()
+ {
+ $cog = $this->byId ("searchImage");
+
+ $search = $this->byName ("query");
+ $this->assertFalse ($search->displayed ());
+
+ $cog->click ();
+
+ $search = $this->byName ("query");
+ $this->assertTrue ($search->displayed ());
+ }
+}
diff --git a/test/baseTest.php b/test/baseTest.php
index 7b2c21c..423930d 100644
--- a/test/baseTest.php
+++ b/test/baseTest.php
@@ -42,4 +42,19 @@ class BaseTest extends PHPUnit_Framework_TestCase
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = "en";
localize ("authors.title", -1, true);
}
+
+ public function testBaseFunction () {
+ global $config;
+
+ $this->assertFalse (Base::isMultipleDatabaseEnabled ());
+ $this->assertEquals (array ("" => dirname(__FILE__) . "/BaseWithSomeBooks/"), Base::getDbList ());
+
+ $config['calibre_directory'] = array ("Some books" => dirname(__FILE__) . "/BaseWithSomeBooks/",
+ "One book" => dirname(__FILE__) . "/BaseWithOneBook/");
+
+ $this->assertTrue (Base::isMultipleDatabaseEnabled ());
+ $this->assertEquals ("Some books", Base::getDbName (0));
+ $this->assertEquals ("One book", Base::getDbName (1));
+ $this->assertEquals ($config['calibre_directory'], Base::getDbList ());
+ }
}
\ No newline at end of file
diff --git a/test/bookTest.php b/test/bookTest.php
index 4a696d9..ed5537c 100644
--- a/test/bookTest.php
+++ b/test/bookTest.php
@@ -118,6 +118,7 @@ class BookTest extends PHPUnit_Framework_TestCase
{
// All books by first letter
list ($entryArray, $totalNumber) = Book::getBooksByStartingLetter ("T", -1);
+ $this->assertEquals (-1, $totalNumber);
$this->assertCount (3, $entryArray);
}
@@ -200,4 +201,30 @@ class BookTest extends PHPUnit_Framework_TestCase
$_GET["search"] = NULL;
}
+ public function testTypeaheadSearchMultiDatabase ()
+ {
+ global $config;
+ $_GET["query"] = "art";
+ $_GET["search"] = "1";
+ $_GET["multi"] = "1";
+
+ $config['calibre_directory'] = array ("Some books" => dirname(__FILE__) . "/BaseWithSomeBooks/",
+ "One book" => dirname(__FILE__) . "/BaseWithOneBook/");
+
+ $array = getJson ();
+
+ $this->assertCount (4, $array);
+ $this->assertEquals ("Some books", $array[0]["title"]);
+ $this->assertEquals ("No book", $array[1]["title"]);
+ $this->assertEquals ("One book", $array[2]["title"]);
+ $this->assertEquals ("1 book", $array[3]["title"]);
+
+ $_GET["query"] = NULL;
+ $_GET["search"] = NULL;
+ }
+
+ public function tearDown () {
+ Base::clearDb ();
+ }
+
}
\ No newline at end of file
diff --git a/test/config_local.php.sauce b/test/config_local.php.sauce
new file mode 100644
index 0000000..7e3c310
--- /dev/null
+++ b/test/config_local.php.sauce
@@ -0,0 +1,17 @@
+ dirname(__FILE__) . "/BaseWithOneBook/");
$page = Base::PAGE_INDEX;
$query = NULL;
- $search = NULL;
$qid = NULL;
$n = "1";
- $database = NULL;
$currentPage = Page::getPage ($page, $qid, $query, $n);
$currentPage->InitializeContent ();
@@ -34,4 +32,31 @@ class PageMultiDatabaseTest extends PHPUnit_Framework_TestCase
$this->assertEquals ("1 book", $currentPage->entryArray [1]->content);
$this->assertFalse ($currentPage->ContainsBook ());
}
-}
\ No newline at end of file
+
+ public function testPageSearchXXX ()
+ {
+ global $config;
+ $config['calibre_directory'] = array ("Some books" => dirname(__FILE__) . "/BaseWithSomeBooks/",
+ "One book" => dirname(__FILE__) . "/BaseWithOneBook/");
+ $page = Base::PAGE_OPENSEARCH_QUERY;
+ $query = "art";
+ $qid = NULL;
+ $n = "1";
+
+ $currentPage = Page::getPage ($page, $qid, $query, $n);
+ $currentPage->InitializeContent ();
+
+ $this->assertEquals ("Search result for *art*", $currentPage->title);
+ $this->assertCount (2, $currentPage->entryArray);
+ $this->assertEquals ("Some books", $currentPage->entryArray [0]->title);
+ $this->assertEquals ("10 books", $currentPage->entryArray [0]->content);
+ $this->assertEquals ("One book", $currentPage->entryArray [1]->title);
+ $this->assertEquals ("1 book", $currentPage->entryArray [1]->content);
+ $this->assertFalse ($currentPage->ContainsBook ());
+ }
+
+ public static function tearDownAfterClass () {
+ Base::clearDb ();
+ }
+
+}
diff --git a/test/pageTest.php b/test/pageTest.php
index e5657c5..61bbbbf 100644
--- a/test/pageTest.php
+++ b/test/pageTest.php
@@ -328,8 +328,7 @@ class PageTest extends PHPUnit_Framework_TestCase
$page = Base::PAGE_SERIE_DETAIL;
$query = NULL;
$qid = "1";
- $n = "1";
-
+ $n = "1";
$currentPage = Page::getPage ($page, $qid, $query, $n);
$currentPage->InitializeContent ();
@@ -374,7 +373,6 @@ class PageTest extends PHPUnit_Framework_TestCase
public function testPageAllTags ()
{
- global $config;
$page = Base::PAGE_ALL_TAGS;
$query = NULL;
$qid = NULL;
@@ -522,7 +520,6 @@ class PageTest extends PHPUnit_Framework_TestCase
public function testPageSearchScopeAuthors ()
{
- global $config;
$page = Base::PAGE_OPENSEARCH_QUERY;
$qid = NULL;
$n = "1";
@@ -543,7 +540,6 @@ class PageTest extends PHPUnit_Framework_TestCase
public function testPageSearchScopeSeries ()
{
- global $config;
$page = Base::PAGE_OPENSEARCH_QUERY;
$qid = NULL;
$n = "1";
@@ -564,7 +560,6 @@ class PageTest extends PHPUnit_Framework_TestCase
public function testPageSearchScopeBooks ()
{
- global $config;
$page = Base::PAGE_OPENSEARCH_QUERY;
$qid = NULL;
$n = "1";
@@ -584,7 +579,6 @@ class PageTest extends PHPUnit_Framework_TestCase
public function testPageSearchScopePublishers ()
{
- global $config;
$page = Base::PAGE_OPENSEARCH_QUERY;
$qid = NULL;
$n = "1";
diff --git a/test/prepareSauceTest.sh b/test/prepareSauceTest.sh
new file mode 100644
index 0000000..5355c6e
--- /dev/null
+++ b/test/prepareSauceTest.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+if [[ -z $SAUCE_ACCESS_KEY ]]
+ then
+ echo "No Sauce Api Key (Pull request)"
+ exit
+fi
+
+PHP_VERSION=`php -v|grep --only-matching --perl-regexp "PHP 5\.\\d+"`
+echo $PHP_VERSION
+
+
+if [[ $PHP_VERSION != "PHP 5.4" ]]
+ then
+ echo "Bad PHP version"
+ exit
+fi
+
+echo "Good PHP version"
+
+curl https://gist.github.com/seblucas/7692094/raw/e2a090e6ea639a0d700e6d02cee048fa2f6c8617/sauce_connect_setup.sh | bash
+curl -s https://raw.github.com/jlipps/sausage-bun/master/givememysausage.php | php
+cp -v test/config_local.php.sauce config_local.php
+php -S 127.0.0.1:8888 &
+vendor/bin/phpunit --no-configuration test/Sauce.php
+
+
diff --git a/util.js b/util.js
index 87c57f7..3c5b88d 100644
--- a/util.js
+++ b/util.js
@@ -281,7 +281,11 @@ updatePage = function (data) {
]);
$('input[name=query]').bind('typeahead:selected', function(obj, datum) {
- navigateTo (datum.navlink);
+ if (isPushStateEnabled) {
+ navigateTo (datum.navlink);
+ } else {
+ window.location = datum.navlink;
+ }
});
};