Move slack, hipchat and jabber integrations to plugins

This commit is contained in:
Frederic Guillot 2015-10-17 22:19:49 -04:00
parent 9283fb88d8
commit 09da289c2f
68 changed files with 544 additions and 1644 deletions

View File

@ -44,7 +44,7 @@ class Config extends Base
$values += array('subtask_restriction' => 0, 'subtask_time_tracking' => 0, 'cfd_include_closed_tasks' => 0);
break;
case 'integrations':
$values += array('integration_slack_webhook' => 0, 'integration_hipchat' => 0, 'integration_gravatar' => 0, 'integration_jabber' => 0);
$values += array('integration_gravatar' => 0);
break;
case 'calendar':
$values += array('calendar_user_subtasks_time_tracking' => 0);

View File

@ -89,28 +89,49 @@ class Project extends Base
*
* @access public
*/
public function integration()
public function integrations()
{
$project = $this->getProject();
if ($this->request->isPost()) {
$params = $this->request->getValues();
$params += array('hipchat' => 0, 'slack' => 0, 'jabber' => 0);
$this->projectIntegration->saveParameters($project['id'], $params);
$this->projectMetadata->save($project['id'], $this->request->getValues());
$this->session->flash(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('project', 'integrations', array('project_id' => $project['id'])));
}
$values = $this->projectIntegration->getParameters($project['id']);
$values += array('hipchat_api_url' => 'https://api.hipchat.com');
$this->response->html($this->projectLayout('project/integrations', array(
'project' => $project,
'title' => t('Integrations'),
'webhook_token' => $this->config->get('webhook_token'),
'values' => $values,
'values' => $this->projectMetadata->getAll($project['id']),
'errors' => array(),
)));
}
/**
* Display project notifications
*
* @access public
*/
public function notifications()
{
$project = $this->getProject();
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->projectNotification->saveSettings($project['id'], $values);
$this->session->flash(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('project', 'notifications', array('project_id' => $project['id'])));
}
$this->response->html($this->projectLayout('project/notifications', array(
'notifications' => $this->projectNotification->readSettings($project['id']),
'types' => $this->projectNotificationType->getTypes(),
'project' => $project,
'title' => t('Notifications'),
)));
}
/**
* Display a form to edit a project
*

View File

@ -213,6 +213,28 @@ class User extends Base
)));
}
/**
* Display user integrations
*
* @access public
*/
public function integrations()
{
$user = $this->getUser();
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->userMetadata->save($user['id'], $values);
$this->session->flash(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('user', 'integrations', array('user_id' => $user['id'])));
}
$this->response->html($this->layout('user/integrations', array(
'user' => $user,
'values' => $this->userMetadata->getall($user['id']),
)));
}
/**
* Display external accounts
*

View File

@ -27,9 +27,6 @@ use Pimple\Container;
* @property \Kanboard\Integration\BitbucketWebhook $bitbucketWebhook
* @property \Kanboard\Integration\GithubWebhook $githubWebhook
* @property \Kanboard\Integration\GitlabWebhook $gitlabWebhook
* @property \Kanboard\Integration\HipchatWebhook $hipchatWebhook
* @property \Kanboard\Integration\Jabber $jabber
* @property \Kanboard\Integration\SlackWebhook $slackWebhook
* @property \Kanboard\Formatter\ProjectGanttFormatter $projectGanttFormatter
* @property \Kanboard\Formatter\TaskFilterGanttFormatter $taskFilterGanttFormatter
* @property \Kanboard\Formatter\TaskFilterAutoCompleteFormatter $taskFilterAutoCompleteFormatter
@ -49,6 +46,7 @@ use Pimple\Container;
* @property \Kanboard\Model\File $file
* @property \Kanboard\Model\LastLogin $lastLogin
* @property \Kanboard\Model\Link $link
* @property \Kanboard\Model\Notification $notification
* @property \Kanboard\Model\OverdueNotification $overdueNotification
* @property \Kanboard\Model\Project $project
* @property \Kanboard\Model\ProjectActivity $projectActivity
@ -56,7 +54,7 @@ use Pimple\Container;
* @property \Kanboard\Model\ProjectDuplication $projectDuplication
* @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats
* @property \Kanboard\Model\ProjectDailyStats $projectDailyStats
* @property \Kanboard\Model\ProjectIntegration $projectIntegration
* @property \Kanboard\Model\ProjectMetadata $projectMetadata
* @property \Kanboard\Model\ProjectPermission $projectPermission
* @property \Kanboard\Model\Subtask $subtask
* @property \Kanboard\Model\SubtaskExport $subtaskExport
@ -76,6 +74,7 @@ use Pimple\Container;
* @property \Kanboard\Model\TaskPosition $taskPosition
* @property \Kanboard\Model\TaskStatus $taskStatus
* @property \Kanboard\Model\TaskValidator $taskValidator
* @property \Kanboard\Model\TaskMetadata $taskMetadata
* @property \Kanboard\Model\Transition $transition
* @property \Kanboard\Model\User $user
* @property \Kanboard\Model\UserImport $userImport
@ -84,6 +83,7 @@ use Pimple\Container;
* @property \Kanboard\Model\UserNotificationFilter $userNotificationFilter
* @property \Kanboard\Model\UserUnreadNotification $userUnreadNotification
* @property \Kanboard\Model\UserSession $userSession
* @property \Kanboard\Model\UserMetadata $userMetadata
* @property \Kanboard\Model\Webhook $webhook
* @property \Psr\Log\LoggerInterface $logger
* @property \League\HTMLToMarkdown\HtmlConverter $htmlConverter

View File

@ -1,93 +0,0 @@
<?php
namespace Kanboard\Integration;
/**
* Hipchat webhook
*
* @package integration
* @author Frederic Guillot
*/
class HipchatWebhook extends \Kanboard\Core\Base
{
/**
* Return true if Hipchat is enabled for this project or globally
*
* @access public
* @param integer $project_id
* @return boolean
*/
public function isActivated($project_id)
{
return $this->config->get('integration_hipchat') == 1 || $this->projectIntegration->hasValue($project_id, 'hipchat', 1);
}
/**
* Get API parameters
*
* @access public
* @param integer $project_id
* @return array
*/
public function getParameters($project_id)
{
if ($this->config->get('integration_hipchat') == 1) {
return array(
'api_url' => $this->config->get('integration_hipchat_api_url'),
'room_id' => $this->config->get('integration_hipchat_room_id'),
'room_token' => $this->config->get('integration_hipchat_room_token'),
);
}
$options = $this->projectIntegration->getParameters($project_id);
return array(
'api_url' => $options['hipchat_api_url'],
'room_id' => $options['hipchat_room_id'],
'room_token' => $options['hipchat_room_token'],
);
}
/**
* Send the notification if activated
*
* @access public
* @param integer $project_id Project id
* @param integer $task_id Task id
* @param string $event_name Event name
* @param array $event Event data
*/
public function notify($project_id, $task_id, $event_name, array $event)
{
if ($this->isActivated($project_id)) {
$params = $this->getParameters($project_id);
$project = $this->project->getbyId($project_id);
$event['event_name'] = $event_name;
$event['author'] = $this->user->getFullname($this->session['user']);
$html = '<img src="http://kanboard.net/assets/img/favicon-32x32.png"/>';
$html .= '<strong>'.$project['name'].'</strong>'.(isset($event['task']['title']) ? '<br/>'.$event['task']['title'] : '').'<br/>';
$html .= $this->projectActivity->getTitle($event);
if ($this->config->get('application_url')) {
$html .= '<br/><a href="'.$this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), false, '', true).'">';
$html .= t('view the task on Kanboard').'</a>';
}
$payload = array(
'message' => $html,
'color' => 'yellow',
);
$url = sprintf(
'%s/v2/room/%s/notification?auth_token=%s',
$params['api_url'],
$params['room_id'],
$params['room_token']
);
$this->httpClient->postJson($url, $payload);
}
}
}

View File

