summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2017-02-18 20:21:48 -0500
committerFrederic Guillot <fred@kanboard.net>2017-02-18 20:21:48 -0500
commit11a774e555786182dd634676811e3c58f316dad0 (patch)
tree4dd0092b45d15d1c6ae1db613e37eb9bb009c0ec
parent93c05371a882b3c07d16457479129e42a2999492 (diff)
Send tasks by email
-rw-r--r--ChangeLog1
-rw-r--r--app/Controller/TaskMailController.php55
-rw-r--r--app/ServiceProvider/AuthenticationProvider.php2
-rw-r--r--app/Template/task/dropdown.php3
-rw-r--r--app/Template/task_mail/create.php17
-rw-r--r--app/Template/task_mail/email.php46
-rw-r--r--app/Validator/TaskValidator.php23
-rw-r--r--tests/units/Validator/TaskValidatorTest.php17
8 files changed, 164 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index e7b81e7a..85daac10 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,7 @@ Version 1.0.40 (unreleased)
New features:
* Send comments by email
+* Send tasks by email
* Add Reply-To header to emails sent from Kanboard
* Upload Sqlite database from user interface
diff --git a/app/Controller/TaskMailController.php b/app/Controller/TaskMailController.php
new file mode 100644
index 00000000..e95ddf03
--- /dev/null
+++ b/app/Controller/TaskMailController.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Class TaskMailController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class TaskMailController extends BaseController
+{
+ public function create(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+ $task = $this->getTask();
+
+ $this->response->html($this->helper->layout->task('task_mail/create', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'task' => $task,
+ 'project' => $project,
+ )));
+ }
+
+ public function send()
+ {
+ $task = $this->getTask();
+ $values = $this->request->getValues();
+
+ list($valid, $errors) = $this->taskValidator->validateEmailCreation($values);
+
+ if ($valid) {
+ $this->sendByEmail($values, $task);
+ $this->flash->success(t('Task sent by email successfully.'));
+ $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true);
+ } else {
+ $this->create($values, $errors);
+ }
+ }
+
+ protected function sendByEmail(array $values, array $task)
+ {
+ $html = $this->template->render('task_mail/email', array(
+ 'task' => $task,
+ ));
+
+ $this->emailClient->send(
+ $values['email'],
+ $values['email'],
+ $values['subject'],
+ $html
+ );
+ }
+}
diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php
index d315daca..868696e0 100644
--- a/app/ServiceProvider/AuthenticationProvider.php
+++ b/app/ServiceProvider/AuthenticationProvider.php
@@ -85,6 +85,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('ColumnController', '*', Role::PROJECT_MANAGER);
$acl->add('CommentController', array('create', 'save', 'edit', 'update', 'confirm', 'remove'), Role::PROJECT_MEMBER);
$acl->add('CommentListController', array('save'), Role::PROJECT_MEMBER);
+ $acl->add('CommentMailController', '*', Role::PROJECT_MEMBER);
$acl->add('CustomFilterController', '*', Role::PROJECT_MEMBER);
$acl->add('ExportController', '*', Role::PROJECT_MANAGER);
$acl->add('TaskFileController', array('screenshot', 'create', 'save', 'remove', 'confirm'), Role::PROJECT_MEMBER);
@@ -110,6 +111,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('TaskExternalLinkController', '*', Role::PROJECT_MEMBER);
$acl->add('TaskModificationController', '*', Role::PROJECT_MEMBER);
$acl->add('TaskStatusController', '*', Role::PROJECT_MEMBER);
+ $acl->add('TaskMailController', '*', Role::PROJECT_MEMBER);
$acl->add('UserAjaxController', array('mention'), Role::PROJECT_MEMBER);
return $acl;
diff --git a/app/Template/task/dropdown.php b/app/Template/task/dropdown.php
index 21b04598..f35abc79 100644
--- a/app/Template/task/dropdown.php
+++ b/app/Template/task/dropdown.php
@@ -36,6 +36,9 @@
<li>
<?= $this->modal->small('clone', t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
+ <li>
+ <?= $this->modal->small('paper-plane', t('Send by email'), 'TaskMailController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </li>
<?php if ($this->projectRole->canRemoveTask($task)): ?>
<li>
<?= $this->modal->confirm('trash-o', t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
diff --git a/app/Template/task_mail/create.php b/app/Template/task_mail/create.php
new file mode 100644
index 00000000..9a1a26b3
--- /dev/null
+++ b/app/Template/task_mail/create.php
@@ -0,0 +1,17 @@
+<div class="page-header">
+ <h2><?= $this->text->e($task['title']) ?> &gt; <?= t('Send by email') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('TaskMailController', 'send', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+
+ <?= $this->form->label(t('Email'), 'email') ?>
+ <?= $this->form->email('email', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?>
+
+ <?= $this->form->label(t('Subject'), 'subject') ?>
+ <?= $this->form->text('subject', $values, $errors, array('required', 'tabindex="2"')) ?>
+
+ <?= $this->modal->submitButtons(array(
+ 'submitLabel' => t('Send by email'),
+ 'tabindex' => 3,
+ )) ?>
+</form>
diff --git a/app/Template/task_mail/email.php b/app/Template/task_mail/email.php
new file mode 100644
index 00000000..51f88178
--- /dev/null
+++ b/app/Template/task_mail/email.php
@@ -0,0 +1,46 @@
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<ul>
+ <li>
+ <?= t('Project: %s', $task['project_name']) ?>
+ </li>
+ <li>
+ <?= t('Created:').' '.$this->dt->datetime($task['date_creation']) ?>
+ </li>
+ <?php if ($task['date_due']): ?>
+ <li>
+ <strong><?= t('Due date:').' '.$this->dt->date($task['date_due']) ?></strong>
+ </li>
+ <?php endif ?>
+ <?php if (! empty($task['creator_username'])): ?>
+ <li>
+ <?= t('Created by %s', $task['creator_name'] ?: $task['creator_username']) ?>
+ </li>
+ <?php endif ?>
+ <li>
+ <strong>
+ <?php if (! empty($task['assignee_username'])): ?>
+ <?= t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) ?>
+ <?php else: ?>
+ <?= t('There is nobody assigned') ?>
+ <?php endif ?>
+ </strong>
+ </li>
+ <li>
+ <?= t('Column on the board:') ?>
+ <strong><?= $this->text->e($task['column_title']) ?></strong>
+ </li>
+ <li><?= t('Task position:').' '.$this->text->e($task['position']) ?></li>
+ <?php if (! empty($task['category_name'])): ?>
+ <li>
+ <?= t('Category:') ?> <strong><?= $this->text->e($task['category_name']) ?></strong>
+ </li>
+ <?php endif ?>
+</ul>
+
+<?php if (! empty($task['description'])): ?>
+ <h2><?= t('Description') ?></h2>
+ <?= $this->text->markdown($task['description'], true) ?>
+<?php endif ?>
+
+<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $this->app->config('application_url'))) ?> \ No newline at end of file
diff --git a/app/Validator/TaskValidator.php b/app/Validator/TaskValidator.php
index 7ca377d9..f9441c8b 100644
--- a/app/Validator/TaskValidator.php
+++ b/app/Validator/TaskValidator.php
@@ -204,4 +204,27 @@ class TaskValidator extends BaseValidator
$v->getErrors()
);
}
+
+ /**
+ * Validate task email creation
+ *
+ * @access public
+ * @param array $values Required parameters to save an action
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateEmailCreation(array $values)
+ {
+ $rules = array(
+ new Validators\Required('subject', t('This field is required')),
+ new Validators\Required('email', t('This field is required')),
+ new Validators\Email('email', t('Email address invalid')),
+ );
+
+ $v = new Validator($values, $rules);
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
}
diff --git a/tests/units/Validator/TaskValidatorTest.php b/tests/units/Validator/TaskValidatorTest.php
index e9d9ac09..0c7fead7 100644
--- a/tests/units/Validator/TaskValidatorTest.php
+++ b/tests/units/Validator/TaskValidatorTest.php
@@ -6,6 +6,23 @@ use Kanboard\Validator\TaskValidator;
class TaskValidatorTest extends Base
{
+ public function testValidationEmailCreation()
+ {
+ $taskValidator = new TaskValidator($this->container);
+
+ $result = $taskValidator->validateEmailCreation(array('email' => 'test@localhost', 'subject' => 'test'));
+ $this->assertTrue($result[0]);
+
+ $result = $taskValidator->validateEmailCreation(array('email' => 'test', 'subject' => 'test'));
+ $this->assertFalse($result[0]);
+
+ $result = $taskValidator->validateEmailCreation(array('subject' => 'test'));
+ $this->assertFalse($result[0]);
+
+ $result = $taskValidator->validateEmailCreation(array('email' => 'test@localhost'));
+ $this->assertFalse($result[0]);
+ }
+
public function testRequiredFields()
{
$taskValidator = new TaskValidator($this->container);