Add email notifications
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Core\Tool;
|
||||
use Core\Registry;
|
||||
use Core\Security;
|
||||
use Core\Translator;
|
||||
@@ -24,6 +25,7 @@ use Model\LastLogin;
|
||||
* @property \Model\GitHub $gitHub
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Ldap $ldap
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\RememberMe $rememberMe
|
||||
* @property \Model\ReverseProxyAuth $reverseProxyAuth
|
||||
@@ -93,9 +95,7 @@ abstract class Base
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
$class = '\Model\\'.ucfirst($name);
|
||||
$this->registry->$name = new $class($this->registry->shared('db'), $this->registry->shared('event'));
|
||||
return $this->registry->shared($name);
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,6 +157,7 @@ abstract class Base
|
||||
$this->action->attachEvents();
|
||||
$this->project->attachEvents();
|
||||
$this->webhook->attachEvents();
|
||||
$this->notification->attachEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -373,7 +373,7 @@ class Board extends Base
|
||||
}
|
||||
|
||||
if (isset($values['positions'])) {
|
||||
$this->board->saveTasksPosition($values['positions']);
|
||||
$this->board->saveTasksPosition($values['positions'], $values['selected_task_id']);
|
||||
}
|
||||
|
||||
$this->response->html(
|
||||
|
||||
@@ -20,7 +20,8 @@ class Config extends Base
|
||||
$this->response->html($this->template->layout('config_index', array(
|
||||
'db_size' => $this->config->getDatabaseSize(),
|
||||
'user' => $_SESSION['user'],
|
||||
'projects' => $this->project->getList(),
|
||||
'user_projects' => $this->project->getAvailableList($this->acl->getUserId()),
|
||||
'notifications' => $this->notification->readSettings($this->acl->getUserId()),
|
||||
'languages' => $this->config->getLanguages(),
|
||||
'values' => $this->config->getAll(),
|
||||
'errors' => array(),
|
||||
@@ -32,6 +33,13 @@ class Config extends Base
|
||||
)));
|
||||
}
|
||||
|
||||
public function notifications()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
$this->notification->saveSettings($this->acl->getUserId(), $values);
|
||||
$this->response->redirect('?controller=config#notifications');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save settings
|
||||
*
|
||||
@@ -57,7 +65,8 @@ class Config extends Base
|
||||
$this->response->html($this->template->layout('config_index', array(
|
||||
'db_size' => $this->config->getDatabaseSize(),
|
||||
'user' => $_SESSION['user'],
|
||||
'projects' => $this->project->getList(),
|
||||
'user_projects' => $this->project->getAvailableList($this->acl->getUserId()),
|
||||
'notifications' => $this->notification->readSettings($this->acl->getUserId()),
|
||||
'languages' => $this->config->getLanguages(),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
|
||||
@@ -31,4 +31,11 @@ class Tool
|
||||
fclose($fp);
|
||||
}
|
||||
}
|
||||
|
||||
public static function loadModel(Registry $registry, $name)
|
||||
{
|
||||
$class = '\Model\\'.ucfirst($name);
|
||||
$registry->$name = new $class($registry);
|
||||
return $registry->shared($name);
|
||||
}
|
||||
}
|
||||
|
||||
76
app/Event/BaseNotificationListener.php
Normal file
76
app/Event/BaseNotificationListener.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Core\Listener;
|
||||
use Model\Notification;
|
||||
|
||||
/**
|
||||
* Base notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class BaseNotificationListener implements Listener
|
||||
{
|
||||
/**
|
||||
* Notification model
|
||||
*
|
||||
* @accesss protected
|
||||
* @var Model\Notification
|
||||
*/
|
||||
protected $notification;
|
||||
|
||||
/**
|
||||
* Template name
|
||||
*
|
||||
* @accesss private
|
||||
* @var string
|
||||
*/
|
||||
private $template = '';
|
||||
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getTemplateData(array $data);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Model\Notification $notification Notification model instance
|
||||
* @param string $template Template name
|
||||
*/
|
||||
public function __construct(Notification $notification, $template)
|
||||
{
|
||||
$this->template = $template;
|
||||
$this->notification = $notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function execute(array $data)
|
||||
{
|
||||
$values = $this->getTemplateData($data);
|
||||
|
||||
// Get the list of users to be notified
|
||||
$users = $this->notification->getUsersList($values['task']['project_id']);
|
||||
|
||||
// Send notifications
|
||||
if ($users) {
|
||||
$this->notification->sendEmails($this->template, $users, $values);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
30
app/Event/CommentNotificationListener.php
Normal file
30
app/Event/CommentNotificationListener.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Event\BaseNotificationListener;
|
||||
|
||||
/**
|
||||
* Comment notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class CommentNotificationListener extends BaseNotificationListener
|
||||
{
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplateData(array $data)
|
||||
{
|
||||
$values = array();
|
||||
$values['comment'] = $this->notification->comment->getById($data['id']);
|
||||
$values['task'] = $this->notification->task->getById($data['task_id'], true);
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
30
app/Event/FileNotificationListener.php
Normal file
30
app/Event/FileNotificationListener.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Event\BaseNotificationListener;
|
||||
|
||||
/**
|
||||
* File notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class FileNotificationListener extends BaseNotificationListener
|
||||
{
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplateData(array $data)
|
||||
{
|
||||
$values = array();
|
||||
$values['file'] = $data;
|
||||
$values['task'] = $this->notification->task->getById($data['task_id'], true);
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
30
app/Event/SubTaskNotificationListener.php
Normal file
30
app/Event/SubTaskNotificationListener.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Event\BaseNotificationListener;
|
||||
|
||||
/**
|
||||
* SubTask notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubTaskNotificationListener extends BaseNotificationListener
|
||||
{
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplateData(array $data)
|
||||
{
|
||||
$values = array();
|
||||
$values['subtask'] = $this->notification->subtask->getById($data['id'], true);
|
||||
$values['task'] = $this->notification->task->getById($data['task_id'], true);
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
29
app/Event/TaskNotificationListener.php
Normal file
29
app/Event/TaskNotificationListener.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Event\BaseNotificationListener;
|
||||
|
||||
/**
|
||||
* Task notification listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskNotificationListener extends BaseNotificationListener
|
||||
{
|
||||
/**
|
||||
* Fetch data for the mail template
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplateData(array $data)
|
||||
{
|
||||
$values = array();
|
||||
$values['task'] = $this->notification->task->getById($data['task_id'], true);
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
@@ -402,4 +402,31 @@ return array(
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
);
|
||||
|
||||
@@ -401,4 +401,31 @@ return array(
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
);
|
||||
|
||||
@@ -401,4 +401,31 @@ return array(
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
);
|
||||
|
||||
@@ -399,4 +399,31 @@ return array(
|
||||
'Clone Project' => 'Cloner le projet',
|
||||
'Project cloned successfully.' => 'Projet cloné avec succès.',
|
||||
'Unable to clone this project.' => 'Impossible de cloner ce projet.',
|
||||
'Email notifications' => 'Notifications par email',
|
||||
'Enable email notifications' => 'Activer les notifications par emails',
|
||||
'Task position:' => 'Position de la tâche :',
|
||||
'The task #%d have been opened.' => 'La tâche #%d a été ouverte.',
|
||||
'The task #%d have been closed.' => 'La tâche #%d a été fermée.',
|
||||
'Sub-task updated' => 'Sous-tâche mise à jour',
|
||||
'Title:' => 'Titre :',
|
||||
'Status:' => 'État :',
|
||||
'Assignee:' => 'Assigné :',
|
||||
'Time tracking:' => 'Gestion du temps :',
|
||||
'New sub-task' => 'Nouvelle sous-tâche',
|
||||
'New attachment added "%s"' => 'Nouvelle pièce-jointe ajoutée « %s »',
|
||||
'Comment updated' => 'Commentaire ajouté',
|
||||
'New comment posted by %s' => 'Nouveau commentaire ajouté par « %s »',
|
||||
'List of due tasks for the project "%s"' => 'Liste des tâches expirées pour le projet « %s »',
|
||||
'[%s][New attachment] %s (#%d)' => '[%s][Pièce-jointe] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Nouveau commentaire] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Commentaire mis à jour] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Nouvelle sous-tâche] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Sous-tâche mise à jour] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Nouvelle tâche] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Tâche mise à jour] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Tâche fermée] %s (#%d)',
|
||||
'[%s][Task opened] %s (#%d)' => '[%s][Tâche ouverte] %s (#%d)',
|
||||
'[%s][Due tasks]' => '[%s][Tâches expirées]',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notification',
|
||||
'I want to receive notifications only for those projects:' => 'Je souhaite reçevoir les notifications uniquement pour les projets sélectionnés :',
|
||||
);
|
||||
|
||||
@@ -402,4 +402,31 @@ return array(
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
);
|
||||
|
||||
@@ -404,4 +404,31 @@ return array(
|
||||
'Clone Project' => 'Clonar Projeto',
|
||||
'Project cloned successfully.' => 'Projeto clonado com sucesso.',
|
||||
'Unable to clone this project.' => 'Impossível clonar este projeto.',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
);
|
||||
|
||||
@@ -401,4 +401,31 @@ return array(
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
);
|
||||
|
||||
@@ -407,4 +407,30 @@ return array(
|
||||
// 'Clone Project' => '',
|
||||
// 'Project cloned successfully.' => '',
|
||||
// 'Unable to clone this project.' => '',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
// '[%s][New subtask] %s (#%d)' => '',
|
||||
// '[%s][Subtask updated] %s (#%d)' => '',
|
||||
// '[%s][New task] %s (#%d)' => '',
|
||||
// '[%s][Task updated] %s (#%d)' => '',
|
||||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
);
|
||||
|
||||
@@ -33,7 +33,7 @@ class Acl extends Base
|
||||
'board' => array('index', 'show', 'assign', 'assigntask', 'save', 'check'),
|
||||
'project' => array('tasks', 'index', 'forbidden', 'search'),
|
||||
'user' => array('index', 'edit', 'update', 'forbidden', 'logout', 'index', 'unlinkgoogle', 'unlinkgithub'),
|
||||
'config' => array('index', 'removeremembermetoken'),
|
||||
'config' => array('index', 'removeremembermetoken', 'notifications'),
|
||||
'comment' => array('create', 'save', 'confirm', 'remove', 'update', 'edit', 'forbidden'),
|
||||
'file' => array('create', 'save', 'download', 'confirm', 'remove', 'open', 'image'),
|
||||
'subtask' => array('create', 'save', 'edit', 'update', 'confirm', 'remove'),
|
||||
|
||||
@@ -224,25 +224,25 @@ class Action extends Base
|
||||
switch ($name) {
|
||||
case 'TaskClose':
|
||||
$className = '\Action\TaskClose';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
return new $className($project_id, new Task($this->registry));
|
||||
case 'TaskAssignCurrentUser':
|
||||
$className = '\Action\TaskAssignCurrentUser';
|
||||
return new $className($project_id, new Task($this->db, $this->event), new Acl($this->db, $this->event));
|
||||
return new $className($project_id, new Task($this->registry), new Acl($this->registry));
|
||||
case 'TaskAssignSpecificUser':
|
||||
$className = '\Action\TaskAssignSpecificUser';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
return new $className($project_id, new Task($this->registry));
|
||||
case 'TaskDuplicateAnotherProject':
|
||||
$className = '\Action\TaskDuplicateAnotherProject';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
return new $className($project_id, new Task($this->registry));
|
||||
case 'TaskAssignColorUser':
|
||||
$className = '\Action\TaskAssignColorUser';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
return new $className($project_id, new Task($this->registry));
|
||||
case 'TaskAssignColorCategory':
|
||||
$className = '\Action\TaskAssignColorCategory';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
return new $className($project_id, new Task($this->registry));
|
||||
case 'TaskAssignCategoryColor':
|
||||
$className = '\Action\TaskAssignCategoryColor';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
return new $className($project_id, new Task($this->registry));
|
||||
default:
|
||||
throw new LogicException('Action not found: '.$name);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ require __DIR__.'/../../vendor/SimpleValidator/Validators/Email.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Numeric.php';
|
||||
|
||||
use Core\Event;
|
||||
use Core\Tool;
|
||||
use Core\Registry;
|
||||
use PicoDb\Database;
|
||||
|
||||
/**
|
||||
@@ -24,6 +26,21 @@ use PicoDb\Database;
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Action $action
|
||||
* @property \Model\Board $board
|
||||
* @property \Model\Category $category
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\File $file
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Ldap $ldap
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\SubTask $subTask
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\User $user
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
@@ -43,16 +60,36 @@ abstract class Base
|
||||
*/
|
||||
protected $event;
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Core\Registry
|
||||
*/
|
||||
protected $registry;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoDb\Database $db Database instance
|
||||
* @param \Core\Event $event Event dispatcher instance
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
*/
|
||||
public function __construct(Database $db, Event $event)
|
||||
public function __construct(Registry $registry)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->event = $event;
|
||||
$this->registry = $registry;
|
||||
$this->db = $this->registry->shared('db');
|
||||
$this->event = $this->registry->shared('event');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load automatically models
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Model name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,17 +24,18 @@ class Board extends Base
|
||||
* Save task positions for each column
|
||||
*
|
||||
* @access public
|
||||
* @param array $values [['task_id' => X, 'column_id' => X, 'position' => X], ...]
|
||||
* @param array $positions [['task_id' => X, 'column_id' => X, 'position' => X], ...]
|
||||
* @param integer $selected_task_id The selected task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function saveTasksPosition(array $values)
|
||||
public function saveTasksPosition(array $positions, $selected_task_id)
|
||||
{
|
||||
$taskModel = new Task($this->db, $this->event);
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
foreach ($values as $value) {
|
||||
if (! $taskModel->move($value['task_id'], $value['column_id'], $value['position'])) {
|
||||
foreach ($positions as $value) {
|
||||
|
||||
// We trigger events only for the selected task
|
||||
if (! $this->task->move($value['task_id'], $value['column_id'], $value['position'], $value['task_id'] == $selected_task_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
@@ -201,8 +202,7 @@ class Board extends Base
|
||||
$filters[] = array('column' => 'project_id', 'operator' => 'eq', 'value' => $project_id);
|
||||
$filters[] = array('column' => 'is_active', 'operator' => 'eq', 'value' => Task::STATUS_OPEN);
|
||||
|
||||
$taskModel = new Task($this->db, $this->event);
|
||||
$tasks = $taskModel->find($filters);
|
||||
$tasks = $this->task->find($filters);
|
||||
|
||||
foreach ($columns as &$column) {
|
||||
|
||||
|
||||
@@ -20,6 +20,14 @@ class Comment extends Base
|
||||
*/
|
||||
const TABLE = 'comments';
|
||||
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EVENT_UPDATE = 'comment.update';
|
||||
const EVENT_CREATE = 'comment.create';
|
||||
|
||||
/**
|
||||
* Get all comments for a given task
|
||||
*
|
||||
@@ -95,7 +103,14 @@ class Comment extends Base
|
||||
{
|
||||
$values['date'] = time();
|
||||
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
if ($this->db->table(self::TABLE)->save($values)) {
|
||||
|
||||
$values['id'] = $this->db->getConnection()->getLastId();
|
||||
$this->event->trigger(self::EVENT_CREATE, $values);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,10 +122,14 @@ class Comment extends Base
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
return $this->db
|
||||
$result = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $values['id'])
|
||||
->update(array('comment' => $values['comment']));
|
||||
|
||||
$this->event->trigger(self::EVENT_UPDATE, $values);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,13 @@ class File extends Base
|
||||
*/
|
||||
const BASE_PATH = 'data/files/';
|
||||
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EVENT_CREATE = 'file.create';
|
||||
|
||||
/**
|
||||
* Get a file by the id
|
||||
*
|
||||
@@ -82,6 +89,8 @@ class File extends Base
|
||||
*/
|
||||
public function create($task_id, $name, $path, $is_image)
|
||||
{
|
||||
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id, 'name' => $name));
|
||||
|
||||
return $this->db->table(self::TABLE)->save(array(
|
||||
'task_id' => $task_id,
|
||||
'name' => $name,
|
||||
|
||||
@@ -26,22 +26,19 @@ class GitHub extends Base
|
||||
*/
|
||||
public function authenticate($github_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
$user = $userModel->getByGitHubId($github_id);
|
||||
$user = $this->user->getByGitHubId($github_id);
|
||||
|
||||
if ($user) {
|
||||
|
||||
// Create the user session
|
||||
$userModel->updateSession($user);
|
||||
$this->user->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$lastLogin = new LastLogin($this->db, $this->event);
|
||||
$lastLogin->create(
|
||||
$this->lastLogin->create(
|
||||
LastLogin::AUTH_GITHUB,
|
||||
$user['id'],
|
||||
$userModel->getIpAddress(),
|
||||
$userModel->getUserAgent()
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
@@ -59,9 +56,7 @@ class GitHub extends Base
|
||||
*/
|
||||
public function unlink($user_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
return $this->user->update(array(
|
||||
'id' => $user_id,
|
||||
'github_id' => '',
|
||||
));
|
||||
@@ -78,9 +73,7 @@ class GitHub extends Base
|
||||
*/
|
||||
public function updateUser($user_id, array $profile)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
return $this->user->update(array(
|
||||
'id' => $user_id,
|
||||
'github_id' => $profile['id'],
|
||||
'email' => $profile['email'],
|
||||
@@ -141,7 +134,7 @@ class GitHub extends Base
|
||||
try {
|
||||
$gitHubService = $this->getService();
|
||||
$gitHubService->requestAccessToken($code);
|
||||
|
||||
|
||||
return json_decode($gitHubService->request('user'), true);
|
||||
}
|
||||
catch (TokenResponseException $e) {
|
||||
@@ -150,7 +143,7 @@ class GitHub extends Base
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Revokes this user's GitHub tokens for Kanboard
|
||||
*
|
||||
|
||||
@@ -27,21 +27,19 @@ class Google extends Base
|
||||
*/
|
||||
public function authenticate($google_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$user = $userModel->getByGoogleId($google_id);
|
||||
$user = $this->user->getByGoogleId($google_id);
|
||||
|
||||
if ($user) {
|
||||
|
||||
// Create the user session
|
||||
$userModel->updateSession($user);
|
||||
$this->user->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$lastLogin = new LastLogin($this->db, $this->event);
|
||||
$lastLogin->create(
|
||||
$this->lastLogin->create(
|
||||
LastLogin::AUTH_GOOGLE,
|
||||
$user['id'],
|
||||
$userModel->getIpAddress(),
|
||||
$userModel->getUserAgent()
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
@@ -59,9 +57,7 @@ class Google extends Base
|
||||
*/
|
||||
public function unlink($user_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
return $this->user->update(array(
|
||||
'id' => $user_id,
|
||||
'google_id' => '',
|
||||
));
|
||||
@@ -77,9 +73,7 @@ class Google extends Base
|
||||
*/
|
||||
public function updateUser($user_id, array $profile)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
return $this->user->update(array(
|
||||
'id' => $user_id,
|
||||
'google_id' => $profile['id'],
|
||||
'email' => $profile['email'],
|
||||
|
||||
@@ -73,8 +73,7 @@ class Ldap extends Base
|
||||
*/
|
||||
public function create($username, $name, $email)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$user = $userModel->getByUsername($username);
|
||||
$user = $this->user->getByUsername($username);
|
||||
|
||||
// There is an existing user account
|
||||
if ($user) {
|
||||
|
||||
215
app/Model/Notification.php
Normal file
215
app/Model/Notification.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use Core\Template;
|
||||
use Event\TaskNotificationListener;
|
||||
use Event\CommentNotificationListener;
|
||||
use Event\FileNotificationListener;
|
||||
use Event\SubTaskNotificationListener;
|
||||
use Swift_Message;
|
||||
use Swift_Mailer;
|
||||
|
||||
/**
|
||||
* Notification model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Notification extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'user_has_notifications';
|
||||
|
||||
/**
|
||||
* Get the list of users to send the notification for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getUsersList($project_id)
|
||||
{
|
||||
$users = $this->db->table(User::TABLE)
|
||||
->columns('id', 'username', 'name', 'email')
|
||||
->eq('notifications_enabled', '1')
|
||||
->neq('email', '')
|
||||
->findAll();
|
||||
|
||||
foreach ($users as $index => $user) {
|
||||
|
||||
$projects = $this->db->table(self::TABLE)
|
||||
->eq('user_id', $user['id'])
|
||||
->findAllByColumn('project_id');
|
||||
|
||||
// The user have selected only some projects
|
||||
if (! empty($projects)) {
|
||||
|
||||
// If the user didn't select this project we remove that guy from the list
|
||||
if (! in_array($project_id, $projects)) {
|
||||
unset($users[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
$this->event->attach(File::EVENT_CREATE, new FileNotificationListener($this, 'notification_file_creation'));
|
||||
|
||||
$this->event->attach(Comment::EVENT_CREATE, new CommentNotificationListener($this, 'notification_comment_creation'));
|
||||
$this->event->attach(Comment::EVENT_UPDATE, new CommentNotificationListener($this, 'notification_comment_update'));
|
||||
|
||||
$this->event->attach(SubTask::EVENT_CREATE, new SubTaskNotificationListener($this, 'notification_subtask_creation'));
|
||||
$this->event->attach(SubTask::EVENT_UPDATE, new SubTaskNotificationListener($this, 'notification_subtask_update'));
|
||||
|
||||
$this->event->attach(Task::EVENT_CREATE, new TaskNotificationListener($this, 'notification_task_creation'));
|
||||
$this->event->attach(Task::EVENT_UPDATE, new TaskNotificationListener($this, 'notification_task_update'));
|
||||
$this->event->attach(Task::EVENT_CLOSE, new TaskNotificationListener($this, 'notification_task_close'));
|
||||
$this->event->attach(Task::EVENT_OPEN, new TaskNotificationListener($this, 'notification_task_open'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the email notifications
|
||||
*
|
||||
* @access public
|
||||
* @param string $template Template name
|
||||
* @param array $users List of users
|
||||
* @param array $data Template data
|
||||
*/
|
||||
public function sendEmails($template, array $users, array $data)
|
||||
{
|
||||
$transport = $this->registry->shared('mailer');
|
||||
$mailer = Swift_Mailer::newInstance($transport);
|
||||
|
||||
$message = Swift_Message::newInstance()
|
||||
->setSubject($this->getMailSubject($template, $data))
|
||||
->setFrom(array(MAIL_FROM => 'Kanboard'))
|
||||
//->setTo(array($user['email'] => $user['name']))
|
||||
->setBody($this->getMailContent($template, $data), 'text/html');
|
||||
|
||||
foreach ($users as $user) {
|
||||
$message->setTo(array($user['email'] => $user['name'] ?: $user['username']));
|
||||
$mailer->send($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail subject for a given template name
|
||||
*
|
||||
* @access public
|
||||
* @param string $template Template name
|
||||
* @param array $data Template data
|
||||
*/
|
||||
public function getMailSubject($template, array $data)
|
||||
{
|
||||
switch ($template) {
|
||||
case 'notification_file_creation':
|
||||
return t('[%s][New attachment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
case 'notification_comment_creation':
|
||||
return t('[%s][New comment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
case 'notification_comment_update':
|
||||
return t('[%s][Comment updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
case 'notification_subtask_creation':
|
||||
return t('[%s][New subtask] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
case 'notification_subtask_update':
|
||||
return t('[%s][Subtask updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
case 'notification_task_creation':
|
||||
return t('[%s][New task] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
case 'notification_task_update':
|
||||
return t('[%s][Task updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
case 'notification_task_close':
|
||||
return t('[%s][Task closed] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
case 'notification_task_open':
|
||||
return t('[%s][Task opened] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
case 'notification_task_due':
|
||||
return t('[%s][Due tasks]', $data['project']);
|
||||
}
|
||||
|
||||
return t('[Kanboard] Notification');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail content for a given template name
|
||||
*
|
||||
* @access public
|
||||
* @param string $template Template name
|
||||
* @param array $data Template data
|
||||
*/
|
||||
public function getMailContent($template, array $data)
|
||||
{
|
||||
$tpl = new Template;
|
||||
return $tpl->load($template, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings for the given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function saveSettings($user_id, array $values)
|
||||
{
|
||||
// Delete all selected projects
|
||||
$this->db->table(self::TABLE)->eq('user_id', $user_id)->remove();
|
||||
|
||||
if (isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1) {
|
||||
|
||||
// Activate notifications
|
||||
$this->db->table(User::TABLE)->eq('id', $user_id)->update(array(
|
||||
'notifications_enabled' => '1'
|
||||
));
|
||||
|
||||
// Save selected projects
|
||||
if (! empty($values['projects'])) {
|
||||
|
||||
foreach ($values['projects'] as $project_id => $checkbox_value) {
|
||||
$this->db->table(self::TABLE)->insert(array(
|
||||
'user_id' => $user_id,
|
||||
'project_id' => $project_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Disable notifications
|
||||
$this->db->table(User::TABLE)->eq('id', $user_id)->update(array(
|
||||
'notifications_enabled' => '0'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read user settings to display the form
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function readSettings($user_id)
|
||||
{
|
||||
$values = array();
|
||||
$values['notifications_enabled'] = $this->db->table(User::TABLE)->eq('id', $user_id)->findOneColumn('notifications_enabled');
|
||||
|
||||
$projects = $this->db->table(self::TABLE)->eq('user_id', $user_id)->findAllByColumn('project_id');
|
||||
|
||||
foreach ($projects as $project_id) {
|
||||
$values['project_'.$project_id] = true;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
@@ -55,10 +55,9 @@ class Project extends Base
|
||||
public function getUsersList($project_id, $prepend_unassigned = true, $prepend_everybody = false)
|
||||
{
|
||||
$allowed_users = $this->getAllowedUsers($project_id);
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
if (empty($allowed_users)) {
|
||||
$allowed_users = $userModel->getList();
|
||||
$allowed_users = $this->user->getList();
|
||||
}
|
||||
|
||||
if ($prepend_unassigned) {
|
||||
@@ -103,8 +102,7 @@ class Project extends Base
|
||||
'not_allowed' => array(),
|
||||
);
|
||||
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$all_users = $userModel->getList();
|
||||
$all_users = $this->user->getList();
|
||||
|
||||
$users['allowed'] = $this->getAllowedUsers($project_id);
|
||||
|
||||
@@ -253,27 +251,23 @@ class Project extends Base
|
||||
->asc('name')
|
||||
->findAll();
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
$taskModel = new Task($this->db, $this->event);
|
||||
$aclModel = new Acl($this->db, $this->event);
|
||||
|
||||
foreach ($projects as $pkey => &$project) {
|
||||
|
||||
if ($check_permissions && ! $this->isUserAllowed($project['id'], $aclModel->getUserId())) {
|
||||
if ($check_permissions && ! $this->isUserAllowed($project['id'], $this->acl->getUserId())) {
|
||||
unset($projects[$pkey]);
|
||||
}
|
||||
else {
|
||||
|
||||
$columns = $boardModel->getcolumns($project['id']);
|
||||
$columns = $this->board->getcolumns($project['id']);
|
||||
$project['nb_active_tasks'] = 0;
|
||||
|
||||
foreach ($columns as &$column) {
|
||||
$column['nb_active_tasks'] = $taskModel->countByColumnId($project['id'], $column['id']);
|
||||
$column['nb_active_tasks'] = $this->task->countByColumnId($project['id'], $column['id']);
|
||||
$project['nb_active_tasks'] += $column['nb_active_tasks'];
|
||||
}
|
||||
|
||||
$project['columns'] = $columns;
|
||||
$project['nb_tasks'] = $taskModel->countByProjectId($project['id']);
|
||||
$project['nb_tasks'] = $this->task->countByProjectId($project['id']);
|
||||
$project['nb_inactive_tasks'] = $project['nb_tasks'] - $project['nb_active_tasks'];
|
||||
}
|
||||
}
|
||||
@@ -416,9 +410,8 @@ class Project extends Base
|
||||
*/
|
||||
public function copyBoardFromAnotherProject($project_from, $project_to)
|
||||
{
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
$columns = $this->db->table(Board::TABLE)->eq('project_id', $project_from)->asc('position')->findAllByColumn('title');
|
||||
return $boardModel->create($project_to, $columns);
|
||||
return $this->board->create($project_to, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,8 +424,7 @@ class Project extends Base
|
||||
*/
|
||||
public function copyCategoriesFromAnotherProject($project_from, $project_to)
|
||||
{
|
||||
$categoryModel = new Category($this->db, $this->event);
|
||||
$categoriesTemplate = $categoryModel->getAll($project_from);
|
||||
$categoriesTemplate = $this->category->getAll($project_from);
|
||||
|
||||
foreach ($categoriesTemplate as $category) {
|
||||
|
||||
@@ -478,8 +470,7 @@ class Project extends Base
|
||||
*/
|
||||
public function copyActionsFromAnotherProject($project_from, $project_to)
|
||||
{
|
||||
$actionModel = new Action($this->db, $this->event);
|
||||
$actionTemplate = $actionModel->getAllByProject($project_from);
|
||||
$actionTemplate = $this->action->getAllByProject($project_from);
|
||||
|
||||
foreach ($actionTemplate as $action) {
|
||||
|
||||
@@ -522,13 +513,11 @@ class Project extends Base
|
||||
case 'project_id':
|
||||
return $project_to;
|
||||
case 'category_id':
|
||||
$categoryModel = new Category($this->db, $this->event);
|
||||
$categoryTemplate = $categoryModel->getById($param['value']);
|
||||
$categoryTemplate = $this->category->getById($param['value']);
|
||||
$categoryFromNewProject = $this->db->table(Category::TABLE)->eq('project_id', $project_to)->eq('name', $categoryTemplate['name'])->findOne();
|
||||
return $categoryFromNewProject['id'];
|
||||
case 'column_id':
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
$boardTemplate = $boardModel->getColumn($param['value']);
|
||||
$boardTemplate = $this->board->getColumn($param['value']);
|
||||
$boardFromNewProject = $this->db->table(Board::TABLE)->eq('project_id', $project_to)->eq('title', $boardTemplate['title'])->findOne();
|
||||
return $boardFromNewProject['id'];
|
||||
default:
|
||||
@@ -603,8 +592,7 @@ class Project extends Base
|
||||
|
||||
$project_id = $this->db->getConnection()->getLastId();
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
$boardModel->create($project_id, array(
|
||||
$this->board->create($project_id, array(
|
||||
t('Backlog'),
|
||||
t('Ready'),
|
||||
t('Work in progress'),
|
||||
|
||||
@@ -92,11 +92,8 @@ class RememberMe extends Base
|
||||
);
|
||||
|
||||
// Create the session
|
||||
$user = new User($this->db, $this->event);
|
||||
$acl = new Acl($this->db, $this->event);
|
||||
|
||||
$user->updateSession($user->getById($record['user_id']));
|
||||
$acl->isRememberMe(true);
|
||||
$this->user->updateSession($this->user->getById($record['user_id']));
|
||||
$this->acl->isRememberMe(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -23,24 +23,22 @@ class ReverseProxyAuth extends Base
|
||||
if (isset($_SERVER[REVERSE_PROXY_USER_HEADER])) {
|
||||
|
||||
$login = $_SERVER[REVERSE_PROXY_USER_HEADER];
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$user = $userModel->getByUsername($login);
|
||||
$user = $this->user->getByUsername($login);
|
||||
|
||||
if (! $user) {
|
||||
$this->createUser($login);
|
||||
$user = $userModel->getByUsername($login);
|
||||
$user = $this->user->getByUsername($login);
|
||||
}
|
||||
|
||||
// Create the user session
|
||||
$userModel->updateSession($user);
|
||||
$this->user->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$lastLogin = new LastLogin($this->db, $this->event);
|
||||
$lastLogin->create(
|
||||
$this->lastLogin->create(
|
||||
LastLogin::AUTH_REVERSE_PROXY,
|
||||
$user['id'],
|
||||
$userModel->getIpAddress(),
|
||||
$userModel->getUserAgent()
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
@@ -58,9 +56,7 @@ class ReverseProxyAuth extends Base
|
||||
*/
|
||||
private function createUser($login)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->create(array(
|
||||
return $this->user->create(array(
|
||||
'email' => strpos($login, '@') !== false ? $login : '',
|
||||
'username' => $login,
|
||||
'is_admin' => REVERSE_PROXY_DEFAULT_ADMIN === $login,
|
||||
|
||||
@@ -41,6 +41,14 @@ class SubTask extends Base
|
||||
*/
|
||||
const STATUS_TODO = 0;
|
||||
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EVENT_UPDATE = 'subtask.update';
|
||||
const EVENT_CREATE = 'subtask.create';
|
||||
|
||||
/**
|
||||
* Get available status
|
||||
*
|
||||
@@ -88,10 +96,27 @@ class SubTask extends Base
|
||||
*
|
||||
* @access public
|
||||
* @param integer $subtask_id Subtask id
|
||||
* @param bool $more Fetch more data
|
||||
* @return array
|
||||
*/
|
||||
public function getById($subtask_id)
|
||||
public function getById($subtask_id, $more = false)
|
||||
{
|
||||
if ($more) {
|
||||
|
||||
$subtask = $this->db->table(self::TABLE)
|
||||
->eq(self::TABLE.'.id', $subtask_id)
|
||||
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->findOne();
|
||||
|
||||
if ($subtask) {
|
||||
$status = $this->getStatusList();
|
||||
$subtask['status_name'] = $status[$subtask['status']];
|
||||
}
|
||||
|
||||
return $subtask;
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('id', $subtask_id)->findOne();
|
||||
}
|
||||
|
||||
@@ -116,7 +141,14 @@ class SubTask extends Base
|
||||
$values['time_spent'] = 0;
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
$result = $this->db->table(self::TABLE)->save($values);
|
||||
|
||||
if ($result) {
|
||||
$values['id'] = $this->db->getConnection()->getLastId();
|
||||
$this->event->trigger(self::EVENT_CREATE, $values);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +168,13 @@ class SubTask extends Base
|
||||
$values['time_spent'] = 0;
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
|
||||
|
||||
if ($result) {
|
||||
$this->event->trigger(self::EVENT_UPDATE, $values);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,6 +62,35 @@ class Task extends Base
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of due tasks for all projects
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getTasksDue()
|
||||
{
|
||||
$tasks = $this->db->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.id',
|
||||
self::TABLE.'.title',
|
||||
self::TABLE.'.date_due',
|
||||
self::TABLE.'.project_id',
|
||||
Project::TABLE.'.name AS project_name',
|
||||
User::TABLE.'.username AS assignee_username',
|
||||
User::TABLE.'.name AS assignee_name'
|
||||
)
|
||||
->join(Project::TABLE, 'id', 'project_id')
|
||||
->join(User::TABLE, 'id', 'owner_id')
|
||||
->eq(Project::TABLE.'.is_active', 1)
|
||||
->eq(self::TABLE.'.is_active', 1)
|
||||
->neq(self::TABLE.'.date_due', '')
|
||||
->lte(self::TABLE.'.date_due', mktime(23, 59, 59))
|
||||
->findAll();
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one task
|
||||
*
|
||||
@@ -182,7 +211,7 @@ class Task extends Base
|
||||
'tasks.category_id',
|
||||
'users.username'
|
||||
)
|
||||
->join('users', 'id', 'owner_id');
|
||||
->join(User::TABLE, 'id', 'owner_id');
|
||||
|
||||
foreach ($filters as $key => $filter) {
|
||||
|
||||
@@ -282,8 +311,6 @@ class Task extends Base
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
|
||||
// Get the original task
|
||||
$task = $this->getById($task_id);
|
||||
|
||||
@@ -296,7 +323,7 @@ class Task extends Base
|
||||
$task['owner_id'] = 0;
|
||||
$task['category_id'] = 0;
|
||||
$task['is_active'] = 1;
|
||||
$task['column_id'] = $boardModel->getFirstColumn($project_id);
|
||||
$task['column_id'] = $this->board->getFirstColumn($project_id);
|
||||
$task['project_id'] = $project_id;
|
||||
$task['position'] = $this->countByColumnId($task['project_id'], $task['column_id']);
|
||||
|
||||
@@ -317,6 +344,32 @@ class Task extends Base
|
||||
return $task_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare data before task creation or modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function prepare(array &$values)
|
||||
{
|
||||
if (isset($values['another_task'])) {
|
||||
unset($values['another_task']);
|
||||
}
|
||||
|
||||
if (! empty($values['date_due']) && ! is_numeric($values['date_due'])) {
|
||||
$values['date_due'] = $this->parseDate($values['date_due']);
|
||||
}
|
||||
|
||||
// Force integer fields at 0 (for Postgresql)
|
||||
if (isset($values['date_due']) && empty($values['date_due'])) {
|
||||
$values['date_due'] = 0;
|
||||
}
|
||||
|
||||
if (isset($values['score']) && empty($values['score'])) {
|
||||
$values['score'] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a task
|
||||
*
|
||||
@@ -329,21 +382,7 @@ class Task extends Base
|
||||
$this->db->startTransaction();
|
||||
|
||||
// Prepare data
|
||||
if (isset($values['another_task'])) {
|
||||
unset($values['another_task']);
|
||||
}
|
||||
|
||||
if (! empty($values['date_due']) && ! is_numeric($values['date_due'])) {
|
||||
$values['date_due'] = $this->parseDate($values['date_due']);
|
||||
}
|
||||
else {
|
||||
$values['date_due'] = 0;
|
||||
}
|
||||
|
||||
if (empty($values['score'])) {
|
||||
$values['score'] = 0;
|
||||
}
|
||||
|
||||
$this->prepare($values);
|
||||
$values['date_creation'] = time();
|
||||
$values['position'] = $this->countByColumnId($values['project_id'], $values['column_id']);
|
||||
|
||||
@@ -368,31 +407,21 @@ class Task extends Base
|
||||
* Update a task
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @param array $values Form values
|
||||
* @param boolean $trigger_events Flag to trigger events
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values)
|
||||
public function update(array $values, $trigger_events = true)
|
||||
{
|
||||
// Prepare data
|
||||
if (! empty($values['date_due']) && ! is_numeric($values['date_due'])) {
|
||||
$values['date_due'] = $this->parseDate($values['date_due']);
|
||||
}
|
||||
|
||||
// Force integer fields at 0 (for Postgresql)
|
||||
if (isset($values['date_due']) && empty($values['date_due'])) {
|
||||
$values['date_due'] = 0;
|
||||
}
|
||||
|
||||
if (isset($values['score']) && empty($values['score'])) {
|
||||
$values['score'] = 0;
|
||||
}
|
||||
|
||||
// Fetch original task
|
||||
$original_task = $this->getById($values['id']);
|
||||
|
||||
if ($original_task === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare data
|
||||
$this->prepare($values);
|
||||
$updated_task = $values;
|
||||
$updated_task['date_modification'] = time();
|
||||
unset($updated_task['id']);
|
||||
@@ -400,31 +429,42 @@ class Task extends Base
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($updated_task);
|
||||
|
||||
// Trigger events
|
||||
if ($result) {
|
||||
|
||||
$events = array(
|
||||
self::EVENT_CREATE_UPDATE,
|
||||
self::EVENT_UPDATE,
|
||||
);
|
||||
|
||||
if (isset($values['column_id']) && $original_task['column_id'] != $values['column_id']) {
|
||||
$events[] = self::EVENT_MOVE_COLUMN;
|
||||
}
|
||||
else if (isset($values['position']) && $original_task['position'] != $values['position']) {
|
||||
$events[] = self::EVENT_MOVE_POSITION;
|
||||
}
|
||||
|
||||
$event_data = array_merge($original_task, $values);
|
||||
$event_data['task_id'] = $original_task['id'];
|
||||
|
||||
foreach ($events as $event) {
|
||||
$this->event->trigger($event, $event_data);
|
||||
}
|
||||
if ($result && $trigger_events) {
|
||||
$this->triggerUpdateEvents($original_task, $updated_task);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger events for task modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $original_task Original task data
|
||||
* @param array $updated_task Updated task data
|
||||
*/
|
||||
public function triggerUpdateEvents(array $original_task, array $updated_task)
|
||||
{
|
||||
$events = array(
|
||||
self::EVENT_CREATE_UPDATE,
|
||||
self::EVENT_UPDATE,
|
||||
);
|
||||
|
||||
if (isset($updated_task['column_id']) && $original_task['column_id'] != $updated_task['column_id']) {
|
||||
$events[] = self::EVENT_MOVE_COLUMN;
|
||||
}
|
||||
else if (isset($updated_task['position']) && $original_task['position'] != $updated_task['position']) {
|
||||
$events[] = self::EVENT_MOVE_POSITION;
|
||||
}
|
||||
|
||||
$event_data = array_merge($original_task, $updated_task);
|
||||
$event_data['task_id'] = $original_task['id'];
|
||||
|
||||
foreach ($events as $event) {
|
||||
$this->event->trigger($event, $event_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a task closed
|
||||
*
|
||||
@@ -482,8 +522,7 @@ class Task extends Base
|
||||
*/
|
||||
public function remove($task_id)
|
||||
{
|
||||
$file = new File($this->db, $this->event);
|
||||
$file->removeAll($task_id);
|
||||
$this->file->removeAll($task_id);
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('id', $task_id)->remove();
|
||||
}
|
||||
@@ -492,20 +531,23 @@ class Task extends Base
|
||||
* Move a task to another column or to another position
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $column_id Column id
|
||||
* @param integer $position Position (must be greater than 1)
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $column_id Column id
|
||||
* @param integer $position Position (must be greater than 1)
|
||||
* @param boolean $trigger_events Flag to trigger events
|
||||
* @return boolean
|
||||
*/
|
||||
public function move($task_id, $column_id, $position)
|
||||
public function move($task_id, $column_id, $position, $trigger_events = true)
|
||||
{
|
||||
$this->event->clearTriggeredEvents();
|
||||
|
||||
return $this->update(array(
|
||||
$values = array(
|
||||
'id' => $task_id,
|
||||
'column_id' => $column_id,
|
||||
'position' => $position,
|
||||
));
|
||||
);
|
||||
|
||||
return $this->update($values, $trigger_events);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -340,8 +340,7 @@ class User extends Base
|
||||
$this->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$lastLogin = new LastLogin($this->db, $this->event);
|
||||
$lastLogin->create(
|
||||
$this->lastLogin->create(
|
||||
$method,
|
||||
$user['id'],
|
||||
$this->getIpAddress(),
|
||||
@@ -350,9 +349,8 @@ class User extends Base
|
||||
|
||||
// Setup the remember me feature
|
||||
if (! empty($values['remember_me'])) {
|
||||
$rememberMe = new RememberMe($this->db, $this->event);
|
||||
$credentials = $rememberMe->create($user['id'], $this->getIpAddress(), $this->getUserAgent());
|
||||
$rememberMe->writeCookie($credentials['token'], $credentials['sequence'], $credentials['expiration']);
|
||||
$credentials = $this->rememberMe->create($user['id'], $this->getIpAddress(), $this->getUserAgent());
|
||||
$this->rememberMe->writeCookie($credentials['token'], $credentials['sequence'], $credentials['expiration']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -384,8 +382,7 @@ class User extends Base
|
||||
|
||||
// LDAP authentication
|
||||
if (! $authenticated && LDAP_AUTH) {
|
||||
$ldap = new Ldap($this->db, $this->event);
|
||||
$authenticated = $ldap->authenticate($username, $password);
|
||||
$authenticated = $this->ldap->authenticate($username, $password);
|
||||
$method = LastLogin::AUTH_LDAP;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,11 +64,9 @@ class Webhook extends Base
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
$config = new Config($this->db, $this->event);
|
||||
|
||||
$this->url_task_creation = $config->get('webhooks_url_task_creation');
|
||||
$this->url_task_modification = $config->get('webhooks_url_task_modification');
|
||||
$this->token = $config->get('webhooks_token');
|
||||
$this->url_task_creation = $this->config->get('webhooks_url_task_creation');
|
||||
$this->url_task_modification = $this->config->get('webhooks_url_task_modification');
|
||||
$this->token = $this->config->get('webhooks_token');
|
||||
|
||||
if ($this->url_task_creation) {
|
||||
$this->attachCreateEvents();
|
||||
|
||||
@@ -4,7 +4,22 @@ namespace Schema;
|
||||
|
||||
use Core\Security;
|
||||
|
||||
const VERSION = 22;
|
||||
const VERSION = 23;
|
||||
|
||||
function version_23($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN notifications_enabled TINYINT(1) DEFAULT '0'");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE user_has_notifications (
|
||||
user_id INT,
|
||||
project_id INT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
UNIQUE(project_id, user_id)
|
||||
);
|
||||
");
|
||||
}
|
||||
|
||||
function version_22($pdo)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,22 @@ namespace Schema;
|
||||
|
||||
use Core\Security;
|
||||
|
||||
const VERSION = 3;
|
||||
const VERSION = 4;
|
||||
|
||||
function version_4($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN notifications_enabled BOOLEAN DEFAULT '0'");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE user_has_notifications (
|
||||
user_id INTEGER,
|
||||
project_id INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
UNIQUE(project_id, user_id)
|
||||
);
|
||||
");
|
||||
}
|
||||
|
||||
function version_3($pdo)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,22 @@ namespace Schema;
|
||||
|
||||
use Core\Security;
|
||||
|
||||
const VERSION = 22;
|
||||
const VERSION = 23;
|
||||
|
||||
function version_23($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN notifications_enabled INTEGER DEFAULT '0'");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE user_has_notifications (
|
||||
user_id INTEGER,
|
||||
project_id INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
UNIQUE(project_id, user_id)
|
||||
);
|
||||
");
|
||||
}
|
||||
|
||||
function version_22($pdo)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
<?= Helper\form_csrf() ?>
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('task_id', $values) ?>
|
||||
<?= Helper\form_textarea('comment', $values, $errors, array('autofocus', 'required', 'placeholder="'.t('Leave a comment').'"'), 'comment-textarea') ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
|
||||
@@ -31,14 +31,25 @@
|
||||
<div class="page-header">
|
||||
<h2><?= t('User settings') ?></h2>
|
||||
</div>
|
||||
<section class="settings">
|
||||
<ul>
|
||||
<li>
|
||||
<strong><?= t('My default project:') ?> </strong>
|
||||
<?= (isset($user['default_project_id']) && isset($projects[$user['default_project_id']])) ? Helper\escape($projects[$user['default_project_id']]) : t('None') ?>,
|
||||
<a href="?controller=user&action=edit&user_id=<?= $user['id'] ?>"><?= t('edit') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
<section>
|
||||
<h3 id="notifications"><?= t('Email notifications') ?></h3>
|
||||
<form method="post" action="?controller=config&action=notifications" autocomplete="off">
|
||||
|
||||
<?= Helper\form_csrf() ?>
|
||||
|
||||
<?= Helper\form_checkbox('notifications_enabled', t('Enable email notifications'), '1', $notifications['notifications_enabled'] == 1) ?><br/>
|
||||
|
||||
<p><?= t('I want to receive notifications only for those projects:') ?><br/><br/></p>
|
||||
|
||||
<div class="form-checkbox-group">
|
||||
<?php foreach ($user_projects as $project_id => $project_name): ?>
|
||||
<?= Helper\form_checkbox('projects['.$project_id.']', $project_name, '1', isset($notifications['project_'.$project_id])) ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<?php if ($user['is_admin']): ?>
|
||||
|
||||
8
app/Templates/notification_comment_creation.php
Normal file
8
app/Templates/notification_comment_creation.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<h2><?= Helper\escape($task['title']) ?> (#<?= $task['id'] ?>)</h2>
|
||||
|
||||
<h3><?= t('New comment posted by %s', $comment['username']) ?></h3>
|
||||
|
||||
<?= Helper\parse($comment['comment']) ?>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
8
app/Templates/notification_comment_update.php
Normal file
8
app/Templates/notification_comment_update.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<h2><?= Helper\escape($task['title']) ?> (#<?= $task['id'] ?>)</h2>
|
||||
|
||||
<h3><?= t('Comment updated') ?></h3>
|
||||
|
||||
<?= Helper\parse($comment['comment']) ?>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
6
app/Templates/notification_file_creation.php
Normal file
6
app/Templates/notification_file_creation.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<h2><?= Helper\escape($task['title']) ?> (#<?= $task['id'] ?>)</h2>
|
||||
|
||||
<h3><?= t('New attachment added "%s"', $file['name']) ?></h3>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
18
app/Templates/notification_subtask_creation.php
Normal file
18
app/Templates/notification_subtask_creation.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<h2><?= Helper\escape($task['title']) ?> (#<?= $task['id'] ?>)</h2>
|
||||
|
||||
<h3><?= t('New sub-task') ?></h3>
|
||||
|
||||
<ul>
|
||||
<li><?= t('Title:') ?> <?= Helper\escape($subtask['title']) ?></li>
|
||||
<li><?= t('Status:') ?> <?= Helper\escape($subtask['status_name']) ?></li>
|
||||
<li><?= t('Assignee:') ?> <?= Helper\escape($subtask['name'] ?: $subtask['username'] ?: '?') ?></li>
|
||||
<li>
|
||||
<?= t('Time tracking:') ?>
|
||||
<?php if (! empty($subtask['time_estimated'])): ?>
|
||||
<strong><?= Helper\escape($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
22
app/Templates/notification_subtask_update.php
Normal file
22
app/Templates/notification_subtask_update.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<h2><?= Helper\escape($task['title']) ?> (#<?= $task['id'] ?>)</h2>
|
||||
|
||||
<h3><?= t('Sub-task updated') ?></h3>
|
||||
|
||||
<ul>
|
||||
<li><?= t('Title:') ?> <?= Helper\escape($subtask['title']) ?></li>
|
||||
<li><?= t('Status:') ?> <?= Helper\escape($subtask['status_name']) ?></li>
|
||||
<li><?= t('Assignee:') ?> <?= Helper\escape($subtask['name'] ?: $subtask['username'] ?: '?') ?></li>
|
||||
<li>
|
||||
<?= t('Time tracking:') ?>
|
||||
<?php if (! empty($subtask['time_spent'])): ?>
|
||||
<strong><?= Helper\escape($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($subtask['time_estimated'])): ?>
|
||||
<strong><?= Helper\escape($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
6
app/Templates/notification_task_close.php
Normal file
6
app/Templates/notification_task_close.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<h2><?= Helper\escape($task['title']) ?> (#<?= $task['id'] ?>)</h2>
|
||||
|
||||
<p><?= t('The task #%d have been closed.', $task['id']) ?></p>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
44
app/Templates/notification_task_creation.php
Normal file
44
app/Templates/notification_task_creation.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<h2><?= Helper\escape($task['title']) ?> (#<?= $task['id'] ?>)</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<?= dt('Created on %B %e, %Y at %k:%M %p', $task['date_creation']) ?>
|
||||
</li>
|
||||
<?php if ($task['date_due']): ?>
|
||||
<li>
|
||||
<strong><?= dt('Must be done before %B %e, %Y', $task['date_due']) ?></strong>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($task['creator_username']): ?>
|
||||
<li>
|
||||
<?= t('Created by %s', $task['creator_username']) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<strong>
|
||||
<?php if ($task['assignee_username']): ?>
|
||||
<?= t('Assigned to %s', $task['assignee_username']) ?>
|
||||
<?php else: ?>
|
||||
<?= t('There is nobody assigned') ?>
|
||||
<?php endif ?>
|
||||
</strong>
|
||||
</li>
|
||||
<li>
|
||||
<?= t('Column on the board:') ?>
|
||||
<strong><?= Helper\escape($task['column_title']) ?></strong>
|
||||
</li>
|
||||
<li><?= t('Task position:').' '.Helper\escape($task['position']) ?></li>
|
||||
<?php if ($task['category_name']): ?>
|
||||
<li>
|
||||
<?= t('Category:') ?> <strong><?= Helper\escape($task['category_name']) ?></strong>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
|
||||
<?php if (! empty($task['description'])): ?>
|
||||
<h2><?= t('Description') ?></h2>
|
||||
<?= Helper\parse($task['description']) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
10
app/Templates/notification_task_due.php
Normal file
10
app/Templates/notification_task_due.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<h2><?= t('List of due tasks for the project "%s"', $project) ?></h2>
|
||||
|
||||
<ul>
|
||||
<?php foreach ($tasks as $task): ?>
|
||||
<li>(<strong>#<?= $task['id'] ?></strong>) <?= Helper\escape($task['title']) ?> (<strong><?= t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) ?></strong>)</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
6
app/Templates/notification_task_open.php
Normal file
6
app/Templates/notification_task_open.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<h2><?= Helper\escape($task['title']) ?> (#<?= $task['id'] ?>)</h2>
|
||||
|
||||
<p><?= t('The task #%d have been opened.', $task['id']) ?></p>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
44
app/Templates/notification_task_update.php
Normal file
44
app/Templates/notification_task_update.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<h2><?= Helper\escape($task['title']) ?> (#<?= $task['id'] ?>)</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<?= dt('Created on %B %e, %Y at %k:%M %p', $task['date_creation']) ?>
|
||||
</li>
|
||||
<?php if ($task['date_due']): ?>
|
||||
<li>
|
||||
<strong><?= dt('Must be done before %B %e, %Y', $task['date_due']) ?></strong>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($task['creator_username']): ?>
|
||||
<li>
|
||||
<?= t('Created by %s', $task['creator_username']) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<strong>
|
||||
<?php if ($task['assignee_username']): ?>
|
||||
<?= t('Assigned to %s', $task['assignee_username']) ?>
|
||||
<?php else: ?>
|
||||
<?= t('There is nobody assigned') ?>
|
||||
<?php endif ?>
|
||||
</strong>
|
||||
</li>
|
||||
<li>
|
||||
<?= t('Column on the board:') ?>
|
||||
<strong><?= Helper\escape($task['column_title']) ?></strong>
|
||||
</li>
|
||||
<li><?= t('Task position:').' '.Helper\escape($task['position']) ?></li>
|
||||
<?php if ($task['category_name']): ?>
|
||||
<li>
|
||||
<?= t('Category:') ?> <strong><?= Helper\escape($task['category_name']) ?></strong>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
|
||||
<?php if (! empty($task['description'])): ?>
|
||||
<h2><?= t('Description') ?></h2>
|
||||
<?= Helper\parse($task['description']) ?: t('There is no description.') ?>
|
||||
<?php endif ?>
|
||||
|
||||
<hr/>
|
||||
<p>Kanboard</p>
|
||||
@@ -4,6 +4,8 @@ require __DIR__.'/Core/Loader.php';
|
||||
require __DIR__.'/helpers.php';
|
||||
require __DIR__.'/translator.php';
|
||||
|
||||
require 'vendor/swiftmailer/swift_required.php';
|
||||
|
||||
use Core\Event;
|
||||
use Core\Loader;
|
||||
use Core\Registry;
|
||||
@@ -63,6 +65,15 @@ defined('REVERSE_PROXY_AUTH') or define('REVERSE_PROXY_AUTH', false);
|
||||
defined('REVERSE_PROXY_USER_HEADER') or define('REVERSE_PROXY_USER_HEADER', 'REMOTE_USER');
|
||||
defined('REVERSE_PROXY_DEFAULT_ADMIN') or define('REVERSE_PROXY_DEFAULT_ADMIN', '');
|
||||
|
||||
// Mail configuration
|
||||
defined('MAIL_FROM') or define('MAIL_FROM', 'notifications@kanboard.net');
|
||||
defined('MAIL_TRANSPORT') or define('MAIL_TRANSPORT', 'mail');
|
||||
defined('MAIL_SMTP_HOSTNAME') or define('MAIL_SMTP_HOSTNAME', '');
|
||||
defined('MAIL_SMTP_PORT') or define('MAIL_SMTP_PORT', 25);
|
||||
defined('MAIL_SMTP_USERNAME') or define('MAIL_SMTP_USERNAME', '');
|
||||
defined('MAIL_SMTP_PASSWORD') or define('MAIL_SMTP_PASSWORD', '');
|
||||
defined('MAIL_SENDMAIL_COMMAND') or define('MAIL_SENDMAIL_COMMAND', '/usr/sbin/sendmail -bs');
|
||||
|
||||
$loader = new Loader;
|
||||
$loader->execute();
|
||||
|
||||
@@ -126,3 +137,25 @@ $registry->db = function() use ($registry) {
|
||||
$registry->event = function() use ($registry) {
|
||||
return new Event;
|
||||
};
|
||||
|
||||
$registry->mailer = function() use ($registry) {
|
||||
|
||||
require_once 'vendor/swiftmailer/swift_required.php';
|
||||
|
||||
$transport = null;
|
||||
|
||||
switch (MAIL_TRANSPORT) {
|
||||
case 'smtp':
|
||||
$transport = Swift_SmtpTransport::newInstance(MAIL_SMTP_HOSTNAME, MAIL_SMTP_PORT);
|
||||
$transport->setUsername(MAIL_SMTP_USERNAME);
|
||||
$transport->setPassword(MAIL_SMTP_PASSWORD);
|
||||
break;
|
||||
case 'sendmail':
|
||||
$transport = Swift_SendmailTransport::newInstance(MAIL_SENDMAIL_COMMAND);
|
||||
break;
|
||||
default:
|
||||
$transport = Swift_MailTransport::newInstance();
|
||||
}
|
||||
|
||||
return $transport;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user