From 187aa620cfbedd6d7a55dc9a6df541fa152ad907 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Mon, 21 Oct 2013 16:42:36 +0200 Subject: [PATCH 1/5] Add login template variable to get last published items --- lib/Alchemy/Phrasea/Controller/Root/Login.php | 1 + lib/classes/Feed/Entry/Item.php | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/lib/Alchemy/Phrasea/Controller/Root/Login.php b/lib/Alchemy/Phrasea/Controller/Root/Login.php index 681b4407f7..b89caf0068 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Login.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Login.php @@ -50,6 +50,7 @@ class Login implements ControllerProviderInterface public static function getDefaultTemplateVariables(Application $app) { return array( + 'last_publication_items' => \Feed_Entry_Item::loadLatest($app, 20), 'instance_title' => $app['phraseanet.registry']->get('GV_homeTitle'), 'has_terms_of_use' => $app->hasTermsOfUse(), 'meta_description' => $app['phraseanet.registry']->get('GV_metaDescription'), diff --git a/lib/classes/Feed/Entry/Item.php b/lib/classes/Feed/Entry/Item.php index 6f54976a63..5246b216d2 100644 --- a/lib/classes/Feed/Entry/Item.php +++ b/lib/classes/Feed/Entry/Item.php @@ -9,6 +9,8 @@ * file that was distributed with this source code. */ +use Alchemy\Phrasea\Application; + /** * * @package Feeds @@ -207,6 +209,73 @@ class Feed_Entry_Item implements Feed_Entry_ItemInterface, cache_cacheableInterf return new self($appbox, $entry, $item_id); } + public static function loadLatest(Application $app, $nbItems = 20) + { + $execution = 0; + $items = array(); + + do { + $feeds = $entries = array(); + + $sql = 'SELECT en.id AS entry, f.id AS feed + FROM feed_entry_elements AS el + INNER JOIN feed_entries AS en ON (el.entry_id = en.id) + INNER JOIN feeds AS f ON (f.id = en.feed_id) + WHERE f.public = 1 AND f.base_id IS null + ORDER BY en.updated_on DESC + LIMIT ' . ($nbItems * $execution) .','. $nbItems; + + $stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql); + $stmt->execute(); + $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC); + $stmt->closeCursor(); + + foreach($rows as $row) { + if (!isset($feeds[$row['feed']])) { + $feeds[$row['feed']] = new Feed_Adapter($app, $row['feed']); + } + + if (!isset($entries[$row['entry']])) { + $entries[$row['entry']] = new Feed_Entry_Adapter($app, $feeds[$row['feed']], $row['entry']); + } + } + + foreach($entries as $entry) { + foreach ($entry->get_content() as $item) { + if (null !== $preview = $item->get_record()->get_subdef('preview')) { + if (null !== $permalink = $preview->get_permalink()) { + if (!isset($items[$item->get_id()])) { + $items[$item->get_id()] = array( + 'entry' => $entry, + 'record' => $item->get_record(), + 'preview' => $preview, + 'permalink' => $permalink + ); + + if (count($items) >= $nbItems) { + break; + } + } + } + } + } + + if (count($items) > $nbItems) { + break; + } + } + + if (count($rows) <= $nbItems) { + break; + } + + $execution++; + } + while (count($items) < $nbItems); + + return $items; + } + public function get_cache_key($option = null) { return 'feedentryitem_' . $this->get_id() . '_' . ($option ? '_' . $option : ''); From 22b2dddcf8cd56ea504fd6344d0085b65397a4ae Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Tue, 22 Oct 2013 11:09:29 +0200 Subject: [PATCH 2/5] Fix #1532 : Add checkbox on feed creation to disable email notifications --- lib/Alchemy/Phrasea/Controller/Prod/Feed.php | 2 +- lib/classes/Feed/Entry/Adapter.php | 4 ++-- lib/classes/eventsmanager/notify/feed.php | 5 +++-- templates/web/prod/actions/publish/publish.html.twig | 2 ++ www/skins/prod/000000/prodcolor.css | 8 ++++++++ www/skins/prod/959595/prodcolor.css | 8 ++++++++ 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Feed.php b/lib/Alchemy/Phrasea/Controller/Prod/Feed.php index b85df97637..e13c099542 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Feed.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Feed.php @@ -58,7 +58,7 @@ class Feed implements ControllerProviderInterface $author_name = $request->request->get('author_name'); $author_mail = $request->request->get('author_mail'); - $entry = \Feed_Entry_Adapter::create($app, $feed, $publisher, $title, $subtitle, $author_name, $author_mail); + $entry = \Feed_Entry_Adapter::create($app, $feed, $publisher, $title, $subtitle, $author_name, $author_mail, $request->request->get('notify')); $publishing = RecordsRequest::fromRequest($app, $request, true, array(), array('bas_chupub')); diff --git a/lib/classes/Feed/Entry/Adapter.php b/lib/classes/Feed/Entry/Adapter.php index ddf4c78856..8afe9b9953 100644 --- a/lib/classes/Feed/Entry/Adapter.php +++ b/lib/classes/Feed/Entry/Adapter.php @@ -508,7 +508,7 @@ class Feed_Entry_Adapter implements Feed_Entry_Interface, cache_cacheableInterfa } public static function create(Application $app, Feed_Adapter $feed - , Feed_Publisher_Adapter $publisher, $title, $subtitle, $author_name, $author_mail) + , Feed_Publisher_Adapter $publisher, $title, $subtitle, $author_name, $author_mail, $notify = true) { if ( ! $feed->is_publisher($publisher->get_user())) { throw new Exception_Feed_PublisherNotFound("Publisher not found"); @@ -544,7 +544,7 @@ class Feed_Entry_Adapter implements Feed_Entry_Interface, cache_cacheableInterfa $entry = new self($app, $feed, $entry_id); - $app['events-manager']->trigger('__FEED_ENTRY_CREATE__', array('entry_id' => $entry_id), $entry); + $app['events-manager']->trigger('__FEED_ENTRY_CREATE__', array('entry_id' => $entry_id, 'notify_email' => (Boolean) $notify), $entry); return $entry; } diff --git a/lib/classes/eventsmanager/notify/feed.php b/lib/classes/eventsmanager/notify/feed.php index 5784bd9f5b..43f8845ed4 100644 --- a/lib/classes/eventsmanager/notify/feed.php +++ b/lib/classes/eventsmanager/notify/feed.php @@ -45,7 +45,8 @@ class eventsmanager_notify_feed extends eventsmanager_notifyAbstract public function fire($event, $params, &$entry) { $params = array( - 'entry_id' => $entry->get_id() + 'entry_id' => $entry->get_id(), + 'notify_email' => $params['notify_email'], ); $dom_xml = new DOMDocument('1.0', 'UTF-8'); @@ -91,7 +92,7 @@ class eventsmanager_notify_feed extends eventsmanager_notifyAbstract /* @var $user_to_notif \User_Adapter */ $mailed = false; - if ($this->shouldSendNotificationFor($user_to_notif->get_id())) { + if ($params['notify_email'] && $this->shouldSendNotificationFor($user_to_notif->get_id())) { $readyToSend = false; try { $token = $this->app['tokens']->getUrlToken( diff --git a/templates/web/prod/actions/publish/publish.html.twig b/templates/web/prod/actions/publish/publish.html.twig index 3bd4fcebfb..a9182d00e7 100644 --- a/templates/web/prod/actions/publish/publish.html.twig +++ b/templates/web/prod/actions/publish/publish.html.twig @@ -39,6 +39,8 @@ {% set title = publishing.basket().getName() %} {% set desc = publishing.basket().getDescription() %} {% endif %} + +
diff --git a/www/skins/prod/000000/prodcolor.css b/www/skins/prod/000000/prodcolor.css index 990ab13a14..7dd36cc001 100644 --- a/www/skins/prod/000000/prodcolor.css +++ b/www/skins/prod/000000/prodcolor.css @@ -3203,6 +3203,14 @@ dans l'onglet thesaurus : arbres, menus contextuels margin: 3px 0 10px 0; } +#modal_feed form label { + display:inline; +} + +#modal_feed form input.checkbox { + width: 20px; +} + #modal_feed form textarea { height: 60px; resize: none; diff --git a/www/skins/prod/959595/prodcolor.css b/www/skins/prod/959595/prodcolor.css index 991c6a9d15..9042ab4d5e 100644 --- a/www/skins/prod/959595/prodcolor.css +++ b/www/skins/prod/959595/prodcolor.css @@ -3283,6 +3283,14 @@ dans l'onglet thesaurus : arbres, menus contextuels margin: 3px 0 10px 0; } +#modal_feed form label { + display:inline; +} + +#modal_feed form input.checkbox { + width: 20px; +} + #modal_feed form textarea { height: 60px; resize: none; From 66facbabeabd3974ad4e68b956fcc9676e120642 Mon Sep 17 00:00:00 2001 From: Nicolas Le Goff Date: Mon, 21 Oct 2013 16:43:12 +0200 Subject: [PATCH 3/5] Add Bootstrap Carousel & Galleria as homepage presentation mode --- bower.json | 3 +- lib/Alchemy/Phrasea/Controller/Root/Login.php | 16 ++++- lib/classes/Feed/Entry/Item.php | 66 ++++++++----------- lib/conf.d/_GV_template.inc | 4 +- .../web/login/include/carousel.html.twig | 16 +++++ .../web/login/include/galleria.html.twig | 25 +++++++ templates/web/login/index.html.twig | 26 ++++++++ .../Feed/Entry/Feed_Entry_ItemTest.php | 47 +++++++++++++ 8 files changed, 162 insertions(+), 41 deletions(-) create mode 100644 templates/web/login/include/carousel.html.twig create mode 100644 templates/web/login/include/galleria.html.twig diff --git a/bower.json b/bower.json index 22cff51a26..2a8a1161c5 100644 --- a/bower.json +++ b/bower.json @@ -25,6 +25,7 @@ "js-fixtures": "https://github.com/badunk/js-fixtures/archive/master.zip", "bootstrap-multiselect": "https://github.com/davidstutz/bootstrap-multiselect.git#bootstrap-2-3", "zxcvbn" : "https://github.com/lowe/zxcvbn.git", - "geonames-server-jquery-plugin" : "~0.2" + "geonames-server-jquery-plugin" : "~0.2", + "jquery-galleria": "1.2.9" } } diff --git a/lib/Alchemy/Phrasea/Controller/Root/Login.php b/lib/Alchemy/Phrasea/Controller/Root/Login.php index b89caf0068..94c3fe05d4 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Login.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Login.php @@ -49,8 +49,22 @@ class Login implements ControllerProviderInterface { public static function getDefaultTemplateVariables(Application $app) { + $items = array(); + + foreach(\Feed_Entry_Item::loadLatest($app, 20) as $item) { + $record = $item->get_record(); + $preview = $record->get_subdef('preview'); + $permalink = $preview->get_permalink(); + + $items[] = array( + 'record' => $record, + 'preview' => $preview, + 'permalink' => $permalink + ); + } + return array( - 'last_publication_items' => \Feed_Entry_Item::loadLatest($app, 20), + 'last_publication_items' => $items, 'instance_title' => $app['phraseanet.registry']->get('GV_homeTitle'), 'has_terms_of_use' => $app->hasTermsOfUse(), 'meta_description' => $app['phraseanet.registry']->get('GV_metaDescription'), diff --git a/lib/classes/Feed/Entry/Item.php b/lib/classes/Feed/Entry/Item.php index 5246b216d2..a05dfbc7c3 100644 --- a/lib/classes/Feed/Entry/Item.php +++ b/lib/classes/Feed/Entry/Item.php @@ -209,21 +209,27 @@ class Feed_Entry_Item implements Feed_Entry_ItemInterface, cache_cacheableInterf return new self($appbox, $entry, $item_id); } + /** + * Gets latest items from public feeds. + * + * @param Application $app + * @param integer $nbItems + * + * @return Feed_Entry_Item[] An array of Feed_Entry_Item + */ public static function loadLatest(Application $app, $nbItems = 20) { $execution = 0; - $items = array(); + $items = $feeds = $entries = array(); do { - $feeds = $entries = array(); - - $sql = 'SELECT en.id AS entry, f.id AS feed - FROM feed_entry_elements AS el - INNER JOIN feed_entries AS en ON (el.entry_id = en.id) - INNER JOIN feeds AS f ON (f.id = en.feed_id) - WHERE f.public = 1 AND f.base_id IS null - ORDER BY en.updated_on DESC - LIMIT ' . ($nbItems * $execution) .','. $nbItems; + $sql = 'SELECT el.id AS item, en.id AS entry, f.id AS feed + FROM feed_entry_elements AS el + INNER JOIN feed_entries AS en ON (el.entry_id = en.id) + INNER JOIN feeds AS f ON (f.id = en.feed_id) + WHERE f.public = 1 AND f.base_id IS null + ORDER BY en.updated_on DESC + LIMIT ' . ((integer) $nbItems * $execution) .','. (integer) $nbItems; $stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql); $stmt->execute(); @@ -238,40 +244,24 @@ class Feed_Entry_Item implements Feed_Entry_ItemInterface, cache_cacheableInterf if (!isset($entries[$row['entry']])) { $entries[$row['entry']] = new Feed_Entry_Adapter($app, $feeds[$row['feed']], $row['entry']); } - } - foreach($entries as $entry) { - foreach ($entry->get_content() as $item) { - if (null !== $preview = $item->get_record()->get_subdef('preview')) { - if (null !== $permalink = $preview->get_permalink()) { - if (!isset($items[$item->get_id()])) { - $items[$item->get_id()] = array( - 'entry' => $entry, - 'record' => $item->get_record(), - 'preview' => $preview, - 'permalink' => $permalink - ); + if (!isset($items[$row['item']])) { + $item = new self($app['phraseanet.appbox'], $entries[$row['entry']], $row['item']); - if (count($items) >= $nbItems) { - break; - } - } - } - } + if (null !== $preview = $item->get_record()->get_subdef('preview')) { + if (null !== $permalink = $preview->get_permalink()) { + $items[$row['item']] = $item; + + if (count($items) >= $nbItems) { + break; + } + } + } } - - if (count($items) > $nbItems) { - break; - } - } - - if (count($rows) <= $nbItems) { - break; } $execution++; - } - while (count($items) < $nbItems); + } while (count($items) < $nbItems && count($rows) !== 0); return $items; } diff --git a/lib/conf.d/_GV_template.inc b/lib/conf.d/_GV_template.inc index 2bf9d89cc9..62c401375a 100644 --- a/lib/conf.d/_GV_template.inc +++ b/lib/conf.d/_GV_template.inc @@ -348,7 +348,9 @@ return call_user_func_array(function(Application $app) { 'available' => array( 'DISPLAYx1' => _('Single image'), 'SCROLL' => _('Slide show'), - 'COOLIRIS' => 'Cooliris' + 'COOLIRIS' => 'Cooliris', + 'CAROUSEL' => _('Carousel'), + 'GALLERIA' => _('Gallery') ), 'required' => true ) diff --git a/templates/web/login/include/carousel.html.twig b/templates/web/login/include/carousel.html.twig new file mode 100644 index 0000000000..1fdb8aa05d --- /dev/null +++ b/templates/web/login/include/carousel.html.twig @@ -0,0 +1,16 @@ + + + diff --git a/templates/web/login/include/galleria.html.twig b/templates/web/login/include/galleria.html.twig new file mode 100644 index 0000000000..b4752d36fb --- /dev/null +++ b/templates/web/login/include/galleria.html.twig @@ -0,0 +1,25 @@ + + + diff --git a/templates/web/login/index.html.twig b/templates/web/login/index.html.twig index c69c67b529..be870bf0a3 100644 --- a/templates/web/login/index.html.twig +++ b/templates/web/login/index.html.twig @@ -123,9 +123,35 @@ {% include 'login/include/cooliris-content.html.twig' %} {% elseif display_layout == "SCROLL" %} {% include 'login/include/scroll-content.html.twig' %} + {% elseif display_layout == "CAROUSEL" %} + {% include 'login/include/carousel.html.twig' %} + {% elseif display_layout == "GALLERIA" %} + {% include 'login/include/galleria.html.twig' %} {% endif %} {% endblock %} +{% block header_stylesheet %} + {{ parent() }} + + {% if display_layout == "CAROUSEL" %} + + {% elseif display_layout == "GALLERIA" %} + + {% endif %} +{% endblock %} + {% block scripts %} {{ parent() }} diff --git a/tests/classes/Feed/Entry/Feed_Entry_ItemTest.php b/tests/classes/Feed/Entry/Feed_Entry_ItemTest.php index 9ada243e7b..b201776a7d 100644 --- a/tests/classes/Feed/Entry/Feed_Entry_ItemTest.php +++ b/tests/classes/Feed/Entry/Feed_Entry_ItemTest.php @@ -66,4 +66,51 @@ class Feed_Entry_ItemTest extends PhraseanetPHPUnitAuthenticatedAbstract $this->assertInstanceOf('Feed_Entry_Adapter', self::$object->get_entry()); $this->assertEquals(self::$entry->get_id(), self::$object->get_entry()->get_id()); } + + public function testLoadLatestItems() + { + $this->deleteEntries(); + + self::$feed->set_public(true); + + foreach(range(1, 2) as $i) { + Feed_Entry_Item::create(self::$DI['app']['phraseanet.appbox'], self::$entry, self::$DI['record_'.$i]); + } + + $this->assertCount(2, Feed_Entry_Item::loadLatest(self::$DI['app'], 20)); + } + + public function testLoadLatestItemsLessItems() + { + $this->deleteEntries(); + + self::$feed->set_public(true); + + foreach(range(1, 2) as $i) { + Feed_Entry_Item::create(self::$DI['app']['phraseanet.appbox'], self::$entry, self::$DI['record_'.$i]); + } + + $this->assertCount(1, Feed_Entry_Item::loadLatest(self::$DI['app'], 1)); + } + + public function testLoadLatestItemsNoPublic() + { + $this->deleteEntries(); + + self::$feed->set_public(false); + + foreach(range(1, 2) as $i) { + Feed_Entry_Item::create(self::$DI['app']['phraseanet.appbox'], self::$entry, self::$DI['record_'.$i]); + } + + $this->assertCount(0, Feed_Entry_Item::loadLatest(self::$DI['app'], 20)); + } + + private function deleteEntries() + { + $sql = "TRUNCATE feed_entry_elements"; + $stmt = self::$DI['app']['phraseanet.appbox']->get_connection()->prepare($sql); + $stmt->execute(); + $stmt->closeCursor(); + } } From 3633c1a9266a99268f0cd6759535f9f5f6610be6 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Tue, 22 Oct 2013 12:15:21 +0200 Subject: [PATCH 4/5] Fix #1531 : Allow preview access on public feed entry items --- lib/Alchemy/Phrasea/Controller/Permalink.php | 31 ++++++++++++------- lib/classes/ACL.php | 4 +++ lib/classes/Feed/Entry/Item.php | 27 ++++++++++++++++ .../Phrasea/Application/OverviewTest.php | 31 +++++++++++++++++++ .../Feed/Entry/Feed_Entry_ItemTest.php | 15 +++++++++ 5 files changed, 97 insertions(+), 11 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Permalink.php b/lib/Alchemy/Phrasea/Controller/Permalink.php index 5c57aafde9..151ccef71a 100644 --- a/lib/Alchemy/Phrasea/Controller/Permalink.php +++ b/lib/Alchemy/Phrasea/Controller/Permalink.php @@ -31,14 +31,26 @@ class Permalink extends AbstractDelivery $that = $this; - $deliverPermaview = function($sbas_id, $record_id, $token, $subdef, PhraseaApplication $app) { + $retrieveRecord = function ($app, $databox, $token, $record_id, $subdef) { + if (\databox_subdef::CLASS_THUMBNAIL === $subdef) { + $record = $databox->get_record($record_id); + } elseif (\databox_subdef::CLASS_PREVIEW === $subdef && \Feed_Entry_Item::is_record_in_public_feed($app, $databox->get_sbas_id(), $record_id)) { + $record = $databox->get_record($record_id); + } else { + $record = \media_Permalink_Adapter::challenge_token($app, $databox, $token, $record_id, $subdef); + + if (!($record instanceof \record_adapter)) { + throw new NotFoundHttpException('Wrong token.'); + } + } + + return $record; + }; + + $deliverPermaview = function($sbas_id, $record_id, $token, $subdef, PhraseaApplication $app) use ($retrieveRecord) { $databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id); - $record = \media_Permalink_Adapter::challenge_token($app, $databox, $token, $record_id, $subdef); - - if (!$record instanceof \record_adapter) { - throw new NotFoundHttpException('bad luck'); - } + $record = $retrieveRecord($app, $databox, $token, $record_id, $subdef); $params = array( 'subdef_name' => $subdef @@ -51,13 +63,10 @@ class Permalink extends AbstractDelivery return $app['twig']->render('overview.html.twig', $params); }; - $deliverPermalink = function(PhraseaApplication $app, $sbas_id, $record_id, $token, $subdef) use ($that) { + $deliverPermalink = function(PhraseaApplication $app, $sbas_id, $record_id, $token, $subdef) use ($that, $retrieveRecord) { $databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id); - $record = \media_Permalink_Adapter::challenge_token($app, $databox, $token, $record_id, $subdef); - if (!($record instanceof \record_adapter)) { - throw new NotFoundHttpException('bad luck'); - } + $record = $retrieveRecord($app, $databox, $token, $record_id, $subdef); $watermark = $stamp = false; diff --git a/lib/classes/ACL.php b/lib/classes/ACL.php index 48f226d3a5..cdd45a0dc7 100644 --- a/lib/classes/ACL.php +++ b/lib/classes/ACL.php @@ -253,6 +253,10 @@ class ACL implements cache_cacheableInterface $granted = true; } + if (false === $granted && \Feed_Entry_Item::is_record_in_public_feed($this->app, $record->get_sbas_id(), $record->get_record_id())) { + $granted = true; + } + return $granted; } diff --git a/lib/classes/Feed/Entry/Item.php b/lib/classes/Feed/Entry/Item.php index a05dfbc7c3..3c20ae0514 100644 --- a/lib/classes/Feed/Entry/Item.php +++ b/lib/classes/Feed/Entry/Item.php @@ -168,6 +168,33 @@ class Feed_Entry_Item implements Feed_Entry_ItemInterface, cache_cacheableInterf return; } + /** + * Checks if a record is published in a public feed. + * + * @param Application $app + * @param integer $sbas_id + * @param integer $record_id + * + * @return Boolean + */ + public static function is_record_in_public_feed(Application $app, $sbas_id, $record_id) + { + $sql = 'SELECT count(i.id) as total + FROM feed_entry_elements as i, feed_entries e, feeds f + WHERE i.sbas_id = :sbas_id + AND i.record_id = :record_id + AND i.entry_id = e.id + AND e.feed_id = f.id + AND f.public = 1'; + + $stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql); + $stmt->execute(array(':sbas_id' => $sbas_id, ':record_id' => $record_id)); + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + $stmt->closeCursor(); + + return $row['total'] > 0; + } + /** * * @param appbox $appbox diff --git a/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php b/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php index 501b04c31a..793882265b 100644 --- a/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php +++ b/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php @@ -61,6 +61,22 @@ class ApplicationOverviewTest extends \PhraseanetWebTestCaseAuthenticatedAbstrac $this->assertForbiddenResponse(self::$DI['client']->getResponse()); } + public function testDatafilesRouteNotAuthenticatedIsOkInPublicFeed() + { + $publicFeed = \Feed_Adapter::create(self::$DI['app'], self::$DI['user'], 'titre', 'subtitre'); + $publicFeed->set_public(true); + $publisher = \Feed_Publisher_Adapter::getPublisher(self::$DI['app']['phraseanet.appbox'], $publicFeed, self::$DI['user']); + $entry = \Feed_Entry_Adapter::create(self::$DI['app'], $publicFeed, $publisher, 'titre', 'sub titre entry', 'author name', 'author email', false); + self::$DI['record_1']->move_to_collection(self::$DI['collection_no_access'], self::$DI['app']['phraseanet.appbox']); + $item = \Feed_Entry_Item::create(self::$DI['app']['phraseanet.appbox'], $entry, self::$DI['record_1']); + + self::$DI['client']->request('GET', '/datafiles/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/preview/'); + + $this->assertEquals(200, self::$DI['client']->getResponse()->getStatusCode()); + self::$DI['record_1']->move_to_collection(self::$DI['collection'], self::$DI['app']['phraseanet.appbox']); + $publicFeed->set_public(false); + } + public function testDatafilesRouteNotAuthenticatedUnknownSubdef() { self::$DI['app']['authentication']->closeAccount(); @@ -189,6 +205,21 @@ class ApplicationOverviewTest extends \PhraseanetWebTestCaseAuthenticatedAbstrac $this->assertEquals(200, $response->getStatusCode()); } + public function testPermalinkRouteNotAuthenticatedIsOkInPublicFeed() + { + $publicFeed = \Feed_Adapter::create(self::$DI['app'], self::$DI['user'], 'titre', 'subtitre'); + $publicFeed->set_public(true); + $publisher = \Feed_Publisher_Adapter::getPublisher(self::$DI['app']['phraseanet.appbox'], $publicFeed, self::$DI['user']); + $entry = \Feed_Entry_Adapter::create(self::$DI['app'], $publicFeed, $publisher, 'titre', 'sub titre entry', 'author name', 'author email', false); + $item = \Feed_Entry_Item::create(self::$DI['app']['phraseanet.appbox'], $entry, self::$DI['record_1']); + + self::$DI['app']['authentication']->closeAccount(); + self::$DI['client']->request('GET', '/permalink/v1/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/preview/'); + + $this->assertEquals(200, self::$DI['client']->getResponse()->getStatusCode()); + $publicFeed->set_public(false); + } + protected function get_a_permaviewBCcompatibility(array $headers = array()) { $token = self::$DI['record_1']->get_preview()->get_permalink()->get_token(); diff --git a/tests/classes/Feed/Entry/Feed_Entry_ItemTest.php b/tests/classes/Feed/Entry/Feed_Entry_ItemTest.php index b201776a7d..e199120f98 100644 --- a/tests/classes/Feed/Entry/Feed_Entry_ItemTest.php +++ b/tests/classes/Feed/Entry/Feed_Entry_ItemTest.php @@ -106,6 +106,21 @@ class Feed_Entry_ItemTest extends PhraseanetPHPUnitAuthenticatedAbstract $this->assertCount(0, Feed_Entry_Item::loadLatest(self::$DI['app'], 20)); } + public function testIs_record_in_public_feed() + { + $this->deleteEntries(); + + $publicFeed = Feed_Adapter::create(self::$DI['app'], self::$DI['user'], self::$feed_title, self::$feed_subtitle); + $publicFeed->set_public(true); + $publisher = Feed_Publisher_Adapter::getPublisher(self::$DI['app']['phraseanet.appbox'], $publicFeed, self::$DI['user']); + $entry = Feed_Entry_Adapter::create(self::$DI['app'], $publicFeed, $publisher, self::$title, self::$subtitle, self::$author_name, self::$author_email, false); + $item = Feed_Entry_Item::create(self::$DI['app']['phraseanet.appbox'], $entry, self::$DI['record_1']); + + $this->assertTrue(\Feed_Entry_Item::is_record_in_public_feed(self::$DI['app'], self::$DI['record_1']->get_sbas_id(), self::$DI['record_1']->get_record_id())); + $publicFeed->set_public(false); + $this->assertFalse(\Feed_Entry_Item::is_record_in_public_feed(self::$DI['app'], self::$DI['record_1']->get_sbas_id(), self::$DI['record_1']->get_record_id())); + } + private function deleteEntries() { $sql = "TRUNCATE feed_entry_elements"; From fafe2dca0fb92681697323641411e9a8685fd2a2 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Thu, 24 Oct 2013 16:53:06 +0200 Subject: [PATCH 5/5] Fix latest merge --- lib/Alchemy/Phrasea/Controller/Permalink.php | 6 +- lib/Alchemy/Phrasea/Controller/Root/Login.php | 2 +- .../Repositories/FeedItemRepository.php | 1 - lib/classes/ACL.php | 5 + lib/classes/User/Adapter.php | 15 +-- lib/classes/databox/field.php | 1 + .../PhraseaFixture/Feed/LoadOneFeed.php | 4 +- .../Phrasea/Application/LightboxTest.php | 11 ++- .../Phrasea/Application/OverviewTest.php | 72 +------------- .../Phrasea/Controller/Prod/FeedTest.php | 1 - tests/classes/PhraseanetPHPUnitAbstract.php | 99 +++++-------------- 11 files changed, 52 insertions(+), 165 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Permalink.php b/lib/Alchemy/Phrasea/Controller/Permalink.php index 6df1a5aa61..5464d7a154 100644 --- a/lib/Alchemy/Phrasea/Controller/Permalink.php +++ b/lib/Alchemy/Phrasea/Controller/Permalink.php @@ -32,9 +32,7 @@ class Permalink extends AbstractDelivery $that = $this; $retrieveRecord = function ($app, $databox, $token, $record_id, $subdef) { - if (\databox_subdef::CLASS_THUMBNAIL === $subdef) { - $record = $databox->get_record($record_id); - } elseif (\databox_subdef::CLASS_PREVIEW === $subdef && $app['EM']->getRepository('Entities\FeedItem')->isRecordInPublicFeed($app, $databox->get_sbas_id(), $record_id)) { + if (in_array($subdef, array(\databox_subdef::CLASS_PREVIEW, \databox_subdef::CLASS_THUMBNAIL)) && $app['EM']->getRepository('Entities\FeedItem')->isRecordInPublicFeed($app, $databox->get_sbas_id(), $record_id)) { $record = $databox->get_record($record_id); } else { $record = \media_Permalink_Adapter::challenge_token($app, $databox, $token, $record_id, $subdef); @@ -119,7 +117,7 @@ class Permalink extends AbstractDelivery $token = $request->query->get('token'); $databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id); - $record = $retrieveRecord($app, $databox, $token, $record_id, 'caption'); + $record = $retrieveRecord($app, $databox, $token, $record_id, \databox_subdef::CLASS_THUMBNAIL); $caption = $record->get_caption(); return new Response($caption->serialize(\caption_record::SERIALIZE_JSON), 200, array("Content-Type" => 'application/json')); diff --git a/lib/Alchemy/Phrasea/Controller/Root/Login.php b/lib/Alchemy/Phrasea/Controller/Root/Login.php index 0a823bf457..2266c4ef76 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Login.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Login.php @@ -52,7 +52,7 @@ class Login implements ControllerProviderInterface $items = array(); foreach($app['EM']->getRepository('Entities\FeedItem')->loadLatest($app, 20) as $item) { - $record = $item->get_record(); + $record = $item->getRecord($app); $preview = $record->get_subdef('preview'); $permalink = $preview->get_permalink(); diff --git a/lib/Doctrine/Repositories/FeedItemRepository.php b/lib/Doctrine/Repositories/FeedItemRepository.php index d1bb1920ae..7203bd3582 100644 --- a/lib/Doctrine/Repositories/FeedItemRepository.php +++ b/lib/Doctrine/Repositories/FeedItemRepository.php @@ -34,7 +34,6 @@ class FeedItemRepository extends EntityRepository $query = $this->_em->createQuery($dql); $query->setParameters(array('sbas_id' => $sbas_id, 'record_id' => $record_id)); - $query->useResultCache(false); return count($query->getResult()) > 0; } diff --git a/lib/classes/ACL.php b/lib/classes/ACL.php index ba8bb45f45..ab3bffcae6 100644 --- a/lib/classes/ACL.php +++ b/lib/classes/ACL.php @@ -119,6 +119,11 @@ class ACL implements cache_cacheableInterface return $this; } + public function set_app(Application $app) + { + $this->app = $app; + } + /** * Check if a hd grant has been received for a record * diff --git a/lib/classes/User/Adapter.php b/lib/classes/User/Adapter.php index 0d2422f1b6..ad8c760978 100644 --- a/lib/classes/User/Adapter.php +++ b/lib/classes/User/Adapter.php @@ -327,6 +327,8 @@ class User_Adapter implements User_Interface, cache_cacheableInterface self::$_instance[$id] = new self($id, $app); $app['phraseanet.appbox']->set_data_to_cache(self::$_instance[$id], '_user_' . $id); } + } else { + self::$_instance[$id]->set_app($app); } return array_key_exists($id, self::$_instance) ? self::$_instance[$id] : false; @@ -349,6 +351,9 @@ class User_Adapter implements User_Interface, cache_cacheableInterface protected function set_app(Application $app) { $this->app = $app; + if (null !== $this->ACL) { + $this->ACL->set_app($app); + } } /** @@ -1092,22 +1097,18 @@ class User_Adapter implements User_Interface, cache_cacheableInterface } } } - $this->notification_preferences_loaded = true; } - protected $notifications_preferences_loaded = false; public function get_notifications_preference(Application $app, $notification_id) { - if (!$this->notifications_preferences_loaded) - $this->load_preferences($app); + $this->load_preferences($app); - return $this->_prefs['notification_' . $notification_id]; + return isset($this->_prefs['notification_' . $notification_id]) ? $this->_prefs['notification_' . $notification_id] : null; } public function set_notification_preference(Application $app, $notification_id, $value) { - if (!$this->notifications_preferences_loaded) - $this->load_preferences($app); + $this->load_preferences($app); return $this->_prefs['notification_' . $notification_id] = $value ? '1' : '0'; } diff --git a/lib/classes/databox/field.php b/lib/classes/databox/field.php index 4790ba138d..6774be8752 100644 --- a/lib/classes/databox/field.php +++ b/lib/classes/databox/field.php @@ -285,6 +285,7 @@ class databox_field implements cache_cacheableInterface $databox->set_data_to_cache(self::$_instance[$instance_id], $cache_key); } } + self::$_instance[$instance_id]->app = $app; return self::$_instance[$instance_id]; } diff --git a/lib/conf.d/PhraseaFixture/Feed/LoadOneFeed.php b/lib/conf.d/PhraseaFixture/Feed/LoadOneFeed.php index c45fd6615e..61e9e8aec9 100644 --- a/lib/conf.d/PhraseaFixture/Feed/LoadOneFeed.php +++ b/lib/conf.d/PhraseaFixture/Feed/LoadOneFeed.php @@ -47,9 +47,7 @@ class LoadOneFeed extends AbstractFixture implements FixtureInterface $feed->setTitle("test"); } - if (isset($this->public) && $this->public !== null) { - $feed->setIsPublic($this->public); - } + $feed->setIsPublic((Boolean) $this->public); $feed->setSubtitle("description"); diff --git a/tests/Alchemy/Tests/Phrasea/Application/LightboxTest.php b/tests/Alchemy/Tests/Phrasea/Application/LightboxTest.php index b8a087e5ad..32984f4479 100644 --- a/tests/Alchemy/Tests/Phrasea/Application/LightboxTest.php +++ b/tests/Alchemy/Tests/Phrasea/Application/LightboxTest.php @@ -2,7 +2,7 @@ namespace Alchemy\Tests\Phrasea\Application; -class ApplicationLightboxTest extends \PhraseanetWebTestCaseAuthenticatedAbstract +class LightboxTest extends \PhraseanetWebTestCaseAuthenticatedAbstract { protected $client; @@ -311,21 +311,24 @@ class ApplicationLightboxTest extends \PhraseanetWebTestCaseAuthenticatedAbstrac $this->assertObjectHasAttribute('error', $datas); } - public function testAjaxSetRelease() + public function testAjaxSetReleaseWithRegularBasket() { $basket = $this->insertOneBasket(); - $this->mockNotificationDeliverer('Alchemy\Phrasea\Notification\Mail\MailInfoValidationDone'); - $crawler = self::$DI['client']->request('POST', '/lightbox/ajax/SET_RELEASE/' . $basket->getId() . '/'); $this->assertEquals(200, self::$DI['client']->getResponse()->getStatusCode()); $this->assertEquals('application/json', self::$DI['client']->getResponse()->headers->get('Content-type')); $datas = json_decode(self::$DI['client']->getResponse()->getContent()); $this->assertTrue(is_object($datas), 'asserting good json datas'); $this->assertTrue($datas->error); + } + public function testAjaxSetReleaseWithRegularBasketWithValidation() + { $validationBasket = $this->insertOneValidationBasket(); + $this->mockNotificationDeliverer('Alchemy\Phrasea\Notification\Mail\MailInfoValidationDone'); + foreach ($validationBasket->getElements() as $element) { $element->getUserValidationDatas(self::$DI['app']['authentication']->getUser(), self::$DI['app'])->setAgreement(true); break; diff --git a/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php b/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php index 4bb342e14e..8cd448adc2 100644 --- a/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php +++ b/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php @@ -5,7 +5,7 @@ namespace Alchemy\Tests\Phrasea\Application; use Alchemy\Phrasea\Border\File; use Symfony\Component\HttpFoundation\File\UploadedFile; -class ApplicationOverviewTest extends \PhraseanetWebTestCaseAuthenticatedAbstract +class OverviewTest extends \PhraseanetWebTestCaseAuthenticatedAbstract { public function testDatafilesRouteAuthenticated() { @@ -61,58 +61,7 @@ class ApplicationOverviewTest extends \PhraseanetWebTestCaseAuthenticatedAbstrac $this->assertForbiddenResponse(self::$DI['client']->getResponse()); } - public function testIs_record_in_public_feed() - { - $feed = $this->createFeed(); - $this->setFeedIsPublic($feed, true); - $entry = $this->createEntry($feed); - - $this->addItem($entry, self::$DI['record_1']); - - $this->assertTrue(self::$DI['app']['EM']->getRepository('Entities\FeedItem')->isRecordInPublicFeed(self::$DI['app'], self::$DI['record_1']->get_sbas_id(), self::$DI['record_1']->get_record_id())); - $this->setFeedIsPublic($feed, false); - $this->assertFalse(self::$DI['app']['EM']->getRepository('Entities\FeedItem')->isRecordInPublicFeed(self::$DI['app'], self::$DI['record_1']->get_sbas_id(), self::$DI['record_1']->get_record_id())); - } - - public function testLoadLatestItems() - { - $feed = $this->createFeed(); - $this->setFeedIsPublic($feed, true); - $entry = $this->createEntry($feed); - - foreach(range(1, 2) as $i) { - $this->addItem($entry, self::$DI['record_'.$i]); - } - - $this->assertCount(2, self::$DI['app']['EM']->getRepository('Entities\FeedItem')->loadLatest(self::$DI['app'], 20)); - } - - public function testLoadLatestItemsLessItems() - { - $feed = $this->createFeed(); - $this->setFeedIsPublic($feed, true); - $entry = $this->createEntry($feed); - - foreach(range(1, 2) as $i) { - $this->addItem($entry, self::$DI['record_'.$i]); - } - - $this->assertCount(1, self::$DI['app']['EM']->getRepository('Entities\FeedItem')->loadLatest(self::$DI['app'], 1)); - } - - public function testLoadLatestItemsNoPublic() - { - $feed = $this->createFeed(); - $entry = $this->createEntry($feed); - - foreach(range(1, 2) as $i) { - $this->addItem($entry, self::$DI['record_'.$i]); - } - - $this->assertCount(0, self::$DI['app']['EM']->getRepository('Entities\FeedItem')->loadLatest(self::$DI['app'], 20)); - } - - public function testDatafilesRouteNotAuthenticatedIsOkInPublicFeed() + public function testDatafilesRouteOnUnaccessibleRecordIsOkInPublicFeed() { $tmp = tempnam(sys_get_temp_dir(), 'testEtag'); copy(__DIR__ . '/../../../../files/cestlafete.jpg', $tmp); @@ -121,20 +70,12 @@ class ApplicationOverviewTest extends \PhraseanetWebTestCaseAuthenticatedAbstrac $file = new File(self::$DI['app'], $media, self::$DI['collection_no_access']); $record = \record_adapter::createFromFile($file, self::$DI['app']); - $record->generate_subdefs($record->get_databox(), self::$DI['app']); - $feed = $this->createFeed(); - $this->setFeedIsPublic($feed, true); - $entry = $this->createEntry($feed); - $this->addItem($entry, $record); - - self::$DI['record_1']->move_to_collection(self::$DI['collection_no_access'], self::$DI['app']['phraseanet.appbox']); + $item = $this->insertOneFeedItem(self::$DI['user'], true, 1, $record); self::$DI['client']->request('GET', '/datafiles/' . $record->get_sbas_id() . '/' . $record->get_record_id() . '/preview/'); - $this->assertEquals(200, self::$DI['client']->getResponse()->getStatusCode()); - self::$DI['record_1']->move_to_collection(self::$DI['collection'], self::$DI['app']['phraseanet.appbox']); unlink($tmp); } @@ -269,13 +210,10 @@ class ApplicationOverviewTest extends \PhraseanetWebTestCaseAuthenticatedAbstrac public function testPermalinkRouteNotAuthenticatedIsOkInPublicFeed() { - $feed = $this->createFeed(); - $this->setFeedIsPublic($feed, true); - $entry = $this->createEntry($feed); - $this->addItem($entry, self::$DI['record_1']); + $record = $this->insertOneFeedItem(self::$DI['user'], true)->getRecord(self::$DI['app']); self::$DI['app']['authentication']->closeAccount(); - self::$DI['client']->request('GET', '/permalink/v1/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/preview/'); + self::$DI['client']->request('GET', '/permalink/v1/' . $record->get_sbas_id() . '/' . $record->get_record_id() . '/preview/'); $this->assertEquals(200, self::$DI['client']->getResponse()->getStatusCode()); } diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/FeedTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/FeedTest.php index 45fd46f35b..2dba86f37f 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/FeedTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/FeedTest.php @@ -24,7 +24,6 @@ class FeedTest extends \PhraseanetWebTestCaseAuthenticatedAbstract $feed = $this->insertOneFeed(self::$DI['user']); $crawler = self::$DI['client']->request('POST', '/prod/feeds/requestavailable/'); -// print(self::$DI['client']->getResponse());exit; $this->assertTrue(self::$DI['client']->getResponse()->isOk()); $feeds = self::$DI['app']['EM']->getRepository('Entities\Feed')->getAllForUser(self::$DI['user']); foreach ($feeds as $one_feed) { diff --git a/tests/classes/PhraseanetPHPUnitAbstract.php b/tests/classes/PhraseanetPHPUnitAbstract.php index 64c52b1a53..8593a103c0 100644 --- a/tests/classes/PhraseanetPHPUnitAbstract.php +++ b/tests/classes/PhraseanetPHPUnitAbstract.php @@ -363,10 +363,10 @@ abstract class PhraseanetPHPUnitAbstract extends WebTestCase * * @return \Entities\FeedEntry */ - protected function insertOneFeedEntry(\User_Adapter $user) + protected function insertOneFeedEntry(\User_Adapter $user, $public = false) { try { - $feed = $this->insertOneFeed($user); + $feed = $this->insertOneFeed($user, '', $public); $em = self::$DI['app']['EM']; @@ -436,22 +436,31 @@ abstract class PhraseanetPHPUnitAbstract extends WebTestCase * * @return \Entities\FeedItem */ - protected function insertOneFeedItem(\User_Adapter $user) + protected function insertOneFeedItem(\User_Adapter $user, $public = false, $qty = 1, \record_adapter $record = null) { try { - $entry = $this->insertOneFeedEntry($user); - - $item = new \Entities\FeedItem(); - $item->setEntry($entry); - $item->setRecordId(self::$DI['record_1']->get_record_id()); - $item->setSbasId(self::$DI['record_1']->get_sbas_id()); - - $entry->addItem($item); - $em = self::$DI['app']['EM']; + $entry = $this->insertOneFeedEntry($user, $public); + + for ($i = 0; $i < $qty; $i++) { + $item = new \Entities\FeedItem(); + $item->setEntry($entry); + + if (null === $record) { + $actual = self::$DI['record_'.($i+1)]; + } else { + $actual = $record; + } + + $item->setRecordId($actual->get_record_id()); + $item->setSbasId($actual->get_sbas_id()); + $item->setEntry($entry); + + $entry->addItem($item); + $em->persist($item); + } $em->persist($entry); - $em->persist($item); $em->flush(); } catch (\Exception $e) { @@ -1144,70 +1153,6 @@ abstract class PhraseanetPHPUnitAbstract extends WebTestCase ->disableOriginalConstructor() ->getMock(); } - - protected function createFeed() - { - $feed = new Feed(); - $feed->setTitle('Feed') - ->setSubtitle('subtitle'); - - self::$DI['app']['EM']->persist($feed); - self::$DI['app']['EM']->flush(); - - return $feed; - } - - protected function createEntry(Feed $feed) - { - $publisher = new FeedPublisher(); - $publisher->setFeed($feed) - ->setIsOwner(true) - ->setUsrId(self::$DI['user']->get_id()); - - $feed->addPublisher($publisher); - - $entry = new FeedEntry(); - $entry->setTitle('entry') - ->setSubtitle('subtitle') - ->setAuthorEmail('email') - ->setAuthorName('name') - ->setFeed($feed) - ->setPublisher($publisher); - - $feed->addEntry($entry); - - self::$DI['app']['EM']->persist($publisher); - self::$DI['app']['EM']->persist($feed); - self::$DI['app']['EM']->persist($entry); - self::$DI['app']['EM']->flush(); - - return $entry; - } - - protected function addItem(FeedEntry $entry, \record_adapter $record) - { - $item = new FeedItem(); - $item->setEntry($entry) - ->setRecordId($record->get_record_id()) - ->setSbasId($record->get_sbas_id()); - - $entry->addItem($item); - - self::$DI['app']['EM']->persist($item); - self::$DI['app']['EM']->persist($entry); - self::$DI['app']['EM']->flush(); - - return $item; - } - - protected function setFeedIsPublic(Feed $feed, $public) - { - $feed->setIsPublic($public); - self::$DI['app']['EM']->persist($feed); - self::$DI['app']['EM']->flush(); - - return $feed; - } } class CsrfTestProvider implements CsrfProviderInterface