From 533386d53e2138937be31606f37e9230e1a000fd Mon Sep 17 00:00:00 2001 From: pbarroca <pbarroca@git-test.afi-sa.fr> Date: Fri, 18 May 2012 15:09:43 +0000 Subject: [PATCH] =?UTF-8?q?OPDS:=20Int=C3=A9gration=20de=20la=20recherche?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 1 + .../admin/controllers/OpdsController.php | 9 +++ .../admin/views/scripts/opds/browse.phtml | 1 + library/Class/OpdsCatalog.php | 19 ++++- .../Class/WebService/OPDS/CatalogReader.php | 57 +++++++++++++-- .../Class/WebService/OPDS/CatalogSearch.php | 71 +++++++++++++++++++ .../admin/controllers/OpdsControllerTest.php | 45 ++++++++++++ 7 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 library/Class/WebService/OPDS/CatalogSearch.php diff --git a/.gitattributes b/.gitattributes index fc6fe172142..b048ca9beee 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1804,6 +1804,7 @@ library/Class/WebService/OAI/ResponseFactory.php -text library/Class/WebService/OAI/ResumptionToken.php -text library/Class/WebService/OPDS/CatalogEntry.php -text library/Class/WebService/OPDS/CatalogReader.php -text +library/Class/WebService/OPDS/CatalogSearch.php -text library/Class/WebService/Premiere.php -text library/Class/WebService/ReseauxSociaux.php -text library/Class/WebService/ResumptionToken.php -text diff --git a/application/modules/admin/controllers/OpdsController.php b/application/modules/admin/controllers/OpdsController.php index 86682732322..d16badcd7b5 100644 --- a/application/modules/admin/controllers/OpdsController.php +++ b/application/modules/admin/controllers/OpdsController.php @@ -87,6 +87,15 @@ class Admin_OpdsController extends Zend_Controller_Action { return; } + if ($this->_request->isPost() + && $catalog->hasSearch() + && ($form = $catalog->getSearchForm()) + && $form->isValid($this->_request->getPost())) { + $this->_redirect('/admin/opds/browse/id/' . $catalog->getId() . '?entry=' . urlencode($catalog->getSearch()->entryForTerm($form->getValue('search')))); + return; + } + + if ($entry_url = $this->_getParam('entry')) $catalog = $catalog->newForEntry($entry_url); diff --git a/application/modules/admin/views/scripts/opds/browse.phtml b/application/modules/admin/views/scripts/opds/browse.phtml index 1263b531431..ed6d1d7b9a2 100644 --- a/application/modules/admin/views/scripts/opds/browse.phtml +++ b/application/modules/admin/views/scripts/opds/browse.phtml @@ -7,6 +7,7 @@ <?php } ?> </ul> <?php } ?> + <?php if ($this->catalog->hasSearch()) echo $this->catalog->getSearchForm();?> </div> <ul> <?php foreach($this->catalog->getEntries() as $entry) { ?> diff --git a/library/Class/OpdsCatalog.php b/library/Class/OpdsCatalog.php index 604b9c810f7..dccdf52b884 100644 --- a/library/Class/OpdsCatalog.php +++ b/library/Class/OpdsCatalog.php @@ -84,9 +84,26 @@ class Class_OpdsCatalog extends Storm_Model_Abstract { return $this; } -} + + public function hasSearch() { + return $this->getCatalogueReader()->hasSearch(); + } + public function getSearchForm() { + $form = new Zend_Form(array('method' => 'post')); + $form + ->addElement('text', 'search', array('required' => true, + 'allowEmpty' => false)) + ->addElement('submit', 'Rechercher'); + return $form; + } + public function getSearch() { + return $this->getCatalogueReader() + ->getSearch() + ->setWebClient($this->_web_client); + } +} ?> \ No newline at end of file diff --git a/library/Class/WebService/OPDS/CatalogReader.php b/library/Class/WebService/OPDS/CatalogReader.php index d0bb1df86dc..0ae1d50f2e3 100644 --- a/library/Class/WebService/OPDS/CatalogReader.php +++ b/library/Class/WebService/OPDS/CatalogReader.php @@ -23,6 +23,9 @@ class Class_WebService_OPDS_CatalogReader { protected $_current_entry; protected $_entries; protected $_metadatas; + protected $_searchUrl; + protected $_selfUrl; + protected $_search; protected $_xml_parser; public static function fromXML($xml) { @@ -32,8 +35,9 @@ class Class_WebService_OPDS_CatalogReader { public function parse($xml) { - $this->_entries = array(); - $this->_metadatas = array(); + $this->_entries = $this->_metadatas = array(); + $this->_search = $this->_searchUrl = $this->_selfUrl = null; + $this->_xml_parser = Class_WebService_XMLParser::newInstance(); $this->_xml_parser->setElementHandler($this); $this->_xml_parser->parse($xml); @@ -51,6 +55,20 @@ class Class_WebService_OPDS_CatalogReader { } + public function getSearch() { + if (null != $this->_search) + return $this->_search; + + if (null != $this->_searchUrl) + return $this->_search = new Class_WebService_OPDS_CatalogSearch($this->_normalizeUrl($this->_searchUrl)); + } + + + public function hasSearch() { + return null !== $this->getSearch(); + } + + public function startEntry() { $this->_current_entry = new Class_WebService_OPDS_CatalogEntry(); $this->_entries[] = $this->_current_entry; @@ -68,11 +86,23 @@ class Class_WebService_OPDS_CatalogReader { public function startLink($attributes) { - if (!$this->_xml_parser->inParents('entry')) + if (!array_key_exists('TYPE', $attributes)) return; + if (!$this->_xml_parser->inParents('entry')) { + if (array_key_exists('REL', $attributes) + && 'search' == $attributes['REL'] + && 'application/opensearchdescription+xml' == $attributes['TYPE']) + $this->_searchUrl = $attributes['HREF']; + + if (array_key_exists('REL', $attributes) + && 'self' == $attributes['REL'] + && 'application/atom+xml' == $attributes['TYPE']) + $this->_selfUrl = $attributes['HREF']; + return; + } + if (array_key_exists('REL', $attributes) - && array_key_exists('TYPE', $attributes) && 'http://opds-spec.org/acquisition' == $attributes['REL'] && in_array($attributes['TYPE'], array('application/epub+zip', 'application/pdf'))) { $this->_current_entry->addFile($attributes['HREF'], $attributes['TYPE']); @@ -134,8 +164,23 @@ class Class_WebService_OPDS_CatalogReader { protected function _concatMetadata($name, $value, $separator = ' - ') { $this->_metadatas[$name] = (isset($this->_metadatas[$name])) - ? $this->_metadatas[$name] . $separator . $value - : $value; + ? $this->_metadatas[$name] . $separator . $value + : $value; + } + + + protected function _normalizeUrl($url) { + if (!$this->_selfUrl) + return $url; + + if ('http' == substr($url, 0, 4)) + return $url; + + $urlInfos = parse_url($this->_selfUrl); + $normalized = $urlInfos['scheme'] . '://' . $urlInfos['host']; + if ('/' == substr($url, 0, 1)) + return $normalized . $url; + return $normalized . dirname($urlInfos['path']) . $url; } } ?> \ No newline at end of file diff --git a/library/Class/WebService/OPDS/CatalogSearch.php b/library/Class/WebService/OPDS/CatalogSearch.php new file mode 100644 index 00000000000..c5ea4bc78db --- /dev/null +++ b/library/Class/WebService/OPDS/CatalogSearch.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved. + * + * AFI-OPAC 2.0 is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * AFI-OPAC 2.0 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with AFI-OPAC 2.0; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +class Class_WebService_OPDS_CatalogSearch { + const TOKEN = '{searchTerms}'; + + protected $_url; + protected $_web_client; + protected $_xml_parser; + protected $_template; + + public function __construct($url) { + $this->_url = $url; + } + + + public function entryForTerm($term) { + $this->_template = null; + $xml = $this->getWebClient()->open_url($this->_url); + $this->_xml_parser = Class_WebService_XMLParser::newInstance(); + $this->_xml_parser->setElementHandler($this); + $this->_xml_parser->parse($xml); + + if (null == $this->_template) + return; + + return str_replace(self::TOKEN, urlencode($term), $this->_template); + } + + + public function startUrl($attributes) { + if (!array_key_exists('TYPE', $attributes) + || !array_key_exists('TEMPLATE', $attributes)) + return; + + if ('application/atom+xml' != $attributes['TYPE']) + return; + + $this->_template = $attributes['TEMPLATE']; + } + + + public function setWebClient($client) { + $this->_web_client = $client; + return $this; + } + + + public function getWebClient() { + if (null != $this->_web_client) + return $this->_web_client; + return new Class_WebService_SimpleWebClient(); + } +} diff --git a/tests/application/modules/admin/controllers/OpdsControllerTest.php b/tests/application/modules/admin/controllers/OpdsControllerTest.php index 8661d533ed0..7e0577df2c5 100644 --- a/tests/application/modules/admin/controllers/OpdsControllerTest.php +++ b/tests/application/modules/admin/controllers/OpdsControllerTest.php @@ -387,6 +387,12 @@ class Admin_OpdsControllerBrowseActionTest extends Admin_OpdsControllerBrowseEbo } + /** @test */ + public function searchFieldShouldBePresent() { + $this->assertXPath('//div[@class="panel"]//input[@name="search"]'); + } + + /** @test */ public function linkToLastPublishedShouldBePresent() { $this->assertXPathContentContains('//a[contains(@href, "/browse/id/1?entry=' . urlencode('http://www.ebooksgratuits.com/opds/feed.php') . '")]', @@ -486,6 +492,28 @@ class Admin_OpdsControllerBrowseEbooksGratuitsImportTest extends Admin_OpdsContr +class Admin_OpdsControllerBrowseSearchPostActionTest extends Admin_OpdsControllerBrowseEbooksGratuitsTestCase { + public function setUp() { + parent::setUp(); + + Class_OpdsCatalog::getLoader()->find(1) + ->getWebClient() + ->whenCalled('open_url') + ->with('http://www.ebooksgratuits.com/opds/opensearch.xml') + ->answers(OPDSFeedFixtures::ebooksGratuitsSearchDescriptionXml()); + + $this->postDispatch('/admin/opds/browse/id/1', array('search' => 'dracula')); + } + + + /** @test */ + public function shouldRedirectToBrowseWithSearchEntry() { + $this->assertRedirectTo('/admin/opds/browse/id/1?entry=' . urlencode('http://www.ebooksgratuits.com/opds/feed.php?mode=search&query=dracula')); + } +} + + + class OPDSFeedFixtures { public static function ebooksGratuitsStartXml() { return '<?xml version="1.0" encoding="UTF-8"?> @@ -615,5 +643,22 @@ class OPDSFeedFixtures { </feed>'; } + + + public static function ebooksGratuitsSearchDescriptionXml() { + return '<?xml version="1.0" encoding="UTF-8"?> +<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> + <ShortName>Ebooksgratuits</ShortName> + <Description>Recherche d\'e-books sur ebooksgratuits</Description> + <InputEncoding>UTF-8</InputEncoding> + <OutputEncoding>UTF-8</OutputEncoding> + <Image type="image/x-icon" width="16" height="16">http://www.ebooksgratuits.com/favicon.ico</Image> + + <Url type="text/html" template="http://www.ebooksgratuits.com/ebooks.php?titre={searchTerms}"/> + <Url type="application/atom+xml" template="http://www.ebooksgratuits.com/opds/feed.php?mode=search&query={searchTerms}"/> + <Url type="application/x-suggestions+json" rel="suggestions" template="http://www.ebooksgratuits.com/opds/search.php?mode=json&query={searchTerms}"/> + <Query role="example" searchTerms="robot" /> +</OpenSearchDescription>'; + } } ?> \ No newline at end of file -- GitLab