summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/css/app.css19
-rw-r--r--controllers/task.php4
-rw-r--r--locales/fr_FR/translations.php8
-rw-r--r--locales/pl_PL/translations.php8
-rw-r--r--models/base.php18
-rw-r--r--models/comment.php8
-rw-r--r--models/schema.php5
-rw-r--r--models/task.php33
-rw-r--r--templates/board_index.php13
-rw-r--r--templates/board_public.php14
-rw-r--r--templates/task_edit.php3
-rw-r--r--templates/task_new.php3
-rw-r--r--templates/task_show.php5
-rw-r--r--tests/TaskTest.php27
-rw-r--r--vendor/SimpleValidator/Validators/Date.php33
15 files changed, 195 insertions, 6 deletions
diff --git a/assets/css/app.css b/assets/css/app.css
index c99a1eac..93321c2e 100644
--- a/assets/css/app.css
+++ b/assets/css/app.css
@@ -210,6 +210,10 @@ textarea.form-error {
margin-right: 15px;
}
+input.form-date {
+ width: 150px;
+}
+
/* alerts */
.alert {
padding: 8px 35px 8px 14px;
@@ -462,6 +466,21 @@ nav .active a {
font-style: italic;
}
+.task-date {
+ font-weight: bold;
+ color: #D90000;
+}
+
+.task-comment-counter {
+ position: absolute;
+ bottom: 0;
+ right: 5px;
+}
+
+.task-footer {
+ margin-top: 10px;
+}
+
.task {
border: 1px solid #000;
padding: 5px;
diff --git a/controllers/task.php b/controllers/task.php
index 89e1ee6c..fba4d4f5 100644
--- a/controllers/task.php
+++ b/controllers/task.php
@@ -216,6 +216,10 @@ class Task extends Base
if (! $task) $this->notfound();
$this->checkProjectPermissions($task['project_id']);
+ if (! empty($task['date_due'])) {
+ $task['date_due'] = date(t('m/d/Y'), $task['date_due']);
+ }
+
$this->response->html($this->template->layout('task_edit', array(
'errors' => array(),
'values' => $task,
diff --git a/locales/fr_FR/translations.php b/locales/fr_FR/translations.php
index b69c0445..29214886 100644
--- a/locales/fr_FR/translations.php
+++ b/locales/fr_FR/translations.php
@@ -151,7 +151,7 @@ return array(
'Task opened successfully.' => 'Tâche ouverte avec succès.',
'Unable to close this task.' => 'Impossible de fermer cette tâche.',
'Task closed successfully.' => 'Tâche fermé avec succès.',
- 'Unable to update your task.' => 'Impossible de fermer cette tâche.',
+ 'Unable to update your task.' => 'Impossible de modifier cette tâche.',
'Task updated successfully.' => 'Tâche mise à jour avec succès.',
'Unable to create your task.' => 'Impossible de créer cette tâche.',
'Task created successfully.' => 'Tâche créée avec succès.',
@@ -212,4 +212,10 @@ return array(
'Unable to create your comment.' => 'Impossible de sauvegarder votre commentaire.',
'The description is required' => 'La description est obligatoire',
'Edit this task' => 'Modifier cette tâche',
+ 'Due Date' => 'Date d\'échéance',
+ 'm/d/Y' => 'd/m/Y', // Date format parsed with php
+ 'month/day/year' => 'jour/mois/année', // Help shown to the user
+ 'Invalid date' => 'Date invalide',
+ 'Must be done before %B %e, %G' => 'Doit être fait avant le %e %B %G',
+ '%B %e, %G' => '%e %B %G',
);
diff --git a/locales/pl_PL/translations.php b/locales/pl_PL/translations.php
index b088e30e..17b27c8e 100644
--- a/locales/pl_PL/translations.php
+++ b/locales/pl_PL/translations.php
@@ -214,5 +214,11 @@ return array(
'Comment added successfully.' => 'Komentarz dodany',
'Unable to create your comment.' => 'Nie udało się dodać komentarza',
'The description is required' => 'Opis jest wymagany',
- 'Edit this task' => 'Edytuj zadanie'
+ 'Edit this task' => 'Edytuj zadanie',
+ // 'Due Date' => '',
+ // 'm/d/Y' => 'd/m/Y', // Date format parsed with php
+ // 'month/day/year' => 'jour/mois/année', // Help shown to the user
+ // 'Invalid date' => '',
+ // 'Must be done before %B %e, %G' => '',
+ // '%B %e, %G' => '%e %B %G',
);
diff --git a/models/base.php b/models/base.php
index 2ecf4280..95c5b07f 100644
--- a/models/base.php
+++ b/models/base.php
@@ -12,13 +12,14 @@ require __DIR__.'/../vendor/SimpleValidator/Validators/Integer.php';
require __DIR__.'/../vendor/SimpleValidator/Validators/Equals.php';
require __DIR__.'/../vendor/SimpleValidator/Validators/AlphaNumeric.php';
require __DIR__.'/../vendor/SimpleValidator/Validators/GreaterThan.php';
+require __DIR__.'/../vendor/SimpleValidator/Validators/Date.php';
require __DIR__.'/../vendor/PicoDb/Database.php';
require __DIR__.'/schema.php';
abstract class Base
{
const APP_VERSION = 'master';
- const DB_VERSION = 8;
+ const DB_VERSION = 9;
private static $dbInstance = null;
protected $db;
@@ -59,4 +60,19 @@ abstract class Base
return hash('crc32b', $token);
}
+
+ public function getTimestampFromDate($value, $format)
+ {
+ $date = \DateTime::createFromFormat($format, $value);
+
+ if ($date !== false) {
+ $errors = \DateTime::getLastErrors();
+ if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) {
+ $timestamp = $date->getTimestamp();
+ return $timestamp > 0 ? $timestamp : 0;
+ }
+ }
+
+ return 0;
+ }
}
diff --git a/models/comment.php b/models/comment.php
index e0944249..9e5cf8fd 100644
--- a/models/comment.php
+++ b/models/comment.php
@@ -25,6 +25,14 @@ class Comment extends Base
->findAll();
}
+ public function count($task_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->eq(self::TABLE.'.task_id', $task_id)
+ ->count();
+ }
+
public function create(array $values)
{
$values['date'] = time();
diff --git a/models/schema.php b/models/schema.php
index 2c3ba7e8..14604e60 100644
--- a/models/schema.php
+++ b/models/schema.php
@@ -2,6 +2,11 @@
namespace Schema;
+function version_9($pdo)
+{
+ $pdo->exec("ALTER TABLE tasks ADD COLUMN date_due INTEGER");
+}
+
function version_8($pdo)
{
$pdo->exec(
diff --git a/models/task.php b/models/task.php
index 77c620c8..017c7806 100644
--- a/models/task.php
+++ b/models/task.php
@@ -34,6 +34,7 @@ class Task extends Base
self::TABLE.'.description',
self::TABLE.'.date_creation',
self::TABLE.'.date_completed',
+ self::TABLE.'.date_due',
self::TABLE.'.color_id',
self::TABLE.'.project_id',
self::TABLE.'.column_id',
@@ -66,6 +67,7 @@ class Task extends Base
self::TABLE.'.description',
self::TABLE.'.date_creation',
self::TABLE.'.date_completed',
+ self::TABLE.'.date_due',
self::TABLE.'.color_id',
self::TABLE.'.project_id',
self::TABLE.'.column_id',
@@ -95,15 +97,23 @@ class Task extends Base
public function getAllByColumnId($project_id, $column_id, $status = array(1))
{
- return $this->db
+ $tasks = $this->db
->table(self::TABLE)
- ->columns('tasks.id', 'title', 'color_id', 'project_id', 'owner_id', 'column_id', 'position', 'score', 'users.username')
+ ->columns('tasks.id', 'title', 'color_id', 'project_id', 'owner_id', 'column_id', 'position', 'score', 'date_due', 'users.username')
->join('users', 'id', 'owner_id')
->eq('project_id', $project_id)
->eq('column_id', $column_id)
->in('is_active', $status)
->asc('position')
->findAll();
+
+ $commentModel = new Comment;
+
+ foreach ($tasks as &$task) {
+ $task['nb_comments'] = $commentModel->count($task['id']);
+ }
+
+ return $tasks;
}
public function countByColumnId($project_id, $column_id, $status = array(1))
@@ -122,10 +132,17 @@ class Task extends Base
unset($values['another_task']);
+ if (! empty($values['date_due'])) {
+ $values['date_due'] = $this->getTimestampFromDate($values['date_due'], t('m/d/Y')) ?: null;
+ }
+
$values['date_creation'] = time();
$values['position'] = $this->countByColumnId($values['project_id'], $values['column_id']);
- $this->db->table(self::TABLE)->save($values);
+ if (! $this->db->table(self::TABLE)->save($values)) {
+ $this->db->cancelTransaction();
+ return false;
+ }
$task_id = $this->db->getConnection()->getLastId();
@@ -136,9 +153,14 @@ class Task extends Base
public function update(array $values)
{
+ if (! empty($values['date_due'])) {
+ $values['date_due'] = $this->getTimestampFromDate($values['date_due'], t('m/d/Y')) ?: null;
+ }
+
return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values);
}
+ // Mark a task closed
public function close($task_id)
{
return $this->db->table(self::TABLE)
@@ -149,6 +171,7 @@ class Task extends Base
));
}
+ // Mark a task open
public function open($task_id)
{
return $this->db->table(self::TABLE)
@@ -159,11 +182,13 @@ class Task extends Base
));
}
+ // Remove a task
public function remove($task_id)
{
return $this->db->table(self::TABLE)->eq('id', $task_id)->remove();
}
+ // Move a task to another column or to another position
public function move($task_id, $column_id, $position)
{
return (bool) $this->db
@@ -184,6 +209,7 @@ class Task extends Base
new Validators\Integer('score', t('This value must be an integer')),
new Validators\Required('title', t('The title is required')),
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
+ new Validators\Date('date_due', t('Invalid date'), t('m/d/Y')),
));
return array(
@@ -220,6 +246,7 @@ class Task extends Base
new Validators\Integer('score', t('This value must be an integer')),
new Validators\Required('title', t('The title is required')),
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
+ new Validators\Date('date_due', t('Invalid date'), t('m/d/Y')),
));
return array(
diff --git a/templates/board_index.php b/templates/board_index.php
index 20862a4b..e6054189 100644
--- a/templates/board_index.php
+++ b/templates/board_index.php
@@ -74,6 +74,19 @@
<?= Helper\escape($task['title']) ?>
</div>
+ <div class="task-footer">
+ <?php if (! empty($task['date_due'])): ?>
+ <div class="task-date">
+ <?= dt('%B %e, %G', $task['date_due']) ?>
+ </div>
+ <?php endif ?>
+
+ <?php if (! empty($task['nb_comments'])): ?>
+ <div class="task-comment-counter">
+ <?= $task['nb_comments'] ?>
+ </div>
+ <?php endif ?>
+ </div>
</div>
</div>
<?php endforeach ?>
diff --git a/templates/board_public.php b/templates/board_public.php
index 339c8035..4c8f7c92 100644
--- a/templates/board_public.php
+++ b/templates/board_public.php
@@ -40,6 +40,20 @@
<?= Helper\escape($task['title']) ?>
</div>
+ <div class="task-footer">
+ <?php if (! empty($task['date_due'])): ?>
+ <div class="task-date">
+ <?= dt('%B %e, %G', $task['date_due']) ?>
+ </div>
+ <?php endif ?>
+
+ <?php if (! empty($task['nb_comments'])): ?>
+ <div class="task-comment-counter">
+ <?= $task['nb_comments'] ?>
+ </div>
+ <?php endif ?>
+ </div>
+
</div>
</div>
<?php endforeach ?>
diff --git a/templates/task_edit.php b/templates/task_edit.php
index e400e790..eca4537a 100644
--- a/templates/task_edit.php
+++ b/templates/task_edit.php
@@ -23,6 +23,9 @@
<?= Helper\form_label(t('Story Points'), 'score') ?>
<?= Helper\form_number('score', $values, $errors) ?><br/>
+ <?= Helper\form_label(t('Due Date'), 'date_due') ?>
+ <?= Helper\form_text('date_due', $values, $errors, array('placeholder="'.t('month/day/year').'"'), 'form-date') ?><br/>
+
<?= Helper\form_label(t('Description'), 'description') ?>
<?= Helper\form_textarea('description', $values, $errors) ?><br/>
<div class="form-help"><a href="http://en.wikipedia.org/wiki/Markdown#Example" target="_blank"><?= t('Write your text in Markdown') ?></a></div>
diff --git a/templates/task_new.php b/templates/task_new.php
index dd2ba3b4..30e97a85 100644
--- a/templates/task_new.php
+++ b/templates/task_new.php
@@ -23,6 +23,9 @@
<?= Helper\form_label(t('Story Points'), 'score') ?>
<?= Helper\form_number('score', $values, $errors) ?><br/>
+ <?= Helper\form_label(t('Due Date'), 'date_due') ?>
+ <?= Helper\form_text('date_due', $values, $errors, array('placeholder="'.t('month/day/year').'"'), 'form-date') ?><br/>
+
<?= Helper\form_label(t('Description'), 'description') ?>
<?= Helper\form_textarea('description', $values, $errors) ?><br/>
diff --git a/templates/task_show.php b/templates/task_show.php
index 1c5cffaa..59e00b1b 100644
--- a/templates/task_show.php
+++ b/templates/task_show.php
@@ -20,6 +20,11 @@
<?= dt('Completed on %B %e, %G at %k:%M %p', $task['date_completed']) ?>
</li>
<?php endif ?>
+ <?php if ($task['date_due']): ?>
+ <li>
+ <strong><?= dt('Must be done before %B %e, %G', $task['date_due']) ?></strong>
+ </li>
+ <?php endif ?>
<li>
<strong>
<?php if ($task['username']): ?>
diff --git a/tests/TaskTest.php b/tests/TaskTest.php
new file mode 100644
index 00000000..415faede
--- /dev/null
+++ b/tests/TaskTest.php
@@ -0,0 +1,27 @@
+<?php
+
+require_once __DIR__.'/../models/base.php';
+require_once __DIR__.'/../models/task.php';
+
+use Model\Task;
+
+class TaskTest extends PHPUnit_Framework_TestCase
+{
+ public function setUp()
+ {
+ defined('DB_FILENAME') or define('DB_FILENAME', ':memory:');
+ }
+
+ public function testDateFormat()
+ {
+ $t = new Task;
+
+ $this->assertEquals('2014-03-05', date('Y-m-d', $t->getTimestampFromDate('05/03/2014', 'd/m/Y')));
+ $this->assertEquals('2014-03-05', date('Y-m-d', $t->getTimestampFromDate('03/05/2014', 'm/d/Y')));
+ $this->assertEquals('2014-03-05', date('Y-m-d', $t->getTimestampFromDate('3/5/2014', 'm/d/Y')));
+ $this->assertEquals('2014-03-05', date('Y-m-d', $t->getTimestampFromDate('5/3/2014', 'd/m/Y')));
+ $this->assertEquals('2014-03-05', date('Y-m-d', $t->getTimestampFromDate('5/3/14', 'd/m/y')));
+ $this->assertEquals(0, $t->getTimestampFromDate('5/3/14', 'd/m/Y'));
+ $this->assertEquals(0, $t->getTimestampFromDate('5-3-2014', 'd/m/Y'));
+ }
+}
diff --git a/vendor/SimpleValidator/Validators/Date.php b/vendor/SimpleValidator/Validators/Date.php
new file mode 100644
index 00000000..36b59fbe
--- /dev/null
+++ b/vendor/SimpleValidator/Validators/Date.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SimpleValidator\Validators;
+
+use SimpleValidator\Base;
+
+class Date extends Base
+{
+ private $format;
+
+ public function __construct($field, $error_message, $format)
+ {
+ parent::__construct($field, $error_message);
+ $this->format = $format;
+ }
+
+ public function execute(array $data)
+ {
+ if (isset($data[$this->field]) && $data[$this->field] !== '') {
+
+ $date = \DateTime::createFromFormat($this->format, $data[$this->field]);
+
+ if ($date !== false) {
+ $errors = \DateTime::getLastErrors();
+ return $errors['error_count'] === 0 && $errors['warning_count'] === 0;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+}