Add a 'due date' field and display the number of comments on the board

This commit is contained in:
Frédéric Guillot 2014-03-05 22:27:48 -05:00
parent 1e994f3448
commit 544f992424
15 changed files with 195 additions and 6 deletions

View File

@ -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;

View File

@ -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,

View File

@ -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',
);

View File

@ -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',
);

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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(

View File

@ -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(

View File

@ -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 ?>

View File

@ -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 ?>

View File

@ -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>

View File

@ -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/>

View File

@ -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']): ?>

27
tests/TaskTest.php Normal file
View File

@ -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'));
}
}

View File

@ -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;
}
}