diff --git a/VERSIONS_HOTLINE/81451 b/VERSIONS_HOTLINE/81451 new file mode 100644 index 0000000000000000000000000000000000000000..8c0780704bcd347c4550840aff8d00d879edd7a3 --- /dev/null +++ b/VERSIONS_HOTLINE/81451 @@ -0,0 +1 @@ + - ticket #81451 : Flux RSS: affichage responsive + mise en cache des flux \ No newline at end of file diff --git a/application/modules/admin/controllers/RssController.php b/application/modules/admin/controllers/RssController.php index fe1299eb083ab794fdeddd7cdad08534757851d5..6983b82f45b4d50c25fbaf30262a6e9f3a4a6e93 100644 --- a/application/modules/admin/controllers/RssController.php +++ b/application/modules/admin/controllers/RssController.php @@ -201,17 +201,11 @@ class Admin_RssController extends Zend_Controller_Action { } - function rssdelAction() - { - $class_rss = new Class_Rss(); - $id = (int)$this->_request->getParam('id'); - $rss = $class_rss->getRssById($id); - $menu_deploy = $this->saveTreeMenu($rss[0]["ID_CAT"]); - if ($id > 0) { - $errorMessage = $class_rss->deleteRss($id); - if ( $errorMessage == ''){$this->_redirect('admin/rss?z='.$this->id_zone.'&b='.$this->id_bib);} - else{$this->_redirect('admin/rss?z='.$this->id_zone.'&b='.$this->id_bib);} - } + function rssdelAction() { + $rss = Class_Rss::find((int)$this->_getParam('id')); + $rss->delete(); + $this->saveTreeMenu($rss->getIdCat()); + $this->_helper->notify($this->_('Flux "%s" supprimé', $rss->getTitre())); $this->_redirect('admin/rss?z='.$this->id_zone.'&b='.$this->id_bib); } diff --git a/application/modules/opac/controllers/RssController.php b/application/modules/opac/controllers/RssController.php index cd8a0763ada37fd30a7fb691c7c99edb831e52bd..123c5ebb2400037cde2afb71e6a41191e48104ee 100644 --- a/application/modules/opac/controllers/RssController.php +++ b/application/modules/opac/controllers/RssController.php @@ -128,87 +128,17 @@ class RssController extends Zend_Controller_Action } - //--------------------------------------------------------------------------- - // Affichage d'1 flux en AJAX - //--------------------------------------------------------------------------- - function afficherrssAction() - { - $rssId = (int)$this->_request->getParam('id_rss', 0); - $rssClass = new Class_Rss(); - - $ret = $rssClass->getRssById($rssId); - $rss=$ret[0]; - - $html[]='<div class="rss_popup">'; - $html[]='<div class="header">'; - $html[]=$rss["TITRE"]; - $html[]='<img src="'.URL_ADMIN_IMG.'fermer.gif" style="cursor:pointer" onclick="closeRSSDiv(\''.$rssId.'\')"/>'; - $html[]='</div>'; - - $html[]='<div class="content">'; - - if(!$rss) - { - $html[] = $this->view->_("L'adresse du flux RSS n'est plus valide."); - } - else - { - $httpClient = Class_HttpClientFactory::getInstance()->newHttpClient(); - try - { - Zend_Feed::setHttpClient($httpClient); - $link = $rss["URL"]; - $rssFeed = ZendAfi_Feed::import($link); - - $i = 0; - $locale = Zend_Registry::get('locale'); - foreach ($rssFeed as $item) - { - $i++; - $titleRss = $item->title(); - $urlRss = $item->link(); - $descriptionRss = $item->description(); - - if ($item->pubDate()) - { - $date = $item->pubDate(); - if ($locale == 'en_US') $dateFormat = 'MM-dd-yyyy'; - else $dateFormat = 'dd-MM-yyyy'; - try - { - $zendDate = new Zend_Date($date, Zend_Date::RFC_2822); - $dateRss = $zendDate->toString($dateFormat); - } - catch (Exception $e) - { - $dateRss = ''; - } - } - else $dateRss = ''; - $html[]='<table width="100%" class="popup" cellspacing="0" align="center">'; - $html[]='<tr>'; - $html[]='<td class="popup_titre" width="70%"><a href="'.$urlRss.'" target="_blank" class="popup" onclick="' . $onclick . '">'.$titleRss.'</a></td>'; - $html[]='<td class="popup_titre" width="1%"></td>'; - $html[]='<td class="popup_titre" width="30%"><a href="#" onclick="return(false);">'.$dateRss.'</a></td>'; - $html[]='</tr>'; - $html[]='<tr>'; - $html[]='<td class="popup_ligne" colspan="3">'. $descriptionRss .'</td>'; - $html[]='</tr>'; - $html[]='</table>'; - } - - } - catch(Exception $e) - { - $html[] = $this->view->_("Il y a un problème avec l'adresse du flux RSS"); - } + function afficherrssAction() { + $renderer = $this->_helper->getHelper('viewRenderer'); + $renderer->setLayoutScript('empty.phtml'); - } - $html[]='</div>'; - - $this->_helper->rssResponse(implode("", $html)); + if (!$this->view->rss = Class_Rss::find((int)$this->_getParam('id_rss', 0))) { + $renderer->setNoRender(); + return; + } } + //--------------------------------------------------------------------------- // Flux RSS de la page des modérations //--------------------------------------------------------------------------- diff --git a/application/modules/opac/views/scripts/rss/afficherrss.phtml b/application/modules/opac/views/scripts/rss/afficherrss.phtml new file mode 100644 index 0000000000000000000000000000000000000000..e8b1c541492f373be84c3c78dca7e4ecc7534cc9 --- /dev/null +++ b/application/modules/opac/views/scripts/rss/afficherrss.phtml @@ -0,0 +1,32 @@ +<div> + <a href="#" onclick="closeRSSDiv('<?php echo $this->rss->getId() ?>')"> + <img src="<?php echo URL_ADMIN_IMG ?>fermer.gif"/> + <?php echo $this->_('Masquer le contenu du fil RSS') ?> + </a> + + <div class="content"> + <?php + $items = $this->rss->getFeedItems(); + + if (!$items) { + echo $this->_('Impossible de charger le flux'); + } + + + foreach($items as $item) + {?> + <div> + <h2> + <?php echo $this->tagAnchor($item->link(), + $item->title(), + ['class' => 'popup', + 'target' => '_blank']) ?> + </h2> + <span><?php echo $item->pubDate() ?></span> + <p> + <?php echo $item->description() ?> + </p> + </div> + <?php } ?> + </div> +</div> diff --git a/library/Class/Rss.php b/library/Class/Rss.php index 1dbdf3dd7146faba9c7ab105ccadc9ab57d60c45..36a850454aad8243334387c1d7c2dea1ca2e77b6 100644 --- a/library/Class/Rss.php +++ b/library/Class/Rss.php @@ -106,7 +106,7 @@ class RssLoader extends Storm_Model_Loader { class Class_Rss extends Storm_Model_Abstract { - use Trait_Indexable, Trait_Translator; + use Trait_Indexable, Trait_Translator, Trait_StaticFileSystem; protected $_loader_class = 'RssLoader', @@ -192,19 +192,51 @@ class Class_Rss extends Storm_Model_Abstract { } + protected function _xmlCacheFileName() { + return sprintf('%srss%d.xml', PATH_TEMP, $this->getId()); + } + + + protected function _clearXMLCache() { + $this->getFileSystem()->unlink($this->_xmlCacheFileName()); + } + + + public function afterSave() { + $this->_clearXMLCache(); + } + + + public function afterDelete() { + $this->_clearXMLCache(); + } + + /** * @return Class_Rss */ public function loadFeedItems() { - $this->_feed_items = array(); + $this->_feed_items = []; + $cache_filename = $this->_xmlCacheFileName(); - ZendAfi_Feed::setHttpClient(Class_HttpClientFactory::getInstance()->newHttpClient()); - $feed = ZendAfi_Feed::import($this->getUrl()); + try { + $feed_xml = (new Class_WebService_Abstract())->httpGet($this->getUrl()); + $feed = ZendAfi_Feed::importString($feed_xml); + + $this->getFileSystem()->file_put_contents($cache_filename, + $feed_xml); + } catch (Exception $e) { + $feed = []; + } + + if (!$feed && ($cached_xml = $this->getFileSystem()->file_get_contents($cache_filename))) { + $feed = ZendAfi_Feed::importString($cached_xml); + } - foreach($feed as $item) - $this->_feed_items []= new Class_RssItem($item); + foreach($feed as $item) + $this->_feed_items []= new Class_RssItem($item); - return $this; + return $this; } diff --git a/library/ZendAfi/Feed.php b/library/ZendAfi/Feed.php index 9259ed4e291ac93a45f046c3a758cb0065e38797..235efeede6695ca5795b8a71935019612448f4d2 100644 --- a/library/ZendAfi/Feed.php +++ b/library/ZendAfi/Feed.php @@ -16,35 +16,15 @@ * * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE * along with BOKEH; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** * Extend Zend_Feed to workaround a bug in Zend_Feed::import() * The original Zend_Feed::import() has a problem displaying Smart Quotes */ class ZendAfi_Feed extends Zend_Feed { - /** - * Imports a feed located at $uri. - * - * @param string $uri - * @throws Zend_Feed_Exception - * @return Zend_Feed_Abstract - */ - public static function import($uri) { - $client = self::getHttpClient(); - $client->setUri($uri); - $response = $client->request('GET'); - - if ($response->getStatus() !== 200) { - /** - * @see Zend_Feed_Exception - */ - //require_once 'Zend/Feed/Exception.php'; - throw new Zend_Feed_Exception('Feed failed to load, got response code ' - . $response->getStatus()); - } - - return self::importString(self::escapeIsoChars($response->getBody())); + public static function importString($string) { + return parent::importString(self::escapeIsoChars($string)); } diff --git a/tests/application/modules/admin/controllers/RssControllerTest.php b/tests/application/modules/admin/controllers/RssControllerTest.php index b21cc30ff2789ed9c8caa0229d50bc2457022a8a..7e0f3241b504110c4ecd44cc5657de23e28f0deb 100644 --- a/tests/application/modules/admin/controllers/RssControllerTest.php +++ b/tests/application/modules/admin/controllers/RssControllerTest.php @@ -20,7 +20,9 @@ */ require_once 'AdminAbstractControllerTestCase.php'; -abstract class RssControllerTestCase extends Admin_AbstractControllerTestCase { +abstract class Admin_RssControllerTestCase extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + public function setUp() { parent::setUp(); @@ -46,13 +48,21 @@ abstract class RssControllerTestCase extends Admin_AbstractControllerTestCase { ]) ]); + + Class_Rss::setFileSystem($this->mock()->whenCalled('unlink')->answers(true)); + } + + + public function tearDown() { + Class_Rss::setFileSystem(null); + parent::tearDown(); } } -class RssControllerIndexActionTest extends RssControllerTestCase { +class Admin_RssControllerIndexActionTest extends Admin_RssControllerTestCase { public function setUp() { parent::setUp(); $this->dispatch('/admin/rss', true); @@ -67,7 +77,7 @@ class RssControllerIndexActionTest extends RssControllerTestCase { -class RssControllerAddActionTest extends RssControllerTestCase { +class Admin_RssControllerAddActionTest extends Admin_RssControllerTestCase { public function setUp() { parent::setUp(); $this->dispatch('/admin/rss/rssadd/id/1', true); @@ -89,7 +99,7 @@ class RssControllerAddActionTest extends RssControllerTestCase { -class RssControllerCatEditActionTest extends RssControllerTestCase { +class Admin_RssControllerCatEditActionTest extends Admin_RssControllerTestCase { public function setUp() { parent::setUp(); $this->dispatch('/admin/rss/catedit/id/1', true); @@ -111,7 +121,7 @@ class RssControllerCatEditActionTest extends RssControllerTestCase { -class RssControllerPostCatEditActionTest extends RssControllerTestCase { +class Admin_RssControllerPostCatEditActionTest extends Admin_RssControllerTestCase { public function setUp() { parent::setUp(); $this->postDispatch('/admin/rss/catedit/id/1', @@ -134,7 +144,7 @@ class RssControllerPostCatEditActionTest extends RssControllerTestCase { -class RssControllerPostWrongDataCatEditActionTest extends RssControllerTestCase { +class Admin_RssControllerPostWrongDataCatEditActionTest extends Admin_RssControllerTestCase { /** @test */ public function withEmptyLibelleErrorShouldBeLibelleCouldNotBeEmpty() { $this->postDispatch('/admin/rss/catedit/id/1', ['libelle' => '']); @@ -163,7 +173,7 @@ class RssControllerPostWrongDataCatEditActionTest extends RssControllerTestCase -class RssControllerCatAddActionTest extends RssControllerTestCase { +class Admin_RssControllerCatAddActionTest extends Admin_RssControllerTestCase { public function setUp() { parent::setUp(); $this->dispatch('/admin/rss/catadd/id/1', true); @@ -193,7 +203,7 @@ class RssControllerCatAddActionTest extends RssControllerTestCase { -class RssControllerPostCatAddActionTest extends RssControllerTestCase { +class Admin_RssControllerPostCatAddActionTest extends Admin_RssControllerTestCase { public function setUp() { parent::setUp(); $this->postDispatch('/admin/rss/catadd/id/1', @@ -216,7 +226,47 @@ class RssControllerPostCatAddActionTest extends RssControllerTestCase { -class RssControllerEditActionTest extends RssControllerTestCase { +class Admin_RssControllerDeleteActionTest extends Admin_RssControllerTestCase { + public function setUp() { + parent::setUp(); + Class_Rss::setFileSystem($this->mock() + ->whenCalled('unlink') + ->with(PATH_TEMP . 'rss3.xml') + ->answers(true)); + + $this->dispatch('/admin/rss/rssdel/id/3', true); + Class_Rss::clearCache(); + } + + + /** @test */ + public function rssShouldHaveBeenDeleted() { + $this->assertNull(Class_Rss::find(3)); + } + + + /** @test */ + public function responseShouldNotifyFeedDeleted() { + $this->assertFlashMessengerContentContains('Flux "linuxfr" supprimé'); + } + + + /** @test */ + public function responseShouldBeARedirect() { + $this->assertRedirect(); + } + + + /** @test */ + public function cacheShouldHaveBeenCleared() { + $this->assertTrue(Class_Rss::getFileSystem()->methodHasBeenCalled('unlink')); + } +} + + + + + class Admin_RssControllerEditActionTest extends Admin_RssControllerTestCase { public function setUp() { parent::setUp(); $this->dispatch('/admin/rss/rssedit/id/3', true); @@ -264,11 +314,17 @@ class RssControllerEditActionTest extends RssControllerTestCase { -class RssControllerEditActionPostDispatchTest extends RssControllerTestCase { +class Admin_RssControllerEditActionPostDispatchTest extends Admin_RssControllerTestCase { protected $_rss; public function setUp() { parent::setUp(); + + Class_Rss::setFileSystem($this->mock() + ->whenCalled('unlink') + ->with(PATH_TEMP . 'rss3.xml') + ->answers(true)); + $this->postDispatch('/admin/rss/rssedit/id/3', ['titre' => 'Journaux LinuxFr', 'description' => 'Les journaux', @@ -281,6 +337,7 @@ class RssControllerEditActionPostDispatchTest extends RssControllerTestCase { $this->_rss = Class_Rss::find(3); } + /** @test */ public function titreShouldBeJournauxLinuxFr() { $this->assertEquals('Journaux LinuxFr', $this->_rss->getTitre()); @@ -315,11 +372,17 @@ class RssControllerEditActionPostDispatchTest extends RssControllerTestCase { public function notificationShouldContainsSuccessfullySaved() { $this->assertFlashMessengerContentContains('Flux RSS sauvegardé'); } + + + /** @test */ + public function cacheShouldHaveBeenCleared() { + $this->assertTrue(Class_Rss::getFileSystem()->methodHasBeenCalled('unlink')); + } } -class RssControllerEditActionPostDispatchErrorTest extends RssControllerTestCase { +class Admin_RssControllerEditActionPostDispatchErrorTest extends Admin_RssControllerTestCase { public function setUp() { parent::setUp(); $this->postDispatch('/admin/rss/rssedit/id/3', @@ -351,11 +414,12 @@ class RssControllerEditActionPostDispatchErrorTest extends RssControllerTestCase -class RssControllerAddActionPostDispatchTest extends RssControllerTestCase { +class Admin_RssControllerAddActionPostDispatchTest extends Admin_RssControllerTestCase { protected $_rss; public function setUp() { parent::setUp(); + $this->postDispatch('/admin/rss/rssadd', ['titre' => 'Journaux LinuxFr', 'description' => 'Les journaux', diff --git a/tests/application/modules/opac/controllers/RssControllerTest.php b/tests/application/modules/opac/controllers/RssControllerTest.php index 38c57b3552147a6fa83de20eab84c899c7a23c06..7463863cc61fdd54d69448cf153dc5076ceb0d75 100644 --- a/tests/application/modules/opac/controllers/RssControllerTest.php +++ b/tests/application/modules/opac/controllers/RssControllerTest.php @@ -18,49 +18,139 @@ * along with BOKEH; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -require_once 'AbstractControllerTestCase.php'; +abstract class RssControllerTestCase extends AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + + public function setUp() { + parent::setUp(); -class MockZendHttpClient extends Zend_Http_Client { - public function request($method = null) { - return new Zend_Http_Response(200, array(), RssFixtures::lemondeRSS()); + $this->fixture('Class_Rss', + ['id' => 15, + 'id_cat' => 11, + 'id_notice' => 86546, + 'titre' => 'Le monde', + 'description' => 'A la Une', + 'url' => 'http://rss.lemonde.fr/c/205/f/3050/index.rss', + 'date_maj' => '2010-04-01 10:47:58']); + + Class_WebService_Abstract::setHttpClient($this + ->mock() + ->whenCalled('open_url') + ->with('http://rss.lemonde.fr/c/205/f/3050/index.rss') + ->answers(RssFixtures::lemondeRSS()) + ->beStrict()); + + Class_Rss::setFileSystem($this->mock() + ->whenCalled('file_get_contents') + ->with(PATH_TEMP . 'rss15.xml') + ->answers(false) + + ->whenCalled('file_put_contents') + ->answers(12345) + + ->whenCalled('unlink') + ->answers(false)); + } + + + public function tearDown() { + Class_Rss::setFileSystem(null); + parent::tearDown(); } } -class RssControllerViewRawRssTest extends AbstractControllerTestCase { +class RssControllerAfficherRssTest extends RssControllerTestCase { public function setUp() { parent::setUp(); + $this->dispatch('rss/afficherrss/id_rss/15'); + } + + + /** @test */ + public function rssPopupShouldContainsLinkToHideContent() { + $this->assertXPathContentContains('//div/a[@onclick="closeRSSDiv(\'15\')"]', + 'Masquer le contenu'); + } + - Class_Rss::getLoader() - ->newInstanceWithId(15) - ->setIdCat(11) - ->setIdNotice(86546) - ->setTitre('Le monde') - ->setDescription('A la Une') - ->setUrl('http://rss.lemonde.fr/c/205/f/3050/index.rss') - ->setDateMaj('2010-04-01 10:47:58'); - - $preferences = array( - 'boite' => '', - 'titre' => 'Fils Rss', - 'type_aff' => '1', - 'id_categorie' => '', - 'id_items' => '15', - 'nb_aff' => '2' ); + /** @test */ + public function rssPopupShouldContainsBlogDelinquance() { + $this->assertXPathContentContains('//h2/a', + utf8_encode('Blog - Délinquance des mineurs : le septième rapport en sept ans')); + } + + + /** @test */ + public function cacheRSSShouldBeCreated() { + $this->assertEquals([PATH_TEMP . 'rss15.xml', RssFixtures::lemondeRSS()], + Class_Rss::getFileSystem()->getAttributesForLastCallOn('file_put_contents')); + } +} + + + + +class RssControllerAfficherRssErrorsTest extends RssControllerTestCase { + public function setUp() { + parent::setUp(); + Class_WebService_Abstract::setHttpClient($this + ->mock() + ->whenCalled('open_url') + ->with('http://rss.lemonde.fr/c/205/f/3050/index.rss') + ->answers('a plus') + ->beStrict()); + + } + + + /** @test */ + public function withInexistingRssShouldAnswerEmptyResponse() { + $this->dispatch('rss/afficherrss/id_rss/666'); + $this->assertEquals('', $this->_response->getBody()); + } + + + /** @test */ + public function withoutCacheShouldDisplayCouldNotGetFeed() { + $this->dispatch('rss/afficherrss/id_rss/15'); + $this->assertXPathContentContains('//div', 'Impossible de charger le flux', $this->_response->getBody()); + } + + + /** @test */ + public function withCacheShouldDisplayCacheContent() { + Class_Rss::getFileSystem()->whenCalled('file_get_contents') + ->with(PATH_TEMP . 'rss15.xml') + ->answers(RssFixtures::lemondeRSS()); + $this->dispatch('rss/afficherrss/id_rss/15'); + $this->assertXPathContentContains('//h2/a', + utf8_encode('Blog - Délinquance des mineurs : le septième rapport en sept ans')); + } +} + + + + +class RssControllerViewRawRssTest extends RssControllerTestCase { + public function setUp() { + parent::setUp(); + + $preferences = ['boite' => '', + 'titre' => 'Fils Rss', + 'type_aff' => '1', + 'id_categorie' => '', + 'id_items' => '15', + 'nb_aff' => '2' ]; Class_Profil::getLoader() ->newInstanceWithId(25) - ->setCfgAccueil(array( - 'modules' => array( - '1' => array( - 'division' => '1', - 'type_module' => 'RSS', - 'preferences' => $preferences)))); - + ->setCfgAccueil(['modules' => ['1' => [ 'division' => '1', + 'type_module' => 'RSS', + 'preferences' => $preferences]]]); - Class_HttpClientFactory::setInstance($this->mock()->whenCalled('newHttpClient')->answers(new MockZendHttpClient())); $this->dispatch('rss/view-raw-rss/id_rss/15/id_profil/25/id_module/1'); }