diff --git a/ChangeLog b/ChangeLog
index e7b81e7af..85daac108 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 000000000..e95ddf03a
--- /dev/null
+++ b/app/Controller/TaskMailController.php
@@ -0,0 +1,55 @@
+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 d315daca4..868696e06 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 21b045987..f35abc79a 100644
--- a/app/Template/task/dropdown.php
+++ b/app/Template/task/dropdown.php
@@ -36,6 +36,9 @@
= $this->modal->small('clone', t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+
+ = $this->modal->small('paper-plane', t('Send by email'), 'TaskMailController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+
projectRole->canRemoveTask($task)): ?>
= $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 000000000..9a1a26b34
--- /dev/null
+++ b/app/Template/task_mail/create.php
@@ -0,0 +1,17 @@
+
+
diff --git a/app/Template/task_mail/email.php b/app/Template/task_mail/email.php
new file mode 100644
index 000000000..51f881787
--- /dev/null
+++ b/app/Template/task_mail/email.php
@@ -0,0 +1,46 @@
+= $this->text->e($task['title']) ?> (#= $task['id'] ?>)
+
+
+ -
+ = t('Project: %s', $task['project_name']) ?>
+
+ -
+ = t('Created:').' '.$this->dt->datetime($task['date_creation']) ?>
+
+
+ -
+ = t('Due date:').' '.$this->dt->date($task['date_due']) ?>
+
+
+
+ -
+ = t('Created by %s', $task['creator_name'] ?: $task['creator_username']) ?>
+
+
+ -
+
+
+ = t('Assigned to %s', $task['assignee_name'] ?: $task['assignee_username']) ?>
+
+ = t('There is nobody assigned') ?>
+
+
+
+ -
+ = t('Column on the board:') ?>
+ = $this->text->e($task['column_title']) ?>
+
+ - = t('Task position:').' '.$this->text->e($task['position']) ?>
+
+ -
+ = t('Category:') ?> = $this->text->e($task['category_name']) ?>
+
+
+
+
+
+ = t('Description') ?>
+ = $this->text->markdown($task['description'], true) ?>
+
+
+= $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 7ca377d95..f9441c8bb 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 e9d9ac09a..0c7fead7e 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);