diff --git a/FEATURES/129798 b/FEATURES/129798 new file mode 100644 index 0000000000000000000000000000000000000000..e0bb1b9f44edd5e6d8d6ffad41e5d8af9badb893 --- /dev/null +++ b/FEATURES/129798 @@ -0,0 +1,10 @@ + '129798' => + ['Label' => $this->_('Horaires multiples pour les articles/événements'), + 'Desc' => $this->_('Les articles représentant un événement peuvent maintenant avoir plusieurs horaires. Cela permet d\'éviter de dupliquer un même article pour plusieurs déroulement du même événement. Variable d\'activation : ENABLE_ARTICLES_TIMINGS'), + 'Image' => '', + 'Video' => 'https://youtu.be/UdXTNImRmtY', + 'Category' => $this->('Rédaction'), + 'Right' => function($feature_description, $user) {return true;}, + 'Wiki' => 'https://wiki.bokeh-library-portal.org/index.php?title=Articles_-_Horaires_multiples', + 'Test' => '', + 'Date' => '2021-07-23'], \ No newline at end of file diff --git a/VERSIONS_WIP/129798 b/VERSIONS_WIP/129798 new file mode 100644 index 0000000000000000000000000000000000000000..ad559c25ca20e3a83323cb1d2d404cd149f19ae8 --- /dev/null +++ b/VERSIONS_WIP/129798 @@ -0,0 +1 @@ + - ticket #129798 : Les articles peuvent avoir plusieurs horaires d'événement (option ENABLE_ARTICLES_TIMINGS). Ajout du filtre Etiquette/Tag pour la boîte agenda. \ No newline at end of file diff --git a/application/modules/admin/controllers/CmsController.php b/application/modules/admin/controllers/CmsController.php index c49761c2634ec0b9f348a3d2312f581298caac88..21274123cc7bdaf5742a97f3c10aabcb9f5e27ab 100644 --- a/application/modules/admin/controllers/CmsController.php +++ b/application/modules/admin/controllers/CmsController.php @@ -23,10 +23,10 @@ class Admin_CmsController extends ZendAfi_Controller_Action { private $_bib; public function getPlugins() { - return ['ZendAfi_Controller_Plugin_ResourceDefinition_Article', - 'ZendAfi_Controller_Plugin_Manager_Article', - 'ZendAfi_Controller_Plugin_MultiSelection_Article', - 'ZendAfi_Controller_Plugin_Versionning_Article']; + return [ZendAfi_Controller_Plugin_ResourceDefinition_Article::class, + ZendAfi_Controller_Plugin_Manager_Article::class, + ZendAfi_Controller_Plugin_MultiSelection_Article::class, + ZendAfi_Controller_Plugin_Versionning_Article::class]; } @@ -170,7 +170,6 @@ class Admin_CmsController extends ZendAfi_Controller_Action { public function appendTreeViewContext() { - if ($this->_getParam('id') && ($article = Class_Article::find($this->_getParam('id')))) { $cat_selected =$article->getIdCat(); @@ -211,4 +210,24 @@ class Admin_CmsController extends ZendAfi_Controller_Action { $this->getResponse()->setHeader('Content-Type', 'application/json; charset=utf-8'); $this->getResponse()->setBody((new Class_ArticleCategorie())->getCategoriesJson()); } + + + public function eventTimingsAction() { + $this->_forward('index', 'event-timings'); + } + + + public function eventTimingDeleteAction() { + $this->_forward('delete', 'event-timings'); + } + + + public function eventTimingEditAction() { + $this->_forward('edit', 'event-timings'); + } + + + public function eventTimingAddAction() { + $this->_forward('add', 'event-timings'); + } } diff --git a/application/modules/admin/controllers/EventTimingsController.php b/application/modules/admin/controllers/EventTimingsController.php new file mode 100644 index 0000000000000000000000000000000000000000..ffbf28c7be0abb99752c63ef16f42a0c61311edc --- /dev/null +++ b/application/modules/admin/controllers/EventTimingsController.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Admin_EventTimingsController extends ZendAfi_Controller_Action { + public function getPlugins() { + return [ZendAfi_Controller_Plugin_ResourceDefinition_EventTimings::class, + ZendAfi_Controller_Plugin_Manager_EventTimings::class]; + } +} diff --git a/application/modules/admin/views/scripts/event-timings/add.phtml b/application/modules/admin/views/scripts/event-timings/add.phtml new file mode 100644 index 0000000000000000000000000000000000000000..b10a9aac54eaf11ba6f76ac7048aa0953745be14 --- /dev/null +++ b/application/modules/admin/views/scripts/event-timings/add.phtml @@ -0,0 +1 @@ +<?php echo $this->renderForm($this->form); ?> diff --git a/application/modules/admin/views/scripts/event-timings/edit.phtml b/application/modules/admin/views/scripts/event-timings/edit.phtml new file mode 100644 index 0000000000000000000000000000000000000000..c52ca489f905555642d9f8210fc3130ba4abfdcd --- /dev/null +++ b/application/modules/admin/views/scripts/event-timings/edit.phtml @@ -0,0 +1 @@ +<?php echo $this->renderForm($this->form); ?> diff --git a/application/modules/admin/views/scripts/event-timings/index.phtml b/application/modules/admin/views/scripts/event-timings/index.phtml new file mode 100644 index 0000000000000000000000000000000000000000..b3c49daebbdab6810f6b83f284a63810d50f2360 --- /dev/null +++ b/application/modules/admin/views/scripts/event-timings/index.phtml @@ -0,0 +1,29 @@ +<?php + +echo $this->Button_Back( + (new Class_Button()) + ->setText($this->_('Retour à l\'article')) + ->setUrl($this->url(['module' => 'admin', + 'controller' => 'cms', + 'action' => 'edit', + 'id' => $this->article->getId() + ], + null, + true)) + ); + +echo $this->Button_New( + (new Class_Button()) + ->setText($this->_('Ajouter un horaire')) + ->setUrl($this->url(['module' => 'admin', + 'controller' => 'cms', + 'action' => 'event-timing-add', + 'article_id' => $this->article->getId() + ], + null, + true)) + ->setAttribs(['data-popup' => 'true']) + ); + +echo $this->renderTable(new Class_TableDescription_EventTimings('event_timings'), + $this->article->getEventTimings()); diff --git a/cosmogramme/sql/patch/patch_415.php b/cosmogramme/sql/patch/patch_415.php new file mode 100644 index 0000000000000000000000000000000000000000..8145b66f2df1c13847050551c6311ce4c788da7b --- /dev/null +++ b/cosmogramme/sql/patch/patch_415.php @@ -0,0 +1,13 @@ +<?php +$adapter = Zend_Db_Table_Abstract::getDefaultAdapter(); + +try { + $adapter->query('CREATE TABLE if not exists `cms_article_timings` ( ' + . 'id int(11) unsigned not null auto_increment,' + . 'article_id int(11) unsigned not null,' + . 'start datetime,' + . 'end datetime,' + . 'primary key (id),' + . 'key `article_id` (`article_id`)' + . ') engine=MyISAM default charset=utf8'); +} catch(Exception $e) { } diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php index 6124d93ae53a48145a8cdadc873ae7d35306c047..8f4a8f74a37ef115bfb843d8cd1a437317be3e59 100644 --- a/library/Class/AdminVar.php +++ b/library/Class/AdminVar.php @@ -321,6 +321,7 @@ class Class_AdminVarLoader extends Storm_Model_Loader { ['value' => 'no-reply@afi-sa.fr']), 'CUSTOM_GENRE_ICON' => Class_AdminVar_Meta::newOnOff($this->_('Activation de l\'interface de personnalisation des icones des genres')), 'DISABLE_BLOCKS_SORTING' => Class_AdminVar_Meta::newOnOff($this->_('Désactivation de la possibilité de déplacer les boîtes par glisser/déposer en front')), + 'ENABLE_ARTICLES_TIMINGS' => Class_AdminVar_Meta::newOnOff($this->_('Activation de la gestion d\'horaires multiples pour les articles d\'événement')), ]; } diff --git a/library/Class/Article.php b/library/Class/Article.php index 4ecaf884b6ae87b1749b3847b9c6bce691e74d84..92b0d5ea628db2799e4bd77f5be1e7d397f1ff26 100644 --- a/library/Class/Article.php +++ b/library/Class/Article.php @@ -19,613 +19,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class ArticleLoader extends Storm_Model_Loader { - const ORDER_SELECTION = 'Selection'; - - /** @var Zend_Db_Table_Select */ - protected $_select; - protected $_sort_order; - protected $_nb_analyse; - protected $_nb_aff; - protected $_id_articles; - protected $_id_categories; - protected $_limit; - /** @var int */ - protected $_status; - /** @var bool */ - protected $_events_only; - /** @var bool */ - protected $_published; - protected $_display_mode; - protected $_all_articles; - - /** - * @return ArticleLoader - */ - protected function _selectArticles() { - if(!$table = $this->getTable()) - return $this; - - $this->_select = $table - ->select('cms_article.*') - ->setIntegrityCheck(false) - ->from('cms_article'); - - return $this; - } - - - protected function _publishedNow() { - if (!$this->_published) - return $this; - - if(!$this->_select) - return $this; - - $this->_select - ->where('(DEBUT IS NULL) OR (DEBUT <= CURDATE())') - ->where('(FIN IS NULL) OR (FIN >= CURDATE())'); - - return $this; - } - - - /** - * @param array $id_categories - * @return array - */ - protected function _mergeCategorieIdsWithSousCategories($id_categories) { - $all_cat_ids = array(); - foreach($id_categories as $id_cat) { - if (!$root_cat = Class_ArticleCategorie::getLoader()->find($id_cat)) - continue; - - $all_cat_ids []= $id_cat; - - $sub_cats = $root_cat->getRecursiveSousCategories(); - foreach ($sub_cats as $cat) - $all_cat_ids []= $cat->getId(); - } - - return array_unique($all_cat_ids); - } - - - /** - * @param array $id_articles - * @param array $id_categories - * @return ArticleLoader - */ - protected function _whereSelectionIn($id_articles, $id_categories) { - $conditions = array(); - - if ($id_articles) - $conditions[] = sprintf('%s in (%s)', - $this->getIdField(), - implode(',', $id_articles)); - - if ($id_categories) - $conditions[] = sprintf('`cms_article`.ID_CAT in (%s)', - implode(',', $id_categories)); - - if ($conditions && $this->_select) - $this->_select->where(implode(' OR ', $conditions)); - - return $this; - } - - - /** - * @param string $event_date - * @return ArticleLoader - */ - protected function _whereEventDateIn($event_date) { - if (!$this->_select) - return $this; - - if ($this->_events_only) { - $this->_select->where('EVENTS_DEBUT IS NOT NULL'); - $this->_select->where('EVENTS_FIN IS NOT NULL'); - } - - if (!$event_date || (7 > strlen($event_date))) - return $this; - - $truncate = (10 == strlen($event_date)) ? '10' : '7'; - - $this->_select->where('left(EVENTS_DEBUT,'.$truncate.') <= ?', $event_date); - $this->_select->where('left(EVENTS_FIN,'.$truncate.') >= ?', $event_date); - - return $this; - } - - - /** - * @param string $event_date - * @return ArticleLoader - */ - protected function _whereEventStartAfter($event_date) { - if (!$this->_select) - return $this; - - if (!$event_date || (7 > strlen($event_date))) - return $this; - - $this->_select->where('EVENTS_DEBUT IS NOT NULL'); - $this->_select->where('EVENTS_FIN IS NOT NULL'); - - $field = (10 == strlen($event_date)) ? 'left(EVENTS_DEBUT,10)' : 'left(EVENTS_DEBUT,7)'; - $this->_select->where("$field > ?", $event_date); - - return $this; - } - - - protected function _whereEventEndAfter($event_date) { - if (!$this->_select) - return $this; - - if (!$event_date || (10 > strlen($event_date))) - return $this; - - $this->_select->where('EVENTS_FIN IS NOT NULL'); - $this->_select->where("left(EVENTS_FIN,10) >= ?", $event_date); - - return $this; - - } - - - /** - * @return ArticleLoader - */ - protected function _orderAndLimit() { - if(!$this->_select) - return $this; - - if ($this->_orderByCommentsCount()) - return $this; - - if ( $this->_orderByTitle() ) - return $this; - - if (!$this->_has_selection) { - $this->_select->order('DATE_CREATION DESC'); - $this->_select->limit($this->_limit); - return $this; - } - - if ($this->_id_categories) - $this->_select->order(sprintf("FIELD(`cms_article`.ID_CAT, %s)", implode(',', $this->_id_categories))); - - if ($this->_id_articles) - $this->_select->order(sprintf("FIELD(ID_ARTICLE, %s)", implode(',', $this->_id_articles))); - - return $this; - } - - - protected function _orderByTitle() { - if ( false === strpos($this->_sort_order, 'Title')) - return false; - - $order = $this->_sort_order == Intonation_Library_Widget_Carousel_Article_Definition::SORT_TITLE_ASC - ? 'asc' - : 'desc'; - - $this->_select - ->order('titre ' . $order); - - return true; - } - - - protected function _orderByCommentsCount() { - if ( false === strpos($this->_sort_order, 'CommentCount')) - return false; - - $order = $this->_sort_order == 'CommentCount' - ? 'desc' - : 'asc'; - - $this->_select - ->join('cms_rank', - 'cms_rank.ID_CMS = cms_article.ID_ARTICLE', - []) - ->order('(cms_rank.abon_nombre_avis + cms_rank.bib_nombre_avis) ' . $order); - return true; - } - - - /** - * @return ArticleLoader - */ - protected function _filterByLangue() { - if(!$this->_select) - return $this; - - if (!$this->_has_selection) { - if ($this->_langue) { - $this->_select->where('LANGUE=?', $this->_langue); - } else { - $this->_select->where('PARENT_ID=?', 0); - } - } - - return $this; - } - - - /** - * @return Zend_Db_Table_Select - */ - protected function _getSelect() { - return $this->_select; - } - - /** - * @param array $articles - * @return array - */ - protected function _sortArticles($articles) { - if ($this->_sort_order == 'Random') { - shuffle($articles); - } else { - $sort_function = 'sortBy'.$this->_sort_order; - if (method_exists('Class_Article', $sort_function)) - usort($articles, 'Class_Article::'.$sort_function); - } - - return $articles; - } - - - public function sortArticles($articles, $sort_order) { - if ($this->_sort_order == 'Random') { - shuffle($articles); - } else { - $sort_function = 'sortBy'.$sort_order; - if (method_exists('Class_Article', $sort_function)) - usort($articles, 'Class_Article::'.$sort_function); - } - - return $articles; - } - - - /** - * @param array $preferences - * @return array - */ - protected function _byIdBib($id_bib) { - if ((0 === $id_bib) or (null === $id_bib)) - return $this; - - $this->_select - ->join('cms_categorie', - 'cms_categorie.ID_CAT = cms_article.ID_CAT', - array()) - ->where('cms_categorie.ID_SITE=?', $id_bib); - return $this; - } - - - protected function _byIdLieu($id_lieu) { - if (null == $id_lieu) - return $this; - - $this->_select->where('ID_LIEU=?', $id_lieu); - return $this; - } - - - protected function _byPlaceTown($town) { - if (!$town || '' == $town) - return $this; - $this->_select - ->join('lieux', - 'lieux.ID = cms_article.ID_LIEU', - array()) - ->where('trim(lieux.VILLE) like ?', $town); - - return $this; - } - - - protected function _byCustomFields($custom_fields) { - foreach ($custom_fields as $id => $value) { - $this->_select - ->join( - [ "cfv$id" => 'custom_field_values' ], - "cms_article.ID_ARTICLE = cfv$id.model_id AND cfv$id.custom_field_id = $id", - [] - ); - } - - return $this; - } - - /** - * @return ArticleLoader - */ - protected function _filterByStatus() { - if (null === $this->_select) - return $this; - - $filters = ($filters = $this->_getFilterByWorkflow()) - ? $filters - : $this->_status; - - if ($filters) - $this->_select->where('STATUS in (?)', $filters); - - return $this; - } - - - protected function _getFilterByWorkflow() { - if (! $this->_filter_by_workflow) - return null; - - if (!Class_AdminVar::isWorkflowEnabled()) - return null; - - $status_filter = [Class_Article::STATUS_VALIDATED]; - - if (Class_Users::isCurrentUserCanAccesBackend()) - $status_filter [] = Class_Article::STATUS_DRAFT; - - if ($this->_status) - $status_filter [] = $this->_status; - - return array_unique($status_filter); - } - - - protected function _filterByLocal() { - if (! $this->_filter_by_local) - return null; - - if (!Class_AdminVar::isTranslationEnabled()) - return null; - - if ($langue = Zend_Registry::get('translate')->getLocale()) - $this->_select - ->where('(cms_article.langue = "' . $langue . '" or exists (select \'x\' from cms_article as translation where translation.PARENT_ID = cms_article.ID_ARTICLE and trim(translation.langue) = "' . $langue . '") or cms_article.langue = "" or cms_article.langue is null)'); - - return function ($articles) use ($langue) { - return array_map(function ($article) use ($langue) - { - return $article->getTraductionLangue($langue); - }, - $articles); - }; - } - - - public function getArticlesByPreferencesDefaults() { - return ['id_categorie' => '', // catégories d'article, ex: 12-2-8-1-89 - 'id_items' => '', // liste d'articles, ex: 39-28-7 - 'display_order' => '', // tri, cf. méthodes Class_Article::sortByXXX, Random, Selection, CommentCount - 'order' => '', - 'nb_analyse' => 0, // afficher nb_aff articles (aléatoires) parmis nb_analyse articles ramenés sur un critère - 'nb_aff' => null, // nb d'article à retourner - 'size' => null, // nb d'article à retourner - 'langue' => null, // que les traductions de cette langue - 'event_date' => null, // que les articles dont les dates de début et/ou de fin inclue cette date - 'event_start_after' => null, // que les articles dont l'évènement commence après cette date - 'event_end_after' => null, // que les articles dont l'évènement termine à ou après cette date - 'id_bib' => null, // filtre par cette bibliothèque - 'status' => null, // filtre par cet état de workflow cf. Class_Article::STATUS_XXX - 'events_only' => false, // filtre que les évènements, - 'published' => true, // seulement les articles dont les date de debut / fin incluent le jour en cours, - 'id_lieu' => null, // id du lieu Class_Lieu - 'place_town' => null, - 'display_mode' => 'Title', - 'custom_fields' => [], - 'filter_by_workflow' => false, - 'filter_by_local' => false]; - } - - - /** - * @param array $preferences - * @return array - */ - public function getArticlesByPreferences($preferences) { - $preferences = array_merge($this->getArticlesByPreferencesDefaults(), - $preferences); - - $this->_sort_order = $preferences['order'] - ? $preferences['order'] - : $preferences['display_order']; - - $this->_nb_aff = (int) $preferences['size'] - ? (int) $preferences['size'] - : (int) $preferences['nb_aff']; - - $this->_nb_analyse = (int)$preferences['nb_analyse']; - $this->_id_articles = array_filter(explode('-', $preferences['id_items'])); - $this->_id_categories = $this->_mergeCategorieIdsWithSousCategories(array_filter(explode('-', $preferences['id_categorie']))); - $this->_has_selection = (count($this->_id_articles)>0 or count($this->_id_categories)>0); - $this->_limit = $this->_sort_order == 'Random' ? $this->_nb_analyse : $this->_nb_aff; - $this->_langue = $preferences['langue']; - $this->_event_date = $preferences['event_date']; - $this->_event_end_after = $preferences['event_end_after']; - $this->_event_start_after = $preferences['event_start_after']; - $this->_id_bib = $preferences['id_bib']; - $this->_id_lieu = (int)$preferences['id_lieu']; - $this->_status = $preferences['status']; - $this->_events_only = (bool)$preferences['events_only']; - $this->_published = (bool)$preferences['published']; - $this->_display_mode = $preferences['display_mode']; - $this->_custom_fields = $preferences['custom_fields']; - - $this->_filter_by_workflow = $preferences['filter_by_workflow']; - $this->_filter_by_local = $preferences['filter_by_local']; - - if ($this->_sort_order == static::ORDER_SELECTION && !$this->_has_selection) - return []; - - $select = $this - ->_selectArticles() - ->_publishedNow() - ->_byIdBib($this->_id_bib) - ->_byIdLieu($this->_id_lieu) - ->_byPlaceTown($preferences['place_town']) - ->_whereSelectionIn($this->_id_articles, $this->_id_categories) - ->_whereEventDateIn($this->_event_date) - ->_whereEventStartAfter($this->_event_start_after) - ->_whereEventEndAfter($this->_event_end_after) - ->_filterByLangue() - ->_filterByStatus() - ->_orderAndLimit() - ->_getSelect(); - - $filter_by_local_callback = $this->_filterByLocal(); - - $articles = Class_Article::getLoader()->findAll($select); - - if ($this->_custom_fields) - $articles = $this->_filterByCustomFields($articles, - $this->_custom_fields); - - if ((new ZendAfi_Validate_DateFormat)->isValid($this->_event_date)) - $articles = $this->_filterByDay($this->_event_date, $articles); - - $articles = $this->_sortArticles($articles); - - $this->_all_articles = $articles = $filter_by_local_callback - ? $filter_by_local_callback($articles) - : $articles; - - if ( - ($this->_sort_order == static::ORDER_SELECTION) - or !$this->_nb_aff - ) - return $articles; - - return array_slice($articles, 0, $this->_nb_aff); - } - - - public function getAllArticles() { - return $this->_all_articles; - } - - - protected function _filterByCustomFields($articles, $custom_fields) { - if (!$custom_fields) - return $articles; - - $filter = function ($article) use ($custom_fields) { - foreach($custom_fields as $id => $value) { - if ($value != $article->findCustomFieldValueMatching($id,$value)) - return false; - } - return true; - }; - $articles = array_filter($articles, $filter); - - return $articles; - } - - - /** - * @param array $articles - * @return array - */ - public static function groupByBibId(array $articles) { - $grouped = []; - - foreach ($articles as $article) { - $bib_id = ($bib = $article->getBib()) ? $bib->getId() : 0; - - if (array_key_exists($bib_id, $grouped)) { - $grouped[$bib_id][] = $article; - } else { - $grouped[$bib_id] = array($article); - } - } - - return $grouped; - } - - - /** - * @param array $articles - * @return array - */ - public function groupByLibelleCategorie(array $articles) { - $grouped = array(); - - foreach ($articles as $article) { - $libelle_cat = $article->getCategorie()->getLibelle(); - - if (!array_key_exists($libelle_cat, $grouped)) - $grouped[$libelle_cat] = array(); - - $grouped[$libelle_cat][] = $article; - } - - return $grouped; - } - - - /** - * Retourne les traductions des articles valides et pour la langue courant - * @param array - * @return array - */ - public function filterByLocaleAndWorkflow($articles) { - return Class_Article::filterByLocaleAndWorkflow($articles); - } - - public function articlesWithFormulaire() { - return Class_Article::findAll('select id_article,titre from cms_article where id_article in (select distinct id_article from formulaires)'); - } - - - public function indexAll() { - $page = 1; - while($articles_to_index = Class_Article::findAllBy(['indexation' => '1', - 'limitPage' => [$page, 500]])) { - foreach($articles_to_index as $article) - $article->index(); - - Class_Article::clearCache(); - $page++; - } - } - - - protected function _filterByDay($day, $articles) { - if(!$day) - return $articles; - - $day_num = date("w", strtotime($day)); - return array_filter($articles, - function($event) use ($day_num) - { - $pick_day_as_array = $event->getPickDayAsArray(); - if(empty($pick_day_as_array) || in_array($day_num, $pick_day_as_array)) - return $event; - }); - } - - - public function findDistinctStatus($articles) { - $ids = array_map(function($article) {return $article->getId();}, - $articles); - - return Class_Article::findAll('select distinct status from cms_article where id_article in ('. - implode(',',$ids). - ')'); - } -} - - - class Class_Article extends Storm_Model_Abstract { use Trait_Translator, @@ -648,40 +41,45 @@ class Class_Article extends Storm_Model_Abstract { public $old_status = null; - protected $_loader_class = 'ArticleLoader'; + protected $_loader_class = Class_Article_Loader::class; protected $_table_name = 'cms_article'; protected $_table_primary = 'ID_ARTICLE'; - protected $_has_many = ['traductions' => ['model' => 'Class_Article', + protected $_has_many = ['traductions' => ['model' => Class_Article::class, 'role' => 'article_original', 'dependents' => 'delete'], - 'avis_users' => ['model' => 'Class_Avis', + 'avis_users' => ['model' => Class_Avis::class, 'role' => 'article', 'dependents' => 'delete', 'order' => 'date_avis desc'], - 'formulaires' => ['model' => 'Class_Formulaire', + 'formulaires' => ['model' => Class_Formulaire::class, 'role' => 'article', 'dependents' => 'delete', 'order' => 'date_creation desc'], - 'formulaires_to_validate' => ['model' => 'Class_Formulaire', + 'formulaires_to_validate' => ['model' => Class_Formulaire::class, 'role' => 'article', 'order' => 'date_creation desc', - 'scope' => ['validated' => false]]]; + 'scope' => ['validated' => false]], - protected $_belongs_to = ['categorie' => ['model' => 'Class_ArticleCategorie', + 'event_timings' => ['model' => Class_Article_EventTiming::class, + 'role' => 'article', + 'order' => 'start', + 'dependents' => 'delete']]; + + protected $_belongs_to = ['categorie' => ['model' => Class_ArticleCategorie::class, 'referenced_in' => 'id_cat'], - 'article_original' => ['model' => 'Class_Article', + 'article_original' => ['model' => Class_Article::class, 'referenced_in' => 'parent_id'], 'bib' => ['through' => 'categorie'], - 'lieu' => ['model' => 'Class_Lieu', + 'lieu' => ['model' => Class_Lieu::class, 'referenced_in' => 'id_lieu'] , - 'auteur' => ['model' => 'Class_Users', + 'auteur' => ['model' => Class_Users::class, 'referenced_in' => 'id_user']]; @@ -1593,13 +991,6 @@ class Class_Article extends Storm_Model_Abstract { } - public static function hasEventForMonth($month) { - return 0 < count(Class_Article::getLoader()->getArticlesByPreferences(['event_date'=>self::getTimeSource()->getMonth($month), - 'events_only' => true, - 'status'=>3])); - } - - public function updateAttributes(Array $attributes) { unset($attributes['id_items']); @@ -1631,6 +1022,7 @@ class Class_Article extends Storm_Model_Abstract { } + public function acceptClefAlphaVisitor($visitor) { $visitor->visitTitre($this->getTitre()) ->visitComplementTitre($this->getId()) @@ -1665,4 +1057,10 @@ class Class_Article extends Storm_Model_Abstract { ? true : $this->isVisible(); } + + + public function hasTag($tag) { + return in_array(strtolower($tag), + array_filter(explode(';', strtolower($this->getTags())))); + } } diff --git a/library/Class/Article/EventTiming.php b/library/Class/Article/EventTiming.php new file mode 100644 index 0000000000000000000000000000000000000000..2397876f0c3d240a7174e5a5fe29536240d2e878 --- /dev/null +++ b/library/Class/Article/EventTiming.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_Article_EventTiming extends Storm_Model_Abstract { + use Trait_TimeSource, Trait_Translator; + + protected + $_table_name = 'cms_article_timings', + $_belongs_to = [ + 'article' => ['model' => Class_Article::class, + 'referenced_in' => 'article_id']], + $_default_attribute_values = ['start' => '', + 'end' => '', + 'article_id' => null]; + + + public function isPast() { + return strtotime($this->getEnd()) < $this->getCurrentTime(); + } + + + public function getLibelle() { + return ($article = $this->getArticle()) + ? $article->getTitre() + : ''; + } + + + public function updateAttributes(Array $attributes) { + $date_iso = new Class_Date_Iso; + if (array_key_exists('start', $attributes)) + $attributes['start'] = $date_iso->ensureDateTime($attributes['start']); + + if (array_key_exists('end', $attributes)) + $attributes['end'] = $date_iso->ensureDateTime($attributes['end']); + + return parent::updateAttributes($attributes); + } + + + public function validate() { + $this->checkAttribute('start', + $this->getStart(), + $this->_('La date de début n\'est pas une date valide')) + ->checkAttribute('end', + $this->getEnd(), + $this->_('La date de fin n\'est pas une date valide')) + ->checkAttribute('end', + Class_Date::isEndDateAfterStartDateNotEmpty($this->getStart(), $this->getEnd()), + $this->_('La date de début doit être antérieure à la date de fin')); + } +} diff --git a/library/Class/Article/Loader.php b/library/Class/Article/Loader.php new file mode 100644 index 0000000000000000000000000000000000000000..a57bfc23575e4b6ea670161c25eb2b5012a77cf6 --- /dev/null +++ b/library/Class/Article/Loader.php @@ -0,0 +1,259 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +class Class_Article_Loader extends Storm_Model_Loader { + const + ORDER_SELECTION = 'Selection', + ORDER_TITLE_ASC = 'TitleAsc', + ORDER_TITLE_DESC = 'TitleDesc', + ORDER_COMMENTS_ASC = 'CommentCountAsc', + ORDER_COMMENTS = 'CommentCount'; + + + protected $_all_articles; + + + public function newFromRow($row) { + $row = array_change_key_case($row, CASE_LOWER); + if (isset($row['start']) && isset($row['end'])) { + $row['events_debut'] = $row['start']; + $row['events_fin'] = $row['end']; + unset($row['start']); + unset($row['end']); + } + + return parent::newFromRow($row); + } + + + /** + * @param array $articles + * @return array + */ + protected function _sortArticles($articles, $sort_order) { + if ($sort_order == 'Random') { + shuffle($articles); + return $articles; + } + + $sort_function = 'sortBy'.$sort_order; + if (method_exists(Class_Article::class, $sort_function)) + usort($articles, 'Class_Article::'.$sort_function); + + return $articles; + } + + + public function getArticlesByPreferencesDefaults() { + return ['id_categorie' => '', // catégories d'article, ex: 12-2-8-1-89 + 'id_items' => '', // liste d'articles, ex: 39-28-7 + 'display_order' => '', // tri, cf. méthodes Class_Article::sortByXXX, Random, Selection, CommentCount + 'order' => '', + 'nb_analyse' => 0, // afficher nb_aff articles (aléatoires) parmis nb_analyse articles ramenés sur un critère + 'nb_aff' => null, // nb d'article à retourner + 'size' => null, // nb d'article à retourner + 'langue' => null, // que les traductions de cette langue + 'event_date' => null, // que les articles dont les dates de début et/ou de fin inclue cette date + 'event_start_after' => null, // que les articles dont l'évènement commence après cette date + 'event_end_after' => null, // que les articles dont l'évènement termine à ou après cette date + 'id_bib' => null, // filtre par cette bibliothèque + 'status' => null, // filtre par cet état de workflow cf. Class_Article::STATUS_XXX + 'events_only' => false, // filtre que les évènements, + 'published' => true, // seulement les articles dont les date de debut / fin incluent le jour en cours, + 'id_lieu' => null, // id du lieu Class_Lieu + 'place_town' => null, + 'display_mode' => 'Title', + 'custom_fields' => [], + 'tag' => '' ]; + } + + + /** + * @param array $preferences + * @return array + */ + public function getArticlesByPreferences($preferences) { + $preferences = array_merge($this->getArticlesByPreferencesDefaults(), + $preferences); + + + $select = Class_AdminVar::isModuleEnabled('ENABLE_ARTICLES_TIMINGS') + ? new Class_Article_SelectWithTimings($this) + : new Class_Article_Select($this); + + $articles = $select->findAll($preferences); + $articles = $this->_filterByCustomFields($articles, + $preferences['custom_fields']); + $articles = $this->_filterByTag($articles, + $preferences['tag']); + + $event_date = $preferences['event_date']; + if ((new ZendAfi_Validate_DateFormat)->isValid($event_date)) + $articles = $this->_filterByDay($event_date, $articles); + + $this->_all_articles = $articles = $this->_sortArticles($articles, + $select->getSortOrder()); + + if ( + ($select->getSortOrder() == static::ORDER_SELECTION) + || + (! $select->getNumberOfArticles()) + ) + return $articles; + + return array_slice($articles, 0, $select->getNumberOfArticles()); + } + + + public function getAllArticles() { + return $this->_all_articles; + } + + + protected function _filterByTag($articles, $tag) { + if (!$tag) + return $articles; + + return array_filter($articles, + function($article) use ($tag) { + return $article->hasTag($tag); + }); + } + + + protected function _filterByCustomFields($articles, $custom_fields) { + if (!$custom_fields) + return $articles; + + $filter = function ($article) use ($custom_fields) { + foreach($custom_fields as $id => $value) { + if ($value != $article->findCustomFieldValueMatching($id,$value)) + return false; + } + return true; + }; + $articles = array_filter($articles, $filter); + + return $articles; + } + + + /** + * @param array $articles + * @return array + */ + public static function groupByBibId(array $articles) { + $grouped = []; + + foreach ($articles as $article) { + $bib_id = ($bib = $article->getBib()) ? $bib->getId() : 0; + + if (array_key_exists($bib_id, $grouped)) { + $grouped[$bib_id][] = $article; + } else { + $grouped[$bib_id] = array($article); + } + } + + return $grouped; + } + + + /** + * @param array $articles + * @return array + */ + public function groupByLibelleCategorie(array $articles) { + $grouped = array(); + + foreach ($articles as $article) { + $libelle_cat = $article->getCategorie()->getLibelle(); + + if (!array_key_exists($libelle_cat, $grouped)) + $grouped[$libelle_cat] = array(); + + $grouped[$libelle_cat][] = $article; + } + + return $grouped; + } + + + /** + * Retourne les traductions des articles valides et pour la langue courant + * @param array + * @return array + */ + public function filterByLocaleAndWorkflow($articles) { + return Class_Article::filterByLocaleAndWorkflow($articles); + } + + public function articlesWithFormulaire() { + return Class_Article::findAll('select id_article,titre from cms_article where id_article in (select distinct id_article from formulaires)'); + } + + + public function indexAll() { + $page = 1; + while($articles_to_index = Class_Article::findAllBy(['indexation' => '1', + 'limitPage' => [$page, 500]])) { + foreach($articles_to_index as $article) + $article->index(); + + Class_Article::clearCache(); + $page++; + } + } + + + protected function _filterByDay($day, $articles) { + if(!$day) + return $articles; + + $day_num = date("w", strtotime($day)); + return array_filter($articles, + function($event) use ($day_num) + { + $pick_day_as_array = $event->getPickDayAsArray(); + if(empty($pick_day_as_array) || in_array($day_num, $pick_day_as_array)) + return $event; + }); + } + + + public function findDistinctStatus($articles) { + $ids = array_map(function($article) {return $article->getId();}, + $articles); + + return Class_Article::findAll('select distinct status from cms_article where id_article in ('. + implode(',',$ids). + ')'); + } + + + public function hasEventForMonth($month) { + return 0 < count(Class_Article::getLoader() + ->getArticlesByPreferences(['event_date'=> Class_Article::getTimeSource()->getMonth($month), + 'events_only' => true, + 'status'=>3])); + } + +} diff --git a/library/Class/Article/Select.php b/library/Class/Article/Select.php new file mode 100644 index 0000000000000000000000000000000000000000..670a1aac497fa6127a5a829624b415ca8da3514b --- /dev/null +++ b/library/Class/Article/Select.php @@ -0,0 +1,433 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_Article_Select { + protected + $_article_loader, + $_select, /** @var Zend_Db_Table_Select */ + $_sort_order, + $_nb_analyse, + $_nb_aff, + $_id_articles, + $_id_categories, + $_limit, + $_status, + $_events_only, + $_published, + $_display_mode, + $_place_town; + + + public function __construct($article_loader) { + $this->_article_loader = $article_loader; + } + + + public function findAll($preferences) { + $this->_initSearchCriterias($preferences); + + if ($this->_sort_order == Class_Article_Loader::ORDER_SELECTION && !$this->_has_selection) + return []; + + $select = $this->_buildSelect(); + return Class_Article::findAll($select); + } + + + public function getNumberOfArticles() { + return $this->_nb_aff; + } + + + public function getSortOrder() { + return $this->_sort_order; + } + + + protected function _initSearchCriterias($preferences) { + $this->_sort_order = $preferences['order'] + ? $preferences['order'] + : $preferences['display_order']; + + $this->_nb_aff = (int) $preferences['size'] + ? (int) $preferences['size'] + : (int) $preferences['nb_aff']; + + $this->_nb_analyse = (int)$preferences['nb_analyse']; + $this->_id_articles = array_filter(explode('-', $preferences['id_items'])); + $this->_id_categories = $this->_mergeCategorieIdsWithSousCategories(array_filter(explode('-', $preferences['id_categorie']))); + $this->_has_selection = (count($this->_id_articles)>0 or count($this->_id_categories)>0); + $this->_limit = $this->_sort_order == 'Random' ? $this->_nb_analyse : $this->_nb_aff; + $this->_langue = $preferences['langue']; + $this->_event_date = $preferences['event_date']; + $this->_event_end_after = $preferences['event_end_after']; + $this->_event_start_after = $preferences['event_start_after']; + $this->_id_bib = $preferences['id_bib']; + $this->_id_lieu = (int)$preferences['id_lieu']; + $this->_status = $preferences['status']; + $this->_events_only = (bool)$preferences['events_only']; + $this->_published = (bool)$preferences['published']; + $this->_display_mode = $preferences['display_mode']; + $this->_custom_fields = $preferences['custom_fields']; + $this->_place_town = $preferences['place_town']; + + return $this; + } + + + protected function _buildSelect() { + return $this + ->_selectArticles() + ->_publishedNow() + ->_byIdBib($this->_id_bib) + ->_byIdLieu($this->_id_lieu) + ->_byPlaceTown($this->_place_town) + ->_whereSelectionIn($this->_id_articles, $this->_id_categories) + ->_whereEventDateIn($this->_event_date) + ->_whereEventStartAfter($this->_event_start_after) + ->_whereEventEndAfter($this->_event_end_after) + ->_filterByLangue() + ->_filterByStatus() + ->_orderAndLimit() + ->_getSelect(); + } + + + protected function _selectArticles() { + if(!$table = $this->_article_loader->getTable()) + return $this; + + $this->_select = $table + ->select('cms_article.*') + ->setIntegrityCheck(false) + ->from('cms_article'); + + return $this; + } + + + protected function _publishedNow() { + if (!$this->_published) + return $this; + + if(!$this->_select) + return $this; + + $this->_select + ->where('(DEBUT IS NULL) OR (DEBUT <= CURDATE())') + ->where('(FIN IS NULL) OR (FIN >= CURDATE())'); + + return $this; + } + + + /** + * @param array $id_categories + * @return array + */ + protected function _mergeCategorieIdsWithSousCategories($id_categories) { + $all_cat_ids = array(); + foreach($id_categories as $id_cat) { + if (!$root_cat = Class_ArticleCategorie::getLoader()->find($id_cat)) + continue; + + $all_cat_ids []= $id_cat; + + $sub_cats = $root_cat->getRecursiveSousCategories(); + foreach ($sub_cats as $cat) + $all_cat_ids []= $cat->getId(); + } + + return array_unique($all_cat_ids); + } + + + /** + * @param array $id_articles + * @param array $id_categories + * @return ArticleLoader + */ + protected function _whereSelectionIn($id_articles, $id_categories) { + $conditions = array(); + + if ($id_articles) + $conditions[] = sprintf('%s in (%s)', + $this->_article_loader->getIdField(), + implode(',', $id_articles)); + + if ($id_categories) + $conditions[] = sprintf('`cms_article`.ID_CAT in (%s)', + implode(',', $id_categories)); + + if ($conditions && $this->_select) + $this->_select->where(implode(' OR ', $conditions)); + + return $this; + } + + + protected function _selectWhereEventStart($template, $value = null) { + $this->_select->where(sprintf($template, 'EVENTS_DEBUT'), $value); + return $this; + } + + + protected function _selectWhereEventEnd($template, $value = null) { + $this->_select->where(sprintf($template, 'EVENTS_FIN'), $value); + return $this; + } + + + protected function _selectAllEvents() { + return $this + ->_selectWhereEventStart('%s IS NOT NULL') + ->_selectWhereEventEnd('%s IS NOT NULL'); + } + + + protected function _selectHasEventStartEnd() { + return $this; + } + + + protected function _selectHasEventEnd() { + $this->_select->where('EVENTS_FIN IS NOT NULL'); + return $this; + } + + + /** + * @param string $event_date + * @return ArticleLoader + */ + protected function _whereEventDateIn($event_date) { + if (!$this->_select) + return $this; + + if ($this->_events_only && empty($event_date)) + return $this->_selectAllEvents(); + + if (!$event_date || !preg_match('/\d{4}-\d{2}(-\d{2})?/',$event_date)) + return $this; + + $truncate = (10 == strlen($event_date)) ? '10' : '7'; + + $this + ->_selectHasEventStartEnd() + ->_selectWhereEventStart('left(%s,'.$truncate.') <= ?', $event_date) + ->_selectWhereEventEnd('left(%s,'.$truncate.') >= ?', $event_date); + + return $this; + } + + + /** + * @param string $event_date + * @return ArticleLoader + */ + protected function _whereEventStartAfter($event_date) { + if (!$this->_select) + return $this; + + if (!$event_date || !preg_match('/\d{4}-\d{2}(-\d{2})?/',$event_date)) + return $this; + + $this + ->_selectWhereEventStart('%s IS NOT NULL') + ->_selectWhereEventEnd('%s IS NOT NULL'); + + $field = (10 == strlen($event_date)) ? 'left(%s,10)' : 'left(%s,7)'; + $this->_selectWhereEventStart("$field > ?", $event_date); + + return $this; + } + + + protected function _selectWhereEventEndAfterDay($day) { + $this + ->_selectHasEventEnd() + ->_selectWhereEventEnd('left(%s,10) >= ?', $day); + return $this; + } + + + protected function _whereEventEndAfter($event_date) { + if (!$this->_select || !$event_date || !preg_match('/\d{4}-\d{2}(-\d{2})?/', $event_date)) + return $this; + + return $this->_selectWhereEventEndAfterDay($event_date); + } + + + protected function _orderAndLimit() { + if(!$this->_select) + return $this; + + if ($this->_orderByCommentsCount()) + return $this; + + if ( $this->_orderByTitle() ) + return $this; + + if (!$this->_has_selection) { + $this->_select->order('DATE_CREATION DESC'); + $this->_select->limit($this->_limit); + return $this; + } + + if ($this->_id_categories) + $this->_select->order(sprintf("FIELD(`cms_article`.ID_CAT, %s)", implode(',', $this->_id_categories))); + + if ($this->_id_articles) + $this->_select->order(sprintf("FIELD(ID_ARTICLE, %s)", implode(',', $this->_id_articles))); + + return $this; + } + + + protected function _orderByTitle() { + if ( false === strpos($this->_sort_order, 'Title')) + return false; + + $order = $this->_sort_order == Class_Article_Loader::ORDER_TITLE_ASC + ? 'asc' + : 'desc'; + + $this->_select + ->order('titre ' . $order); + + return true; + } + + + protected function _orderByCommentsCount() { + if ( false === strpos($this->_sort_order, 'CommentCount')) + return false; + + $order = $this->_sort_order == Class_Article_Loader::ORDER_COMMENTS + ? 'desc' + : 'asc'; + + $this->_select + ->join('cms_rank', + 'cms_rank.ID_CMS = cms_article.ID_ARTICLE', + []) + ->order('(cms_rank.abon_nombre_avis + cms_rank.bib_nombre_avis) ' . $order); + return true; + } + + + protected function _filterByLangue() { + if(!$this->_select) + return $this; + + if ($this->_has_selection) + return $this; + + $where = $this->_langue + ? 'LANGUE=?' + : 'PARENT_ID=?' ; + + $value = $this->_langue + ? $this->_langue + : 0 ; + + $this->_select->where($where, $value); + return $this; + } + + + protected function _getSelect() { + return $this->_select; + } + + + + /** + * @param array $preferences + * @return array + */ + protected function _byIdBib($id_bib) { + if ((0 === $id_bib) or (null === $id_bib)) + return $this; + + $this->_select + ->join('cms_categorie', + 'cms_categorie.ID_CAT = cms_article.ID_CAT', + array()) + ->where('cms_categorie.ID_SITE=?', $id_bib); + return $this; + } + + + protected function _byIdLieu($id_lieu) { + if (null == $id_lieu) + return $this; + + $this->_select->where('ID_LIEU=?', $id_lieu); + return $this; + } + + + protected function _byPlaceTown($town) { + if (!$town || '' == $town) + return $this; + $this->_select + ->join('lieux', + 'lieux.ID = cms_article.ID_LIEU', + array()) + ->where('trim(lieux.VILLE) like ?', $town); + + return $this; + } + + + protected function _byCustomFields($custom_fields) { + foreach ($custom_fields as $id => $value) { + $this->_select + ->join( + [ "cfv$id" => 'custom_field_values' ], + "cms_article.ID_ARTICLE = cfv$id.model_id AND cfv$id.custom_field_id = $id", + [] + ); + } + + return $this; + } + + + protected function _filterByStatus() { + if (null === $this->_status || !is_array($this->_status)) { + return $this; + } + + if (null === $this->_select) { + return $this; + } + + $this->_select->where('STATUS in (?)', $this->_status); + + return $this; + } + + +} diff --git a/library/Class/Article/SelectWithTimings.php b/library/Class/Article/SelectWithTimings.php new file mode 100644 index 0000000000000000000000000000000000000000..2ca761cab01547dcbdf436b11112a396d8d9ebd0 --- /dev/null +++ b/library/Class/Article/SelectWithTimings.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_Article_SelectWithTimings extends Class_Article_Select { + use Trait_TimeSource; + + protected function _selectArticles() { + parent::_selectArticles(); + $this->_select + ->joinLeft('cms_article_timings', + 'cms_article.id_article=cms_article_timings.article_id', + ['start', 'end']) + ->group('id_article'); + return $this; + } + + + protected function _selectWhereEventStart($template, $value = null) { + $this->_select->where(sprintf('%s OR %s', + sprintf($template, 'EVENTS_DEBUT'), + sprintf($template, 'START')), + $value); + return $this; + } + + + protected function _selectWhereEventEnd($template, $value = null) { + /* + * if article as multiple timings and asking for current month, for calendar we need + * to select the first timing above current date so users can see the next event. + */ + $is_query_current_month = empty($value) || ($value == date('Y-m', $this->getCurrentTime())); + + $clauses = $is_query_current_month + ? '%s OR (%s AND END >= CURDATE())' + : '%s OR %s'; + + $this->_select->where(sprintf($clauses, + sprintf($template, 'EVENTS_FIN'), + sprintf($template, 'END')), + $value); + return $this; + } + + + protected function _selectHasEventStartEnd() { + return $this + ->_selectHasEventStart() + ->_selectHasEventEnd(); + } + + + protected function _selectHasEventStart() { + $this->_select->where('EVENTS_DEBUT IS NOT NULL OR START IS NOT NULL'); + return $this; + } + + + protected function _selectHasEventEnd() { + $this->_select->where('EVENTS_FIN IS NOT NULL OR END IS NOT NULL'); + return $this; + } +} diff --git a/library/Class/Calendar.php b/library/Class/Calendar.php index a04851a259374d56ac21aaa7f067d2f3c23809ac..b58a5c4dae13b3935fef22188c1761440ec0dfd1 100644 --- a/library/Class/Calendar.php +++ b/library/Class/Calendar.php @@ -22,17 +22,6 @@ class Class_Calendar { use Trait_TimeSource, Trait_Translator; - var $PREFIX = "calendar_"; - var $URL_PARAMETER = "date"; - var $PRESERVE_URL = true; - var $LANGUAGE_CODE = "fr"; - var $WEEK_DAYS; - var $MONTH_HEADER = array('fr' => "%m %y"); - var $events = array(); - var $day; - var $month; - var $year; - protected $id_module, $preferences, @@ -41,6 +30,7 @@ class Class_Calendar { $id_categorie = '', $id_bib, $date, + $day, $_article_event_helper; @@ -159,10 +149,14 @@ class Class_Calendar { && (!$id_lieu) && !$town) return []; + $prefs = []; if ($town) $prefs['place_town'] = $town; + if ($tag = $this->_getParam('tag', null)) + $prefs['tag'] = $tag; + $prefs = array_merge($prefs, ['display_order' => (isset($this->preferences['display_order']) ? $this->preferences['display_order'] @@ -239,8 +233,13 @@ class Class_Calendar { 'event_date' => '', 'event_end_after' => '', 'limit' => $this->_getSize()]); - $articles = array_merge($articles, - Class_Article::filterByLocaleAndWorkflow($next_articles)); + $next_articles = array_udiff($next_articles, + $articles, + function ($article, $next_article) + { + return $article->getId() - $next_article->getId(); + }); + $articles = array_merge($articles, $next_articles); } return $articles; @@ -396,4 +395,4 @@ class Class_Calendar { $this->preferences['display_full_page'] = $boolean; return $this; } -} \ No newline at end of file +} diff --git a/library/Class/Date.php b/library/Class/Date.php index ca0f9635734484adc2237357b0bcb0da5a17c69f..d86cc7d562b8514ef930a791b8a23b7ddf9d4b43 100644 --- a/library/Class/Date.php +++ b/library/Class/Date.php @@ -41,24 +41,6 @@ class Class_Date { } - /* - * @param string $dateFormat Format used to generate the date - * @return string Today's date - * @return false if failed - */ - function DateDuJour($dateFormat) - { - try{ - $zendDate = new Zend_Date(); - $dateDuJour = $zendDate->toString($dateFormat); - }catch (Exception $e){ - logErrorMessage('Class: Class_Date; Function: DateDuJour' . NL . $e->getMessage()); - $dateDuJour = false; - } - - return $dateDuJour; - } - /* * Uses Date Format = 'yyyy-MM-dd HH:mm:ss' * @return string Today's date and time @@ -88,60 +70,6 @@ class Class_Date { return $localizedDate; } - /* - * @param string $varDate date to be localized - * @param string $dateFormat Format used to generate the date - * @return string localized date - * @return false if failed - */ - function RendDateSql($varDate) - { - try{ - $zendDate = new Zend_Date($varDate, $this->_localeDateFormat, $this->_locale); - $sqlDate = $zendDate->toString('-MM-dd'); - }catch (Exception $e){ - logErrorMessage('Class: Class_Date; Function: RendDateSql' . NL . $e->getMessage()); - $sqlDate = false; - } - - return $sqlDate; - } - - /* - * @param string $varDate1 date - * @param string $varDate1 date - * @return int The difference in days between the 2 dates - */ - function SoustraitDates($varDate1, $varDate2) - { - - $date1 = strtotime($varDate1); - $date2 = strtotime($varDate2); - - $differenceDays = (integer)(($date1 - $date2) / (24*60*60)); - - return $differenceDays; - } - - /* - * @param string $varDate date - * @param int $jours number of days - * @param string $dateFormat Format used to generate the date - * @return string new date = $varDate + $jours - * @return false if failed - */ - function AjouterJours($varDate, $jours, $dateFormat) - { - try{ - $zendDate = new Zend_Date($varDate, $dateFormat, $this->_locale); - $zendDate->add((int)$jours, Zend_Date::DAY); - return $zendDate->toString($dateFormat); - }catch (Exception $e){ - logErrorMessage('Class: Class_Date; Function: AjouterJours' . NL . $e->getMessage()); - return false; - } - - } /* * @return int current time @@ -156,25 +84,6 @@ class Class_Date { return $time; } - /* - * @param int $t time - * @return string in format '11 h 40 min. 33 sec.' - */ - function getSecMinHTime($t) - { - $temps = ""; - $secondes = $t % 60; - $minutes = (int)($t/60); - $heures = (int)($minutes/60); - $minutes = $minutes % 60; - if( $heures > 0 )$temps = $heures . " h "; - if( $minutes > 0 ) $temps .= $minutes . " min. "; - $temps .= $secondes . " sec."; - - return $temps; - } - - public static function isEndDateAfterStartDate($start_date, $end_date) { if (empty($start_date) || empty($end_date)) diff --git a/library/Class/Date/Iso.php b/library/Class/Date/Iso.php index 896a4479ddea07d699f41ef0be1ab0a9fcd4f637..45eca1e086ffe5d55fa1490a02ad0fa5714d9635 100644 --- a/library/Class/Date/Iso.php +++ b/library/Class/Date/Iso.php @@ -92,6 +92,22 @@ class Class_Date_Iso { class Class_Date_IsoWithTime extends Class_Date_Iso { + + const TIME_PART_ISO = 'T(\d{2}:\d{2})?:\d{2}\.\d{3}Z$'; + + public function ensure($str) { + if ($matches = $this->_match($this->_isoPattern(), $str)) + return $this->_fromIso($matches); + + return parent::ensure($str); + } + + + protected function _isoPattern() { + return static::DATE_PART_US . static::TIME_PART_ISO . '$'; + } + + protected function _frPattern() { return static::DATE_PART_FR . static::TIME_PART . '$'; } @@ -112,6 +128,11 @@ class Class_Date_IsoWithTime extends Class_Date_Iso { } + protected function _fromIso($matches) { + return parent::_fromUs($matches) . ' ' . $this->_ensureTime($matches); + } + + protected function _ensureTime($matches) { return isset($matches[4]) ? $matches[4] : ' 00:00'; } diff --git a/library/Class/ExternalAgenda.php b/library/Class/ExternalAgenda.php index 7a97ee78788e5ee67617a42e5f1133ef5bb0f8d4..6780dc2a719f08a58249bdb832b546ec79eaeff3 100644 --- a/library/Class/ExternalAgenda.php +++ b/library/Class/ExternalAgenda.php @@ -122,8 +122,8 @@ class Class_ExternalAgenda extends Storm_Model_Abstract { protected function _newProvider() { - $map = [static::OPEN_AGENDA => 'Class_ExternalAgenda_OpenAgenda', - static::ICALENDAR => 'Class_ExternalAgenda_ICalendar']; + $map = [static::OPEN_AGENDA => Class_ExternalAgenda_OpenAgenda::class, + static::ICALENDAR => Class_ExternalAgenda_ICalendar::class]; return array_key_exists($this->getProvider(), $map) ? new $map[$this->getProvider()] diff --git a/library/Class/ExternalAgenda/OpenAgenda.php b/library/Class/ExternalAgenda/OpenAgenda.php index dcdef4757ee42164629fd4b71b319d59c36c28ec..f4584f891f7282f90449c15334656899a64b4e51 100644 --- a/library/Class/ExternalAgenda/OpenAgenda.php +++ b/library/Class/ExternalAgenda/OpenAgenda.php @@ -21,16 +21,17 @@ class Class_ExternalAgenda_OpenAgenda extends Class_ExternalAgenda_Provider { + protected function _import() { + $event_builder = Class_AdminVar::isModuleEnabled('ENABLE_ARTICLES_TIMINGS') + ? (new Timings()) + : (new Articles()); - public function _import() { $page = 1; while ($content = json_decode($this->httpGet($this->_pageUrl($page)), true)) { if (!isset($content['events']) || !is_array($content['events']) || !$content['events']) break; - foreach($content['events'] as $event) - $this->_processEvent($event); - + $this->_buildPage($event_builder, $content['events']); $page++; } @@ -38,37 +39,90 @@ class Class_ExternalAgenda_OpenAgenda extends Class_ExternalAgenda_Provider { } + protected function _buildPage($builder, $events) { + foreach($events as $event) + $builder->processEvent(new Class_ExternalAgenda_OpenAgenda_Event($event), + $this); + } + + + public function newEvent() { + return $this->_newEvent(); + } + + + public function appendEvent($article) { + return $this->_events->append($article); + } + + protected function _pageUrl($page) { return $this->_url() . '&page=' . $page; } +} - protected function _processEvent($datas){ - $event = new Class_ExternalAgenda_OpenAgenda_Event($datas); - foreach($event->get('timings') as $timing) { - $article = $this->_buildArticleForTiming($event, $timing); - $this->_events->append($article); - } +abstract class EventBuilder_Abstract { + public function processEvent($event, $open_agenda) { return $this; } - protected function _buildArticleForTiming($event, $timing) { - return $this - ->_newEvent() + protected function _buildArticle($event, $open_agenda) { + return $open_agenda + ->newEvent() ->setTitre($event->getString('title')) ->setContenu($event->getImageTagWithCredits() .$event->getHtml() .$event->getInfosTag() .$event->getInscriptionsTag()) - ->setIdOrigine($event->get('uid') . '_' . base64_encode($timing['start'])) ->setDescription($event->getImageTag().'<p>'.$event->getString('description').'</p>') + ->setDateMaj($event->getUpdatedAt()) ->setTags(implode(';', $event->getKeywords())) ->setLieu($event->getLocation()) - ->setEventsDebut(date('Y-m-d H:i', strtotime($timing['start']))) - ->setEventsFin(date('Y-m-d H:i', strtotime($timing['end']))); + ->setIdOrigine($event->getUid()); + } +} + + + + +class Articles extends EventBuilder_Abstract { + public function processEvent($event, $open_agenda){ + foreach($event->get('timings') as $timing) { + $article = $this->_buildArticleForTiming($event, $timing, $open_agenda); + $open_agenda->appendEvent($article); + } + + return $this; + } + + + protected function _buildArticleForTiming($event, $timing, $open_agenda) { + return $this->_buildArticle($event, $open_agenda) + ->setIdOrigine($event->getUid($timing['start'])) + ->setEventsDebut(date('Y-m-d H:i', strtotime($timing['start']))) + ->setEventsFin(date('Y-m-d H:i', strtotime($timing['end']))); + } +} + + + + +class Timings extends EventBuilder_Abstract { + public function processEvent($event, $open_agenda){ + $article = $this->_buildArticle($event, $open_agenda) + ->setIdOrigine($event->getUid()); + + foreach($event->get('timings') as $timing) + $article->addEventTiming((new Class_Article_EventTiming()) + ->setStart($timing['start']) + ->setEnd($timing['end'])); + + $open_agenda->appendEvent($article); + return $this; } } @@ -95,6 +149,11 @@ class Class_ExternalAgenda_OpenAgenda_Event { } + public function getUpdatedAt() { + return $this->_event['updatedAt']; + } + + public function getImageTag(){ return ($src = $this->_event['image']) ? sprintf('<img src="%s" alt=""/>',$src) @@ -102,6 +161,12 @@ class Class_ExternalAgenda_OpenAgenda_Event { } + public function getUid($start = null) { + return !Class_AdminVar::isModuleEnabled('ENABLE_ARTICLES_TIMINGS') + ? $this->get('uid') . '_' . base64_encode($start) + : $this->get('uid'); + } + public function getInfosTag() { $infos = ''; diff --git a/library/Class/FilterSettings.php b/library/Class/FilterSettings.php index e28f2828344f7911b8ce1aa1df65b43433c58c65..e26728280869e1a91abb7e4d854d5ae8d693c705 100644 --- a/library/Class/FilterSettings.php +++ b/library/Class/FilterSettings.php @@ -20,9 +20,62 @@ */ -class Class_FilterSettings extends Class_Entity { +class Class_FilterSettings { + protected + $_rendered_models = [], + $_filter_names, + $_calendar, + $_selected_filters, + $_default_filters, + $_use_default_filters, + $_model_label, + $_url_params, + $_filters_display_mode, + $_filters_position, + $_id_module, + $_on_load_complete, + $_module; + + public function __construct($module) { - $this->setModule($module); + $this->_module = $module; + } + + + public function getModule() { + return $this->_module; + } + + + public function setFilters($filter_names) { + $this->_filter_names = $filter_names; + return $this; + } + + + public function getFilters() { + return $this->_filter_names; + } + + + public function setCalendar($calendar) { + $this->_calendar = $calendar; + return $this; + } + + + public function getCalendar() { + return $this->_calendar; + } + + + public function setRenderedModels($models) { + $this->_rendered_models = $models; + } + + + public function renderedModelsMap($callable) { + return array_map($callable, $this->_rendered_models); } @@ -78,4 +131,103 @@ class Class_FilterSettings extends Class_Entity { ? $active_filters : []; } -} \ No newline at end of file + + + public function setSelectedFilters($filter_names) { + $this->_selected_filters = $filter_names; + return $this; + } + + + public function getSelectedFilters() { + return $this->_selected_filters; + } + + + public function setUseDefaultFilters($use_defaults) { + $this->_use_default_filters = $use_defaults; + return $this; + } + + + public function getUseDefaultFilters() { + return $this->_use_default_filters; + } + + + public function setDefaultFilters($filter_names) { + $this->_default_filters = $filter_names; + return $this; + } + + + public function getDefaultFilters() { + return $this->_default_filters; + } + + + public function setModelLabel($label) { + $this->_model_label = $label; + return $this; + } + + + public function getModelLabel() { + return $this->_model_label; + } + + + public function setUrlParams($url_params) { + $this->_url_params = $url_params; + return $this; + } + + + public function getUrlParams() { + return $this->_url_params; + } + + + public function setFiltersDisplayMode($display_mode) { + $this->_filters_display_mode = $display_mode; + return $this; + } + + + public function getFiltersDisplayMode() { + return $this->_filters_display_mode; + } + + + public function setFiltersPosition($position) { + $this->_filters_position = $position; + return $this; + } + + + public function getFiltersPosition() { + return $this->_filters_position; + } + + + public function setIdModule($id_module) { + $this->_id_module = $id_module; + return $this; + } + + + public function getIdModule() { + return $this->_id_module; + } + + + public function setOnLoadComplete($javascript) { + $this->_on_load_complete = $javascript; + return $this; + } + + + public function getOnLoadComplete() { + return $this->_on_load_complete; + } +} diff --git a/library/Class/Systeme/ModulesAccueil/Calendrier.php b/library/Class/Systeme/ModulesAccueil/Calendrier.php index a9b9e9436daba78c847bf64564951132ec515674..3f8833bf589d3fe93ec88814ff1576c0f433a964 100644 --- a/library/Class/Systeme/ModulesAccueil/Calendrier.php +++ b/library/Class/Systeme/ModulesAccueil/Calendrier.php @@ -63,7 +63,8 @@ class Class_Systeme_ModulesAccueil_Calendrier extends Class_Systeme_ModulesAccue $available_filters = ['day' => $this->_('Date'), 'date' => $this->_('Mois'), 'place' => $this->_('Lieu'), - 'place_town' => $this->_('Ville')]; + 'place_town' => $this->_('Ville'), + 'tag' => $this->_('Étiquette')]; $custom_fields = Class_CustomField_Model::getModel('Article')->getFields(); @@ -72,4 +73,4 @@ class Class_Systeme_ModulesAccueil_Calendrier extends Class_Systeme_ModulesAccue return $available_filters; } -} \ No newline at end of file +} diff --git a/library/Class/TableDescription/EventTimings.php b/library/Class/TableDescription/EventTimings.php new file mode 100644 index 0000000000000000000000000000000000000000..4542fb819e8ab344f5b6d9cd1f6eebce5f54072d --- /dev/null +++ b/library/Class/TableDescription/EventTimings.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_TableDescription_EventTimings extends Class_TableDescription { + + public function init() { + + $edit_url = Class_Url::assemble(['module' => 'admin', + 'controller' => 'cms', + 'action' => 'event-timing-edit'], + null, + true) . '/id/%s'; + + $delete_url = Class_Url::assemble(['module' => 'admin', + 'controller' => 'cms', + 'action' => 'event-timing-delete'], + null, + true) . '/id/%s'; + + $this->addColumn($this->_('Début'), 'start') + ->addColumn($this->_('Fin'), 'end') + ->addRowAction(['url' => $edit_url, + 'label' => function($event_timing) { + return $this->_('Modifier l\'horaire %s - %s', + $event_timing->getStart(), + $event_timing->getEnd()); + }, + 'icon' => 'edit', + 'anchorOptions' => ['data-popup' => 'true']]) + ->addRowAction(['url' => $delete_url, + 'label' => function($event_timing) { + return $this->_('Supprimer l\'horaire %s - %s', + $event_timing->getStart(), + $event_timing->getEnd()); + }, + 'icon' => 'delete', + 'anchorOptions' => ['onclick' => "return confirm('" + . $this->_('êtes-vous sûr de supprimer cet horaire?') . ");"]]); + } +} diff --git a/library/ZendAfi/Acl/AdminControllerRoles.php b/library/ZendAfi/Acl/AdminControllerRoles.php index 8f26a14f7df17dbb575b1053e45f27d99cd97332..f2250c1fc9c19581c98abfe69cb0628106cd3826 100644 --- a/library/ZendAfi/Acl/AdminControllerRoles.php +++ b/library/ZendAfi/Acl/AdminControllerRoles.php @@ -59,6 +59,7 @@ class ZendAfi_Acl_AdminControllerRoles extends Zend_Acl { $this->add(new Zend_Acl_Resource('agenda')); $this->add(new Zend_Acl_Resource('auth')); $this->add(new Zend_Acl_Resource('cms')); + $this->add(new Zend_Acl_Resource('event-timings')); $this->add(new Zend_Acl_Resource('cms-category')); $this->add(new Zend_Acl_Resource('ckeditor')); $this->add(new Zend_Acl_Resource('data')); @@ -151,6 +152,7 @@ class ZendAfi_Acl_AdminControllerRoles extends Zend_Acl { $this->allow('invite','auth'); $this->allow('modo_bib','cms'); + $this->allow('modo_bib','event-timings'); $this->allow('modo_bib','ckeditor'); $this->allow('modo_bib','cms-category'); $this->allow('modo_bib','ajax'); diff --git a/library/ZendAfi/Controller/Plugin/Manager/Article.php b/library/ZendAfi/Controller/Plugin/Manager/Article.php index f4459b7f25aae63e82c3ed34b795286798dad233..ebce05da8c2cbe73d688ff766b338afc1f22f518 100644 --- a/library/ZendAfi/Controller/Plugin/Manager/Article.php +++ b/library/ZendAfi/Controller/Plugin/Manager/Article.php @@ -313,61 +313,64 @@ class ZendAfi_Controller_Plugin_Manager_Article extends ZendAfi_Controller_Plugi if (!$model) return []; - if ('Class_Article' == get_class($model)) + if (Class_Article::class == get_class($model)) return $this->_articleActions($model); - if ('Class_ArticleCategorie' == get_class($model)) + if (Class_ArticleCategorie::class == get_class($model)) return $this->_articleCategoryActions($model); - if ('Class_Bib' == get_class($model)) + if (Class_Bib::class == get_class($model)) return $this->_bibActions($model); return []; } - protected function _articleActions($model) { - $this->identity = Class_Users::getIdentity(); - - $permission_closure = function($model) { - return $this->identity - ->hasAnyPermissionOn($model->getCategorie(), - [Class_Permission::createArticle(), - Class_Permission::createArticleCategory()]); - }; - return [ - ['url' => '/admin/cms/makeinvisible/id/%s', - 'icon' => 'show', - 'label' => $this->_('Rendre cet article invisible'), - 'condition' => function($model) use ($permission_closure) { - return $permission_closure($model) && $model->isVisible(); - }], - - ['url' => '/admin/cms/makevisible/id/%s', - 'icon' => 'hide', - 'label' => $this->_('Rendre cet article visible'), - 'condition' => function($model) use ($permission_closure) { - return $permission_closure($model) && $model->isNotVisible(); - }], - - ['url' => $this->_view->url(['module' => 'admin', - 'controller' => 'cms', - 'action' => 'edit', - 'id' => $model->getId()]), - 'icon' => 'edit', - 'label' => $this->_('Modifier'), - 'condition' => $permission_closure], - - ['url' => '/admin/cms/newsduplicate/id/%s', - 'icon' => 'copy', - 'label' => $this->_('Dupliquer'), - 'condition' => $permission_closure], - - ['url' => '/admin/cms/delete/id/%s', - 'icon' => 'delete', - 'label' => $this->_('Supprimer'), - 'condition' => $permission_closure] - ]; + protected function _articleActions($article) { + $actions = []; + + if (!Class_Users::getIdentity() + ->hasAnyPermissionOn($article->getCategorie(), + [Class_Permission::createArticle(), + Class_Permission::createArticleCategory()])) + return $actions; + + if ($article->isVisible()) + $actions []= ['url' => '/admin/cms/makeinvisible/id/%s', + 'icon' => 'show', + 'label' => $this->_('Rendre cet article invisible : %s', $article->getTitre())]; + + if ($article->isNotVisible()) + $actions []= ['url' => '/admin/cms/makevisible/id/%s', + 'icon' => 'hide', + 'label' => $this->_('Rendre l\'article visible : %s', $article->getTitre())]; + + $actions []= ['url' => $this->_view->url(['module' => 'admin', + 'controller' => 'cms', + 'action' => 'edit', + 'id' => $article->getId()]), + 'icon' => 'edit', + 'label' => $this->_('Modifier l\'article : %s', $article->getTitre())]; + + if (Class_AdminVar::isModuleEnabled('ENABLE_ARTICLES_TIMINGS')) + $actions []= ['url' => $this->_view->url(['module' => 'admin', + 'controller' => 'cms', + 'action' => 'event-timings', + 'article_id' => $article->getId()], + null, + true), + 'icon' => 'calendar', + 'label' => $this->_('Horaires de l\'événement : %s', $article->getTitre())]; + + $actions []= ['url' => '/admin/cms/newsduplicate/id/%s', + 'icon' => 'copy', + 'label' => $this->_('Dupliquer l\'article : %s', $article->getTitre())]; + + $actions []= ['url' => '/admin/cms/delete/id/%s', + 'icon' => 'delete', + 'label' => $this->_('Supprimer l\'article : %s', $article->getTitre())]; + + return $actions; } diff --git a/library/ZendAfi/Controller/Plugin/Manager/EventTimings.php b/library/ZendAfi/Controller/Plugin/Manager/EventTimings.php new file mode 100644 index 0000000000000000000000000000000000000000..2044f0e0472e8c26d253f29f90d88ca0569bcdcf --- /dev/null +++ b/library/ZendAfi/Controller/Plugin/Manager/EventTimings.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_Controller_Plugin_Manager_EventTimings extends ZendAfi_Controller_Plugin_Manager_Manager { + protected + $_event_timing, + $_article; + + public function init() { + parent::init(); + $this->_event_timing = Class_Article_EventTiming::find($this->_request->getParam('id')); + $this->_view->article = $this->_article = $this->_event_timing + ? $this->_event_timing->getArticle() + : Class_Article::find($this->_request->getParam('article_id')); + } + + + public function indexAction() { + if (!$this->_article) + return $this->_redirect('admin/cms'); + + $this->_view->titre = $this->_('Horaires de l\'événement : %s', + $this->_article->getTitre()); + $this->_view->article = $this->_article; + } + + + protected function _redirectToIndex() { + $url = 'admin/cms'; + if ($this->_article) + $url .= '/event-timings/article_id/' . $this->_article->getId(); + + $this->_redirect($url); + } + + + protected function _doBeforeSave($event_timing) { + if ($event_timing->isNew()) + $event_timing->setArticle($this->_article); + } + + + protected function _getEditUrl($event_timing) { + return ['module' => 'admin', + 'controller' => 'cms', + 'action' => 'event-timing-edit', + 'id' => $event_timing->getId(), + 'article_id' => $event_timing->getArticleId()]; + } + + + protected function _canEdit($event_timing) { + return Class_Users::getIdentity()->canEditArticle($this->_article); + } +} diff --git a/library/ZendAfi/Controller/Plugin/ResourceDefinition/EventTimings.php b/library/ZendAfi/Controller/Plugin/ResourceDefinition/EventTimings.php new file mode 100644 index 0000000000000000000000000000000000000000..64776ccae537c75e546ae231a3b5ca7ebc4a2a27 --- /dev/null +++ b/library/ZendAfi/Controller/Plugin/ResourceDefinition/EventTimings.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_Controller_Plugin_ResourceDefinition_EventTimings extends ZendAfi_Controller_Plugin_ResourceDefinition_Abstract { + + public function getDefinitions() { + return ['model' => ['class' => Class_Article_EventTiming::class, + 'name' => 'event-timings', + 'order' => 'start', + 'scope' => 'article_id'], + 'messages' => ['successful_add' => $this->_('Un nouvel horaire a été créé'), + 'successful_save' => $this->_('Les horaires ont été mis à jour'), + 'successful_delete' => $this->_('L\'horaire a été supprimé')], + 'actions' => ['edit' => ['title' => $this->_('%s : modifier un horaire')], + 'add' => ['title' => $this->_('Ajouter un horaire')]], + 'form_class_name' => ZendAfi_Form_Admin_EventTimings::class]; + } +} diff --git a/library/ZendAfi/Form/Admin/EventTimings.php b/library/ZendAfi/Form/Admin/EventTimings.php new file mode 100644 index 0000000000000000000000000000000000000000..757ace548856d917ac8a13ad1b6e9ed7980f2057 --- /dev/null +++ b/library/ZendAfi/Form/Admin/EventTimings.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_Form_Admin_EventTimings extends ZendAfi_Form { + public function init() { + parent::init(); + + $this->addElement('datePicker', + 'start', + ['dateOnly' => false, + 'required' => true, + 'allowEmpty' => false, + 'validators' => [(new ZendAfi_Validate_DateFormat)->setFormat('d/m/Y H:i')], + 'label' => $this->_('Date et heure de début')]) + ->addElement('datePicker', + 'end', + ['dateOnly' => false, + 'required' => true, + 'allowEmpty' => false, + 'validators' => [(new ZendAfi_Validate_DateFormat)->setFormat('d/m/Y H:i')], + 'label' => $this->_('Date et heure de fin')]) + ->addUniqDisplayGroup('timings'); + } +} diff --git a/library/ZendAfi/View/Helper/Admin/HelpLink.php b/library/ZendAfi/View/Helper/Admin/HelpLink.php index 094a4449c5642b688fd8140cf359ddc3ffafa99d..15710800919cff552aeffc915ba7149a07612f0a 100644 --- a/library/ZendAfi/View/Helper/Admin/HelpLink.php +++ b/library/ZendAfi/View/Helper/Admin/HelpLink.php @@ -80,7 +80,10 @@ class ZendAfi_View_Helper_Admin_HelpLinkBokehWiki { 'add-website' => 'Sitothèque_ressource_numérique', 'import_ead' => 'Import-Export_EAD'], 'catalogue' => ['index' => 'Domaines'], - 'cms' => ['index' => 'Articles_-_Créer,_rédiger_et_ordonner'], + 'cms' => ['index' => 'Articles_-_Créer,_rédiger_et_ordonner', + 'event-timings' => 'Articles_-_Horaires_multiples', + 'event-timing-edit' => 'Articles_-_Horaires_multiples', + 'event-timing-add' => 'Articles_-_Horaires_multiples'], 'custom-fields' => ['index' => 'Gestion_des_champs_personnalisés'], 'custom-fields-report' => ['index' => 'Rapports_statistiques'], 'activity' => ['index' => 'Gestion_des_activités' ], diff --git a/library/ZendAfi/View/Helper/Article/RenderAbstract.php b/library/ZendAfi/View/Helper/Article/RenderAbstract.php index 5d1ee892d2748f7dbe8480a398c58d9f5893c141..b99f325c0f6903837f20b3cecf52920e5c4749b1 100644 --- a/library/ZendAfi/View/Helper/Article/RenderAbstract.php +++ b/library/ZendAfi/View/Helper/Article/RenderAbstract.php @@ -34,6 +34,7 @@ abstract class ZendAfi_View_Helper_Article_RenderAbstract ['class' => 'article_content']) . $this->_tag('footer', $this->renderLieu($article) + .$this->renderTimingsIfEnabled($article) .$this->renderICalLink($article) .$this->renderPrintLink($article) .$this->renderReseauxSociaux($article) @@ -52,7 +53,6 @@ abstract class ZendAfi_View_Helper_Article_RenderAbstract return $this->view->tagArticleEvent($article); } - public function renderArticleHTML($html, $article) { return $this->getRenderContainerStrategy()->renderArticleHTML($html, $article); } @@ -140,4 +140,14 @@ abstract class ZendAfi_View_Helper_Article_RenderAbstract public function renderArticleInfo($article) { return $this->view->tagArticleInfo($article); } + + + public function renderTimingsIfEnabled($article) { + return Class_AdminVar::isModuleEnabled('ENABLE_ARTICLES_TIMINGS') + ? $this->renderTimings($article) + : ''; + } + + + public function renderTimings($article) { return ''; } } diff --git a/library/ZendAfi/View/Helper/Article/RenderEventTiming.php b/library/ZendAfi/View/Helper/Article/RenderEventTiming.php new file mode 100644 index 0000000000000000000000000000000000000000..3912fc929e1e8a7b608e8741de6f69412b96c618 --- /dev/null +++ b/library/ZendAfi/View/Helper/Article/RenderEventTiming.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_Article_RenderEventTiming extends ZendAfi_View_Helper_BaseHelper { + public function article_RenderEventTiming($start, $end, $all_day = false, $pick_days = []) { + $date_start = strtotime(substr($start, 0, 10)); + + $time_start = date('H:i', strtotime($start)); + $time_end = date('H:i', strtotime($end)); + + if (!$date_end = strtotime(substr($end, 0, 10))) { + $date_end = $date_start; + $time_end = $time_start; + } + + return $this->_formatEventString($date_start, + $date_end, + $time_start, + $time_end, + $all_day, + $pick_days); + } + + + + public function _formatEventString($date_start, + $date_end, + $time_start, + $time_end, + $all_day, + $picked_days) { + if ($date_start == $date_end) { + $event_string = strftime('%A %d %B %Y', $date_start); + + if ($all_day) + return $event_string; + + return $event_string . (($time_start == $time_end) ? $this->_(' à %s', $time_start) + : $this->_(' de %s à %s', $time_start, $time_end)); + } + + $year_start = strftime('%Y', $date_start); + $year_end = strftime('%Y', $date_end); + + $month_start = strftime('%B', $date_start); + $month_end = strftime('%B', $date_end); + + + if ($month_start == $month_end && $year_start == $year_end) + $month_start = ''; + + if ($year_start == $year_end) + $year_start = ''; + + if(empty($picked_days) || 7 == count($picked_days)) + return $this->_('Du %s au %s', + trim(strftime('%A %d', $date_start) . ' ' . $month_start . ' ' . $year_start), + trim(strftime('%A %d', $date_end) . ' ' . $month_end . ' ' . $year_end)); + + $open_days = $this->getTextualDays($picked_days); + + return $this->_('Les ' . $open_days . ' du %s au %s', + trim(strftime('%e', $date_start) . ' ' . $month_start . ' ' . $year_start), + trim(strftime('%e', $date_end) . ' ' . $month_end . ' ' . $year_end)); + } + + + protected function getTextualDays($days) { + if(1 == count($days)) + return $this->_('%ss', $this->numericDayToTextual($days[0])); + + $last = array_pop($days); + $last = $this->_('%ss', $this->numericDayToTextual($last)); + + $textual_days = ''; + foreach($days as $day) + $textual_days.= $this->_('%ss, ', $this->numericDayToTextual($day)); + + return substr($textual_days, 0, -2) . $this->_(' et ') . $last; + } + + + protected function numericDayToTextual($nb) { + return strftime('%A', strtotime('Sunday +' . $nb . ' days')); + } +} diff --git a/library/ZendAfi/View/Helper/Article/RenderEventTimings.php b/library/ZendAfi/View/Helper/Article/RenderEventTimings.php new file mode 100644 index 0000000000000000000000000000000000000000..2162b48f89b24e728dbdf94a789c37c4a22949f0 --- /dev/null +++ b/library/ZendAfi/View/Helper/Article/RenderEventTimings.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_Article_RenderEventTimings extends ZendAfi_View_Helper_BaseHelper { + public function article_RenderEventTimings($article) { + $current_and_future_timings = array_filter($article->getEventTimings(), + function($timing) + { + return !$timing->isPast(); + }); + + $human_timings = array_map( + function($timing) + { + return $this->view + ->article_RenderEventTiming($timing->getStart(), + $timing->getEnd()); + }, + $current_and_future_timings); + + return $this->view->tag('div', + $this->view->tag('h2', + $this->_('Dates et Horaires')) + . + $this->view->tagUlLi($human_timings), + ['class' => 'article_timings']); + } +} diff --git a/library/ZendAfi/View/Helper/Article/RenderFullContent.php b/library/ZendAfi/View/Helper/Article/RenderFullContent.php index 8ba940fcc8e36c7a044bd922d75aed66409def8b..48d281c35523048b421feed4fe1ecb25cbbd91c1 100644 --- a/library/ZendAfi/View/Helper/Article/RenderFullContent.php +++ b/library/ZendAfi/View/Helper/Article/RenderFullContent.php @@ -55,4 +55,9 @@ class ZendAfi_View_Helper_Article_RenderFullContent extends ZendAfi_View_Helper_ ->setStrategy('Article_List')), ['class' => 'print']); } + + + public function renderTimings($article) { + return $this->view->article_RenderEventTimings($article); + } } diff --git a/library/ZendAfi/View/Helper/CalendarContent.php b/library/ZendAfi/View/Helper/CalendarContent.php index cf0ff91de1b7e44a94343c75a62565a887f4912b..6cc118d8b05fe531d2b43bab05d377a13488b0d7 100644 --- a/library/ZendAfi/View/Helper/CalendarContent.php +++ b/library/ZendAfi/View/Helper/CalendarContent.php @@ -35,17 +35,23 @@ class ZendAfi_View_Helper_CalendarContent extends ZendAfi_View_Helper_BaseHelper */ public function calendarContent($calendar, $settings = []) { $this->param = $settings; - $this->calendar = $calendar; $calendar->setTimeSource($this->getTimeSource()); $calendar->initializeParams(); - $html='<div class="calendar">'; - $html.= $this->rendSelectionCategories(); - $html.= $this->rendDateSelection($calendar); - $html.= $this->renderFilters($calendar); - $html.= $this->renderArticlesByList($calendar->getArticles()); - return $html.='</div>'; + return $this->view->div(['class' => 'calendar'], + $this->_renderHTML($calendar, $settings)); + } + + + protected function _renderHTML($calendar, $settings) { + $articles = $calendar->getArticles(); + + return + $this->rendSelectionCategories() + . $this->rendDateSelection($calendar) + . $this->renderFilters($calendar, $articles) + . $this->renderArticlesByList($articles); } @@ -85,17 +91,18 @@ class ZendAfi_View_Helper_CalendarContent extends ZendAfi_View_Helper_BaseHelper } - protected function renderFilters() { - if (!$this->calendar->hasEnabledFilters()) + protected function renderFilters($calendar, $articles) { + if (!$calendar->hasEnabledFilters()) return ''; $filter_settings = new Class_FilterSettings(new Class_Systeme_ModulesAccueil_Calendrier()); $filter_settings - ->setFilters($this->calendar->getEnabledFilters()) - ->setSelectedFilters($this->calendar->getActiveFilters()) + ->setFilters($calendar->getEnabledFilters()) + ->setSelectedFilters($calendar->getActiveFilters()) ->setModelLabel('Article') - ->setUrlParams($this->calendar->getBaseUrl()) - ->setCalendar($this->calendar); + ->setUrlParams($this->_getBaseUrl()) + ->setCalendar($calendar) + ->setRenderedModels($articles); return $this->view->filters_Render($filter_settings); } @@ -180,4 +187,9 @@ class ZendAfi_View_Helper_CalendarContent extends ZendAfi_View_Helper_BaseHelper ? $this->view->article_RenderTitleOnlyCalendarWithCatalogue($article) : $this->view->article_RenderTitleOnlyCalendar($article)); } + + + protected function _getBaseUrl() { + return $this->calendar->getBaseUrl(); + } } diff --git a/library/ZendAfi/View/Helper/Filters/Element/Tag.php b/library/ZendAfi/View/Helper/Filters/Element/Tag.php new file mode 100644 index 0000000000000000000000000000000000000000..64e0bb84e0be843681d8cc22f07a3de0e2183aa0 --- /dev/null +++ b/library/ZendAfi/View/Helper/Filters/Element/Tag.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +class ZendAfi_View_Helper_Filters_Element_Tag extends ZendAfi_View_Helper_Filters_Element { + public function __construct($custom_field_id = null) { + $this->_custom_field_id = 'tag'; + } + + + public function elements() { + $all_tags = implode(';', + $this + ->_settings + ->renderedModelsMap( + function($article) + { + return trim($article->getTags()); + })); + + $tags = array_unique(array_filter(explode(';', strtolower($all_tags)))); + sort($tags); + return array_combine($tags, $tags); + } +} diff --git a/library/ZendAfi/View/Helper/TagArticleEvent.php b/library/ZendAfi/View/Helper/TagArticleEvent.php index b063dfeaa6a0ac19c1c3da3f55a521adea93b5ec..923dda7d02f7ee13823d7645ec663b7abaed60e2 100644 --- a/library/ZendAfi/View/Helper/TagArticleEvent.php +++ b/library/ZendAfi/View/Helper/TagArticleEvent.php @@ -26,86 +26,12 @@ class ZendAfi_View_Helper_TagArticleEvent extends Zend_View_Helper_HtmlElement { public function tagArticleEvent($article) { return $article->hasEventsDebut() ? $this->view->tag('span', - $this->formatEventString($article), + $this->view->article_RenderEventTiming($article->getEventsDebut(), + $article->getEventsFin(), + $article->getAllDay(), + $article->getPickDayAsArray()), ['class' => 'calendar_event_date']) : ''; } - - - protected function getStartDate($article) { - return strtotime(substr($article->getEventsDebut(), 0, 10)); - } - - - protected function getEndDate($article) { - if (!$date_end=strtotime(substr($article->getEventsFin(), 0, 10))) - return $this->getStartDate($article); - return $date_end; - } - - public function formatEventString($article) { - $date_start = $this->getStartDate($article); - $date_end = $this->getEndDate($article); - - if ($date_start == $date_end) { - $event_string = strftime('%A %d %B %Y', $date_start); - - if ($article->getAllDay()) - return $event_string; - - $time_start = date('H:i', strtotime($article->getEventsDebut())); - $time_end = date('H:i', strtotime($article->getEventsFin())); - - - return $event_string . (($time_start == $time_end) ? $this->view->_(' à %s', $time_start) - : $this->view->_(' de %s à %s', $time_start, $time_end)); - } - - $year_start = strftime('%Y', $date_start); - $year_end = strftime('%Y', $date_end); - - $month_start = strftime('%B', $date_start); - $month_end = strftime('%B', $date_end); - - - if ($month_start == $month_end && $year_start == $year_end) - $month_start = ''; - - if ($year_start == $year_end) - $year_start = ''; - - $picked_days = $article->getPickDayAsArray(); - - if(empty($picked_days) || 7 == count($picked_days)) - return $this->view->_('Du %s au %s', - trim(strftime('%A %d', $date_start) . ' ' . $month_start . ' ' . $year_start), - trim(strftime('%A %d', $date_end) . ' ' . $month_end . ' ' . $year_end)); - - $open_days = $this->getTextualDays($picked_days); - - return $this->view->_('Les ' . $open_days . 's du %s au %s', - trim(strftime('%e', $date_start) . ' ' . $month_start . ' ' . $year_start), - trim(strftime('%e', $date_end) . ' ' . $month_end . ' ' . $year_end)); - } - - - protected function getTextualDays($days) { - if(1 == count($days)) - return $this->numericDayToTextual($days[0]); - - $last = array_pop($days); - $last = $this->numericDayToTextual($last); - - $textual_days = ''; - foreach($days as $day) - $textual_days.= $this->numericDayToTextual($day) . $this->view->_('s, '); - - return substr($textual_days, 0, -2) . $this->view->_(' et ') . $last; - } - - - protected function numericDayToTextual($nb) { - return strftime('%A', strtotime('Sunday +' . $nb . ' days')); - } } ?> diff --git a/library/templates/Intonation/Library/Widget/Carousel/Article/Definition.php b/library/templates/Intonation/Library/Widget/Carousel/Article/Definition.php index fd90fe2f154a3331f4d087fa0e94d009f7977ef0..9c5900b7b4457e07eac5ba510d15a3c4c7bc71c5 100644 --- a/library/templates/Intonation/Library/Widget/Carousel/Article/Definition.php +++ b/library/templates/Intonation/Library/Widget/Carousel/Article/Definition.php @@ -28,12 +28,6 @@ class Intonation_Library_Widget_Carousel_Article_Definition extends Intonation_L SORT_SELECTION = 'Selection', SORT_RANDOM = 'Random', - SORT_COMMENTS_ASC = 'CommentCountAsc', - SORT_COMMENTS = 'CommentCount', - - SORT_TITLE_ASC = 'TitleAsc', - SORT_TITLE_DESC = 'TitleDesc', - SORT_CREATION_ASC = 'DateCreationAsc', SORT_CREATION_DESC = 'DateCreationDesc', diff --git a/library/templates/Intonation/Library/Widget/Carousel/Article/Form.php b/library/templates/Intonation/Library/Widget/Carousel/Article/Form.php index 141779259e0db73a4070e24980cf794ebffe722b..148898492f3f0c7d1b784c934565e9c4103cbf48 100644 --- a/library/templates/Intonation/Library/Widget/Carousel/Article/Form.php +++ b/library/templates/Intonation/Library/Widget/Carousel/Article/Form.php @@ -58,12 +58,12 @@ class Intonation_Library_Widget_Carousel_Article_Form extends Intonation_Library Intonation_Library_Widget_Carousel_Article_Definition::SORT_PUBLICATION_ASC => $this->_('Date de publication croissant'), Intonation_Library_Widget_Carousel_Article_Definition::SORT_PUBLICATION_DESC => $this->_('Date de publication décroissant'), - Intonation_Library_Widget_Carousel_Article_Definition::SORT_COMMENTS_ASC => $this->_('Nombre d\'avis croissant'), - Intonation_Library_Widget_Carousel_Article_Definition::SORT_COMMENTS => $this->_('Nombre d\'avis décroissant'), + Class_Article_Loader::ORDER_COMMENTS_ASC => $this->_('Nombre d\'avis croissant'), + Class_Article_Loader::ORDER_COMMENTS => $this->_('Nombre d\'avis décroissant'), Intonation_Library_Widget_Carousel_Article_Definition::SORT_SELECTION => $this->_('Sélection'), - Intonation_Library_Widget_Carousel_Article_Definition::SORT_TITLE_ASC => $this->_('Titre A-z'), - Intonation_Library_Widget_Carousel_Article_Definition::SORT_TITLE_DESC => $this->_('Titre Z-a')]; + Class_Article_Loader::ORDER_TITLE_ASC => $this->_('Titre A-z'), + Class_Article_Loader::ORDER_TITLE_DESC => $this->_('Titre Z-a')]; } } \ No newline at end of file diff --git a/library/templates/Intonation/View/CalendarContent.php b/library/templates/Intonation/View/CalendarContent.php index 46b22bbf913efe38a22df9fcee137f47b21aa3f4..1d52a63a48a9b86c9619149ae571149939e56d30 100644 --- a/library/templates/Intonation/View/CalendarContent.php +++ b/library/templates/Intonation/View/CalendarContent.php @@ -21,47 +21,22 @@ class Intonation_View_CalendarContent extends ZendAfi_View_Helper_CalendarContent { - - public function calendarContent($calendar, $settings = []) { + protected function _renderHTML($calendar, $settings) { Class_ScriptLoader::getInstance()->addOPACScript('calendrier'); - $this->param = $settings; - $this->calendar = $calendar; - - $this->calendar->setTimeSource($this->getTimeSource()); - $this->calendar->initializeParams(); + if (isset($settings['layout']) + && + ($settings['layout'] == Intonation_Library_Widget_Carousel_Agenda_Definition::CALENDAR)) + return $this->view->calendar_Table($calendar); - $html = ((isset($settings['layout'])) && ( $settings['layout'] == Intonation_Library_Widget_Carousel_Agenda_Definition::CALENDAR) - ? [$this->view->calendar_Table($this->calendar)] - : [$this->renderFilters($calendar), - (new Intonation_Library_Widget_Carousel_Agenda_View($this->calendar->getIdModule(), - $settings)) + $widget = (new Intonation_Library_Widget_Carousel_Agenda_View($calendar->getIdModule(), + $settings)) ->setView($this->view) - ->setCalendar($this->calendar) - ->renderElements()]); - - return $this->view->div(['class' => 'calendar'], implode($html)); - } - - - protected function renderFilters() { - if (!$this->calendar->hasEnabledFilters()) - return ''; - - $filter_settings = new Class_FilterSettings(new Class_Systeme_ModulesAccueil_Calendrier()); - - $filter_settings - ->setFilters($this->calendar->getEnabledFilters()) - ->setSelectedFilters($this->calendar->getActiveFilters()) - ->setModelLabel('Article') - ->setUrlParams($this->_getBaseUrl()) - ->setCalendar($this->calendar); - - return $this->view->filters_Render($filter_settings); - } - + ->setCalendar($calendar); - protected function _getBaseUrl() { - return $this->calendar->getBaseUrl(); + return + $this->renderFilters($calendar, $calendar->getArticles()) + . + $widget->renderElements(); } } diff --git a/library/templates/Intonation/View/RenderArticle.php b/library/templates/Intonation/View/RenderArticle.php index 32aa3cdd1daf201e511f007d2d033d2ef3df9f7e..32bb9f52566c80518ea588da1ddbc6db3660ffff 100644 --- a/library/templates/Intonation/View/RenderArticle.php +++ b/library/templates/Intonation/View/RenderArticle.php @@ -49,6 +49,10 @@ class Intonation_View_RenderArticle extends ZendAfi_View_Helper_BaseHelper { ['class' => 'card-text']), $location_content]; + + if (Class_AdminVar::isModuleEnabled('ENABLE_ARTICLES_TIMINGS')) + $body_content []= $this->view->article_RenderEventTimings($article); + $body = $this->_tag('div', implode($body_content), ['class' => 'card-body']); diff --git a/tests/application/modules/admin/controllers/CmsControllerTest.php b/tests/application/modules/admin/controllers/CmsControllerTest.php index 65cb5d945015926cc3e32515470d72fad85e697c..8859c19d9ca356a760a426ace80a63d3f318739f 100644 --- a/tests/application/modules/admin/controllers/CmsControllerTest.php +++ b/tests/application/modules/admin/controllers/CmsControllerTest.php @@ -23,6 +23,7 @@ require_once 'AdminAbstractControllerTestCase.php'; abstract class CmsControllerTestCase extends Admin_AbstractControllerTestCase { protected + $_storm_default_to_volatile = true, $concert, /** @var Class_Article */ $lieu_bonlieu, $lieu_arcadium, @@ -34,8 +35,6 @@ abstract class CmsControllerTestCase extends Admin_AbstractControllerTestCase { $_cat_a_la_une, $_cat_atelier; - protected $_storm_default_to_volatile = true; - public function setUp() { parent::setUp(); @@ -3086,4 +3085,4 @@ class CmsControllerEditArticleWithQueryTest extends CmsControllertestCase { public function formActionShouldContainsId4() { $this->assertXPath('//form[@action="/admin/cms/edit/id/4"]'); } -} \ No newline at end of file +} diff --git a/tests/application/modules/admin/controllers/WidgetControllerTest.php b/tests/application/modules/admin/controllers/WidgetControllerTest.php index 9d3a32966eb45138f0f190694be105ef8d1d3fad..e3703dbb5d11aa1a1e031c704afd2fc0eb236e7e 100644 --- a/tests/application/modules/admin/controllers/WidgetControllerTest.php +++ b/tests/application/modules/admin/controllers/WidgetControllerTest.php @@ -381,8 +381,8 @@ class WidgetControllerCalendarTest extends WidgetControllerDispatchWidgetConfigu /** @test */ - public function secondListShouldContainsThreeElements() { - $this->assertXPathCount('//div[@id="input_enabled_filters"]/div[2]/ul/li', 3); + public function secondListShouldContainsFourElements() { + $this->assertXPathCount('//div[@id="input_enabled_filters"]/div[2]/ul/li', 4); } @@ -398,6 +398,12 @@ class WidgetControllerCalendarTest extends WidgetControllerDispatchWidgetConfigu } + /** @test */ + public function secondListShouldContainsEtiquetteForTag() { + $this->assertXPathContentContains('//div[@id="input_enabled_filters"]/div[2]/ul/li[@data-value="tag"]', 'Étiquette'); + } + + /** @test */ public function firstListShouldContainsThreeElements() { $this->assertXPathCount('//div[@id="input_enabled_filters"]/div[1]/ul/li', 3); @@ -2820,4 +2826,4 @@ class WidgetControllerAddActionWithExisting2Columns 'styles_reload' => '0'], $preferences); } -} \ No newline at end of file +} diff --git a/tests/application/modules/opac/controllers/CmsControllerCalendarActionTest.php b/tests/application/modules/opac/controllers/CmsControllerCalendarActionTest.php index 0a2fd59e717f6572b707077962fff06ff7dd86a0..397d59cab3dd8129a017ddb1aa1a597cf6a258c0 100644 --- a/tests/application/modules/opac/controllers/CmsControllerCalendarActionTest.php +++ b/tests/application/modules/opac/controllers/CmsControllerCalendarActionTest.php @@ -38,12 +38,12 @@ abstract class CmsControllerCalendarActionTestCase extends AbstractControllerTes 'rss_avis' => false, 'id_categorie' => '12-2', 'display_cat_select' => true, - 'enabled_filters' => 'day;date;place;custom_field_2;custom_field_20;zork', + 'enabled_filters' => 'day;date;place;custom_field_2;custom_field_20;zork;tag', 'display_event_info' => 'none']]], 'options' => []]; - $this->fixture('Class_Profil', + $this->fixture(Class_Profil::class, ['id' => 3, 'browser' => 'opac', 'libelle' => 'Rendez-vous', @@ -52,21 +52,22 @@ abstract class CmsControllerCalendarActionTestCase extends AbstractControllerTes Class_AdminVar::newInstanceWithId('CACHE_ACTIF', ['valeur' => '1']); - $this->_nanook2 = $this->fixture('Class_Article', + $this->_nanook2 = $this->fixture(Class_Article::class, ['id' => 4, 'titre' => 'Nanook 2 en prod !', 'contenu' => 'youpi !', 'events_debut' => '2011-02-17', - 'events_fin' => '2011-02-22']); + 'events_fin' => '2011-02-22', + 'tags' => 'logiciel']); - $this->onLoaderOfModel('Class_Article') + $this->onLoaderOfModel(Class_Article::class) ->whenCalled('getArticlesByPreferences') ->answers([$this->_nanook2]); } public function setupCustomFields() { - $theme = $this->fixture('Class_CustomField', + $theme = $this->fixture(Class_CustomField::class, ['id' => 2, 'priority' => 3, 'label' => 'Theme', @@ -74,7 +75,7 @@ abstract class CmsControllerCalendarActionTestCase extends AbstractControllerTes 'options_list' => 'sigb;opac', 'model' => 'Article']); - $multi_options = $this->fixture('Class_CustomField', + $multi_options = $this->fixture(Class_CustomField::class, ['id' => 20, 'priority' => 3, 'label' => 'Options "classieuses"', @@ -82,13 +83,13 @@ abstract class CmsControllerCalendarActionTestCase extends AbstractControllerTes 'options_list' => 'wifi;restauration;projection', 'model' => 'Article']); - $this->fixture('Class_CustomField_Value', + $this->fixture(Class_CustomField_Value::class, ['id' => 59, 'custom_field_id' => $theme->getId(), 'model_id' => $this->_nanook2->getId(), 'value' => 'sigb']); - $this->fixture('Class_CustomField_Value', + $this->fixture(Class_CustomField_Value::class, ['id' => 5, 'custom_field_id' => $multi_options->getId(), 'model_id' => $this->_nanook2->getId(), @@ -139,23 +140,24 @@ class CmsControllerCalendarActionLanguageEnTest extends CmsControllerCalendarAct /** @test */ function withLocaleEnMonthShouldBeFebruary() { - $this->assertXPathContentContains('//td[@class="calendar_title_month"]/a', "february", $this->_response->getBody()); + $this->assertXPathContentContains('//td[@class="calendar_title_month"]/a', "february"); } /** @test **/ function calendarShouldContains6AwithClassCalendarTitleMonthClickable() { - $this->assertXPathCount('//a[@class="calendar_title_month_clickable"]', 6); + $this->assertXPathCount('//a[@class="calendar_title_month_clickable"]', 6, $this->_response->getBody()); } } + class CmsControllerCalendarActionWithMultipleFiltersTest extends CmsControllerCalendarActionTestCase { protected $_permaculture; public function setupCustomFields() { Class_CustomField_Meta::beVolatile(); - $theme = $this->fixture('Class_CustomField', + $theme = $this->fixture(Class_CustomField::class, ['id' => 2, 'priority' => 3, 'label' => 'Theme', @@ -163,7 +165,7 @@ class CmsControllerCalendarActionWithMultipleFiltersTest extends CmsControllerCa 'options_list' => 'culture;bd;logiciels libres', 'model' => 'Article']); - $multi_options = $this->fixture('Class_CustomField', + $multi_options = $this->fixture(Class_CustomField::class, ['id' => 20, 'priority' => 3, 'label' => 'Options', @@ -171,7 +173,7 @@ class CmsControllerCalendarActionWithMultipleFiltersTest extends CmsControllerCa 'options_list' => 'wifi;jardinage;projection', 'model' => 'Article']); - $this->fixture('Class_CustomField_Value', + $this->fixture(Class_CustomField_Value::class, ['id' => 59, 'custom_field_id' => $theme->getId(), 'model_id' => $this->_nanook2->getId(), @@ -179,13 +181,13 @@ class CmsControllerCalendarActionWithMultipleFiltersTest extends CmsControllerCa - $this->fixture('Class_CustomField_Value', + $this->fixture(Class_CustomField_Value::class, ['id' => 5, 'custom_field_id' => $multi_options->getId(), 'model_id' => $this->_permaculture->getId(), 'value' => ';jardinage;']); - $this->fixture('Class_CustomField_Value', + $this->fixture(Class_CustomField_Value::class, ['id' => 2, 'custom_field_id' => $theme->getId(), 'model_id' => $this->_permaculture->getId(), @@ -195,12 +197,13 @@ class CmsControllerCalendarActionWithMultipleFiltersTest extends CmsControllerCa public function setUp() { parent::setUp(); - $this->_permaculture = $this->fixture('Class_Article', + $this->_permaculture = $this->fixture(Class_Article::class, ['id' => 5, 'titre' => 'Liberons la permaculture !', 'contenu' => 'Venez avec vos poireaux !', 'events_debut' => '2011-02-17', - 'events_fin' => '2011-02-22']); + 'events_fin' => '2011-02-22', + 'tags' => '']); $this->setupCustomFields(); $common_preferences = ['display_order' => 'EventDebut', @@ -261,7 +264,6 @@ class CmsControllerCalendarActionWithMultipleFiltersTest extends CmsControllerCa } - /** @test */ public function articleNanookShouldBeDisplayWithLLSelected() { $this->dispatch('/cms/calendar/id_profil/3/id_module/1/date/2014-07/custom_field_2/logiciels libres/render/ajax', true); @@ -281,7 +283,7 @@ abstract class CmsControllerCalendarActionWithFiltersTestCase public function setUp() { parent::setUp(); - $this->_opac4 = $this->fixture('Class_Article', + $this->_opac4 = $this->fixture(Class_Article::class, ['id' => 5, 'titre' => 'OPAC 4 en prod !', 'contenu' => 'youpi !', @@ -331,7 +333,7 @@ class CmsControllerCalendarActionWithFiltersDateTest public function setupCustomFields() { parent::setupCustomFields(); - $this->fixture('Class_CustomField_Value', + $this->fixture(Class_CustomField_Value::class, ['id' => 2, 'custom_field_id' => 2, 'model_id' => 5, @@ -421,8 +423,7 @@ class CmsControllerCalendarActionWithFiltersDateTest /** @test */ public function linkNextMonthShouldContainsDayParameterAndDateParameter() { - $this->assertXPath('//a[@class="calendar_title_month_clickable"][contains(@href,"/day/2014-08")][contains(@href, "/date/2014-08")]', - $this->_response->getBody()); + $this->assertXPath('//a[@class="calendar_title_month_clickable"][contains(@href,"/day/2014-08")][contains(@href, "/date/2014-08")]'); } @@ -440,8 +441,7 @@ class CmsControllerCalendarActionWithFiltersDateTest /** @test */ public function linkInsideCalendarShouldContainsDayButNoDateParameters() { - $this->assertXPath('//a[contains(@class,"day_clickable")][contains(@href,"/day/2014-07-17")][not(contains(@href, "/date/"))]', - $this->_response->getBody()); + $this->assertXPath('//a[contains(@class,"day_clickable")][contains(@href,"/day/2014-07-17")][not(contains(@href, "/date/"))]'); } } @@ -486,7 +486,7 @@ class CmsControllerCalendarActionWithFiltersDateAndNoCalendarTest -class CmsControllerCalendarActionWithFiltersDayTest extends CmsControllerCalendarActionWithFiltersTestCase { +class CmsControllerCalendarActionWithFiltersDayTest extends CmsControllerCalendarActionWithFiltersTestCase { public function setUp() { parent::setUp(); @@ -533,21 +533,21 @@ class CmsControllerCalendarActionWithDayTest extends AbstractControllerTestCase 'options' => []]; - $this->fixture('Class_Profil', + $this->fixture(Class_Profil::class, ['id' => 3, 'browser' => 'opac', 'libelle' => 'Plop', 'cfg_accueil' => $cfg_accueil]); - $this->fixture('Class_Article', + $this->fixture(Class_Article::class, ['id' => 5, 'titre' => 'News of the 15th September', 'contenu' => 'youpi !', 'events_debut' => '2014-09-15', 'events_fin' => '2014-09-15']); - $this->fixture('Class_Article', + $this->fixture(Class_Article::class, ['id' => 2, 'titre' => 'News of the 30th September', 'contenu' => 'youpi !', @@ -558,7 +558,7 @@ class CmsControllerCalendarActionWithDayTest extends AbstractControllerTestCase ZendAfi_View_Helper_CalendarContent::setTimeSource($time_source); ZendAfi_View_Helper_Calendar_Table::setTimeSource($time_source); - $this->onLoaderOfModel('Class_Article') + $this->onLoaderOfModel(Class_Article::class) ->whenCalled('getArticlesByPreferences') ->with(['display_order' => 'EventDebut', 'id_categorie' => '', @@ -664,11 +664,11 @@ class CmsControllerCalendarActionHeaderTest extends AbstractControllerTestCase { ]; - $this->onLoaderOfModel('Class_Article') + $this->onLoaderOfModel(Class_Article::class) ->whenCalled('getArticlesByPreferences') ->answers([]); - $this->fixture('Class_Profil', + $this->fixture(Class_Profil::class, ['id' => 4, 'browser' => 'opac', 'libelle' => 'Rendez-vous', @@ -715,13 +715,13 @@ class CmsControllerCalendarActionAjaxLinkTest extends AbstractControllerTestCase ]; - $this->fixture('Class_Profil', + $this->fixture(Class_Profil::class, ['id' => 5, 'browser' => 'opac', 'libelle' => 'Rendez-vous', 'cfg_accueil' => $cfg_accueil]); - $articles = [$this->fixture('Class_Article', + $articles = [$this->fixture(Class_Article::class, ['id' => 25, 'titre' => 'News of the 30th September', 'contenu' => 'youpi !', @@ -732,7 +732,7 @@ class CmsControllerCalendarActionAjaxLinkTest extends AbstractControllerTestCase ZendAfi_View_Helper_CalendarContent::setTimeSource($time_source); ZendAfi_View_Helper_Calendar_Table::setTimeSource($time_source); - $this->onLoaderOfModel('Class_Article') + $this->onLoaderOfModel(Class_Article::class) ->whenCalled('getArticlesByPreferences') ->answers($articles); @@ -866,7 +866,7 @@ class CmsControllerCalendarActionIcalExportSimpleTest extends CmsControllerCalendarActionIcalExportTestCase { protected function _prepareFixtures () { - $capsule = $this->fixture('Class_Article', + $capsule = $this->fixture(Class_Article::class, ['id' => 42, 'titre' => 'CAPSULE TEMPORELLE 2017-2054', 'contenu' => 'Curieux envers l\'avenir ?', @@ -1156,7 +1156,7 @@ class CmsControllerCalendarActionIcalExportSimpleRecurrentTest extends CmsControllerCalendarActionIcalExportTestCase { protected function _prepareFixtures () { - $capsule = $this->fixture('Class_Article', + $capsule = $this->fixture(Class_Article::class, ['id' => 42, 'titre' => 'CAPSULE TEMPORELLE 2017-2054', 'contenu' => 'Curieux envers l\'avenir ?', @@ -1199,7 +1199,7 @@ class CmsControllerCalendarActionIcalExportRecurrentAllDayTest extends CmsControllerCalendarActionIcalExportTestCase { protected function _prepareFixtures () { - $opac4 = $this->fixture('Class_Article', + $opac4 = $this->fixture(Class_Article::class, ['id' => 5, 'titre' => 'OPAC 4 en prod !', 'contenu' => '<h3>youpi & oui c'est beau !</h3><img src="/userfiles/images/youpi.png">', @@ -1217,7 +1217,7 @@ class CmsControllerCalendarActionIcalExportRecurrentAllDayTest public function setupCustomFields() { parent::setupCustomFields(); - $this->fixture('Class_CustomField_Value', + $this->fixture(Class_CustomField_Value::class, ['id' => 2, 'custom_field_id' => 2, 'model_id' => 5, @@ -1317,7 +1317,7 @@ class CmsControllerCalendarActionWithOutDateTest extends AbstractControllerTestC ZendAfi_View_Helper_CalendarContent::setTimeSource($time_source); ZendAfi_View_Helper_Calendar_Table::setTimeSource($time_source); - $this->onLoaderOfModel('Class_Article') + $this->onLoaderOfModel(Class_Article::class) ->whenCalled('getArticlesByPreferences') ->with(['display_order' => 'EventDebut', 'id_categorie' => '', @@ -1342,7 +1342,7 @@ class CmsControllerCalendarActionWithOutDateTest extends AbstractControllerTestC 'custom_fields' => [], 'published' => true, 'event_end_after' => '2014-09-02']) - ->answers([$this->fixture('Class_Article', + ->answers([$this->fixture(Class_Article::class, ['id' => 1, 'titre' => 'Kitchen', 'contenu' => 'Cook'])]) @@ -1391,7 +1391,7 @@ class CmsControllerCalendarActionIcalExportOneArticleByIdTest extends CmsControllerCalendarActionIcalExportTestCase { protected function _prepareFixtures () { - $this->fixture('Class_Article', + $this->fixture(Class_Article::class, ['id' => 5, 'titre' => 'OPAC 4 en prod !', 'contenu' => '<h3>youpi & oui c'est beau !</h3><img src="/userfiles/images/youpi.png">', @@ -1458,7 +1458,7 @@ class CmsControllerCalenderWithAllCatSelectedAndIdCategorieSetTest extends CmsCo public function setUp() { parent::setUp(); - $this->onLoaderOfModel('Class_Article') + $this->onLoaderOfModel(Class_Article::class) ->whenCalled('getArticlesByPreferences') ->with(['display_order' => 'EventDebut', 'id_categorie' => '12-2', @@ -1511,13 +1511,13 @@ class CmsControllerCalenderWithAllCatSelectedAndIdCategorieEmptyTest extends Cms 'display_event_info' => 'none']]], 'options' => []]; - $this->fixture('Class_Profil', + $this->fixture(Class_Profil::class, ['id' => 3, 'browser' => 'opac', 'libelle' => 'Rendez-vous', 'cfg_accueil' => $this->cfg_accueil]); - $this->onLoaderOfModel('Class_Article') + $this->onLoaderOfModel(Class_Article::class) ->whenCalled('getArticlesByPreferences') ->with(['display_order' => 'EventDebut', 'id_categorie' => '', @@ -1545,4 +1545,52 @@ class CmsControllerCalenderWithAllCatSelectedAndIdCategorieEmptyTest extends Cms function shouldDisplayArticleNanook2() { $this->assertXPathContentContains('//div', 'Nanook 2 en prod !'); } -} \ No newline at end of file +} + + + + +class CmsControllerCalendarActionWithFiltersTagTest extends CmsControllerCalendarActionTestCase { + public function setUp() { + parent::setUp(); + + Class_Article::getLoader() + ->whenCalled('getArticlesByPreferences') + ->with(['tag' => 'logiciel', + 'display_order' => 'EventDebut', + 'id_categorie' => '12-2', + 'events_only' => true, + 'event_date' => '2011-02', + 'id_bib' => 0, + 'id_lieu' => '', + 'custom_fields' => [], + 'published' => false]) + ->answers([$this->_nanook2]) + + ->whenCalled('getArticlesByPreferences') + ->with(['tag' => 'logiciel', + 'display_order' => 'EventDebut', + 'id_categorie' => '12-2', + 'events_only' => true, + 'event_date' => '', + 'id_bib' => 0, + 'id_lieu' => '', + 'custom_fields' => [], + 'published' => false, + 'event_start_after' => '2011-02', + 'event_end_after' => '', + 'limit' => 3]) + ->answers([$this->_nanook2]) + + ->beStrict(); + + $this->dispatch('/cms/calendar/id_profil/3/id_module/1/tag/logiciel/date/2011-02/render/ajax', true); + } + + + /** @test */ + public function responseShouldContainsArticleNanook() { + $this->assertXPathContentContains('//a[@class="calendar_event_title"]', 'Nanook 2 en prod !'); + } + +} diff --git a/tests/application/modules/opac/controllers/CmsControllerTest.php b/tests/application/modules/opac/controllers/CmsControllerTest.php index 30edc8b27dcac5c62badfe64fbc7aa4665b14920..e85ebc30ea4b0b0c3b4f15091b1a9f600440e5e2 100644 --- a/tests/application/modules/opac/controllers/CmsControllerTest.php +++ b/tests/application/modules/opac/controllers/CmsControllerTest.php @@ -329,8 +329,6 @@ abstract class AbstractCmsControllerArticleViewByDateTest extends AbstractContro 'libelle' => 'Alimentaire'])]) ]; - $articles = Class_Article::getLoader()->sortArticles($articles, $this->display_order); - $prefs_module_calendar = ['titre' => "Calendrier des animations", 'event_date' => '2011-09-03', 'id_bib' => null, @@ -2076,4 +2074,4 @@ class CmsControllerWithArticleWithCubeKioskTest extends CmsControllerWithArticle $this->assertXPathContentContains('//div[contains(@class, "boite kiosque")]/div/a', 'Boite kiosque'); } -} \ No newline at end of file +} diff --git a/tests/application/modules/opac/controllers/ProfilOptionsControllerTest.php b/tests/application/modules/opac/controllers/ProfilOptionsControllerTest.php index c77e2a42e0a17633870e444eba7bf7db575c4216..ba4e11711669689ba1f66799fb17973ad3cbc215 100644 --- a/tests/application/modules/opac/controllers/ProfilOptionsControllerTest.php +++ b/tests/application/modules/opac/controllers/ProfilOptionsControllerTest.php @@ -2501,8 +2501,8 @@ class ProfilOptionsControllerProfilBoiteCalendarWithFilterOnMonthsTest extends P -class ProfilOptionsControllerProfilBoiteCalendarWithNoFilterTest extends ProfilOptionsControllerProfilBoiteCalendarWithFilterTestCase { +class ProfilOptionsControllerProfilBoiteCalendarWithNoFilterTest extends ProfilOptionsControllerProfilBoiteCalendarWithFilterTestCase { public function setup() { parent::setup(); @@ -2521,6 +2521,7 @@ class ProfilOptionsControllerProfilBoiteCalendarWithNoFilterTest extends ProfilO $this->dispatch('/opac'); } + /** @test **/ public function boiteCalendarShouldBeDisplay() { $this->assertXPath('//div[contains(@class,"calendar")]'); @@ -2529,14 +2530,15 @@ class ProfilOptionsControllerProfilBoiteCalendarWithNoFilterTest extends ProfilO /** @test **/ public function articleFromOctoberShouldBePresent() { - $this->assertXPath('//article//header//a[contains(text(), "News from October")]'); + $this->assertXPath('//article//header//a[contains(text(), "News from October")]', + $this->_response->getBody()); } } -class ProfilOptionsControllerWidgetCalendarWithDisplayFullPageDeactivateShouldUseAjaxLink extends ProfilOptionsControllerProfilBoiteCalendarWithFilterTestCase { +class ProfilOptionsControllerWidgetCalendarWithDisplayFullPageDeactivateShouldUseAjaxLink extends ProfilOptionsControllerProfilBoiteCalendarWithFilterTestCase { public function setUp() { parent::setUp(); @@ -2919,4 +2921,4 @@ class ProfilOptionsControllerRecentSitoTest extends ProfilOptionsControllerProfi $this->dispatch('/opac/index/index/clef/zork?id_profil=12', true); $this->assertXPathCount('//div[@class="sitotheque"]', 2); } -} \ No newline at end of file +} diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php index 81ec25679178025ea7aa55c02d4093e9b367d664..47a8a956cd2eddcdf0896a6236eba5c1b2ff252a 100644 --- a/tests/db/UpgradeDBTest.php +++ b/tests/db/UpgradeDBTest.php @@ -3953,6 +3953,33 @@ class UpgradeDB_414_Test extends UpgradeDBTestCase { current($this ->query('select valeur from bib_admin_var where clef="STATUS_REPORT_PUSH_URL"') ->fetch())); + } +} + + + + +class UpgradeDB_415_Test extends UpgradeDBTestCase { + public function prepare() { + $this->dropTable('cms_article_timings'); + } + + public function datas() { + return + [ + ['id', 'int(11) unsigned'], + ['article_id', 'int(11) unsigned'], + ['start', 'datetime'], + ['end', 'datetime'] + ]; + } + + /** + * @test + * @dataProvider datas + */ + public function tableCmsArticleTimingShouldHaveField($field, $type) { + $this->assertFieldType('cms_article_timings', $field, $type); } } \ No newline at end of file diff --git a/tests/library/Class/ArticleLoaderTest.php b/tests/library/Class/ArticleLoaderTest.php index 7478ebc65ab1c12f66170594cad3f74f0aaed97e..4d9486df50acdd7b20a75d4ce7155f208c04d28e 100644 --- a/tests/library/Class/ArticleLoaderTest.php +++ b/tests/library/Class/ArticleLoaderTest.php @@ -21,10 +21,17 @@ abstract class ArticleLoaderGetArticlesByPreferencesTestCase extends ModelTestCase { + const WHERE_VISIBLE_CLAUSE = + '((DEBUT IS NULL) OR (DEBUT <= CURDATE())) AND ((FIN IS NULL) OR (FIN >= CURDATE()))'; + public function setUp() { - $this->select = new Zend_Db_Table_Select(new Storm_Model_Table(array('name' => 'cms_article'))); + Class_AdminVar::set('ENABLE_ARTICLES_TIMINGS', 0); + + $this->select = new Zend_Db_Table_Select( + new Storm_Model_Table(['name' => 'cms_article'])); - $this->tbl_articles = $this->_buildTableMock('Class_Article', array('select', 'fetchAll')); + $this->tbl_articles = $this->_buildTableMock(Class_Article::class, + ['select', 'fetchAll']); $this->tbl_articles ->expects($this->any()) ->method('select') @@ -38,10 +45,10 @@ abstract class ArticleLoaderGetArticlesByPreferencesTestCase extends ModelTestCa Class_ArticleCategorie::getLoader() ->newInstanceWithId(2) ->setLibelle('Fêtes') - ->setSousCategories(array(Class_ArticleCategorie::getLoader() - ->newInstanceWithId(4) - ->setLibelle('Exotiques') - ->setSousCategories(array()))); + ->setSousCategories([Class_ArticleCategorie::getLoader() + ->newInstanceWithId(4) + ->setLibelle('Exotiques') + ->setSousCategories([])]); } @@ -55,14 +62,16 @@ abstract class ArticleLoaderGetArticlesByPreferencesTestCase extends ModelTestCa 'DATE_CREATION' => '2011-04-02', 'DEBUT' => '2011-10-02', 'FIN' => '2011-10-22', - 'EVENTS_DEBUT' => '2011-10-20'], + 'EVENTS_DEBUT' => '2011-10-20', + 'TAGS' => 'Fête;Pomme'], ['ID_ARTICLE' => 18, 'ID_CAT' => 2, 'TITRE' => 'Fête de la poire', 'DATE_CREATION' => '2010-03-25', 'DEBUT' => '2010-03-25', - 'EVENTS_DEBUT' => '2010-03-25'], + 'EVENTS_DEBUT' => '2010-03-25', + 'TAGS' => 'Fête;Poire'], ['ID_ARTICLE' => 55, 'ID_CAT' => 4, @@ -70,7 +79,8 @@ abstract class ArticleLoaderGetArticlesByPreferencesTestCase extends ModelTestCa 'DEBUT' => '2011-01-01', 'DATE_CREATION' => '2011-05-01', 'EVENTS_DEBUT' => '2011-03-25', - 'EVENTS_FIN' => '2011-03-27'] + 'EVENTS_FIN' => '2011-03-27', + 'TAGS' => 'Fête;Banane'] ]; } @@ -78,21 +88,18 @@ abstract class ArticleLoaderGetArticlesByPreferencesTestCase extends ModelTestCa public function getArticles($prefs) { return Class_Article::getLoader()->getArticlesByPreferences($prefs); } -} - - - -class ArticleLoaderGetArticlesByPreferencesTest extends ArticleLoaderGetArticlesByPreferencesTestCase { - const WHERE_VISIBLE_CLAUSE = - '((DEBUT IS NULL) OR (DEBUT <= CURDATE())) AND ((FIN IS NULL) OR (FIN >= CURDATE()))'; public function assertSelect($expected) { $this->assertEquals("SELECT `cms_article`.* FROM `cms_article` ".$expected, str_replace("\n", "", $this->select->assemble())); } +} + + +class ArticleLoaderGetArticlesByPreferencesTest extends ArticleLoaderGetArticlesByPreferencesTestCase { /** @test */ function withNoSelectionAnd5ArticlesDisplayedOrderedByDate() { $articles = $this->getArticles(array('display_order' => 'DateCreationDesc', @@ -197,9 +204,6 @@ class ArticleLoaderGetArticlesByPreferencesTest extends ArticleLoaderGetArticles } - - - /** @test */ function withArticleSelectionRandomAndNbAffOneShouldReturnOneArticle() { $articles = $this->getArticles(array('display_order' => 'Random', @@ -385,6 +389,31 @@ class ArticleLoaderGetArticlesByPreferencesTest extends ArticleLoaderGetArticles $article = $this->getArticles(array('published' => false)); $this->assertSelect('WHERE (PARENT_ID=0) ORDER BY `DATE_CREATION` DESC'); } + + + + /** @test */ + function withArticleSelectionTagPoireShouldAnswersArticleFetePoire() { + $articles = $this->getArticles(['display_order' => 'EventDebut', + 'id_items' => '', + 'id_categorie' => '', + 'tag' => 'poire']); + $this->assertEquals(['Fête de la poire'], + array_map(function($article) { return $article->getTitre(); }, + $articles)); + } + + + /** @test */ + function withArticleSelectionTagPoMMeShouldAnswersArticleFetePomme() { + $articles = $this->getArticles(['display_order' => 'EventDebut', + 'id_items' => '', + 'id_categorie' => '', + 'tag' => 'poMMe']); + $this->assertEquals(['Fête de la pomme'], + array_map(function($article) { return $article->getTitre(); }, + $articles)); + } } @@ -397,7 +426,7 @@ abstract class ArticleLoaderGroupByBibTestCase extends ModelTestCase { protected function setUp() { parent::setUp(); - $this->articles = ArticleLoader::groupByBibId($this->_getArticlesFixture()); + $this->articles = Class_Article_Loader::groupByBibId($this->_getArticlesFixture()); } } diff --git a/tests/library/Class/ArticleTest.php b/tests/library/Class/ArticleTest.php index 47b9d8fb92508da95bed79bc7ab4ac9269642315..c21326f38635fca5dd0bf40e31ab6465035cb4a3 100644 --- a/tests/library/Class/ArticleTest.php +++ b/tests/library/Class/ArticleTest.php @@ -1024,9 +1024,7 @@ abstract class EventsByMonthWithArticleTestCase extends ModelTestCase { ->with(['event_date' => '2013-03', 'events_only' => true, 'status' => 3]) - ->answers([$this->concert]) - - ->beStrict(); + ->answers([$this->concert]); } diff --git a/tests/library/ZendAfi/View/Helper/Accueil/CalendarTest.php b/tests/library/ZendAfi/View/Helper/Accueil/CalendarTest.php index 7460cd7b0190efad31737e0c93e14536793bbf6a..f3b1e8cdeffce1f8a02f4603ab299346fd6b4e51 100644 --- a/tests/library/ZendAfi/View/Helper/Accueil/CalendarTest.php +++ b/tests/library/ZendAfi/View/Helper/Accueil/CalendarTest.php @@ -119,7 +119,7 @@ abstract class CalendarViewTodayTestCase extends CalendarWithEmptyPreferencesTes public function setUp() { parent::setUp(); - $article_loader = Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Article') + $article_loader = Storm_Test_ObjectWrapper::onLoaderOfModel(Class_Article::class) ->whenCalled('getArticlesByPreferences') ->with([ 'display_order' => 'EventDebut', @@ -131,7 +131,7 @@ abstract class CalendarViewTodayTestCase extends CalendarWithEmptyPreferencesTes 'id_lieu' => '', 'custom_fields' => [], 'published' => true]) - ->answers(array($this->nanook2, $this->opac4, $this->amber)) + ->answers([$this->nanook2, $this->opac4, $this->amber]) ->whenCalled('getArticlesByPreferences') ->with([ @@ -143,9 +143,7 @@ abstract class CalendarViewTodayTestCase extends CalendarWithEmptyPreferencesTes 'id_lieu' => '', 'custom_fields' => [], 'published' => true]) - ->answers(array($this->nanook2, $this->opac4, $this->amber)) - - ->beStrict(); + ->answers([$this->nanook2, $this->opac4, $this->amber]); } } @@ -269,7 +267,7 @@ class CalendarViewTodayWithFriseChronoTest extends CalendarViewTodayTestCase { 'nb_events' => 1, 'display_calendar' => 2]]; - Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Article') + Class_Article::getLoader() ->whenCalled('getArticlesByPreferences') ->answers([]); @@ -974,15 +972,16 @@ abstract class CalendarHelperDisplayModeTestCase extends CalendarViewHelperTestC $time_source = new TimeSourceForTest('2014-07-19 09:00:00'); ZendAfi_View_Helper_Calendar_Months::setTimeSource($time_source); - $this->_article_bokeh = $this->fixture('Class_Article', - [ 'id' => 8, + $this->_article_bokeh = $this->fixture(Class_Article::class, + ['id' => 8, 'titre' => 'Bokeh en prod !', 'events_debut' => '2013-08-21', 'events_fin' => '2013-09-01', 'categorie' => '', - 'contenu' => 'toto' + 'contenu' => 'toto', + 'tags' => 'Geek;bokeh' ]); - $this->onLoaderOfModel('Class_Article') + $this->onLoaderOfModel(Class_Article::class) ->whenCalled('getArticlesByPreferences') ->answers([$this->_article_bokeh]); @@ -1110,17 +1109,19 @@ class CalendarHelperWithWallNavigationModeTest extends CalendarHelperDisplayMode class CalendarHelperWithFiltersTest extends CalendarHelperDisplayModeTestCase { public function setupParams(&$params) { - $this->fixture('Class_Lieu', [ - 'id' => 1, - 'libelle' => 'Place 1', - ]); - - $this->fixture('Class_Lieu', [ - 'id' => 2, - 'libelle' => 'Place 2', - ]); - - $theme = $this->fixture('Class_CustomField', + $this->fixture(Class_Lieu::class, + [ + 'id' => 1, + 'libelle' => 'Place 1', + ]); + + $this->fixture(Class_Lieu::class, + [ + 'id' => 2, + 'libelle' => 'Place 2', + ]); + + $theme = $this->fixture(Class_CustomField::class, ['id' => 2, 'priority' => 3, 'label' => 'Theme', @@ -1128,19 +1129,19 @@ class CalendarHelperWithFiltersTest extends CalendarHelperDisplayModeTestCase { 'options_list' => 'music;theater;movie', 'model' => 'Article']); - $this->fixture('Class_CustomField_Value', + $this->fixture(Class_CustomField_Value::class, ['id' => 24, 'custom_field_id' => $theme->getId(), 'model' => $this->_article_bokeh, 'value' => 'music']); - $this->fixture('Class_CustomField_Value', + $this->fixture(Class_CustomField_Value::class, ['id' => 25, 'custom_field_id' => $theme->getId(), 'model' => $this->nanook2, 'value' => 'dream theater']); - $params['preferences']['enabled_filters'] = ';date;place;custom_field_2'; + $params['preferences']['enabled_filters'] = ';date;place;custom_field_2;tag'; } @@ -1151,8 +1152,8 @@ class CalendarHelperWithFiltersTest extends CalendarHelperDisplayModeTestCase { /** @test */ - public function filtersShouldContainsThreeUL() { - $this->assertXPathCount($this->html, '//ul[contains(@class, "filters")]//ul', 3, $this->html); + public function filtersShouldContainsFourUL() { + $this->assertXPathCount($this->html, '//ul[contains(@class, "filters")]//ul', 4, $this->html); } @@ -1211,6 +1212,17 @@ class CalendarHelperWithFiltersTest extends CalendarHelperDisplayModeTestCase { '//ul[contains(@class, "filters")]/li[@class="custom_field_2"]//li[3]/a[contains(@href, "custom_field_2/music")]', 'music'); } + + + /** @test */ + public function tagFilterShouldContainsGeekAndBokeh() { + $this->assertXPath($this->html, + '//ul[contains(@class, "filters")]/li[@class="tag"]' + . '//li[child::a[contains(@href, "tag/bokeh")][text()="bokeh"]]' + . '/following-sibling::li/a[contains(@href, "tag/geek")][text()="geek"]', + $this->html); + + } } @@ -1288,4 +1300,4 @@ class CalendarHelperWithPreviousMonthEventsTest extends ViewHelperTestCase { '//a[contains(@href, "2015-10-29")][contains(@class, "calendar_other_month")]', '29'); } -} \ No newline at end of file +} diff --git a/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php b/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php index 3ec772945226e2ead6192a1a2d5ae8fa9bb1cf63..4685fcd494242a15840f3876b0b2b5b6eddc57d0 100644 --- a/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php +++ b/tests/library/ZendAfi/View/Helper/TagArticleEventTest.php @@ -94,6 +94,15 @@ class TagArticleEventTest extends ViewHelperTestCase { } + /** @test */ + function withEventDebutAndNoFinShouldAnswerLundi05Septembre2011AtEight() { + $this->article + ->setEventsDebut('2011-09-05 08:00') + ->setEventsFin(''); + $this->assertTagContains('lundi 05 septembre 2011 à 08:00'); + } + + /** @test */ function withAllDayAndEventDebutAndFinSameDayShouldAnswerLe05() { $this->article @@ -135,7 +144,7 @@ class TagArticleEventTest extends ViewHelperTestCase { - /** @test */ + /** @test */ public function withMondayThuesdayThursdayAndSaturdayPickShouldAnswersTousLesLundisMardisMercredisEtTousLesSamedi() { $this->article ->setEventsDebut('2011-09-05 08:00') diff --git a/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsAdminTest.php b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsAdminTest.php new file mode 100644 index 0000000000000000000000000000000000000000..30844c9458bcd741a6c9473e3a2d8deddc22d821 --- /dev/null +++ b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsAdminTest.php @@ -0,0 +1,530 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ArticlesMultipleTimingsAdminVarTest extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/index/adminvar'); + } + + + /** @test */ + public function pageShouldContainsLinkToAdminVarENABLE_ARTICLES_TIMINGS() { + $this->assertXPathContentContains('//a/@href', + '/admin/index/adminvaredit/cle/ENABLE_ARTICLES_TIMINGS'); + } +} + + + + +class ArticlesMultipleTimingsAdminActivationCmsControllerTest extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + + + public function setUp() { + parent::setUp(); + $this->fixture(Class_Article::class, + ['id' => 2, + 'titre' => 'Heure du conte', + 'contenu' => 'Tous les mercredis']); + + } + + + /** @test */ + public function with_ENABLE_ARTICLES_TIMINGS_setDivHeaderActionsShouldContainsLinkToEventTimings() { + Class_AdminVar::set('ENABLE_ARTICLES_TIMINGS', 1); + $this->dispatch('/admin/cms/edit/id/2'); + $this->assertXPath('//div[@class="header_actions"]//a[contains(@href,"/admin/cms/event-timings/article_id/2")]/img[@title="Horaires de l\'événement : Heure du conte"]'); + } + + + + /** @test */ + public function without_ENABLE_ARTICLES_TIMINGS_setDivHeaderActionsShouldNotContainsLinkToEventTimings() { + Class_AdminVar::set('ENABLE_ARTICLES_TIMINGS', 0); + $this->dispatch('/admin/cms/edit/id/2'); + $this->assertNotXPathContentContains('//div[@class="header_actions"]//a/@href', + '/admin/cms/event-timings'); + } +} + + + + +abstract class ArticlesMultipleTimingsAdminTestCase extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + + + public function setUp() { + parent::setUp(); + $this->fixture(Class_Article::class, + ['id' => 2, + 'titre' => 'Heure du conte', + 'contenu' => 'Tous les mercredis']) + ->addEventTiming($this->fixture(Class_Article_EventTiming::class, + ['id' => 1, + 'start' => '2021-07-07 10:00:00', + 'end' => '2021-07-07 11:00:00'])) + ->addEventTiming($this->fixture(Class_Article_EventTiming::class, + ['id' => 2, + 'start' => '2021-07-14 10:00:00', + 'end' => '2021-07-14 11:00:00'])); + + + Class_AdminVar::set('ENABLE_ARTICLES_TIMINGS', 1); + } +} + + + + + +class ArticlesMultipleTimingsAdminCmsControllerEventTimingIndexArticleNotFoundTest extends ArticlesMultipleTimingsAdminTestCase { + + + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/cms/event-timings/article_id/666'); + } + + /** @test */ + public function responseShouldRedirectToIndex() { + $this->assertRedirectTo('/admin/cms'); + } +} + + + + +class ArticlesMultipleTimingsAdminCmsControllerEventTimingIndexTest extends ArticlesMultipleTimingsAdminTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/cms/event-timings/article_id/2'); + } + + + /** @test */ + public function pageTitleShouldBeHorairesPourLHeureDuConte() { + $this->assertXPathContentContains('//h1', 'Horaires de l\'événement : Heure du conte'); + } + + + /** @test */ + public function pageShouldContainsButtonToEditAction() { + $this->assertXPath('//button[contains(@data-url, "/admin/cms/edit/id/2")]', + 'Retour à l\'article : Heure du conte'); + } + + + /** @test */ + public function pageShouldContainsButtonToAddAction() { + $this->assertXPath('//button[@data-popup="true"][contains(@data-url, "/admin/cms/event-timing-add/article_id/2")]', + 'Ajouter un horaire pour : Heure du conte'); + } + + + /** @test */ + public function pageShouldContainsTableWithRowForFirstEventTiming() { + $this->assertXPath('//tr/td[text()="2021-07-07 10:00"]/following-sibling::td[text()="2021-07-07 11:00"]'); + } + + + /** @test */ + public function pageShouldContainsTableWithRowForSecondEventTiming() { + $this->assertXPath('//tr/td[text()="2021-07-14 10:00"]/following-sibling::td[text()="2021-07-14 11:00"]'); + } + + + /** @test */ + public function tableShouldContainsActionToEditFirstEventTiming() { + $this->assertXPathContentContains('//tr/td/a[@data-popup="true"]/@href', + '/admin/cms/event-timing-edit/id/1'); + } + + + /** @test */ + public function tableShouldContainsActionToEditSecondEventTiming() { + $this->assertXPathContentContains('//tr/td/a[@data-popup="true"]/@href', + '/admin/cms/event-timing-edit/id/2'); + } + + + /** @test */ + public function tableShouldContainsActionToDeleteFirstEventTiming() { + $this->assertXPathContentContains('//tr/td/a/@href', + '/admin/cms/event-timing-delete/id/1'); + } + + + /** @test */ + public function onDeleteShouldAskForConfirmation() { + $this->assertXPath('//tr/td/a[contains(@href,"delete")][contains(@onclick,"confirm")]'); + } +} + + + + +class ArticlesMultipleTimingsAdminDeleteTest extends ArticlesMultipleTimingsAdminTestCase { + + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/cms/event-timing-delete/id/1'); + } + + + /** @test */ + public function eventTimingOneShouldNotExist() { + $this->assertNull(Class_Article_EventTiming::find(1)); + } + + + /** @test */ + public function afterDeleteShouldRedirectToEventTimingForArticle2() { + $this->assertRedirect('/admin/cms/event-timings/article_id/2'); + } + + + /** @test */ + public function deleteShouldTriggerDeleteMessage() { + $this->assertFlashMessengerContentContains('L\'horaire a été supprimé'); + } +} + + + + +class ArticlesMultipleTimingsAdminCmsDeleteArticleTest extends ArticlesMultipleTimingsAdminTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/cms/force-delete/id/2'); + } + + + /** @test */ + public function eventTimingsShouldBeCascadeDeleted() { + $this->assertEmpty(Class_Article_EventTiming::findAll()); + } + + + /** @test */ + public function articleTwoShouldBeDeleted() { + $this->assertEmpty(Class_Article::find(2)); + } +} + + + + + +class ArticlesMultipleTimingsAdminEditTest extends ArticlesMultipleTimingsAdminTestCase { + + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/cms/event-timing-edit/id/1'); + } + + +/** @test */ + public function editFormShouldHaveTitleHeureDuConteEditerUnHoraire() { + $this->assertXPathContentContains('//head//title','Heure du conte : modifier un horaire'); + } + + + /** @test */ + public function editFormShouldContainStartDateInput() { + $this->assertXPath('//input[@id="start"][@value="07/07/2021 10:00"]'); + } + + + /** @test */ + public function editFormShouldContainEndDateInput() { + $this->assertXPath('//input[@id="end"][@value="07/07/2021 11:00"]'); + } + + + /** @test */ + public function editFormShouldContainsActionToEventTimingEdit() { + $this->assertXPath('//form[contains(@action,"event-timing-edit")]',$this->_response->getBody()); + } +} + + + + +class ArticlesMultipleTimingsAdminEditWithWrongIdTest extends ArticlesMultipleTimingsAdminTestCase { + public function setUp() { + parent::setup(); + $this->dispatch('/admin/cms/event-timing-edit/id/42'); + } + + + /** @test */ + public function withNonExistingEventTimingShouldRedirectToCmsIndex() { + $this->assertRedirectTo('/admin/cms'); + } +} + + + + +class ArticlesMultipleTimingsAdminEditWithParamsTest extends ArticlesMultipleTimingsAdminTestCase { + + protected $_event_time; + + public function setUp() { + parent::setup(); + $this->postDispatch('/admin/cms/event-timing-edit/id/1', + ['start' => '07/07/2021 11:00', + 'end' => '07/07/2021 12:00']); + + $this->_event_time = Class_Article_EventTiming::find(1); + } + + + /** @test */ + public function eventTimingOneShouldStart2021_07_07_11H00() { + $this->assertEquals('2021-07-07 11:00', $this->_event_time->getStart()); + } + + + /** @test */ + public function eventTimingOneShouldEnd2021_07_07_12H00() { + $this->assertEquals('2021-07-07 12:00', $this->_event_time->getEnd()); + } + + + /** @test */ + public function onSaveShouldNotifyMisAJour() { + $this->assertFlashMessengerContentContains('Les horaires ont été mis à jour'); + } + + + /** @test */ + public function articleShouldStillBeHeureDuConte() { + $this->assertEquals('Heure du conte', $this->_event_time->getLibelle()); + } + + + /** @test */ + public function onSaveShouldRedirectToAdminCmsEventTimingEditIdOne() { + $this->assertRedirectTo(Class_Url::absolute('/admin/cms/event-timing-edit/id/1/article_id/2')); + } +} + + + + +class ArticlesMultipleTimingsAdminAddDispatchTest + extends ArticlesMultipleTimingsAdminTestCase { + + public function setUp() { + parent::setup(); + $this->dispatch('/admin/cms/event-timing-add/article_id/2'); + } + + + /** @test */ + public function pageTitleShouldBeAjouterUnHoraire() { + $this->assertXPathContentContains('//head//title', 'Ajouter un horaire'); + } + + + /** @test */ + public function pageInputLabelShouldContainsDateEtHeureDebut() { + $this->assertXPathContentContains('//form//label[@for="start"]', 'Date et heure de début'); + } + + + /** @test */ + public function pageInputLabelShouldContainsDateEtHeureFin() { + $this->assertXPathContentContains('//form//label[@for="end"]', 'Date et heure de fin'); + } +} + + + + + +class ArticlesMultipleTimingsAdminAddPostDispatchTest extends ArticlesMultipleTimingsAdminTestCase { + protected $_event_time; + + public function setUp() { + parent::setup(); + $this->postDispatch('/admin/cms/event-timing-add/article_id/2', + ['start' => '21/07/2021 10:00', + 'end' => '21/07/2021 11:00']); + $this->_event_time = Class_Article_EventTiming::find(3); + } + + + + /** @test */ + public function eventTimingThreeShouldStart2021_07_21_10H00() { + $this->assertEquals('2021-07-21 10:00', $this->_event_time->getStart()); + } + + + /** @test */ + public function eventTimingThreeShouldEnd2021_07_21_11H00() { + $this->assertEquals('2021-07-21 11:00', $this->_event_time->getEnd()); + } + + + /** @test */ + public function eventTimingThreeShouldBelongToArticle2() { + $this->assertEquals(Class_Article::find(2), $this->_event_time->getArticle()); + } + + + /** @test */ + public function onSaveShouldNotifyNouvelHoraireCrée() { + $this->assertFlashMessengerContentContains('Un nouvel horaire a été créé'); + } + + + /** @test */ + public function onSaveShouldRedirectToAdminCmsEventTimingArticle2Id3() { + $this->assertRedirectTo(Class_Url::absolute('/admin/cms/event-timing-edit/article_id/2/id/3')); + } +} + + + + + +class ArticlesMultipleTimingsAdminAddPostDispatchWithEndBeforeStartTest extends ArticlesMultipleTimingsAdminTestCase { + + public function setUp() { + parent::setup(); + $this->postDispatch('/admin/cms/event-timing-add/article_id/2', + ['start' => '22/07/2021 10:00', + 'end' => '21/07/2021 11:00']); + } + + + /** @test */ + public function contextShouldExpectation() { + $this->assertXPathContentContains('//ul[@class="errors"]/li','La date de début doit être antérieure à la date de fin',$this->_response->getBody()); + } +} + + + + +class ArticlesMultipleTimingsAdminAddPostDispatchWithSingleDateTest extends ArticlesMultipleTimingsAdminTestCase { + + public function setUp() { + parent::setup(); + $this->postDispatch('/admin/cms/event-timing-add/article_id/2', + ['start' => '22/07/2021 10:00']); + } + + + /** @test */ + public function contextShouldExpectation() { + $this->assertXPathContentContains('//ul[@class="errors"]/li','Une valeur est requise'); + } +} + + + + +class ArticlesEventTimingAddPostDispatchWithWrongFormatDateTest extends ArticlesMultipleTimingsAdminTestCase { + + public function setUp() { + parent::setup(); + $this->postDispatch('/admin/cms/event-timing-add/article_id/2', + ['start' => 'test']); + } + + + /** @test */ + public function withInvalidDateShouldContainError() { + $this->assertXPathContentContains('//ul[@class="errors"]/li',"'test' n'est pas une date au format attendu"); + } +} + + + + +class ArticlesMultipleTimingsAdminCmsEditEventTimingAsRedacteurTest extends ArticlesMultipleTimingsAdminTestCase { + + public function setUp() { + parent::setUp(); + $this->fixture(Class_Bib::class, + ['id' => 1, + 'libelle' => 'Spirou' ]); + + $this->fixture(Class_Bib::class, + ['id' => 2, + 'libelle' => 'Fantasio']); + + $article_categorie = $this->fixture(Class_ArticleCategorie::class, + ['id' => 1, + 'libelle' => 'Root', + 'id_site' => 1]); + + Class_Article::find(2) + ->setCategorie($article_categorie) + ->assertSave(); + + $redacteur_group = $this->fixture(Class_UserGroup::class, + ['id' => 1, + 'libelle' => 'Allowed Group']); + + $redacteur = $this->fixture(Class_Users::class, + ['id' => 5, + 'login' => 'redac', + 'password' => 'chef', + 'role_level' => ZendAfi_Acl_AdminControllerRoles::MODO_BIB, + 'user_groups' => [$redacteur_group], + 'id_site' => 1]); + + $this->fixture(Class_Permission::class, + ['id' => 666, + 'code' => 'ARTICLE', + 'module' => 'ARTICLE']); + + Class_Permission::createArticle()->permitTo($redacteur_group, $article_categorie); + ZendAfi_Auth::getInstance()->logUser($redacteur); + } + + + /** @test */ + public function withUserAllowedToEditArticleShouldDisplayForm() { + $this->dispatch('/admin/cms/event-timing-edit/id/1'); + $this->assertXPath('//form[contains(@action,"event-timing-edit")]'); + } + + + /** @test */ + public function withUserForbiddenShouldRedirect() { + Class_Users::find(5)->setIdSite(2)->assertSave(); + $this->dispatch('/admin/cms/event-timing-edit/id/1'); + $this->assertRedirectTo('/admin/cms/event-timings/article_id/2'); + } +} \ No newline at end of file diff --git a/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsCalendarTest.php b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsCalendarTest.php new file mode 100644 index 0000000000000000000000000000000000000000..54f2d6ba7f37e1b0f5f15e05f36a9600cb45e03d --- /dev/null +++ b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsCalendarTest.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ArticlesMultipleTimingsCalendarTest extends ViewHelperTestCase { +protected $_storm_default_to_volatile = true; + + + public function setUp() { + parent::setUp(); + + $apero_timing_one = $this->fixture(Class_Article_EventTiming::class, + ['id' => 1, + 'start' => '2021-06-01 10:00:00', + 'end' => '2021-06-01 11:00:00']); + + + $apero_timing_two = $this->fixture(Class_Article_EventTiming::class, + ['id' => 2, + 'start' => '2021-06-10 10:00:00', + 'end' => '2021-06-10 11:00:00']); + + /** + * See Class_Article_Loader::newFromRow + * + * For improved performances, loading of articles is joined with + * timings table. + * As there's a group by (see Class_Article_SelectWithTimings) and + * that all code use events_debut / event_fin, with timings start is moved to events_debut + * and end to events_fin + * + * But Calendar may fetch several times same Article and finally merge all results + * (see Calendar::_getNextEvents). array_merge fails as the same Article fetched + * twice may have different events_debut / events_fin. + * + * These fixtures reproduce the problem + */ + $apero_first_fetch = Class_Article::newInstance(['id' => 1, + 'titre' => 'Apéro !', + 'contenu' => 'En toute modération', + 'events_debut' => $apero_timing_one->getStart(), + 'events_fin' => $apero_timing_one->getEnd(), + 'event_timings' => [$apero_timing_one, + $apero_timing_two]]); + + $apero_second_fetch = Class_Article::newInstance(['id' => 1, + 'titre' => 'Apéro !', + 'contenu' => 'En toute modération', + 'events_debut' => $apero_timing_two->getStart(), + 'events_fin' => $apero_timing_two->getEnd(), + 'event_timings' => [$apero_timing_one, + $apero_timing_two]]); + + + $this->onLoaderOfModel(Class_Article::class) + ->whenCalled('getArticlesByPreferences') + ->with([ + 'display_order' => 'EventDebut', + 'id_categorie' => '', + 'events_only' => true, + 'event_date' => '2021-06', + 'id_bib' => 0, + 'id_lieu' => '', + 'custom_fields' => [], + 'published' => true, + 'event_end_after' => '2021-06-01']) + ->answers([$apero_first_fetch]) + + ->whenCalled('getArticlesByPreferences') + ->with([ + 'display_order' => 'EventDebut', + 'id_categorie' => '', + 'events_only' => true, + 'event_date' => '', + 'id_bib' => 0, + 'id_lieu' => '', + 'custom_fields' => [], + 'published' => true, + 'event_start_after' => '2021-06', + 'event_end_after' => '', + 'limit' => 3]) + ->answers([$apero_second_fetch, + Class_Article::newInstance(['id' => 2, + 'titre' => 'Dessert', + 'events_debut' => '2021-06-12 09:00']) + ]) + + ->whenCalled('getArticlesByPreferences') + ->with([ + 'display_order' => 'EventDebut', + 'id_categorie' => '', + 'events_only' => true, + 'event_date' => '2021-06', + 'id_bib' => 0, + 'id_lieu' => '', + 'custom_fields' => [], + 'published' => true]) + ->answers([]) + + ->beStrict(); + + + ZendAfi_View_Helper_CalendarContent::setTimeSource(new TimeSourceForTest('2021-06-01 09:00:00')); + + $params = ['division' => '2', + 'type_module' => 'CALENDAR', + 'preferences' => ['titre' => 'Agenda']]; + $helper = new ZendAfi_View_Helper_Accueil_Calendar(2, $params); + $helper->setView(new ZendAfi_Controller_Action_Helper_View()); + $this->html = $helper->getBoite(); + } + + + /** @test */ + public function calendarShouldContainsOnlyOneArticleApero() { + $this + ->assertXPathCount($this->html, + '//a[@class="calendar_event_title"][contains(@href, "cms/articleview/id/1")]', + 1); + } + + + /** @test */ + public function calendarShouldContainsArticleWithFirstEventsDebut2021_06_01() { + $this + ->assertXPathContentContains($this->html, + '//span[@class="calendar_event_date"]', + 'mardi 01 juin 2021'); + + } + + + /** @test */ + public function calendarShouldContainsTwoArticles() { + $this + ->assertXPathCount($this->html, + '//a[@class="calendar_event_title"]', + 2); + } + + + /** @test */ + public function calendarSecondArticleShouldBeDessert() { + $this + ->assertXPath($this->html, + '//a[@class="calendar_event_title"][contains(@href, "cms/articleview/id/2")]'); + } +} diff --git a/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsLoaderTest.php b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsLoaderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..884846da7f9801083a3142be1fa4a62e98ac235c --- /dev/null +++ b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsLoaderTest.php @@ -0,0 +1,146 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +require_once(__DIR__ . '/../../library/Class/ArticleLoaderTest.php'); + +class ArticlesMultipleTimingsLoaderTest extends ArticleLoaderGetArticlesByPreferencesTestCase { + public function setUp() { + parent::setUp(); + Class_Article_SelectWithTimings::setTimeSource(new TimeSourceForTest('2011-10-20 10:00:00')); + Class_AdminVar::set('ENABLE_ARTICLES_TIMINGS', 1); + } + + + public function tearDown() { + Class_Article_SelectWithTimings::setTimeSource(null); + parent::tearDown(); + } + + + protected function _articlesFixtures() { + return [ + [ + 'ID_ARTICLE' => 23, + 'ID_CAT' => 2, + 'TITRE' => 'Fête de la pomme', + 'DATE_CREATION' => '2011-04-02', + 'DEBUT' => '2011-10-02', + 'FIN' => '2011-10-22', + 'EVENTS_DEBUT' => '', + 'EVENTS_FIN' => '', + 'START' => '2011-10-20', + 'END' => '2011-10-21'] + ]; + } + + + public function assertSelect($expected) { + $this->assertEquals("SELECT `cms_article`.*, `cms_article_timings`.`start`, `cms_article_timings`.`end` FROM `cms_article` ".$expected, + str_replace("\n", "", $this->select->assemble())); + } + + + /** @test */ + public function withEventsOnlyShouldFilterOnEventsDates() { + $article = $this->getArticles(['events_only' => true]); + $this->assertSelect(sprintf('LEFT JOIN `cms_article_timings` ON cms_article.id_article=cms_article_timings.article_id WHERE %s AND (EVENTS_DEBUT IS NOT NULL OR START IS NOT NULL) AND (EVENTS_FIN IS NOT NULL OR (END IS NOT NULL AND END >= CURDATE())) AND (PARENT_ID=0) GROUP BY `id_article` ORDER BY `DATE_CREATION` DESC', self::WHERE_VISIBLE_CLAUSE)); + } + + + /** @test */ + public function withDayDateSqlShouldFilterEventsByDay() { + $articles = $this->getArticles(['display_order' => 'EventDebut', + 'id_items' => '', + 'id_categorie' => '', + 'event_date' => '2011-03-15', + 'id_bib' => 0]); + $this->assertSelect(sprintf("LEFT JOIN `cms_article_timings` ON cms_article.id_article=cms_article_timings.article_id WHERE %s AND (EVENTS_DEBUT IS NOT NULL OR START IS NOT NULL) AND (EVENTS_FIN IS NOT NULL OR END IS NOT NULL) AND (left(EVENTS_DEBUT,10) <= '2011-03-15' OR left(START,10) <= '2011-03-15') AND (left(EVENTS_FIN,10) >= '2011-03-15' OR left(END,10) >= '2011-03-15') AND (PARENT_ID=0) GROUP BY `id_article` ORDER BY `DATE_CREATION` DESC", + self::WHERE_VISIBLE_CLAUSE)); + } + + + /** @test */ + public function withEventEndAfterSqlShouldFilterOnEventsFin() { + $articles = $this->getArticles(['event_end_after' => '2011-03-15']); + $this->assertSelect(sprintf("LEFT JOIN `cms_article_timings` ON cms_article.id_article=cms_article_timings.article_id WHERE %s AND (EVENTS_FIN IS NOT NULL OR END IS NOT NULL) AND (left(EVENTS_FIN,10) >= '2011-03-15' OR left(END,10) >= '2011-03-15') AND (PARENT_ID=0) GROUP BY `id_article` ORDER BY `DATE_CREATION` DESC", self::WHERE_VISIBLE_CLAUSE)); + } + + + /** @test */ + public function withMonthDateSqlShouldFilterEventsByMonth() { + $articles = $this->getArticles(['display_order' => 'EventDebut', + 'id_items' => '', + 'id_categorie' => '', + 'event_date' => '2011-03', + 'id_bib' => 0]); + $this->assertSelect(sprintf("LEFT JOIN `cms_article_timings` ON cms_article.id_article=cms_article_timings.article_id WHERE %s AND (EVENTS_DEBUT IS NOT NULL OR START IS NOT NULL) AND (EVENTS_FIN IS NOT NULL OR END IS NOT NULL) AND (left(EVENTS_DEBUT,7) <= '2011-03' OR left(START,7) <= '2011-03') AND (left(EVENTS_FIN,7) >= '2011-03' OR left(END,7) >= '2011-03') AND (PARENT_ID=0) GROUP BY `id_article` ORDER BY `DATE_CREATION` DESC", + self::WHERE_VISIBLE_CLAUSE)); + return $articles; + } + + + /** + * @depends withMonthDateSqlShouldFilterEventsByMonth + * @test + **/ + public function articleEventsDebutAndFinShouldBeReplacedByStartEnd($articles) { + $this->assertEquals(['2011-10-20', '2011-10-21'], + [$articles[0]->getEventsDebut(), $articles[0]->getEventsFin()]); + } + + + + /** @test */ + public function withMonthDateAndEventsOnlySqlShouldFilterEventsByMonth() { + $articles = $this->getArticles(['display_order' => 'EventDebut', + 'id_items' => '', + 'id_categorie' => '', + 'events_only' => true, + 'event_date' => '2011-03', + 'id_bib' => 0]); + $this->assertSelect(sprintf("LEFT JOIN `cms_article_timings` ON cms_article.id_article=cms_article_timings.article_id WHERE %s AND (EVENTS_DEBUT IS NOT NULL OR START IS NOT NULL) AND (EVENTS_FIN IS NOT NULL OR END IS NOT NULL) AND (left(EVENTS_DEBUT,7) <= '2011-03' OR left(START,7) <= '2011-03') AND (left(EVENTS_FIN,7) >= '2011-03' OR left(END,7) >= '2011-03') AND (PARENT_ID=0) GROUP BY `id_article` ORDER BY `DATE_CREATION` DESC", + self::WHERE_VISIBLE_CLAUSE)); + } + + + /** @test */ + public function withMonthDateOnCurrentMonthSqlShouldSelectEventsAfterCurdate() { + Class_Article_SelectWithTimings::setTimeSource(new TimeSourceForTest('2011-03-20 10:00:00')); + $articles = $this->getArticles(['display_order' => 'EventDebut', + 'id_items' => '', + 'id_categorie' => '', + 'events_only' => true, + 'event_date' => '2011-03', + 'id_bib' => 0]); + $this->assertSelect(sprintf("LEFT JOIN `cms_article_timings` ON cms_article.id_article=cms_article_timings.article_id WHERE %s AND (EVENTS_DEBUT IS NOT NULL OR START IS NOT NULL) AND (EVENTS_FIN IS NOT NULL OR END IS NOT NULL) AND (left(EVENTS_DEBUT,7) <= '2011-03' OR left(START,7) <= '2011-03') AND (left(EVENTS_FIN,7) >= '2011-03' OR (left(END,7) >= '2011-03' AND END >= CURDATE())) AND (PARENT_ID=0) GROUP BY `id_article` ORDER BY `DATE_CREATION` DESC", + self::WHERE_VISIBLE_CLAUSE)); + } + + + /** @test */ + public function withInvalidEventDateShouldNotRestrictOnEventDate() { + $articles = $this->getArticles(['event_date' => 'trololo', + 'event_start_after' => 'test', + 'event_end_after' => 'raté']); + $this->assertSelect(sprintf("LEFT JOIN `cms_article_timings` ON cms_article.id_article=cms_article_timings.article_id WHERE %s AND (PARENT_ID=0) GROUP BY `id_article` ORDER BY `DATE_CREATION` DESC", + self::WHERE_VISIBLE_CLAUSE)); + } +} diff --git a/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsOpenAgendaImportTest.php b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsOpenAgendaImportTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9263b16681a3b32566f0ba70c672d472aab12b92 --- /dev/null +++ b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsOpenAgendaImportTest.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +require_once(__DIR__ . '/../ExternalAgendas/ExternalAgendasOpenAgendaTest.php'); + + +class ArticlesMultipleTimingsOpenAgendaImportTest extends ExternalAgendasOpenAgendaImportTestCase { + + public function setUp() { + $this->fixture(Class_Article::class, + ['id' => 2, + 'id_origine' => '84528178', + 'titre' => 'Risette', + 'contenu' => 'le contenu et les horaires doivent être réécrits', + ]); + + $this->fixture(Class_Article_EventTiming::class, + ['id' => 1, + 'id_article' => 2, + 'start' => '2021-07-08 10:00', + 'end' => '2021-07-08 11:00' + ]); + + parent::setUp(); + Class_AdminVar::set('ENABLE_ARTICLES_TIMINGS', 1); + Class_ExternalAgenda::find(12)->import(); + } + + + /** @test */ + public function countArticlesShouldBeFour() { + $this->assertCount(4, Class_Article::findAll()); + } + + + /** @test */ + public function firstArticleIdOrigineShouldBe5519006() { + $this->assertEquals('5519006', + Class_Article::find(1)->getIdOrigine()); + } + + + + /** @test */ + public function firstArticleUpdatedAtShouldBe20191126() { + $this->assertEquals('2019-11-26T14:12:06.000Z', Class_Article::find(1)->getDateMaj()); + } + + + /** @test */ + public function firstArticleImageShouldContainsHTMLAndImage() { + $this->assertEquals('<figure><img src="https://cibul.s3.amazonaws.com/9c3729cce33140c5a011056c8168ec5b.base.image.jpg" alt=""/><figcaption>Credits : moi</figcaption></figure><p>Voyons ça dans une session de coding dojo</p><p>Infos pratiques :</p><dl><dt>Conditions</dt><dd>être geek</dd><dt>Âge</dt><dd>de 6 à 99 ans</dd></dl><p>Pour s\'inscrire :</p><dl><dt>Courriel</dt><dd><a href="mailto:llaffont@afi-sa.fr">llaffont@afi-sa.fr</a></dd><dt>Téléphone</dt><dd><a href="tel:0123456789">0123456789</a></dd><dt>Site</dt><dd><a href="https://www.website.org">https://www.website.org</a></dd><dt>Lien</dt><dd><a href="https://registration.website.org/">https://registration.website.org/</a></dd></dl>', + Class_Article::find(1)->getContenu()); + } + + + /** @test */ + public function firstArticleShouldHaveThreeTimings() { + $timings = array_map(function($timing) { return $timing->getRawAttributes(); }, + Class_Article::find(1)->getEventTimings()); + $this->assertEquals([ + ['start' => '2019-11-25 09:30', + 'end' => '2019-11-25 11:30', + 'article_id' => 1, + 'id' => 1], + + ['start' => '2019-11-29 09:00', + 'end' => '2019-11-29 11:00', + 'article_id' => 1, + 'id' => 2], + + ['start' => '2019-12-01 09:30', + 'end' => '2019-12-01 10:30', + 'article_id' => 1, + 'id' => 3]], + + $timings); + + } + + + /** @test */ + public function articleTwoShouldHaveIdOrigine84528178() { + $this->assertEquals('84528178', Class_Article::find(2)->getIdOrigine()); + } + + + /** @test */ + public function articleTwoShouldHaveTimingsSameAsOrigine() { + $timings = array_map(function($timing) { return $timing->getRawAttributes(); }, + Class_Article::find(2)->getEventTimings()); + $this->assertEquals([ + ['start' => '2019-11-25 10:00', + 'end' => '2019-11-25 10:30', + 'article_id' => 2, + 'id' => 4], + ['start' => '2019-12-02 10:00', + 'end' => '2019-12-02 10:30', + 'article_id' => 2, + 'id' => 5] + ], + $timings); + + } +} diff --git a/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsViewTest.php b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsViewTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d729d5c85fa1a53bf30243b1c8394c43698fbfb5 --- /dev/null +++ b/tests/scenarios/ArticlesMultipleTimings/ArticlesMultipleTimingsViewTest.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH 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). + * + * BOKEH 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 BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +abstract class ArticlesMultipleTimingsViewTestCase extends AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + + + public function setUp() { + parent::setUp(); + Class_AdminVar::set('ENABLE_ARTICLES_TIMINGS', 1); + + $heure_du_conte = $this->fixture(Class_Article::class, + ['id' => 5, + 'titre' => 'Heure du conte', + 'contenu' => 'Pour les grands et petits', + 'events_debut' => '2021-06-08 10:00:00', + 'events_fin' => '2021-06-08 11:00:00']); + $heure_du_conte + ->addEventTiming($this->fixture(Class_Article_EventTiming::class, + ['id' => 1, + 'start' => '2021-06-01 10:00:00', + 'end' => '2021-06-01 11:00:00'])) + ->addEventTiming($this->fixture(Class_Article_EventTiming::class, + ['id' => 1, + 'start' => '2021-06-08 10:00:00', + 'end' => '2021-06-08 11:00:00'])) + ->addEventTiming($this->fixture(Class_Article_EventTiming::class, + ['id' => 1, + 'start' => '2021-06-15 10:00:00', + 'end' => '2021-06-15 11:00:00'])) + ->assertSave(); + + $this->fixture(Class_ArticleCategorie::class, + ['id' => 2, + 'libelle' => 'Agenda', + 'articles' => [$heure_du_conte]]); + + Storm_Test_ObjectWrapper::onLoaderOfModel(Class_Article::class) + ->whenCalled('getArticlesByPreferences') + ->answers([$heure_du_conte]); + + $time_source = new TimeSourceForTest('2021-06-08 09:00:00'); + Class_Article_EventTiming::setTimeSource($time_source); + } + + + public function tearDown() { + Class_Article_EventTiming::setTimeSource(null); + parent::tearDown(); + } +} + + + + +class ArticlesMultipleTimingsViewTest extends ArticlesMultipleTimingsViewTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/cms/articleview/id/5'); + } + + + /** @test */ + public function articleShouldContainsDivForTimings() { + $this->assertXPathContentContains('//div[@class="article_timings"]/h2', 'Dates et Horaires'); + } + + + /** @test */ + public function timingsShouldContainsMardiHuitJuinDeDixAOnzeHeures() { + $this->assertXPathContentContains('//div[@class="article_timings"]/ul/li', + 'mardi 08 juin 2021 de 10:00 à 11:00'); + } + + + /** @test */ + public function timingsShouldContainsMardiQuinzeJuinDeDixAOnzeHeures() { + $this->assertXPathContentContains('//div[@class="article_timings"]/ul/li', + 'mardi 15 juin 2021 de 10:00 à 11:00'); + } + + + /** @test */ + public function timingsShouldNotContainsPremierJuinDeDixAOnzeHeures() { + $this->assertNotXPathContentContains('//div[@class="article_timings"]/ul/li', + '01 juin'); + } +} + + + + +class ArticlesMultipleTimingsTemplatesViewTest extends ArticlesMultipleTimingsViewTestCase { + public function setUp() { + parent::setUp(); + $this->_buildTemplateProfil(['id' => 4, + 'libelle' => 'Dans le train']); + + $this->dispatch('/cms/articleview/id/5/id_profil/4'); + } + + + /** @test */ + public function articleShouldContainsDivForTimings() { + $this->assertXPathContentContains('//div[@class="article_timings"]/h2', 'Dates et Horaires'); + } + + + /** @test */ + public function timingsShouldContainsMardiHuitJuinDeDixAOnzeHeures() { + $this->assertXPathContentContains('//div[@class="article_timings"]/ul/li', + 'mardi 08 juin 2021 de 10:00 à 11:00'); + } + + + /** @test */ + public function timingsShouldContainsMardiQuinzeJuinDeDixAOnzeHeures() { + $this->assertXPathContentContains('//div[@class="article_timings"]/ul/li', + 'mardi 15 juin 2021 de 10:00 à 11:00'); + } + + + /** @test */ + public function timingsShouldNotContainsPremierJuinDeDixAOnzeHeures() { + $this->assertNotXPathContentContains('//div[@class="article_timings"]/ul/li', + '01 juin'); + } +} diff --git a/tests/scenarios/ExternalAgendas/ExternalAgendasOpenAgendaTest.php b/tests/scenarios/ExternalAgendas/ExternalAgendasOpenAgendaTest.php index 2dd27dba0ca5d1cdbdec4e0f2f61d10fccc16894..b4a9275cf64d9df1dc1792650cb712833c5eaf35 100644 --- a/tests/scenarios/ExternalAgendas/ExternalAgendasOpenAgendaTest.php +++ b/tests/scenarios/ExternalAgendas/ExternalAgendasOpenAgendaTest.php @@ -19,20 +19,18 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -class ExternalAgendasOpenAgendaImportTest extends ModelTestCase { - public function setup() { - parent::setup(); - - $events_category = $this->fixture('Class_ArticleCategorie', +abstract class ExternalAgendasOpenAgendaImportTestCase extends ModelTestCase { + public function setUp() { + parent::setUp(); + $events_category = $this->fixture(Class_ArticleCategorie::class, ['id'=>123, 'libelle' => 'Coding Gouter', - 'bib'=> $this->fixture('Class_Bib', + 'bib'=> $this->fixture(Class_Bib::class, ['id'=>7, 'libelle'=>'Joliville'])]); $agenda_url = 'https://openagenda.com/agendas/36758196/events.json?lang=fr&key=e0f9e6b4302a439f99b78549b9d63fd6'; - $this->fixture('Class_ExternalAgenda', + $this->fixture(Class_ExternalAgenda::class, ['id' => 12, 'label' => 'agenda PNB', 'url' => $agenda_url, @@ -61,8 +59,6 @@ class ExternalAgendasOpenAgendaImportTest extends ModelTestCase { $time_source = new TimeSourceForTest('2019-11-01 08:00:00'); Class_ExternalAgenda::setTimeSource($time_source); Class_Article::setTimeSource($time_source); - - Class_ExternalAgenda::find(12)->import(); } @@ -71,6 +67,16 @@ class ExternalAgendasOpenAgendaImportTest extends ModelTestCase { Class_Article::setTimeSource(null); parent::tearDown(); } +} + + + + +class ExternalAgendasOpenAgendaImportTest extends ExternalAgendasOpenAgendaImportTestCase { + public function setup() { + parent::setup(); + Class_ExternalAgenda::find(12)->import(); + } /** @test */ @@ -117,7 +123,7 @@ class ExternalAgendasOpenAgendaImportTest extends ModelTestCase { /** @test */ public function afterImportWhenNotDeleteOrphanOldArticleShouldBePresent() { Class_ExternalAgenda::find(12)->setDeleteOrphanEvents(0); - $this->fixture('Class_Article', + $this->fixture(Class_Article::class, ['id' => 234, 'titre' => 'Test', 'description' => 'test', diff --git a/tests/scenarios/ExternalAgendas/open-agenda-2.json b/tests/scenarios/ExternalAgendas/open-agenda-2.json index e01201dfb437b8f466e2a31991f4df8cf98eab4f..620ede530ef8cfb898c58cdc62c8b68892a96d49 100644 --- a/tests/scenarios/ExternalAgendas/open-agenda-2.json +++ b/tests/scenarios/ExternalAgendas/open-agenda-2.json @@ -60,38 +60,6 @@ { "start": "2019-12-02T10:00:00.000Z", "end": "2019-12-02T10:30:00.000Z" - }, - { - "start": "2019-12-09T10:00:00.000Z", - "end": "2019-12-09T10:30:00.000Z" - }, - { - "start": "2019-12-16T10:00:00.000Z", - "end": "2019-12-16T10:30:00.000Z" - }, - { - "start": "2019-12-23T10:00:00.000Z", - "end": "2019-12-23T10:30:00.000Z" - }, - { - "start": "2019-12-30T10:00:00.000Z", - "end": "2019-12-30T10:30:00.000Z" - }, - { - "start": "2020-01-06T10:00:00.000Z", - "end": "2020-01-06T10:30:00.000Z" - }, - { - "start": "2020-01-13T10:00:00.000Z", - "end": "2020-01-13T10:30:00.000Z" - }, - { - "start": "2020-01-20T10:00:00.000Z", - "end": "2020-01-20T10:30:00.000Z" - }, - { - "start": "2020-01-27T10:00:00.000Z", - "end": "2020-01-27T10:30:00.000Z" } ], "location": { diff --git a/tests/scenarios/Templates/TemplatesMenuTest.php b/tests/scenarios/Templates/TemplatesMenuTest.php index b2e4e0e3c4a52cb7e0ba76acb4a771caf9273d07..1cf242a1c26727cbf7f1aba61051bf4865146751 100644 --- a/tests/scenarios/Templates/TemplatesMenuTest.php +++ b/tests/scenarios/Templates/TemplatesMenuTest.php @@ -1,4 +1,4 @@ -$<?php +<?php /** * Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved. * @@ -18,7 +18,7 @@ $<?php * along with BOKEH; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - require_once 'TemplatesTest.php'; +require_once 'TemplatesTest.php'; class TemplatesMenuDispatchEditMenuHTest extends TemplatesIntonationTestCase { @@ -331,7 +331,7 @@ class TemplatesMenuDefaultSettingsTest extends Admin_AbstractControllerTestCase -class TemplatesMenuTestCase extends AbstractControllerTestCase { +abstract class TemplatesMenuTestCase extends AbstractControllerTestCase { protected $_storm_default_to_volatile = true, @@ -493,4 +493,4 @@ class TemplatesMenuNavWithLayoutTest extends TemplatesMenuTestCase { public function homeShouldBeRenderInANavTag() { $this->assertXPathContentContains('//footer//nav', 'Home'); } -} \ No newline at end of file +} diff --git a/tests/scenarios/Templates/TemplatesWidgetTest.php b/tests/scenarios/Templates/TemplatesWidgetTest.php index c4150955d60a6d5d57d9c7f9069e3d99772ad0ab..f1f616149cf3fe3c7fd3b254b1558bdac50b8bbe 100644 --- a/tests/scenarios/Templates/TemplatesWidgetTest.php +++ b/tests/scenarios/Templates/TemplatesWidgetTest.php @@ -236,7 +236,8 @@ class TemplatesWidgetRenderAllAgendaTest extends TemplatesWidgetRenderAllTestCas /** @test */ public function townGrandAnnecyShouldBeSelectable() { - $this->assertXPathContentContains('//div[contains(@class, "filters")]//li//a[contains(@href, "/cms/render-all/id_module/21/place_town/Grand+Annecy")]', 'Grand Annecy'); + $this->assertXPathContentContains('//div[contains(@class, "filters")]//li//a[contains(@href, "/cms/render-all/id_module/21/place_town/Grand+Annecy")]', + 'Grand Annecy'); } }