Commit bfadf610 authored by Laurent's avatar Laurent
Browse files

Merge branch 'dev#129798_plage_horaire_d_un_evenement_open_agenda' into 'master'

dev #129798 Articles supports multiple event timings

See merge request !4170
parents 63e83df9 8dbc967b
'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
- 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
......@@ -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');
}
}
<?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];
}
}
<?php echo $this->renderForm($this->form); ?>
<?php echo $this->renderForm($this->form); ?>
<?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());
<?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) { }
......@@ -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')),
];
}
......
This diff is collapsed.
<?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'));
}
}
<?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]));
}
}
<?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;