Commit 86cbac91 authored by Henri-Damien LAURENT's avatar Henri-Damien LAURENT

dev#88078 : Notification des rendez vous

parent 6438d8c6
'88078' =>
['Label' => $this->_('Gestion des rendez-vous'),
'Desc' => $this->_('Bokeh permet d'établir des agendas de rendez-vous concernant un ou plusieurs participants.'),
'Image' => '',
'Video' => 'https://youtu.be/imar4izniAY',
'Category' => $this->_('Administration'),
'Right' => function($feature_description, $user) {return true;},
'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php?title=Rendez-vous',
'Test' => '',
'Date' => '2019-04-04'],
\ No newline at end of file
- ticket #88078 : Nouveau module de gestion des rendez-vous.
\ No newline at end of file
......@@ -20,6 +20,7 @@
*/
class Admin_ErrorController extends ZendAfi_Controller_Action {
public function errorAction() {
$this->_response->setHttpResponseCode(500);
$this->view->titre = $this->_('Une erreur est survenue');
$this->view->errors = $this->_getParam('error_handler');
$this->view->database = array_at('dbname',
......
<?php
echo $this->button_New((new Class_Entity())
->setText($this->_('Ajouter un rendez-vous')));
echo $this->button_Back((new Class_Entity())
->setText($this->_('Retour aux agendas'))
->setUrl($this->url(['module' => 'admin',
'controller' => 'usergroup-agenda'], null, true)));
$description = (new Class_TableDescription('rendez-vous'))
->addColumn($this->_('Date'), 'date')
->addColumn($this->_('Heure de début'), 'formatted_begin_time')
->addColumn($this->_('Heure de fin'), 'formatted_end_time')
->addColumn($this->_('Lieu'), 'location_label')
->addRowAction(function($model) { return $this->renderPluginsActions($model); });
echo $this->renderTable($description, $this->rendezvous);
echo $this->renderTable(new Class_TableDescription_RendezVous('rendez-vous'), $this->rendezvous);
<?php
echo $this->rendezVous_PurgeButton($this->report, $this->_('Vider tout l\'historique'));
$manuals = $this->report->manualOnly();
echo $this->tag('section',
$this->tag('h2', $this->_('Manuelles'))
. $this->rendezVous_PurgeButton($manuals,
$this->_('Vider l\'historique des notifications manuelles'),
Class_RendezVous_UserNotification::MANUAL_TYPE)
. $this->renderTable(new Class_TableDescription_RendezVousNotification('notificationsManual'),
$manuals));
$batches = $this->report->batchOnly();
echo $this->tag('section',
$this->tag('h2', $this->_('Automatiques'))
. $this->rendezVous_PurgeButton($batches,
$this->_('Vider l\'historique des notifications automatiques'),
Class_RendezVous_UserNotification::BATCH_TYPE)
. $this->renderTable((new Class_TableDescription_RendezVousNotification('notificationsBatch'))->withoutActions(),
$batches));
<?php
$adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
try {
$adapter->query(
'create table `rendez_vous_user_notification` ('
. '`id` int(11) unsigned not null auto_increment,'
. '`rendez_vous_id` int(11) unsigned not null,'
. '`user_id` int(11) not null,'
. '`created_at` datetime null,'
. '`type` varchar(255) not null,'
. '`status` varchar(255) null,'
. '`error` text null,'
. 'primary key (id),'
. 'key `rendez_vous_id` (`rendez_vous_id`),'
. 'key `user_id` (`user_id`),'
. 'key `status` (`status`),'
. 'key `type`(`type`),'
. 'key `created_at` (`created_at`)'
. ') engine=MyISAM default charset=utf8'
);
} catch(Exception $e) {}
......@@ -471,7 +471,11 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
protected function _getRendezVousVars() {
return ['ENABLE_RENDEZ_VOUS' => Class_AdminVar_Meta::newOnOff($this->_('Activer la gestion des rendez-vous'))];
return ['ENABLE_RENDEZ_VOUS' => Class_AdminVar_Meta::newOnOff($this->_('Activer la gestion des rendez-vous')),
'NOTIFICATION_TEMPLATE_RENDEZ_VOUS' => Class_AdminVar_Meta::newEditor($this->_('Modèle utilisé pour les courriels de notifications de rendez-vous. <a class="ardans_help" title="Aide" href="http://wiki.bokeh-library-portal.org/index.php?title=Rendez-vous" target="_blank"><img src="/hdl/public/admin/skins/bokeh74/icons/actions/help_16.png" alt=""></a>'), ['value'=> '<p>Bonjour {user.nom_complet},</p> <p>nous vous rappelons votre rendez-vous <strong> {rendez_vous.agenda_label} &agrave; {rendez_vous.formatted_date} entre {rendez_vous.formatted_begin_time} et {rendez_vous.formatted_end_time} &agrave; {rendez_vous.location_label}</strong>.</p> <br> <p>Nous vous rappelons les conditions particulières suivantes :</p><p> {rendez_vous.comment}</p>']),
'NOTIFICATION_DELAY_RENDEZ_VOUS' => Class_AdminVar_Meta::newDefault($this->_('Durée pour la notification de rendez-vous (en jours)'),
['value' => '3'])
];
}
......
......@@ -41,6 +41,7 @@ class Class_BatchLoader extends Storm_Model_Loader {
Class_Batch_BuildSiteMap::TYPE => new Class_Batch_BuildSiteMap(),
Class_Batch_PremierChapitre::TYPE => new Class_Batch_PremierChapitre(),
Class_Batch_NoveltyFacet::TYPE => new Class_Batch_NoveltyFacet(),
Class_Batch_SendRendezVousNotification::TYPE => new Class_Batch_SendRendezVousNotification(),
Class_Batch_ExternalAgenda::TYPE => new Class_Batch_ExternalAgenda]);
}
......
<?php
/**
* Copyright (c) 2012-2014, 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_Batch_SendRendezVousNotification extends Class_Batch_Abstract {
const TYPE = 'RENDEZ_VOUS_NOTIFICATION';
public function getLabel() {
return $this->_('Envoi des notifications de rendez-vous');
}
public function isEnabled() {
return Class_AdminVar::isRendezVousEnabled();
}
public function run() {
if (!$delay = (int)Class_AdminVar::get('NOTIFICATION_DELAY_RENDEZ_VOUS'))
return;
$search_string = 'date >= NOW() AND DATEDIFF(date, NOW()) <= ' . $delay;
if (!$rendezvous = Class_RendezVous::findAllBy(['where' => $search_string ]))
return;
$body = [];
foreach($rendezvous as $model) {
$report = $model->notifyBatch();
$body[] = $this->_generateMail($model, $report);
}
$mailer = new Class_MailHtml();
$mailer->mail($mailer->getMailFrom(),
$this->_('Rapport de notifications envoyées pour les rendez-vous'),
'<dl>' . implode($body) . '</dl>');
}
protected function _generateMail($rendez_vous, $report) {
return sprintf('<dt><a href="%s">%s, %s</a></dt><dd>%s</dd>',
Class_Url::absolute(['module' => 'admin',
'controller' => 'rendez-vous',
'action' => 'notification',
'group_id' => $rendez_vous->getGroupId(),
'id' => $rendez_vous->getId()], null, true),
$rendez_vous->getAgendaLabel(),
$rendez_vous->getLibelle(),
$report->getActionStatus()
);
}
}
......@@ -43,10 +43,11 @@ class Class_Mail {
$mail = new ZendAfi_Mail('utf8');
$mail
->setSubject($sujet)
->setBodyText($body)
->setFrom($this->mail_from)
->addTo($destinataire);
$this->_setBodyIn($body, $mail);
try {
$mail->send();
return true;
......@@ -56,6 +57,11 @@ class Class_Mail {
}
protected function _setBodyIn($body, $mail) {
$mail->setBodyText($body);
}
public function sendMail($sujet, $body, $destinataire, $data=false) {
$error_message = sprintf('%s <br/> %s',
$this->_("Les paramètres d'envoi de mails du portail sont incomplets."),
......@@ -94,4 +100,9 @@ class Class_Mail {
$validator = new Zend_Validate_EmailAddress();
return $validator->isValid($mail);
}
public function getMailFrom() {
return $this->mail_from;
}
}
\ No newline at end of file
<?php
/**
* Copyright (c) 2012, 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_MailHtml extends Class_Mail {
protected function _setBodyIn($body, $mail) {
$mail->setBodyHtml($body);
}
}
\ No newline at end of file
......@@ -100,7 +100,8 @@ class Class_ModeleFusionLoader extends Storm_Model_Loader {
Class_ModeleFusion::ARTICLES_TEMPLATE => $this->_('Page d\'articles'),
Class_ModeleFusion::RECORDS_TEMPLATE => $this->_('Resultats de recherche'),
Class_ModeleFusion::RECORD_TEMPLATE => $this->_('Page de notice'),
Class_ModeleFusion::LOANS_TEMPLATE => $this->_('Liste des prêts')];
Class_ModeleFusion::LOANS_TEMPLATE => $this->_('Liste des prêts'),
];
}
......
......@@ -100,7 +100,9 @@ class Class_Profil_Skin {
public function getImageUrl($img) {
return ($this->imageExists($img)
? $this->getUrl() . self::IMAGE_DIR : URL_SHARED_IMG) . '/' . $img;
? ($this->getUrl() . self::IMAGE_DIR . '/')
: URL_SHARED_IMG)
. $img;
}
......
......@@ -43,6 +43,13 @@ class Class_RendezVous extends Storm_Model_Abstract {
'location' => ['model' => 'Class_Lieu',
'referenced_in' => 'location_id']];
protected $_has_many = ['notifications' => ['model' => 'Class_RendezVous_UserNotification',
'role' => 'rendez_vous',
'order' => 'id desc',
'dependents' => 'delete'],
'users' => ['through' => 'agenda']];
protected $_default_attribute_values = ['group_id'=> null,
'location_id' => null,
'date' => null,
......@@ -60,7 +67,7 @@ class Class_RendezVous extends Storm_Model_Abstract {
public function getFormattedDate() {
return strftime('%A %d %B', strtotime($this->getDate()));
return strftime('%a %d %B %Y', strtotime($this->getDate()));
}
......@@ -93,12 +100,19 @@ class Class_RendezVous extends Storm_Model_Abstract {
}
public function getAgendaUsers() {
return $this->hasAgenda()
? $this->getAgenda()->getUsers()
: [];
}
public function validate() {
$this->check($this->hasAgenda(), $this->_('L\'agenda est obligatoire'));
$this->checkAttribute('date',
$this->hasDate()
&& (new ZendAfi_Validate_DateFormat())->isValid($this->getDate()),
$this->_('La date doit être au forma JJ/MM/AAAA'));
$this->_('La date doit être au format JJ/MM/AAAA'));
$this->_validateTime('begin_time', $this->_('L\'heure de début est obligatoire'));
$this->_validateTime('end_time', $this->_('L\'heure de fin est obligatoire'));
$this->checkAttribute('end_time', $this->getBeginTime() < $this->getEndTime(),
......@@ -154,4 +168,32 @@ class Class_RendezVous extends Storm_Model_Abstract {
return strnatcmp($this->getEndTime(), $other->getEndTime());
}
public function notifyBatch() {
$notify = Class_RendezVous_Notify::newBatchFor($this);
return $notify();
}
public function notifyManual() {
$notify = Class_RendezVous_Notify::newManualFor($this);
return $notify();
}
public function notifyUserById($user_id) {
$notify = Class_RendezVous_Notify::newUniqueUserFor($this, $user_id);
return $notify();
}
public function getNotificationStatus() {
return $this->getNotificationReport()->getStatus();
}
public function getNotificationReport() {
return new Class_RendezVous_UserNotificationReport($this->getNotifications());
}
}
\ No newline at end of file
<?php
/**
* Copyright (c) 2012-2019, 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 Class_RendezVous_Notify {
protected
$_type,
$_rendez_vous;
public static function newBatchFor($rendez_vous) {
return new Class_RendezVous_NotifyBatch($rendez_vous);
}
public static function newManualFor($rendez_vous) {
return new Class_RendezVous_NotifyManual($rendez_vous);
}
public static function newUniqueUserFor($rendez_vous, $user_id) {
return (new Class_RendezVous_NotifyUniqueUser($rendez_vous))
->setUserId($user_id);
}
public function __construct($rendez_vous) {
$this->_rendez_vous = $rendez_vous;
}
public function __invoke() {
if (!$users = $this->getAgendaUsers())
return new Class_RendezVous_UserNotificationReport([]);
$notifications = [];
foreach ($users as $user) {
$notif = Class_RendezVous_UserNotification::newInstance(['rendez_vous' => $this->_rendez_vous,
'user' => $user,
]);
$method = $this->_getMethod();
$notif->$method();
$notifications[] = $notif;
}
return new Class_RendezVous_UserNotificationReport($notifications);
}
public function getAgendaUsers() {
return $this->_rendez_vous->getAgendaUsers();
}
protected function _getMethod() {
return 'notify' . $this->_type;
}
}
class Class_RendezVous_NotifyManual extends Class_RendezVous_Notify {
protected $_type = Class_RendezVous_UserNotification::MANUAL_TYPE;
}
class Class_RendezVous_NotifyBatch extends Class_RendezVous_Notify {
protected $_type = Class_RendezVous_UserNotification::BATCH_TYPE;
public function getAgendaUsers() {
$users = parent::getAgendaUsers();
return (new Storm_Model_Collection($users))
->select(function($user)
{
return !Class_RendezVous_UserNotification::isAlreadyBatchSentFor($user,
$this->_rendez_vous);
})
->getArrayCopy();
}
}
class Class_RendezVous_NotifyUniqueUser extends Class_RendezVous_Notify {
protected
$_type = Class_RendezVous_UserNotification::MANUAL_TYPE,
$_user_id;
public function setUserId($user_id) {
$this->_user_id = $user_id;
return $this;
}
public function getAgendaUsers() {
if (!$this->_user_id)
return [];
$users = parent::getAgendaUsers();
return (new Storm_Model_Collection($users))
->select(function($user)
{
return $user->getId() == $this->_user_id;
})
->getArrayCopy();
}
}
......@@ -73,9 +73,12 @@ class Class_RendezVous_SearchCriteria_Date extends Class_SearchCriteria_Abstract
protected function _filterDate($value) {
if (!$value)
if (null === $value)
return;
if ('' === $value)
return '';
if (!(new ZendAfi_Validate_DateFormat())->isValid($value, static::DATE_FORMAT)) {
$this->_element->addError($this->_('Les dates doivent être au format JJ/MM/AAAA'));
return;
......
<?php
/**
* Copyright (c) 2012, 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_RendezVous_UserNotificationLoader extends Storm_Model_Loader {
public function isAlreadyBatchSentFor($user, $rendez_vous) {
$params = ['status' => Class_RendezVous_UserNotification::SENT_STATUS,
'type' => Class_RendezVous_UserNotification::BATCH_TYPE,
'user_id' => $user->getId(),
'rendez_vous_id'=> $rendez_vous->getId()];
return 0 < Class_RendezVous_UserNotification::countBy($params);
}
public function isKnownType($type) {
return in_array($type, [Class_RendezVous_UserNotification::BATCH_TYPE,
Class_Rendezvous_Usernotification::MANUAL_TYPE]);
}
}
class Class_RendezVous_UserNotification extends Storm_Model_Abstract {
use Trait_Translator, Trait_TimeSource;
const
BATCH_TYPE = 'Batch',
MANUAL_TYPE = 'Manual',
SENT_STATUS = 'sent',
ERROR_STATUS = 'error',
NOMAIL_STATUS = 'nomail';
protected $_table_name = 'rendez_vous_user_notification';
protected $_loader_class = 'Class_RendezVous_UserNotificationLoader';
protected $_belongs_to = ['user' => ['model' => 'Class_Users'],
'rendez_vous' => ['model' => 'Class_RendezVous']];
protected $_default_attribute_values = ['rendez_vous_id'=> null,
'user_id' => null,
'created_at' => null,
'status' => null,
'type'=> null,
'error' => null];
public function getUserName() {
return $this->hasUser()
? $this->getUser()->getNomComplet()
: '';
}
public function getUserMail() {
return $this->hasUser()
? $this->getUser()->getMail()
: '';
}
public function notifyBatch() {
return $this->setType(static::BATCH_TYPE)->_send();
}
public function notifyManual() {
return $this->setType(static::MANUAL_TYPE)->_send();
}
public function beforeSave() {
if ($this->isNew())
$this->setCreatedAt($this->getCurrentDateTime());
}
public function _send() {