@ -1,126 +0,0 @@
<?php
namespace Kanboard\Integration;
use Exception;
use Fabiang\Xmpp\Options;
use Fabiang\Xmpp\Client;
use Fabiang\Xmpp\Protocol\Message;
use Fabiang\Xmpp\Protocol\Presence;
/**
* Jabber
*
* @package integration
* @author Frederic Guillot
*/
class Jabber extends \Kanboard\Core\Base
{
/**
* Return true if Jabber is enabled for this project or globally
*
* @access public
* @param integer $project_id
* @return boolean
*/
public function isActivated($project_id)
{
return $this->config->get('integration_jabber') == 1 || $this->projectIntegration->hasValue($project_id, 'jabber', 1);
}
/**
* Get connection parameters
*
* @access public
* @param integer $project_id
* @return array
*/
public function getParameters($project_id)
{
if ($this->config->get('integration_jabber') == 1) {
return array(
'server' => $this->config->get('integration_jabber_server'),
'domain' => $this->config->get('integration_jabber_domain'),
'username' => $this->config->get('integration_jabber_username'),
'password' => $this->config->get('integration_jabber_password'),
'nickname' => $this->config->get('integration_jabber_nickname'),
'room' => $this->config->get('integration_jabber_room'),
);
}
$options = $this->projectIntegration->getParameters($project_id);
return array(
'server' => $options['jabber_server'],
'domain' => $options['jabber_domain'],
'username' => $options['jabber_username'],
'password' => $options['jabber_password'],
'nickname' => $options['jabber_nickname'],
'room' => $options['jabber_room'],
);
}
/**
* Build and send the message
*
* @access public
* @param integer $project_id Project id
* @param integer $task_id Task id
* @param string $event_name Event name
* @param array $event Event data
*/
public function notify($project_id, $task_id, $event_name, array $event)
{
if ($this->isActivated($project_id)) {
$project = $this->project->getbyId($project_id);
$event['event_name'] = $event_name;
$event['author'] = $this->user->getFullname($this->session['user']);
$payload = '['.$project['name'].'] '.str_replace('&quot;', '"', $this->projectActivity->getTitle($event)).(isset($event['task']['title']) ? ' ('.$event['task']['title'].')' : '');
if ($this->config->get('application_url')) {
$payload .= ' '.$this->helper->url->to('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), '', true);
}
$this->sendMessage($project_id, $payload);
}
}
/**
* Send message to the XMPP server
*
* @access public
* @param integer $project_id
* @param string $payload
*/
public function sendMessage($project_id, $payload)
{
try {
$params = $this->getParameters($project_id);
$options = new Options($params['server']);
$options->setUsername($params['username']);
$options->setPassword($params['password']);
$options->setTo($params['domain']);
$options->setLogger($this->container['logger']);
$client = new Client($options);
$channel = new Presence;
$channel->setTo($params['room'])->setNickName($params['nickname']);
$client->send($channel);
$message = new Message;
$message->setMessage($payload)
->setTo($params['room'])
->setType(Message::TYPE_GROUPCHAT);
$client->send($message);
$client->disconnect();
} catch (Exception $e) {
$this->container['logger']->error('Jabber error: '.$e->getMessage());
}
}
}

View File

@ -1,126 +0,0 @@
<?php
namespace Kanboard\Integration;
/**
* Slack Webhook
*
* @package integration
* @author Frederic Guillot
*/
class SlackWebhook extends \Kanboard\Core\Base
{
/**
* Return true if Slack is enabled for this project or globally
*
* @access public
* @param integer $project_id
* @return boolean
*/
public function isActivated($project_id)
{
return $this->config->get('integration_slack_webhook') == 1 || $this->projectIntegration->hasValue($project_id, 'slack', 1);
}
/**
* Get wehbook url
*
* @access public
* @param integer $project_id
* @return string
*/
public function getWebhookUrl($project_id)
{
if ($this->config->get('integration_slack_webhook') == 1) {
return $this->config->get('integration_slack_webhook_url');
}
$options = $this->projectIntegration->getParameters($project_id);
return isset($options['slack_webhook_url']) ? $options['slack_webhook_url'] : '';
}
/**
* Get optional Slack channel
*
* @access public
* @param integer $project_id
* @return string
*/
public function getChannel($project_id)
{
$channel = $this->config->get('integration_slack_webhook_channel');
if (! empty($channel)) {
return $channel;
}
$options = $this->projectIntegration->getParameters($project_id);
return isset($options['slack_webhook_channel']) ? $options['slack_webhook_channel'] : '';
}
/**
* Send notification to Slack
*
* @access public
* @param integer $project_id Project id
* @param integer $task_id Task id
* @param string $event_name Event name
* @param array $event Event data
*/
public function notify($project_id, $task_id, $event_name, array $event)
{
if ($this->isActivated($project_id)) {
$project = $this->project->getbyId($project_id);
$event['event_name'] = $event_name;
$event['author'] = $this->user->getFullname($this->session['user']);
$message = '*['.$project['name'].']* ';
$message .= str_replace('&quot;', '"', $this->projectActivity->getTitle($event));
$message .= isset($event['task']['title']) ? ' ('.$event['task']['title'].')' : '';
if ($this->config->get('application_url')) {
$message .= ' - <'.$this->helper->url->href('task', 'show', array('task_id' => $task_id, 'project_id' => $project_id), false, '', true);
$message .= '|'.t('view the task on Kanboard').'>';
}
$this->sendMessage($project_id, $message);
}
}
/**
* Send message to Slack
*
* @access public
* @param integer $project_id
* @param string $message
*/
public function sendMessage($project_id, $message)
{
$payload = array(
'text' => $message,
'username' => 'Kanboard',
'icon_url' => 'http://kanboard.net/assets/img/favicon.png',
);
$this->sendPayload($project_id, $payload);
}
/**
* Send payload to Slack
*
* @access public
* @param integer $project_id
* @param array $payload
*/
public function sendPayload($project_id, array $payload)
{
$channel = $this->getChannel($project_id);
if (! empty($channel)) {
$payload['channel'] = $channel;
}
$this->httpClient->postJson($this->getWebhookUrl($project_id), $payload);
}
}

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Referenční měna',
'The currency rate have been added successfully.' => 'Směnný kurz byl úspěšně přidán.',
'Unable to add this currency rate.' => 'Nelze přidat tento směnný kurz',
'Send notifications to a Slack channel' => 'Zaslání upozornění do Slack kanálu',
'Webhook URL' => 'Webhook URL',
'Help on Slack integration' => 'Nápověda pro Slack integraci.',
'%s remove the assignee of the task %s' => '%s odstranil přiřazení úkolu %s ',
'Send notifications to Hipchat' => 'Odeslat upozornění přes Hipchat',
'API URL' => 'API URL',
'Room API ID or name' => 'Raum API ID oder Name',
'Room notification token' => 'Raum Benachrichtigungstoken',
'Help on Hipchat integration' => 'Hilfe bei Hipchat Integration',
'Enable Gravatar images' => 'Aktiviere Gravatar Bilder',
'Information' => 'Informace',
'Check two factor authentication code' => 'Prüfe Zwei-Faktor-Authentifizierungscode',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
'Calendar settings' => 'Nastavení kalendáře',
// 'Project calendar view' => '',
'Project settings' => 'Nastavení projektu',
@ -820,8 +806,6 @@ return array(
'%s moved the task #%d to the swimlane "%s"' => '%s hat die Aufgabe #%d in die Swimlane "%s" verschoben',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
'%s moved the task %s to the first swimlane' => '%s hat die Aufgabe %s in die erste Swimlane verschoben',
'%s moved the task %s to the swimlane "%s"' => '%s hat die Aufgaben %s in die Swimlane "%s" verschoben',
'This report contains all subtasks information for the given date range.' => 'Report obsahuje všechny informace o dílčích úkolech pro daný časový úsek',
@ -918,7 +902,6 @@ return array(
'Help on Google authentication' => 'Nápověda k ověřování pomocí služby Google',
'Github Authentication' => 'Ověřování pomocí služby Github',
'Help on Github authentication' => 'Nápověda k ověřování pomocí služby Github',
'Channel/Group/User (Optional)' => 'Kanál/Skupina/Uživatel (volitelně)',
'Lead time: ' => 'Dodací lhůta: ',
'Cycle time: ' => 'Doba cyklu: ',
'Time spent into each column' => 'Čas strávený v každé fázi',

View File

@ -697,15 +697,9 @@ return array(
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Referenzwährung',
'The currency rate have been added successfully.' => 'Der Währungskurs wurde erfolgreich hinzugefügt.',
'Unable to add this currency rate.' => 'Währungskurs konnte nicht hinzugefügt werden',
'Send notifications to a Slack channel' => 'Benachrichtigung an einen Slack-Kanal senden',
'Webhook URL' => 'Webhook-URL',
'Help on Slack integration' => 'Hilfe für Slack-Integration.',
'%s remove the assignee of the task %s' => '%s Zuordnung für die Aufgabe %s entfernen',
'Send notifications to Hipchat' => 'Sende Benachrichtigung an Hipchat',
'API URL' => 'API-URL',
'Room API ID or name' => 'Raum-API-ID oder -Name',
'Room notification token' => 'Raum-Benachrichtigungstoken',
'Help on Hipchat integration' => 'Hilfe bei Hipchat-Integration',
'Enable Gravatar images' => 'Aktiviere Gravatar-Bilder',
'Information' => 'Information',
'Check two factor authentication code' => 'Prüfe Zwei-Faktor-Authentifizierungscode',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'Wenn Aufgabe von erster Spalte verschoben wird',
'When task is moved to last column' => 'Wenn Aufgabe in letzte Spalte verschoben wird',
'Year(s)' => 'Jahr(e)',
'Jabber (XMPP)' => 'Jabber (XMPP)',
'Send notifications to Jabber' => 'Benachrichtigungen an Jabber senden',
'XMPP server address' => 'XMPP-Server-Adresse',
'Jabber domain' => 'Jabber-Domain',
'Jabber nickname' => 'Jabber-Nickname',
'Multi-user chat room' => 'Multi-User-Chatroom',
'Help on Jabber integration' => 'Hilfe zur Jabber-Integration',
'The server address must use this format: "tcp://hostname:5222"' => 'Die Server-Adresse muss in diesem Format sein: "tcp://hostname:5222"',
'Calendar settings' => 'Kalender-Einstellungen',
'Project calendar view' => 'Projekt-Kalendarsicht',
'Project settings' => 'Projekteinstellungen',
@ -820,8 +806,6 @@ return array(
'%s moved the task #%d to the swimlane "%s"' => '%s hat die Aufgabe #%d in die Swimlane "%s" verschoben',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
'%s moved the task %s to the first swimlane' => '%s hat die Aufgabe %s in die erste Swimlane verschoben',
'%s moved the task %s to the swimlane "%s"' => '%s hat die Aufgaben %s in die Swimlane "%s" verschoben',
'This report contains all subtasks information for the given date range.' => 'Der Bericht beinhaltet alle Teilaufgaben im gewählten Zeitraum',
@ -918,7 +902,6 @@ return array(
'Help on Google authentication' => 'Hilfe bei Google-Authentifizierung',
'Github Authentication' => 'Github-Authentifizierung',
'Help on Github authentication' => 'Hilfe bei Github-Authentifizierung',
'Channel/Group/User (Optional)' => 'Kanal/Gruppe/Benutzer (optional)',
'Lead time: ' => 'Durchlaufzeit:',
'Cycle time: ' => 'Zykluszeit:',
'Time spent into each column' => 'zeit verbracht in jeder Spalte',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Moneda de referencia',
'The currency rate have been added successfully.' => 'Se ha añadido el cambio de moneda con éxito',
'Unable to add this currency rate.' => 'No pude añadir este cambio de moneda.',
'Send notifications to a Slack channel' => 'Enviar notificaciones a un canal Desatendido',
'Webhook URL' => 'URL de Disparador Web (webhook)',
'Help on Slack integration' => 'Ayuda sobre integración Desatendida',
'%s remove the assignee of the task %s' => '%s quita el concesionario de la tarea %s',
'Send notifications to Hipchat' => 'Enviar notificaciones a Hipchat',
'API URL' => 'URL de API',
'Room API ID or name' => 'ID de API de habitación o nombre',
'Room notification token' => 'Notificación de ficha de Habitación',
'Help on Hipchat integration' => 'Ayuda sobre integración de Hipchat',
'Enable Gravatar images' => 'Activar imágenes Gravatar',
'Information' => 'Información',
'Check two factor authentication code' => 'Revisar código de autenticación de dos factores',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'Cuando la tarea es movida desde la primera columna',
'When task is moved to last column' => 'Cuando la tarea es movida a la última columna',
'Year(s)' => 'Año(s)',
'Jabber (XMPP)' => 'Jabber (XMPP)',
'Send notifications to Jabber' => 'Enviar notificaciones a Jabber',
'XMPP server address' => 'Dirección del servidor XMPP',
'Jabber domain' => 'Dominio Jabber',
'Jabber nickname' => 'Apodo Jabber',
'Multi-user chat room' => 'Sala de chat multiusuario',
'Help on Jabber integration' => 'Ayuda para la integración con Jabber',
'The server address must use this format: "tcp://hostname:5222"' => 'La dirección del servidor debe usar este formato: "tcp://hostname:5222"',
'Calendar settings' => 'Parámetros del Calendario',
'Project calendar view' => 'Vista de Calendario para el Proyecto',
'Project settings' => 'Parámetros del Proyecto',
@ -820,8 +806,6 @@ return array(
'%s moved the task #%d to the swimlane "%s"' => '%s movió la tarea #%d a la calle "%s"',
'Swimlane' => 'Calle',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Desatendida',
'%s moved the task %s to the first swimlane' => '%s movió la tarea %s a la primera calle',
'%s moved the task %s to the swimlane "%s"' => '%s movió la tarea %s a la calle "%s"',
'This report contains all subtasks information for the given date range.' => 'Este informe contiene todas la información de las subtareas para el rango proporcionado de fechas.',
@ -918,7 +902,6 @@ return array(
'Help on Google authentication' => 'Ayuda con la aAutenticación de Google',
'Github Authentication' => 'Autenticación de Github',
'Help on Github authentication' => 'Ayuda con la autenticación de Github',
'Channel/Group/User (Optional)' => 'Canal/Grupo/Usuario (Opcional)',
'Lead time: ' => 'Plazo de entrega: ',
'Cycle time: ' => 'Tiempo de Ciclo: ',
'Time spent into each column' => 'Tiempo empleado en cada columna',

View File

@ -697,15 +697,9 @@ return array(
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -699,15 +699,9 @@ return array(
'Reference currency' => 'Devise de référence',
'The currency rate have been added successfully.' => 'Le taux de change a été ajouté avec succès.',
'Unable to add this currency rate.' => 'Impossible d\'ajouter ce taux de change',
'Send notifications to a Slack channel' => 'Envoyer les notifications sur un salon de discussion Slack',
'Webhook URL' => 'URL du webhook',
'Help on Slack integration' => 'Aide sur l\'intégration avec Slack',
'%s remove the assignee of the task %s' => '%s a enlevé la personne assignée à la tâche %s',
'Send notifications to Hipchat' => 'Envoyer les notifications vers Hipchat',
'API URL' => 'URL de l\'api',
'Room API ID or name' => 'Nom ou identifiant du salon de discussion',
'Room notification token' => 'Jeton de sécurité du salon de discussion',
'Help on Hipchat integration' => 'Aide sur l\'intégration avec Hipchat',
'Enable Gravatar images' => 'Activer les images Gravatar',
'Information' => 'Informations',
'Check two factor authentication code' => 'Vérification du code pour l\'authentification à deux-facteurs',
@ -770,14 +764,6 @@ return array(
'When task is moved from first column' => 'Lorsque la tâche est déplacée en dehors de la première colonne',
'When task is moved to last column' => 'Lorsque la tâche est déplacée dans la dernière colonne',
'Year(s)' => 'Année(s)',
'Jabber (XMPP)' => 'Jabber (XMPP)',
'Send notifications to Jabber' => 'Envoyer les notifications vers Jabber',
'XMPP server address' => 'Adresse du serveur XMPP',
'Jabber domain' => 'Nom de domaine Jabber',
'Jabber nickname' => 'Pseudonyme Jabber',
'Multi-user chat room' => 'Salon de discussion multi-utilisateurs',
'Help on Jabber integration' => 'Aide sur l\'intégration avec Jabber',
'The server address must use this format: "tcp://hostname:5222"' => 'L\'adresse du serveur doit utiliser le format suivant : « tcp://hostname:5222 »',
'Calendar settings' => 'Paramètres du calendrier',
'Project calendar view' => 'Vue en mode projet du calendrier',
'Project settings' => 'Paramètres du projet',
@ -822,8 +808,6 @@ return array(
'%s moved the task #%d to the swimlane "%s"' => '%s a déplacé la tâche n°%d dans la swimlane « %s »',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
'%s moved the task %s to the first swimlane' => '%s a déplacé la tâche %s dans la première swimlane',
'%s moved the task %s to the swimlane "%s"' => '%s a déplacé la tâche %s dans la swimlane « %s »',
'This report contains all subtasks information for the given date range.' => 'Ce rapport contient les informations de toutes les sous-tâches pour la période sélectionnée.',
@ -920,7 +904,6 @@ return array(
'Help on Google authentication' => 'Aide sur l\'authentification Google',
'Github Authentication' => 'Authentification Github',
'Help on Github authentication' => 'Aide sur l\'authentification Github',
'Channel/Group/User (Optional)' => 'Canal/Groupe/Utilisateur (Optionnel)',
'Lead time: ' => 'Lead time : ',
'Cycle time: ' => 'Temps de cycle : ',
'Time spent into each column' => 'Temps passé dans chaque colonne',

View File

@ -697,15 +697,9 @@ return array(
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Referensi mata uang',
'The currency rate have been added successfully.' => 'Nilai tukar mata uang berhasil ditambahkan.',
'Unable to add this currency rate.' => 'Tidak dapat menambahkan nilai tukar mata uang',
'Send notifications to a Slack channel' => 'Kirim pemberitahuan ke saluran Slack',
'Webhook URL' => 'URL webhook',
'Help on Slack integration' => 'Bantuan pada integrasi Slack',
'%s remove the assignee of the task %s' => '%s menghapus penugasan dari tugas %s',
'Send notifications to Hipchat' => 'Kirim pemberitahuan ke Hipchat',
'API URL' => 'API URL',
'Room API ID or name' => 'Id kamar API atau nama ',
'Room notification token' => 'Token notifikasi kamar',
'Help on Hipchat integration' => 'Bantuan pada integrasi Hipchat',
'Enable Gravatar images' => 'Mengaktifkan gambar Gravatar',
'Information' => 'Informasi',
'Check two factor authentication code' => 'Cek dua faktor kode otentifikasi',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'Ketika tugas dipindahkan dari kolom pertama',
'When task is moved to last column' => 'Ketika tugas dipindahkan ke kolom terakhir',
'Year(s)' => 'Tahun',
'Jabber (XMPP)' => 'Jabber (XMPP)',
'Send notifications to Jabber' => 'Kirim pemberitahuan ke Jabber',
'XMPP server address' => 'alamat server XMPP',
'Jabber domain' => 'Domain Jabber',
'Jabber nickname' => 'Nickname Jabber',
'Multi-user chat room' => 'Multi-pengguna kamar obrolan',
'Help on Jabber integration' => 'Bantuan pada integrasi Jabber',
'The server address must use this format: "tcp://hostname:5222"' => 'Alamat server harus menggunakan format ini : « tcp://hostname:5222 »',
'Calendar settings' => 'Pengaturan kalender',
'Project calendar view' => 'Tampilan kalender proyek',
'Project settings' => 'Pengaturan proyek',
@ -820,8 +806,6 @@ return array(
'%s moved the task #%d to the swimlane "%s"' => '%s memindahkan tugas n°%d ke swimlane « %s »',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
'%s moved the task %s to the first swimlane' => '%s memindahkan tugas %s ke swimlane pertama',
'%s moved the task %s to the swimlane "%s"' => '%s memindahkan tugas %s ke swimlane « %s »',
'This report contains all subtasks information for the given date range.' => 'Laporan ini berisi semua informasi subtugas untuk rentang tanggal tertentu.',
@ -918,7 +902,6 @@ return array(
'Help on Google authentication' => 'Bantuan pada otentifikasi Google',
'Github Authentication' => 'Otentifikasi Github',
'Help on Github authentication' => 'Bantuan pada otentifikasi Github',
'Channel/Group/User (Optional)' => 'Kanal/Grup/Pengguna (pilihan)',
'Lead time: ' => 'Lead time : ',
'Cycle time: ' => 'Siklus waktu : ',
'Time spent into each column' => 'Waktu yang dihabiskan di setiap kolom',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Valuta di riferimento',
'The currency rate have been added successfully.' => 'Il tasso di cambio è stato aggiunto con successo.',
'Unable to add this currency rate.' => 'Impossibile aggiungere questo tasso di cambio.',
'Send notifications to a Slack channel' => 'Invia notifiche al canale Slack',
'Webhook URL' => 'URL Webhook',
'Help on Slack integration' => 'Guida all\'integrazione con Slack',
'%s remove the assignee of the task %s' => '%s rimuove l\'assegnatario del compito %s',
'Send notifications to Hipchat' => 'Invia notifiche a Hipchat',
'API URL' => 'URL API',
'Room API ID or name' => 'Nome o ID API della Room',
'Room notification token' => 'Token per le notifiche della Room',
'Help on Hipchat integration' => 'Guida all\'integrazione di Hipchat',
'Enable Gravatar images' => 'Abilita immagini Gravatar',
'Information' => 'Informazioni',
'Check two factor authentication code' => 'Controlla il codice di autenticazione a due fattori',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => '基軸通貨',
// 'The currency rate have been added successfully.' => '',
'Unable to add this currency rate.' => 'この通貨レートを追加できません。',
'Send notifications to a Slack channel' => 'Slack チャンネルに通知を送信',
'Webhook URL' => 'Webhook URL',
'Help on Slack integration' => 'Slack 連携のヘルプ',
'%s remove the assignee of the task %s' => '%s がタスク「%s」の担当を解除しました。',
'Send notifications to Hipchat' => 'Hipchat に通知を送信',
'API URL' => 'API URL',
'Room API ID or name' => 'Room API ID または名前',
'Room notification token' => 'Room 通知トークン',
'Help on Hipchat integration' => 'Hipchat 連携のヘルプ',
'Enable Gravatar images' => 'Gravatar イメージを有効化',
'Information' => '情報 ',
'Check two factor authentication code' => '2 段認証をチェックする',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'Når oppgaven er flyttet fra første kolon',
'When task is moved to last column' => 'Når oppgaven er flyttet til siste kolonne',
'Year(s)' => 'år',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
'Calendar settings' => 'Kalenderinstillinger',
'Project calendar view' => 'Visning prosjektkalender',
'Project settings' => 'Prosjektinnstillinger',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
'Swimlane' => 'Svømmebane',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Waluta referencyjna',
'The currency rate have been added successfully.' => 'Dodano kurs waluty',
'Unable to add this currency rate.' => 'Nie można dodać kursu waluty',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
'%s remove the assignee of the task %s' => '%s usunął osobę przypisaną do zadania %s',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
'Information' => 'Informacje',
'Check two factor authentication code' => 'Sprawdź kod weryfikujący',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'Przeniesienie zadania z pierwszej kolumny',
'When task is moved to last column' => 'Przeniesienie zadania do ostatniej kolumny',
'Year(s)' => 'Lat',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
'Calendar settings' => 'Ustawienia kalendarza',
'Project calendar view' => 'Widok kalendarza projektu',
'Project settings' => 'Ustawienia Projektu',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Moeda de Referência',
'The currency rate have been added successfully.' => 'A taxa de câmbio foi adicionada com sucesso.',
'Unable to add this currency rate.' => 'Impossível de adicionar essa taxa de câmbio.',
'Send notifications to a Slack channel' => 'Enviar as notificações em um canal Slack',
'Webhook URL' => 'URL do webhook',
'Help on Slack integration' => 'Ajuda sobre integração com o Slack',
'%s remove the assignee of the task %s' => '%s removeu a pessoa designada para a tarefa %s',
'Send notifications to Hipchat' => 'Enviar as notificações para o Hipchat',
'API URL' => 'URL da API',
'Room API ID or name' => 'Nome ou ID da sala de discussão',
'Room notification token' => 'Código de segurança da sala de discussão',
'Help on Hipchat integration' => 'Ajuda sobre integração com o Hipchat',
'Enable Gravatar images' => 'Ativar imagens do Gravatar',
'Information' => 'Informações',
'Check two factor authentication code' => 'Verificação do código de autenticação à fator duplo',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'Quando a tarefa é movida fora da primeira coluna',
'When task is moved to last column' => 'Quando a tarefa é movida para a última coluna',
'Year(s)' => 'Ano(s)',
'Jabber (XMPP)' => 'Jabber (XMPP)',
'Send notifications to Jabber' => 'Enviar notificações para o Jabber',
'XMPP server address' => 'Endereço do servidor XMPP',
'Jabber domain' => 'Nome de domínio Jabber',
'Jabber nickname' => 'Apelido Jabber',
'Multi-user chat room' => 'Sala de chat multi-usuário',
'Help on Jabber integration' => 'Ajuda sobre integração com o Jabber',
'The server address must use this format: "tcp://hostname:5222"' => 'O endereço do servidor deve usar o seguinte formato: "tcp://hostname:5222"',
'Calendar settings' => 'Configurações do calendário',
'Project calendar view' => 'Vista em modo projeto do calendário',
'Project settings' => 'Configurações dos projetos',
@ -820,8 +806,6 @@ return array(
'%s moved the task #%d to the swimlane "%s"' => '%s moveu a tarefa #%d para a swimlane "%s"',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
'%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s para a primeira swimlane',
'%s moved the task %s to the swimlane "%s"' => '%s moveu a tarefa %s para a swimlane "%s"',
'This report contains all subtasks information for the given date range.' => 'Este relatório contém informações de todas as sub-tarefas para o período selecionado.',
@ -918,7 +902,6 @@ return array(
'Help on Google authentication' => 'Ajuda com a autenticação Google',
'Github Authentication' => 'Autenticação Github',
'Help on Github authentication' => 'Ajuda com a autenticação Github',
'Channel/Group/User (Optional)' => 'Canal/Grupo/Utilizador (Opcional)',
'Lead time: ' => 'Lead time: ',
'Cycle time: ' => 'Cycle time: ',
'Time spent into each column' => 'Tempo gasto em cada coluna',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Moeda de Referência',
'The currency rate have been added successfully.' => 'A taxa de câmbio foi adicionada com sucesso.',
'Unable to add this currency rate.' => 'Impossível adicionar essa taxa de câmbio.',
'Send notifications to a Slack channel' => 'Enviar as notificações por canal Slack',
'Webhook URL' => 'URL do webhook',
'Help on Slack integration' => 'Ajuda na integração com o Slack',
'%s remove the assignee of the task %s' => '%s removeu a pessoa assignada à tarefa %s',
'Send notifications to Hipchat' => 'Enviar as notificações para o Hipchat',
'API URL' => 'URL da API',
'Room API ID or name' => 'Nome ou ID da sala de discussão',
'Room notification token' => 'Código de segurança da sala de discussão',
'Help on Hipchat integration' => 'Ajuda na integração com o Hipchat',
'Enable Gravatar images' => 'Activar imagem Gravatar',
'Information' => 'Informações',
'Check two factor authentication code' => 'Verificação do código de autenticação com factor duplo',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'Quando a tarefa é movida fora da primeira coluna',
'When task is moved to last column' => 'Quando a tarefa é movida para a última coluna',
'Year(s)' => 'Ano(s)',
'Jabber (XMPP)' => 'Jabber (XMPP)',
'Send notifications to Jabber' => 'Enviar notificações para o Jabber',
'XMPP server address' => 'Endereço do servidor XMPP',
'Jabber domain' => 'Nome de domínio Jabber',
'Jabber nickname' => 'Apelido Jabber',
'Multi-user chat room' => 'Sala de chat multi-utilizador',
'Help on Jabber integration' => 'Ajuda na integração com Jabber',
'The server address must use this format: "tcp://hostname:5222"' => 'O endereço do servidor deve usar o seguinte formato: "tcp://hostname:5222"',
'Calendar settings' => 'Configurações do calendário',
'Project calendar view' => 'Vista em modo projecto do calendário',
'Project settings' => 'Configurações dos projectos',
@ -820,8 +806,6 @@ return array(
'%s moved the task #%d to the swimlane "%s"' => '%s moveu a tarefa n° %d no swimlane "%s"',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
'%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s no primeiro swimlane',
'%s moved the task %s to the swimlane "%s"' => '%s moveu a tarefa %s no swimlane "%s"',
'This report contains all subtasks information for the given date range.' => 'Este relatório contém informações de todas as sub-tarefas para o período selecionado.',
@ -918,7 +902,6 @@ return array(
'Help on Google authentication' => 'Ajuda com autenticação Google',
'Github Authentication' => 'Autenticação Github',
'Help on Github authentication' => 'Ajuda com autenticação Github',
'Channel/Group/User (Optional)' => 'Canal/Grupo/Utilizador (Opcional)',
'Lead time: ' => 'Tempo de Espera: ',
'Cycle time: ' => 'Tempo de Ciclo: ',
'Time spent into each column' => 'Tempo gasto em cada coluna',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Справочник валют',
'The currency rate have been added successfully.' => 'Курс валюты был успешно добавлен.',
'Unable to add this currency rate.' => 'Невозможно добавить этот курс валюты.',
'Send notifications to a Slack channel' => 'Отправлять уведомления в канал Slack',
'Webhook URL' => 'Webhook URL',
'Help on Slack integration' => 'Помощь по интеграции Slack',
'%s remove the assignee of the task %s' => '%s удалить назначенную задачу %s',
'Send notifications to Hipchat' => 'Отправлять уведомления в Hipchat',
'API URL' => 'API URL',
'Room API ID or name' => 'API ID комнаты или имя',
'Room notification token' => 'Ключь комнаты для уведомлений',
'Help on Hipchat integration' => 'Помощь по интеграции Hipchat',
'Enable Gravatar images' => 'Включить Gravatar изображения',
'Information' => 'Информация',
'Check two factor authentication code' => 'Проверка кода двухфакторной авторизации',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'Когда задача перемещается из первой колонки',
'When task is moved to last column' => 'Когда задача перемещается в последнюю колонку',
'Year(s)' => 'Год(а)',
'Jabber (XMPP)' => 'Jabber (XMPP)',
'Send notifications to Jabber' => 'Отправлять уведомления в Jabber',
'XMPP server address' => 'Адрес Jabber сервера',
'Jabber domain' => 'Домен Jabber',
'Jabber nickname' => 'Имя пользователя Jabber',
'Multi-user chat room' => 'Многопользовательский чат',
'Help on Jabber integration' => 'Помощь по интеграции Jabber',
'The server address must use this format: "tcp://hostname:5222"' => 'Адрес сервера должен быть в формате: tcp://hostname:5222',
'Calendar settings' => 'Настройки календаря',
'Project calendar view' => 'Вид календаря проекта',
'Project settings' => 'Настройки проекта',
@ -820,8 +806,6 @@ return array(
'%s moved the task #%d to the swimlane "%s"' => '%s задач перемещено #%d в дорожке "%s"',
'Swimlane' => 'Дорожки',
'Gravatar' => 'Граватар',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
'This report contains all subtasks information for the given date range.' => 'Этот отчет содержит всю информацию подзадач в заданном диапазоне дат.',
@ -918,7 +902,6 @@ return array(
'Help on Google authentication' => 'Помощь в авторизации Google',
'Github Authentication' => 'Авторизация Github',
'Help on Github authentication' => 'Помощь в авторизации Github',
'Channel/Group/User (Optional)' => 'Канал/Группа/Пользователь (опционально)',
'Lead time: ' => 'Время выполнения:',
'Cycle time: ' => 'Время цикла:',
'Time spent into each column' => 'Время, проведенное в каждой колонке',

View File

@ -697,15 +697,9 @@ return array(
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => 'Referensvaluta',
'The currency rate have been added successfully.' => 'Valutakursen har lagts till.',
'Unable to add this currency rate.' => 'Kunde inte lägga till valutakursen.',
'Send notifications to a Slack channel' => 'Skicka notiser till en Slack kanal',
'Webhook URL' => 'Webhook URL',
'Help on Slack integration' => 'Hjälp för Slack integration',
'%s remove the assignee of the task %s' => '%s ta bort tilldelningen av uppgiften %s',
'Send notifications to Hipchat' => 'Skicka notiser till Hipchat',
'API URL' => 'API URL',
'Room API ID or name' => 'Room API ID eller namn',
'Room notification token' => 'Room notistoken',
'Help on Hipchat integration' => 'Hjälp för Hipchat integration',
'Enable Gravatar images' => 'Aktivera Gravatar bilder',
'Information' => 'Information',
'Check two factor authentication code' => 'Kolla tvåfaktorsverifieringskod',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'När uppgiften flyttas från första kolumnen',
'When task is moved to last column' => 'När uppgiften flyttas till sista kolumnen',
'Year(s)' => 'År',
'Jabber (XMPP)' => 'Jabber (XMPP)',
'Send notifications to Jabber' => 'Skicka notiser till Jabber',
'XMPP server address' => 'XMPP serveradress',
'Jabber domain' => 'Jabber domän',
'Jabber nickname' => 'Jabber smeknamn',
'Multi-user chat room' => 'Multi-user chatrum',
'Help on Jabber integration' => 'Hjälp för Jabber integration',
'The server address must use this format: "tcp://hostname:5222"' => 'Serveradressen måste använda detta format: "tcp://hostname:5222"',
'Calendar settings' => 'Inställningar för kalendern',
'Project calendar view' => 'Projektkalendervy',
'Project settings' => 'Projektinställningar',
@ -820,8 +806,6 @@ return array(
'%s moved the task #%d to the swimlane "%s"' => '%s flyttade uppgiften #%d till swimlane "%s"',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'Hipchat' => 'Hipchat',
'Slack' => 'Slack',
'%s moved the task %s to the first swimlane' => '%s flyttade uppgiften %s till första swimlane',
'%s moved the task %s to the swimlane "%s"' => '%s flyttade uppgiften %s till swimlane "%s"',
'This report contains all subtasks information for the given date range.' => 'Denna rapport innehåller all deluppgiftsinformation för det givna datumintervallet.',
@ -918,7 +902,6 @@ return array(
'Help on Google authentication' => 'Hjälp för Google autentisering',
'Github Authentication' => 'Github autentisering',
'Help on Github authentication' => 'Hjälp för Github autentisering',
'Channel/Group/User (Optional)' => 'Kanal/Grupp/Användare (valfri)',
'Lead time: ' => 'Ledtid',
'Cycle time: ' => 'Cykeltid',
'Time spent into each column' => 'Tidsåtgång per kolumn',

View File

@ -697,15 +697,9 @@ return array(
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
'Send notifications to a Slack channel' => 'ส่งการแจ้งเตือนไปทาง Slack channel',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
'Enable Gravatar images' => 'สามารถใช้งานภาพ Gravatar',
'Information' => 'ข้อมูลสารสนเทศ',
// 'Check two factor authentication code' => '',
@ -768,14 +762,6 @@ return array(
'When task is moved from first column' => 'เมื่องานถูกย้ายจากคอลัมน์แรก',
'When task is moved to last column' => 'เมื่องานถูกย้ายไปคอลัมน์สุดท้าย',
'Year(s)' => 'ปี',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
'Calendar settings' => 'ตั้งค่าปฏิทิน',
'Project calendar view' => 'มุมมองปฏิทินของโปรเจค',
'Project settings' => 'ตั้งค่าโปรเจค',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
// 'Reference currency' => '',
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Send notifications to a Slack channel' => '',
// 'Webhook URL' => '',
// 'Help on Slack integration' => '',
// '%s remove the assignee of the task %s' => '',
// 'Send notifications to Hipchat' => '',
// 'API URL' => '',
// 'Room API ID or name' => '',
// 'Room notification token' => '',
// 'Help on Hipchat integration' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -697,15 +697,9 @@ return array(
'Reference currency' => '参考货币',
'The currency rate have been added successfully.' => '成功添加汇率。',
'Unable to add this currency rate.' => '无法添加此汇率',
'Send notifications to a Slack channel' => '发送通知到 Slack 频道',
'Webhook URL' => '网络钩子 URL',
'Help on Slack integration' => 'Slack 整合帮助',
'%s remove the assignee of the task %s' => '%s删除了任务%s的负责人',
'Send notifications to Hipchat' => '发送通知到 Hipchat',
'API URL' => 'API URL',
'Room API ID or name' => '房间 API ID 或名称',
'Room notification token' => '房间通知令牌',
'Help on Hipchat integration' => 'Hipchat 整合帮助',
'Enable Gravatar images' => '启用 Gravatar 图像',
'Information' => '信息',
'Check two factor authentication code' => '检查双重认证码',
@ -768,14 +762,6 @@ return array(
// 'When task is moved from first column' => '',
// 'When task is moved to last column' => '',
// 'Year(s)' => '',
// 'Jabber (XMPP)' => '',
// 'Send notifications to Jabber' => '',
// 'XMPP server address' => '',
// 'Jabber domain' => '',
// 'Jabber nickname' => '',
'Multi-user chat room' => '多用户聊天室',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
'Calendar settings' => '日程设置',
// 'Project calendar view' => '',
'Project settings' => '项目设置',
@ -820,8 +806,6 @@ return array(
// '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// 'Hipchat' => '',
// 'Slack' => '',
// '%s moved the task %s to the first swimlane' => '',
// '%s moved the task %s to the swimlane "%s"' => '',
// 'This report contains all subtasks information for the given date range.' => '',
@ -918,7 +902,6 @@ return array(
// 'Help on Google authentication' => '',
// 'Github Authentication' => '',
// 'Help on Github authentication' => '',
// 'Channel/Group/User (Optional)' => '',
// 'Lead time: ' => '',
// 'Cycle time: ' => '',
// 'Time spent into each column' => '',

View File

@ -64,7 +64,7 @@ class Acl extends Base
'column' => '*',
'export' => '*',
'taskimport' => '*',
'project' => array('edit', 'update', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'),
'project' => array('edit', 'update', 'share', 'integrations', 'notifications', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'),
'swimlane' => '*',
'gantt' => array('project', 'savetaskdate', 'task', 'savetask'),
);

142
app/Model/Notification.php Normal file
View File

@ -0,0 +1,142 @@
<?php
namespace Kanboard\Model;
/**
* Notification
*
* @package model
* @author Frederic Guillot
*/
class Notification extends Base
{
/**
* Get the event title with author
*
* @access public
* @param string $event_author
* @param string $event_name
* @param array $event_data
* @return string
*/
public function getTitleWithAuthor($event_author, $event_name, array $event_data)
{
switch ($event_name) {
case Task::EVENT_ASSIGNEE_CHANGE:
$assignee = $event_data['task']['assignee_name'] ?: $event_data['task']['assignee_username'];
if (! empty($assignee)) {
return e('%s change the assignee of the task #%d to %s', $event_author, $event_data['task']['id'], $assignee);
}
return e('%s remove the assignee of the task %s', $event_author, e('#%d', $event_data['task']['id']));
case Task::EVENT_UPDATE:
return e('%s updated the task #%d', $event_author, $event_data['task']['id']);
case Task::EVENT_CREATE:
return e('%s created the task #%d', $event_author, $event_data['task']['id']);
case Task::EVENT_CLOSE:
return e('%s closed the task #%d', $event_author, $event_data['task']['id']);
case Task::EVENT_OPEN:
return e('%s open the task #%d', $event_author, $event_data['task']['id']);
case Task::EVENT_MOVE_COLUMN:
return e(
'%s moved the task #%d to the column "%s"',
$event_author,
$event_data['task']['id'],
$event_data['task']['column_title']
);
case Task::EVENT_MOVE_POSITION:
return e(
'%s moved the task #%d to the position %d in the column "%s"',
$event_author,
$event_data['task']['id'],
$event_data['task']['position'],
$event_data['task']['column_title']
);
case Task::EVENT_MOVE_SWIMLANE:
if ($event_data['task']['swimlane_id'] == 0) {
return e('%s moved the task #%d to the first swimlane', $event_author, $event_data['task']['id']);
}
return e(
'%s moved the task #%d to the swimlane "%s"',
$event_author,
$event_data['task']['id'],
$event_data['task']['swimlane_name']
);
case Subtask::EVENT_UPDATE:
return e('%s updated a subtask for the task #%d', $event_author, $event_data['task']['id']);
case Subtask::EVENT_CREATE:
return e('%s created a subtask for the task #%d', $event_author, $event_data['task']['id']);
case Comment::EVENT_UPDATE:
return e('%s updated a comment on the task #%d', $event_author, $event_data['task']['id']);
case Comment::EVENT_CREATE:
return e('%s commented on the task #%d', $event_author, $event_data['task']['id']);
case File::EVENT_CREATE:
return e('%s attached a file to the task #%d', $event_author, $event_data['task']['id']);
default:
return e('Notification');
}
}
/**
* Get the event title without author
*
* @access public
* @param string $event_name
* @param array $event_data
* @return string
*/
public function getTitleWithoutAuthor($event_name, array $event_data)
{
switch ($event_name) {
case File::EVENT_CREATE:
$title = e('New attachment on task #%d: %s', $event_data['file']['task_id'], $event_data['file']['name']);
break;
case Comment::EVENT_CREATE:
$title = e('New comment on task #%d', $event_data['comment']['task_id']);
break;
case Comment::EVENT_UPDATE:
$title = e('Comment updated on task #%d', $event_data['comment']['task_id']);
break;
case Subtask::EVENT_CREATE:
$title = e('New subtask on task #%d', $event_data['subtask']['task_id']);
break;
case Subtask::EVENT_UPDATE:
$title = e('Subtask updated on task #%d', $event_data['subtask']['task_id']);
break;
case Task::EVENT_CREATE:
$title = e('New task #%d: %s', $event_data['task']['id'], $event_data['task']['title']);
break;
case Task::EVENT_UPDATE:
$title = e('Task updated #%d', $event_data['task']['id']);
break;
case Task::EVENT_CLOSE:
$title = e('Task #%d closed', $event_data['task']['id']);
break;
case Task::EVENT_OPEN:
$title = e('Task #%d opened', $event_data['task']['id']);
break;
case Task::EVENT_MOVE_COLUMN:
$title = e('Column changed for task #%d', $event_data['task']['id']);
break;
case Task::EVENT_MOVE_POSITION:
$title = e('New position for task #%d', $event_data['task']['id']);
break;
case Task::EVENT_MOVE_SWIMLANE:
$title = e('Swimlane changed for task #%d', $event_data['task']['id']);
break;
case Task::EVENT_ASSIGNEE_CHANGE:
$title = e('Assignee changed on task #%d', $event_data['task']['id']);
break;
case Task::EVENT_OVERDUE:
$nb = count($event_data['tasks']);
$title = $nb > 1 ? e('%d overdue tasks', $nb) : e('Task #%d is overdue', $event_data['tasks'][0]['id']);
break;
default:
$title = e('Notification');
}
return $title;
}
}

View File

@ -108,4 +108,20 @@ abstract class NotificationType extends Base
{
return $this->hiddens;
}
/**
* Keep only loaded notification types
*
* @access public
* @param string[] $types
* @return array
*/
public function filterTypes(array $types)
{
$classes = $this->classes;
return array_filter($types, function($type) use ($classes) {
return isset($classes[$type]);
});
}
}

View File

@ -153,7 +153,7 @@ class ProjectActivity extends Base
unset($event['data']);
$event['author'] = $event['author_name'] ?: $event['author_username'];
$event['event_title'] = $this->getTitle($event);
$event['event_title'] = $this->notification->getTitleWithAuthor($event['author'], $event['event_name'], $event);
$event['event_content'] = $this->getContent($event);
}
@ -195,56 +195,6 @@ class ProjectActivity extends Base
);
}
/**
* Get the event title (translated)
*
* @access public
* @param array $event Event properties
* @return string
*/
public function getTitle(array $event)
{
switch ($event['event_name']) {
case Task::EVENT_ASSIGNEE_CHANGE:
$assignee = $event['task']['assignee_name'] ?: $event['task']['assignee_username'];
if (! empty($assignee)) {
return t('%s change the assignee of the task #%d to %s', $event['author'], $event['task']['id'], $assignee);
}
return t('%s remove the assignee of the task %s', $event['author'], e('#%d', $event['task']['id']));
case Task::EVENT_UPDATE:
return t('%s updated the task #%d', $event['author'], $event['task']['id']);
case Task::EVENT_CREATE:
return t('%s created the task #%d', $event['author'], $event['task']['id']);
case Task::EVENT_CLOSE:
return t('%s closed the task #%d', $event['author'], $event['task']['id']);
case Task::EVENT_OPEN:
return t('%s open the task #%d', $event['author'], $event['task']['id']);
case Task::EVENT_MOVE_COLUMN:
return t('%s moved the task #%d to the column "%s"', $event['author'], $event['task']['id'], $event['task']['column_title']);
case Task::EVENT_MOVE_POSITION:
return t('%s moved the task #%d to the position %d in the column "%s"', $event['author'], $event['task']['id'], $event['task']['position'], $event['task']['column_title']);
case Task::EVENT_MOVE_SWIMLANE:
if ($event['task']['swimlane_id'] == 0) {
return t('%s moved the task #%d to the first swimlane', $event['author'], $event['task']['id']);
}
return t('%s moved the task #%d to the swimlane "%s"', $event['author'], $event['task']['id'], $event['task']['swimlane_name']);
case Subtask::EVENT_UPDATE:
return t('%s updated a subtask for the task #%d', $event['author'], $event['task']['id']);
case Subtask::EVENT_CREATE:
return t('%s created a subtask for the task #%d', $event['author'], $event['task']['id']);
case Comment::EVENT_UPDATE:
return t('%s updated a comment on the task #%d', $event['author'], $event['task']['id']);
case Comment::EVENT_CREATE:
return t('%s commented on the task #%d', $event['author'], $event['task']['id']);
case File::EVENT_CREATE:
return t('%s attached a file to the task #%d', $event['author'], $event['task']['id']);
default:
return '';
}
}
/**
* Decode event data, supports unserialize() and json_decode()
*

View File

@ -1,66 +0,0 @@
<?php
namespace Kanboard\Model;
/**
* Project integration
*
* @package model
* @author Frederic Guillot
*/
class ProjectIntegration extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'project_integrations';
/**
* Get all parameters for a project
*
* @access public
* @param integer $project_id
* @return array
*/
public function getParameters($project_id)
{
return $this->db->table(self::TABLE)->eq('project_id', $project_id)->findOne() ?: array();
}
/**
* Save parameters for a project
*
* @access public
* @param integer $project_id
* @param array $values
* @return boolean
*/
public function saveParameters($project_id, array $values)
{
if ($this->db->table(self::TABLE)->eq('project_id', $project_id)->exists()) {
return $this->db->table(self::TABLE)->eq('project_id', $project_id)->update($values);
}
return $this->db->table(self::TABLE)->insert($values + array('project_id' => $project_id));
}
/**
* Check if a project has the given parameter/value
*
* @access public
* @param integer $project_id
* @param string $option
* @param string $value
* @return boolean
*/
public function hasValue($project_id, $option, $value)
{
return $this->db
->table(self::TABLE)
->eq('project_id', $project_id)
->eq($option, $value)
->exists();
}
}

View File

@ -22,8 +22,44 @@ class ProjectNotification extends Base
{
$project = $this->project->getById($project_id);
foreach ($this->projectNotificationType->getSelectedTypes($project_id) as $type) {
$types = array_merge(
$this->projectNotificationType->getHiddenTypes(),
$this->projectNotificationType->getSelectedTypes($project_id)
);
foreach ($types as $type) {
$this->projectNotificationType->getType($type)->notifyProject($project, $event_name, $event_data);
}
}
/**
* Save settings for the given project
*
* @access public
* @param integer $project_id
* @param array $values
*/
public function saveSettings($project_id, array $values)
{
$this->db->startTransaction();
$types = empty($values['notification_types']) ? array() : array_keys($values['notification_types']);
$this->projectNotificationType->saveSelectedTypes($project_id, $types);
$this->db->closeTransaction();
}
/**
* Read user settings to display the form
*
* @access public
* @param integer $project_id
* @return array
*/
public function readSettings($project_id)
{
return array(
'notification_types' => $this->projectNotificationType->getSelectedTypes($project_id),
);
}
}

View File

@ -26,13 +26,13 @@ class ProjectNotificationType extends NotificationType
*/
public function getSelectedTypes($project_id)
{
$selectedTypes = $this->db
$types = $this->db
->table(self::TABLE)
->eq('project_id', $project_id)
->asc('notification_type')
->findAllByColumn('notification_type');
return array_merge($this->getHiddenTypes(), $selectedTypes);
return $this->filterTypes($types);
}
/**
@ -52,6 +52,6 @@ class ProjectNotificationType extends NotificationType
$results[] = $this->db->table(self::TABLE)->insert(array('project_id' => $project_id, 'notification_type' => $type));
}
return ! in_array(false, $results);
return ! in_array(false, $results, true);
}
}

View File

@ -26,7 +26,8 @@ class UserNotificationType extends NotificationType
*/
public function getSelectedTypes($user_id)
{
return $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('notification_type')->findAllByColumn('notification_type');
$types = $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('notification_type')->findAllByColumn('notification_type');
return $this->filterTypes($types);
}
/**
@ -46,6 +47,6 @@ class UserNotificationType extends NotificationType
$results[] = $this->db->table(self::TABLE)->insert(array('user_id' => $user_id, 'notification_type' => $type));
}
return ! in_array(false, $results);
return ! in_array(false, $results, true);
}
}

View File

@ -48,7 +48,7 @@ class UserUnreadNotification extends Base
foreach ($events as &$event) {
$event['event_data'] = json_decode($event['event_data'], true);
$event['title'] = $this->getTitleFromEvent($event['event_name'], $event['event_data']);
$event['title'] = $this->notification->getTitleWithoutAuthor($event['event_name'], $event['event_data']);
}
return $events;
@ -90,65 +90,4 @@ class UserUnreadNotification extends Base
{
return $this->db->table(self::TABLE)->eq('user_id', $user_id)->exists();
}
/**
* Get title from event
*
* @access public
* @param string $event_name
* @param array $event_data
* @return string
*/
public function getTitleFromEvent($event_name, array $event_data)
{
switch ($event_name) {
case File::EVENT_CREATE:
$title = t('New attachment on task #%d: %s', $event_data['file']['task_id'], $event_data['file']['name']);
break;
case Comment::EVENT_CREATE:
$title = t('New comment on task #%d', $event_data['comment']['task_id']);
break;
case Comment::EVENT_UPDATE:
$title = t('Comment updated on task #%d', $event_data['comment']['task_id']);
break;
case Subtask::EVENT_CREATE:
$title = t('New subtask on task #%d', $event_data['subtask']['task_id']);
break;
case Subtask::EVENT_UPDATE:
$title = t('Subtask updated on task #%d', $event_data['subtask']['task_id']);
break;
case Task::EVENT_CREATE:
$title = t('New task #%d: %s', $event_data['task']['id'], $event_data['task']['title']);
break;
case Task::EVENT_UPDATE:
$title = t('Task updated #%d', $event_data['task']['id']);
break;
case Task::EVENT_CLOSE:
$title = t('Task #%d closed', $event_data['task']['id']);
break;
case Task::EVENT_OPEN:
$title = t('Task #%d opened', $event_data['task']['id']);
break;
case Task::EVENT_MOVE_COLUMN:
$title = t('Column changed for task #%d', $event_data['task']['id']);
break;
case Task::EVENT_MOVE_POSITION:
$title = t('New position for task #%d', $event_data['task']['id']);
break;
case Task::EVENT_MOVE_SWIMLANE:
$title = t('Swimlane changed for task #%d', $event_data['task']['id']);
break;
case Task::EVENT_ASSIGNEE_CHANGE:
$title = t('Assignee changed on task #%d', $event_data['task']['id']);
break;
case Task::EVENT_OVERDUE:
$nb = count($event_data['tasks']);
$title = $nb > 1 ? t('%d overdue tasks', $nb) : t('Task #%d is overdue', $event_data['tasks'][0]['id']);
break;
default:
$title = e('Notification');
}
return $title;
}
}

View File

@ -35,7 +35,6 @@ class ActivityStream extends Base implements NotificationInterface
public function notifyProject(array $project, $event_name, array $event_data)
{
if ($this->userSession->isLogged()) {
$this->projectActivity->createEvent(
$project['id'],
$event_data['task']['id'],
@ -43,16 +42,6 @@ class ActivityStream extends Base implements NotificationInterface
$event_name,
$event_data
);
// TODO: need to be moved to external plugins
foreach (array('slackWebhook', 'hipchatWebhook', 'jabber') as $model) {
$this->$model->notify(
$project['id'],
$event_data['task']['id'],
$event_name,
$event_data
);
}
}
}
}

View File

@ -39,6 +39,23 @@ function version_93($pdo)
UNIQUE(task_id, name)
) ENGINE=InnoDB CHARSET=utf8
");
$pdo->exec("DROP TABLE project_integrations");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_server'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_domain'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_username'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_password'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_nickname'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_room'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_hipchat'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_hipchat_api_url'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_hipchat_room_id'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_hipchat_room_token'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_slack_webhook'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_slack_webhook_url'");
$pdo->exec("DELETE FROM settings WHERE `option`='integration_slack_webhook_channel'");
}
function version_92($pdo)

View File

@ -39,6 +39,23 @@ function version_73($pdo)
UNIQUE(task_id, name)
)
");
$pdo->exec("DROP TABLE project_integrations");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_server'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_domain'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_username'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_password'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_nickname'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_room'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_api_url'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_room_id'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_room_token'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook_url'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook_channel'");
}
function version_72($pdo)

View File

@ -39,6 +39,23 @@ function version_88($pdo)
UNIQUE(task_id, name)
)
");
$pdo->exec("DROP TABLE project_integrations");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_server'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_domain'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_username'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_password'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_nickname'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_room'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_api_url'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_room_id'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_room_token'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook_url'");
$pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook_channel'");
}
function version_87($pdo)

View File

@ -31,6 +31,7 @@ class ClassProvider implements ServiceProviderInterface
'File',
'LastLogin',
'Link',
'Notification',
'OverdueNotification',
'Project',
'ProjectActivity',
@ -38,7 +39,6 @@ class ClassProvider implements ServiceProviderInterface
'ProjectDuplication',
'ProjectDailyColumnStats',
'ProjectDailyStats',
'ProjectIntegration',
'ProjectPermission',
'ProjectNotification',
'ProjectMetadata',
@ -98,9 +98,6 @@ class ClassProvider implements ServiceProviderInterface
'BitbucketWebhook',
'GithubWebhook',
'GitlabWebhook',
'HipchatWebhook',
'Jabber',
'SlackWebhook',
)
);

View File

@ -31,60 +31,6 @@
<?= $this->form->checkbox('integration_gravatar', t('Enable Gravatar images'), 1, $values['integration_gravatar'] == 1) ?>
</div>
<h3><img src="<?= $this->url->dir() ?>assets/img/jabber-icon.png"/> <?= t('Jabber (XMPP)') ?></h3>
<div class="listing">
<?= $this->form->checkbox('integration_jabber', t('Send notifications to Jabber'), 1, $values['integration_jabber'] == 1) ?>
<?= $this->form->label(t('XMPP server address'), 'integration_jabber_server') ?>
<?= $this->form->text('integration_jabber_server', $values, $errors, array('placeholder="tcp://myserver:5222"')) ?>
<p class="form-help"><?= t('The server address must use this format: "tcp://hostname:5222"') ?></p>
<?= $this->form->label(t('Jabber domain'), 'integration_jabber_domain') ?>
<?= $this->form->text('integration_jabber_domain', $values, $errors, array('placeholder="example.com"')) ?>
<?= $this->form->label(t('Username'), 'integration_jabber_username') ?>
<?= $this->form->text('integration_jabber_username', $values, $errors) ?>
<?= $this->form->label(t('Password'), 'integration_jabber_password') ?>
<?= $this->form->password('integration_jabber_password', $values, $errors) ?>
<?= $this->form->label(t('Jabber nickname'), 'integration_jabber_nickname') ?>
<?= $this->form->text('integration_jabber_nickname', $values, $errors) ?>
<?= $this->form->label(t('Multi-user chat room'), 'integration_jabber_room') ?>
<?= $this->form->text('integration_jabber_room', $values, $errors, array('placeholder="myroom@conference.example.com"')) ?>
<p class="form-help"><?= $this->url->doc(t('Help on Jabber integration'), 'jabber') ?></p>
</div>
<h3><img src="<?= $this->url->dir() ?>assets/img/hipchat-icon.png"/> <?= t('Hipchat') ?></h3>
<div class="listing">
<?= $this->form->checkbox('integration_hipchat', t('Send notifications to Hipchat'), 1, $values['integration_hipchat'] == 1) ?>
<?= $this->form->label(t('API URL'), 'integration_hipchat_api_url') ?>
<?= $this->form->text('integration_hipchat_api_url', $values, $errors) ?>
<?= $this->form->label(t('Room API ID or name'), 'integration_hipchat_room_id') ?>
<?= $this->form->text('integration_hipchat_room_id', $values, $errors) ?>
<?= $this->form->label(t('Room notification token'), 'integration_hipchat_room_token') ?>
<?= $this->form->text('integration_hipchat_room_token', $values, $errors) ?>
<p class="form-help"><?= $this->url->doc(t('Help on Hipchat integration'), 'hipchat') ?></p>
</div>
<h3><i class="fa fa-slack fa-fw"></i>&nbsp;<?= t('Slack') ?></h3>
<div class="listing">
<?= $this->form->checkbox('integration_slack_webhook', t('Send notifications to a Slack channel'), 1, $values['integration_slack_webhook'] == 1) ?>
<?= $this->form->label(t('Webhook URL'), 'integration_slack_webhook_url') ?>
<?= $this->form->text('integration_slack_webhook_url', $values, $errors) ?>
<?= $this->form->label(t('Channel/Group/User (Optional)'), 'integration_slack_webhook_channel') ?>
<?= $this->form->text('integration_slack_webhook_channel', $values, $errors) ?>
<p class="form-help"><?= $this->url->doc(t('Help on Slack integration'), 'slack') ?></p>
</div>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
</div>

View File

@ -2,9 +2,10 @@
<h2><?= t('Integration with third-party services') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('project', 'integration', array('project_id' => $project['id'])) ?>" autocomplete="off">
<form method="post" action="<?= $this->url->href('project', 'integrations', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->hook->render('template:project:integrations', array('values' => $values)) ?>
<h3><i class="fa fa-github fa-fw"></i>&nbsp;<?= t('Github webhooks') ?></h3>
<div class="listing">
@ -12,86 +13,15 @@
<p class="form-help"><?= $this->url->doc(t('Help on Github webhooks'), 'github-webhooks') ?></p>
</div>
<h3><img src="<?= $this->url->dir() ?>assets/img/gitlab-icon.png"/>&nbsp;<?= t('Gitlab webhooks') ?></h3>
<div class="listing">
<input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'gitlab', array('token' => $webhook_token, 'project_id' => $project['id']), false, '', true) ?>"/><br/>
<p class="form-help"><?= $this->url->doc(t('Help on Gitlab webhooks'), 'gitlab-webhooks') ?></p>
</div>
<h3><i class="fa fa-bitbucket fa-fw"></i>&nbsp;<?= t('Bitbucket webhooks') ?></h3>
<div class="listing">
<input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->href('webhook', 'bitbucket', array('token' => $webhook_token, 'project_id' => $project['id']), false, '', true) ?>"/><br/>
<p class="form-help"><?= $this->url->doc(t('Help on Bitbucket webhooks'), 'bitbucket-webhooks') ?></p>
</div>
<h3><img src="<?= $this->url->dir() ?>assets/img/jabber-icon.png"/> <?= t('Jabber (XMPP)') ?></h3>
<div class="listing">
<?= $this->form->checkbox('jabber', t('Send notifications to Jabber'), 1, isset($values['jabber']) && $values['jabber'] == 1) ?>
<?= $this->form->label(t('XMPP server address'), 'jabber_server') ?>
<?= $this->form->text('jabber_server', $values, $errors, array('placeholder="tcp://myserver:5222"')) ?>
<p class="form-help"><?= t('The server address must use this format: "tcp://hostname:5222"') ?></p>
<?= $this->form->label(t('Jabber domain'), 'jabber_domain') ?>
<?= $this->form->text('jabber_domain', $values, $errors, array('placeholder="example.com"')) ?>
<?= $this->form->label(t('Username'), 'jabber_username') ?>
<?= $this->form->text('jabber_username', $values, $errors) ?>
<?= $this->form->label(t('Password'), 'jabber_password') ?>
<?= $this->form->password('jabber_password', $values, $errors) ?>
<?= $this->form->label(t('Jabber nickname'), 'jabber_nickname') ?>
<?= $this->form->text('jabber_nickname', $values, $errors) ?>
<?= $this->form->label(t('Multi-user chat room'), 'jabber_room') ?>
<?= $this->form->text('jabber_room', $values, $errors, array('placeholder="myroom@conference.example.com"')) ?>
<p class="form-help"><?= $this->url->doc(t('Help on Jabber integration'), 'jabber') ?></p>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
</div>
</div>
<h3><img src="<?= $this->url->dir() ?>assets/img/hipchat-icon.png"/> <?= t('Hipchat') ?></h3>
<div class="listing">
<?= $this->form->checkbox('hipchat', t('Send notifications to Hipchat'), 1, isset($values['hipchat']) && $values['hipchat'] == 1) ?>
<?= $this->form->label(t('API URL'), 'hipchat_api_url') ?>
<?= $this->form->text('hipchat_api_url', $values, $errors) ?>
<?= $this->form->label(t('Room API ID or name'), 'hipchat_room_id') ?>
<?= $this->form->text('hipchat_room_id', $values, $errors) ?>
<?= $this->form->label(t('Room notification token'), 'hipchat_room_token') ?>
<?= $this->form->text('hipchat_room_token', $values, $errors) ?>
<p class="form-help"><?= $this->url->doc(t('Help on Hipchat integration'), 'hipchat') ?></a></p>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
</div>
</div>
<h3><i class="fa fa-slack fa-fw"></i>&nbsp;<?= t('Slack') ?></h3>
<div class="listing">
<?= $this->form->checkbox('slack', t('Send notifications to a Slack channel'), 1, isset($values['slack']) && $values['slack'] == 1) ?>
<?= $this->form->label(t('Webhook URL'), 'slack_webhook_url') ?>
<?= $this->form->text('slack_webhook_url', $values, $errors) ?>
<?= $this->form->label(t('Channel/Group/User (Optional)'), 'slack_webhook_channel') ?>
<?= $this->form->text('slack_webhook_channel', $values, $errors) ?>
<p class="form-help"><?= $this->url->doc(t('Help on Slack integration'), 'slack') ?></p>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
</div>
</div>
</form>

View File

@ -0,0 +1,20 @@
<div class="page-header">
<h2><?= t('Notifications') ?></h2>
</div>
<?php if (empty($types)): ?>
<p class="alert"><?= t('There is no notification method registered.') ?></p>
<?php else: ?>
<form method="post" action="<?= $this->url->href('project', 'notifications', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<h4><?= t('Notification methods:') ?></h4>
<?= $this->form->checkboxes('notification_types', $types, $notifications) ?>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
<?= t('or') ?>
<?= $this->url->link(t('cancel'), 'project', 'show', array('project_id' => $project['id'])) ?>
</div>
</form>
<?php endif ?>

View File

@ -12,8 +12,11 @@
<li <?= $this->app->getRouterController() === 'project' && $this->app->getRouterAction() === 'share' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('Public access'), 'project', 'share', array('project_id' => $project['id'])) ?>
</li>
<li <?= $this->app->getRouterController() === 'project' && $this->app->getRouterAction() === 'integration' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('Integrations'), 'project', 'integration', array('project_id' => $project['id'])) ?>
<li <?= $this->app->getRouterController() === 'project' && $this->app->getRouterAction() === 'notifications' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('Notifications'), 'project', 'notifications', array('project_id' => $project['id'])) ?>
</li>
<li <?= $this->app->getRouterController() === 'project' && $this->app->getRouterAction() === 'integrations' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('Integrations'), 'project', 'integrations', array('project_id' => $project['id'])) ?>
</li>
<li <?= $this->app->getRouterController() === 'project' && $this->app->getRouterAction() === 'edit' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('Edit project'), 'project', 'edit', array('project_id' => $project['id'])) ?>

View File

@ -0,0 +1,13 @@
<div class="page-header">
<h2><?= t('Integrations') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('user', 'integrations', array('user_id' => $user['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?php $hooks = $this->hook->render('template:user:integrations', array('values' => $values)) ?>
<?php if (! empty($hooks)): ?>
<?= $hooks ?>
<?php else: ?>
<p class="alert"><?= t('No external integration registered.') ?></p>
<?php endif ?>
</form>

View File

@ -56,6 +56,9 @@
<li <?= $this->app->getRouterController() === 'user' && $this->app->getRouterAction() === 'external' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('External accounts'), 'user', 'external', array('user_id' => $user['id'])) ?>
</li>
<li <?= $this->app->getRouterController() === 'user' && $this->app->getRouterAction() === 'integrations' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('Integrations'), 'user', 'integrations', array('user_id' => $user['id'])) ?>
</li>
<?php endif ?>
<?php if ($this->user->isAdmin()): ?>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -4,12 +4,9 @@ Notifications
Kanboard est capable d'envoyer des notifications via différents canaux :
- Email
- Jabber/XMPP
- Hipchat
- Slack
- Web (Liste de message non lus)
En fait, pour Jabber/Hipchat/Slack les notifications sont envoyées dans des salons de discussion car ils sont configurés au niveau projet.
Cependant, les notifications par email sont envoyées à un individu.
Vous pouvez ajouter d'autres cannaux an ajoutant des extensions comme par exemple Hipchat, Slack ou encore Jabber.
Configuration
--------------
@ -26,7 +23,7 @@ Vous pouvez choisir votre méthode favorite de notification :
- Web
Pour chaque projet dont vous êtes membre, vous pouvez choisir de recevoir des notifications pour :
- Toutes les tâches
- Seulement les tâches qui vous sont assignées
- Seulement les tâches que vous avez créées
@ -41,7 +38,7 @@ Les notifications web sont accéssibles depuis le tableau de bord ou depuis l'ic
![Icône des notifications web](http://kanboard.net/screenshots/documentation/web-notifications-icon.png)
Les notifications sont affichés sous forme de liste. Vous pouvez marquer comme lu chacune d'entre-elle ou toutes en même temps.
Les notifications sont affichés sous forme de liste. Vous pouvez marquer comme lu chacune d'entre-elle ou toutes en même temps.
![Notifications web](http://kanboard.net/screenshots/documentation/web-notifications.png)

View File

@ -1,38 +0,0 @@
Hipchat integration
===================
You can send notifications to Hipchat for all projects or only for specific projects.
- To send notifications for all projects, go to **Settings > Integrations > Hipchat**
- To send notifications for only some projects, go to **Project settings > Integrations > Hipchat**
Each project can send notifications to a separate room.
Send notifications to a room
-----------------------------
Example of notifications:
![Hipchat notification](http://kanboard.net/screenshots/documentation/hipchat-notification.png)
This feature use the room notification token system of Hipchat.
### Hipchat configuration
![Hipchat room token](http://kanboard.net/screenshots/documentation/hipchat-room-token.png)
1. Go to to **My account**
2. Click on the tab **Rooms** and select the room you want to send the notifications
3. On the left, choose **Tokens**
4. Enter a label, by example "Kanboard" and save
### Kanboard configuration
![Hipchat settings](http://kanboard.net/screenshots/documentation/hipchat-settings.png)
1. Go to **Settings > Integrations > Hipchat** or **Project settings > Integrations > Hipchat**
2. Replace the API url if you use the self-hosted version of Hipchat
3. Set the room name or the room API ID
4. Copy and paste the token generated previously
Now, Kanboard events will be sent to the Hipchat room.

View File

@ -66,9 +66,6 @@ Using Kanboard
- [Bitbucket webhooks](bitbucket-webhooks.markdown)
- [Github webhooks](github-webhooks.markdown)
- [Gitlab webhooks](gitlab-webhooks.markdown)
- [Hipchat](hipchat.markdown)
- [Jabber](jabber.markdown)
- [Slack](slack.markdown)
- [iCalendar subscriptions](ical.markdown)
- [RSS/Atom subscriptions](rss.markdown)
- [Json-RPC API](api-json-rpc.markdown)

View File

@ -1,34 +0,0 @@
Jabber/XMPP integration
=======================
You can send notifications to a Jabber room for all projects or only for specific projects.
- To send notifications for all projects, go to **Settings > Integrations > Jabber**
- To send notifications for only some projects, go to **Project settings > Integrations > Jabber**
Each project can send notifications to a separate room.
## Example of notification
Here an example with the Jabber client Adium:
![Jabber notification](http://kanboard.net/screenshots/documentation/jabber-notification.png)
## Configuration
![Jabber settings](http://kanboard.net/screenshots/documentation/jabber-settings.png)
1. Go to **Settings > Integrations > Jabber** or **Project settings > Integrations > Jabber**
2. **XMPP server address**: URL of the XMPP server, example: **tcp://172.28.128.3:5222**
3. **Jabber domain**: The **"to"** attribute of the XMPP protocol, example: **example.com**
4. **Username**: The Jabber username used by Kanboard, example: **kanboard**
5. **Password**: The Jabber password
6. **Jabber nickname**: The nickname used to connect to the room
7. **Multi-user chat room**: The address of the room, example: **demo@conference.example.com**
Now, Kanboard events will be sent to the Jabber conference room.
## Troubleshooting
- Enable the debug mode
- All connection errors with the XMPP server are recorded in the log files `data/debug.log` or syslog

View File

@ -4,14 +4,9 @@ Notifications
Kanboard is able to send notifications through several channels:
- Email
- Web
- Jabber/XMPP
- Hipchat
- Slack
- Web (List of unread messages)
Actually, Jabber/Hipchat/Slack notifications are sent to a room or group channel because they are configured at the project level.
However, email or web notifications are sent to an individual person.
External plugins allow you to send notifications to Slack, Hipchat, Jabber or any chat system.
Configuration
-------------

View File

@ -134,6 +134,8 @@ List of template hooks:
- `template:dashboard:sidebar`
- `template:config:sidebar`
- `template:config:integrations`
- `template:project:integrations`
- `template:user:integrations`
- `template:export:sidebar`
- `template:layout:head`
- `template:layout:top`

View File

@ -0,0 +1,60 @@
Add Notification Types with Plugins
===================================
You can send notifications to almost any system by adding a new type.
There are two kinds of notifications: project and user.
- Project: Notifications configured at the project level
- User: Notifications sent individually and configured at the user profile
Register a new notification type
--------------------------------
In your plugin registration file call the method `setType()`:
```php
$this->userNotificationType->setType('irc', t('IRC'), '\Kanboard\Plugin\IRC\Notification\IrcHandler');
$this->projectNotificationType->setType('irc', t('IRC'), '\Kanboard\Plugin\IRC\Notification\IrcHandler');
```
Your handler can be registered for user or project notification. You don't necessary need to support both.
When your handler is registered, the end-user can choose to receive the new notification type or not.
Notification Handler
--------------------
Your notification handler must implements the interface `Kanboard\Notification\NotificationInterface`:
```php
interface NotificationInterface
{
/**
* Send notification to a user
*
* @access public
* @param array $user
* @param string $event_name
* @param array $event_data
*/
public function notifyUser(array $user, $event_name, array $event_data);
/**
* Send notification to a project
*
* @access public
* @param array $project
* @param string $event_name
* @param array $event_data
*/
public function notifyProject(array $project, $event_name, array $event_data);
}
```
Example of notification plugins
-------------------------------
- [Slack](https://github.com/kanboard/plugin-slack)
- [Hipchat](https://github.com/kanboard/plugin-hipchat)
- [Jabber](https://github.com/kanboard/plugin-jabber)

View File

@ -156,8 +156,8 @@ class Plugin extends Base
public function initialize()
{
$this->action->extendActions(
'\Kanboard\Plugin\AutomaticAction\Action\SendSlackMessage', // Use absolute namespace
t('Send a message to Slack when the task color change')
'\Kanboard\Plugin\AutomaticAction\Action\DoSomething', // Use absolute namespace
t('Do something when the task color change')
);
}
}

View File

@ -12,10 +12,14 @@ Plugin creators should specify explicitly the compatible versions of Kanboard. I
- [Override default application behaviors](plugin-overrides.markdown)
- [Add schema migrations for plugins](plugin-schema-migrations.markdown)
- [Add mail transports](plugin-mail-transports.markdown)
- [Add notification types](plugin-notifications.markdown)
Examples of plugins
-------------------
- [Slack](https://github.com/kanboard/plugin-slack)
- [Hipchat](https://github.com/kanboard/plugin-hipchat)
- [Jabber](https://github.com/kanboard/plugin-jabber)
- [Sendgrid](https://github.com/kanboard/plugin-sendgrid)
- [Mailgun](https://github.com/kanboard/plugin-mailgun)
- [Postmark](https://github.com/kanboard/plugin-postmark)

View File

@ -1,38 +0,0 @@
Slack integration
=================
You can send notifications to Slack for all projects or only for specific projects.
- To send notifications for all projects, go to **Settings > Integrations > Slack**
- To send notifications for only some projects, go to **Project settings > Integrations > Slack**
Each project can send notifications to a separate channel.
Send notifications to a channel
-------------------------------
Example of notifications:
![Slack notification](http://kanboard.net/screenshots/documentation/slack-notification.png)
This feature use the [Incoming webhook](https://api.slack.com/incoming-webhooks) system of Slack.
### Slack configuration
![Slack webhook creation](http://kanboard.net/screenshots/documentation/slack-add-incoming-webhook.png)
1. Click on the Team dropdown and choose **Configure Integrations**
2. On the list of services, scroll-down and choose **DIY Integrations & Customizations > Incoming WebHooks**
3. Copy the webhook url to the Kanboard settings page: **Settings > Integrations > Slack** or **Project settings > Integrations > Slack**
Now, Kanboard events will be sent to the Slack channel.
### Overriding Channel (Optional)
Optionally you can override the channel, private group or send direct messages by filling up **Channel/Group/User** text box. Leaving it empty will post to the channel configured during webhook configuration.
Examples:
- Send messages to another channel: **#mychannel1**
- Send messages to a private group: **#myprivategroup1**
- Send messages directly to someone: **@anotheruser1**

View File

@ -91,7 +91,7 @@ abstract class Base extends PHPUnit_Framework_TestCase
$this->container['userNotificationType'] = $this
->getMockBuilder('\Kanboard\Model\UserNotificationType')
->setConstructorArgs(array($this->container))
->setMethods(array('getType'))
->setMethods(array('getType', 'getSelectedTypes'))
->getMock();
}

View File

@ -1,377 +0,0 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\Integration\SlackWebhook;
use Kanboard\Model\Project;
use Kanboard\Model\Task;
class SlackWebhookTest extends Base
{
public function testIsActivatedFromGlobalConfig()
{
$slack = new SlackWebhook($this->container);
$this->container['config'] = $this
->getMockBuilder('\Kanboard\Model\Config')
->setConstructorArgs(array($this->container))
->setMethods(array(
'get',
))
->getMock();
$this->container['config']
->expects($this->once())
->method('get')
->with($this->equalTo('integration_slack_webhook'))
->will($this->returnValue(1));
$this->assertTrue($slack->isActivated(1));
}
public function testIsActivatedFromProjectConfig()
{
$slack = new SlackWebhook($this->container);
$this->container['config'] = $this
->getMockBuilder('\Kanboard\Model\Config')
->setConstructorArgs(array($this->container))
->setMethods(array(
'get',
))
->getMock();
$this->container['projectIntegration'] = $this
->getMockBuilder('\Kanboard\Model\ProjectIntegration')
->setConstructorArgs(array($this->container))
->setMethods(array(
'hasValue',
))
->getMock();
$this->container['config']
->expects($this->once())
->method('get')
->with($this->equalTo('integration_slack_webhook'))
->will($this->returnValue(0));
$this->container['projectIntegration']
->expects($this->once())
->method('hasValue')
->with(
$this->equalTo(1),
$this->equalTo('slack'),
$this->equalTo(1)
)
->will($this->returnValue(true));
$this->assertTrue($slack->isActivated(1));
}
public function testIsNotActivated()
{
$slack = new SlackWebhook($this->container);
$this->container['config'] = $this
->getMockBuilder('\Kanboard\Model\Config')
->setConstructorArgs(array($this->container))
->setMethods(array(
'get',
))
->getMock();
$this->container['projectIntegration'] = $this
->getMockBuilder('\Kanboard\Model\ProjectIntegration')
->setConstructorArgs(array($this->container))
->setMethods(array(
'hasValue',
))
->getMock();
$this->container['config']
->expects($this->once())
->method('get')
->with($this->equalTo('integration_slack_webhook'))
->will($this->returnValue(0));
$this->container['projectIntegration']
->expects($this->once())
->method('hasValue')
->with(
$this->equalTo(1),
$this->equalTo('slack'),
$this->equalTo(1)
)
->will($this->returnValue(false));
$this->assertFalse($slack->isActivated(1));
}
public function testGetChannelFromGlobalConfig()
{
$slack = new SlackWebhook($this->container);
$this->container['config'] = $this
->getMockBuilder('\Kanboard\Model\Config')
->setConstructorArgs(array($this->container))
->setMethods(array(
'get',
))
->getMock();
$this->container['config']
->expects($this->once())
->method('get')
->with($this->equalTo('integration_slack_webhook_channel'))
->will($this->returnValue('mychannel'));
$this->assertEquals('mychannel', $slack->getChannel(1));
}
public function testGetChannelFromProjectConfig()
{
$slack = new SlackWebhook($this->container);
$this->container['config'] = $this
->getMockBuilder('\Kanboard\Model\Config')
->setConstructorArgs(array($this->container))
->setMethods(array(
'get',
))
->getMock();
$this->container['projectIntegration'] = $this
->getMockBuilder('\Kanboard\Model\ProjectIntegration')
->setConstructorArgs(array($this->container))
->setMethods(array(
'getParameters',
))
->getMock();
$this->container['config']
->expects($this->once())
->method('get')
->with($this->equalTo('integration_slack_webhook_channel'))
->will($this->returnValue(''));
$this->container['projectIntegration']
->expects($this->once())
->method('getParameters')
->with($this->equalTo(1))
->will($this->returnValue(array('slack_webhook_channel' => 'my_project_channel')));
$this->assertEquals('my_project_channel', $slack->getChannel(1));
}
public function testGetWebhoookUrlFromGlobalConfig()
{
$slack = new SlackWebhook($this->container);
$this->container['config'] = $this
->getMockBuilder('\Kanboard\Model\Config')
->setConstructorArgs(array($this->container))
->setMethods(array(
'get',
))
->getMock();
$this->container['config']
->expects($this->at(0))
->method('get')
->with($this->equalTo('integration_slack_webhook'))
->will($this->returnValue(1));
$this->container['config']
->expects($this->at(1))
->method('get')
->with($this->equalTo('integration_slack_webhook_url'))
->will($this->returnValue('url'));
$this->assertEquals('url', $slack->getWebhookUrl(1));
}
public function testGetWebhookUrlFromProjectConfig()
{
$slack = new SlackWebhook($this->container);
$this->container['config'] = $this
->getMockBuilder('\Kanboard\Model\Config')
->setConstructorArgs(array($this->container))
->setMethods(array(
'get',
))
->getMock();
$this->container['projectIntegration'] = $this
->getMockBuilder('\Kanboard\Model\ProjectIntegration')
->setConstructorArgs(array($this->container))
->setMethods(array(
'getParameters',
))
->getMock();
$this->container['config']
->expects($this->once())
->method('get')
->with($this->equalTo('integration_slack_webhook'))
->will($this->returnValue(0));
$this->container['projectIntegration']
->expects($this->once())
->method('getParameters')
->with($this->equalTo(1))
->will($this->returnValue(array('slack_webhook_url' => 'my_project_url')));
$this->assertEquals('my_project_url', $slack->getWebhookUrl(1));
}
public function testSendPayloadWithChannel()
{
$this->container['httpClient'] = $this
->getMockBuilder('\Kanboard\Core\HttpClient')
->setConstructorArgs(array($this->container))
->setMethods(array(
'postJson',
))
->getMock();
$slack = $this
->getMockBuilder('\Kanboard\Integration\SlackWebhook')
->setConstructorArgs(array($this->container))
->setMethods(array(
'getChannel',
'getWebhookUrl',
))
->getMock();
$slack
->expects($this->at(0))
->method('getChannel')
->with(
$this->equalTo(1)
)
->will($this->returnValue('mychannel'));
$slack
->expects($this->at(1))
->method('getWebhookUrl')
->with(
$this->equalTo(1)
)
->will($this->returnValue('url'));
$this->container['httpClient']
->expects($this->once())
->method('postJson')
->with(
$this->equalTo('url'),
$this->equalTo(array('text' => 'test', 'channel' => 'mychannel'))
);
$slack->sendPayload(1, array('text' => 'test'));
}
public function testSendPayloadWithoutChannel()
{
$this->container['httpClient'] = $this
->getMockBuilder('\Kanboard\Core\HttpClient')
->setConstructorArgs(array($this->container))
->setMethods(array(
'postJson',
))
->getMock();
$slack = $this
->getMockBuilder('\Kanboard\Integration\SlackWebhook')
->setConstructorArgs(array($this->container))
->setMethods(array(
'getChannel',
'getWebhookUrl',
))
->getMock();
$slack
->expects($this->at(0))
->method('getChannel')
->with(
$this->equalTo(1)
)
->will($this->returnValue(''));
$slack
->expects($this->at(1))
->method('getWebhookUrl')
->with(
$this->equalTo(1)
)
->will($this->returnValue('url'));
$this->container['httpClient']
->expects($this->once())
->method('postJson')
->with(
$this->equalTo('url'),
$this->equalTo(array('text' => 'test'))
);
$slack->sendPayload(1, array('text' => 'test'));
}
public function testSendMessage()
{
$message = 'test';
$payload = array(
'text' => $message,
'username' => 'Kanboard',
'icon_url' => 'http://kanboard.net/assets/img/favicon.png',
);
$slack = $this
->getMockBuilder('\Kanboard\Integration\SlackWebhook')
->setConstructorArgs(array($this->container))
->setMethods(array(
'sendPayload',
))
->getMock();
$slack
->expects($this->once())
->method('sendPayload')
->with(
$this->equalTo(1),
$this->equalTo($payload)
);
$slack->sendMessage(1, $message);
}
public function testNotify()
{
$message = '*[foobar]* FooBar created the task #1 (task #1)';
$this->container['session']['user'] = array('username' => 'foobar', 'name' => 'FooBar');
$p = new Project($this->container);
$this->assertEquals(1, $p->create(array('name' => 'foobar')));
$this->assertTrue($this->container['config']->save(array('integration_slack_webhook' => 1)));
$slack = $this
->getMockBuilder('\Kanboard\Integration\SlackWebhook')
->setConstructorArgs(array($this->container))
->setMethods(array(
'sendMessage',
))
->getMock();
$slack
->expects($this->once())
->method('sendMessage')
->with(
$this->equalTo(1),
$this->equalTo($message)
);
$slack->notify(1, 1, Task::EVENT_CREATE, array('task' => array('id' => 1, 'title' => 'task #1')));
}
}

View File

@ -0,0 +1,69 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\Model\TaskFinder;
use Kanboard\Model\TaskCreation;
use Kanboard\Model\Subtask;
use Kanboard\Model\Comment;
use Kanboard\Model\User;
use Kanboard\Model\File;
use Kanboard\Model\Task;
use Kanboard\Model\Project;
use Kanboard\Model\Notification;
use Kanboard\Subscriber\NotificationSubscriber;
class NotificationTest extends Base
{
public function testGetTitle()
{
$wn = new Notification($this->container);
$p = new Project($this->container);
$tf = new TaskFinder($this->container);
$tc = new TaskCreation($this->container);
$s = new Subtask($this->container);
$c = new Comment($this->container);
$f = new File($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
$this->assertEquals(1, $s->create(array('title' => 'test', 'task_id' => 1)));
$this->assertEquals(1, $c->create(array('comment' => 'test', 'task_id' => 1, 'user_id' => 1)));
$this->assertEquals(1, $f->create(1, 'test', 'blah', 123));
$task = $tf->getDetails(1);
$subtask = $s->getById(1, true);
$comment = $c->getById(1);
$file = $c->getById(1);
$this->assertNotEmpty($task);
$this->assertNotEmpty($subtask);
$this->assertNotEmpty($comment);
$this->assertNotEmpty($file);
foreach (NotificationSubscriber::getSubscribedEvents() as $event_name => $values) {
$title = $wn->getTitleWithoutAuthor($event_name, array(
'task' => $task,
'comment' => $comment,
'subtask' => $subtask,
'file' => $file,
'changes' => array()
));
$this->assertNotEmpty($title);
$title = $wn->getTitleWithAuthor('foobar', $event_name, array(
'task' => $task,
'comment' => $comment,
'subtask' => $subtask,
'file' => $file,
'changes' => array()
));
$this->assertNotEmpty($title);
}
$this->assertNotEmpty($wn->getTitleWithoutAuthor(Task::EVENT_OVERDUE, array('tasks' => array(array('id' => 1)))));
$this->assertNotEmpty($wn->getTitleWithoutAuthor('unkown', array()));
}
}

View File

@ -10,49 +10,9 @@ use Kanboard\Model\Project;
use Kanboard\Model\Subtask;
use Kanboard\Model\Comment;
use Kanboard\Model\File;
use Kanboard\Subscriber\NotificationSubscriber;
class ProjectActivityTest extends Base
{
public function testGetTitle()
{
$pa = new ProjectActivity($this->container);
$p = new Project($this->container);
$tf = new TaskFinder($this->container);
$tc = new TaskCreation($this->container);
$s = new Subtask($this->container);
$c = new Comment($this->container);
$f = new File($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
$this->assertEquals(1, $s->create(array('title' => 'test', 'task_id' => 1)));
$this->assertEquals(1, $c->create(array('comment' => 'test', 'task_id' => 1, 'user_id' => 1)));
$this->assertEquals(1, $f->create(1, 'test', 'blah', 123));
$task = $tf->getDetails(1);
$subtask = $s->getById(1, true);
$comment = $c->getById(1);
$file = $c->getById(1);
$this->assertNotEmpty($task);
$this->assertNotEmpty($subtask);
$this->assertNotEmpty($comment);
$this->assertNotEmpty($file);
foreach (NotificationSubscriber::getSubscribedEvents() as $event_name => $listeners) {
$this->assertNotEmpty($pa->getTitle(array(
'event_name' => $event_name,
'task' => $task,
'comment' => $comment,
'subtask' => $subtask,
'file' => $file,
'author' => 'bob',
'changes' => array())
));
}
}
public function testDecode()
{
$e = new ProjectActivity($this->container);

View File

@ -32,10 +32,15 @@ class ProjectNotificationTypeTest extends Base
// Hidden type
$nt->setType('baz', 'Baz', 'Something3', true);
$this->assertEquals(array('baz'), $nt->getSelectedTypes(1));
$this->assertEmpty($nt->getSelectedTypes(1));
// User defined types
// User defined types but not registered
$this->assertTrue($nt->saveSelectedTypes(1, array('foo', 'bar')));
$this->assertEquals(array('baz', 'bar', 'foo'), $nt->getSelectedTypes(1));
$this->assertEmpty($nt->getSelectedTypes(1));
// User defined types and registered
$nt->setType('bar', 'Bar', 'Something4');
$nt->setType('foo', 'Foo', 'Something3');
$this->assertEquals(array('bar', 'foo'), $nt->getSelectedTypes(1));
}
}

View File

@ -50,6 +50,22 @@ class UserNotificationTest extends Base
'notification_projects' => array(),
));
$this->container['userNotificationType']
->expects($this->at(0))
->method('getSelectedTypes')
->will($this->returnValue(array('email')));
$this->container['userNotificationType']
->expects($this->at(1))
->method('getSelectedTypes')
->will($this->returnValue(array('email')));
$this->container['userNotificationType']
->expects($this->at(2))
->method('getSelectedTypes')
->with($this->equalTo(1))
->will($this->returnValue(array('email', 'web')));
$settings = $n->readSettings(1);
$this->assertNotEmpty($settings);
$this->assertEquals(1, $settings['notifications_enabled']);
@ -183,12 +199,17 @@ class UserNotificationTest extends Base
$this->container['userNotificationType']
->expects($this->at(0))
->method('getSelectedTypes')
->will($this->returnValue(array('email', 'web')));
$this->container['userNotificationType']
->expects($this->at(1))
->method('getType')
->with($this->equalTo('email'))
->will($this->returnValue($notifier));
$this->container['userNotificationType']
->expects($this->at(1))
->expects($this->at(2))
->method('getType')
->with($this->equalTo('web'))
->will($this->returnValue($notifier));

View File

@ -19,12 +19,21 @@ class UserNotificationTypeTest extends Base
public function testGetSelectedTypes()
{
$nt = new UserNotificationType($this->container);
$types = $nt->getSelectedTypes(1);
$this->assertEmpty($types);
$this->assertTrue($nt->saveSelectedTypes(1, array('email', 'web')));
$types = $nt->getSelectedTypes(1);
$this->assertNotEmpty($types);
$this->assertEquals(array('email', 'web'), $types);
// No type defined
$this->assertEmpty($nt->getSelectedTypes(1));
// Hidden type
$nt->setType('baz', 'Baz', 'Something3', true);
$this->assertEmpty($nt->getSelectedTypes(1));
// User defined types but not registered
$this->assertTrue($nt->saveSelectedTypes(1, array('foo', 'bar')));
$this->assertEmpty($nt->getSelectedTypes(1));
// User defined types and registered
$nt->setType('bar', 'Bar', 'Something4');
$nt->setType('foo', 'Foo', 'Something3');
$this->assertEquals(array('bar', 'foo'), $nt->getSelectedTypes(1));
}
}

View File

@ -11,52 +11,9 @@ use Kanboard\Model\File;
use Kanboard\Model\Task;
use Kanboard\Model\Project;
use Kanboard\Model\UserUnreadNotification;
use Kanboard\Subscriber\NotificationSubscriber;
class UserUnreadNotificationTest extends Base
{
public function testGetTitle()
{
$wn = new UserUnreadNotification($this->container);
$p = new Project($this->container);
$tf = new TaskFinder($this->container);
$tc = new TaskCreation($this->container);
$s = new Subtask($this->container);
$c = new Comment($this->container);
$f = new File($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
$this->assertEquals(1, $s->create(array('title' => 'test', 'task_id' => 1)));
$this->assertEquals(1, $c->create(array('comment' => 'test', 'task_id' => 1, 'user_id' => 1)));
$this->assertEquals(1, $f->create(1, 'test', 'blah', 123));
$task = $tf->getDetails(1);
$subtask = $s->getById(1, true);
$comment = $c->getById(1);
$file = $c->getById(1);
$this->assertNotEmpty($task);
$this->assertNotEmpty($subtask);
$this->assertNotEmpty($comment);
$this->assertNotEmpty($file);
foreach (NotificationSubscriber::getSubscribedEvents() as $event_name => $values) {
$title = $wn->getTitleFromEvent($event_name, array(
'task' => $task,
'comment' => $comment,
'subtask' => $subtask,
'file' => $file,
'changes' => array()
));
$this->assertNotEmpty($title);
}
$this->assertNotEmpty($wn->getTitleFromEvent(Task::EVENT_OVERDUE, array('tasks' => array(array('id' => 1)))));
$this->assertNotEmpty($wn->getTitleFromEvent('unkown', array()));
}
public function testHasNotification()
{
$wn = new UserUnreadNotification($this->container);