Prevent people to remove swimlanes that contains tasks

This commit is contained in:
Frederic Guillot 2017-02-18 13:38:51 -05:00
parent 948b7fbaaa
commit 49c8e5c1be
13 changed files with 132 additions and 54 deletions

View File

@ -7,12 +7,14 @@ New features:
Improvements: Improvements:
* Prevent people to remove swimlanes that contains tasks
* Show task count in swimlane table
* Use contextual menu instead of action column in users management * Use contextual menu instead of action column in users management
Breaking changes: Breaking changes:
* The concept of "default swimlane" is removed * The concept of "default swimlane" has been removed
* Previous default swimlanes are migrated to an independent swimlane * Previous default swimlanes are migrated to an independent swimlanes
* Columns "default_swimlane" and "show_default_swimlane" from "projects" table are not used anymore * Columns "default_swimlane" and "show_default_swimlane" from "projects" table are not used anymore
* Remove API method "getDefaultSwimlane()" * Remove API method "getDefaultSwimlane()"
* Add mandatory argument "project_id" to API method "updateSwimlane()" * Add mandatory argument "project_id" to API method "updateSwimlane()"

View File

@ -20,7 +20,7 @@ class ColumnController extends BaseController
public function index() public function index()
{ {
$project = $this->getProject(); $project = $this->getProject();
$columns = $this->columnModel->getAllWithTasksCount($project['id']); $columns = $this->columnModel->getAllWithTaskCount($project['id']);
$this->response->html($this->helper->layout->project('column/index', array( $this->response->html($this->helper->layout->project('column/index', array(
'columns' => $columns, 'columns' => $columns,

View File

@ -16,7 +16,7 @@ class ProjectOverviewController extends BaseController
public function show() public function show()
{ {
$project = $this->getProject(); $project = $this->getProject();
$columns = $this->columnModel->getAllWithTasksCount($project['id']); $columns = $this->columnModel->getAllWithTaskCount($project['id']);
$this->response->html($this->helper->layout->app('project_overview/show', array( $this->response->html($this->helper->layout->app('project_overview/show', array(
'project' => $project, 'project' => $project,

View File

@ -18,7 +18,7 @@ class ProjectViewController extends BaseController
public function show() public function show()
{ {
$project = $this->getProject(); $project = $this->getProject();
$columns = $this->columnModel->getAllWithTasksCount($project['id']); $columns = $this->columnModel->getAllWithTaskCount($project['id']);
$this->response->html($this->helper->layout->project('project_view/show', array( $this->response->html($this->helper->layout->project('project_view/show', array(
'project' => $project, 'project' => $project,

View File

@ -40,10 +40,11 @@ class SwimlaneController extends BaseController
public function index() public function index()
{ {
$project = $this->getProject(); $project = $this->getProject();
$swimlanes = $this->swimlaneModel->getAllWithTaskCount($project['id']);
$this->response->html($this->helper->layout->project('swimlane/index', array( $this->response->html($this->helper->layout->project('swimlane/index', array(
'active_swimlanes' => $this->swimlaneModel->getAllByStatus($project['id'], SwimlaneModel::ACTIVE), 'active_swimlanes' => $swimlanes['active'],
'inactive_swimlanes' => $this->swimlaneModel->getAllByStatus($project['id'], SwimlaneModel::INACTIVE), 'inactive_swimlanes' => $swimlanes['inactive'],
'project' => $project, 'project' => $project,
'title' => t('Swimlanes') 'title' => t('Swimlanes')
))); )));

View File

@ -121,13 +121,13 @@ class ColumnModel extends Base
} }
/** /**
* Get all columns with tasks count * Get all columns with task count
* *
* @access public * @access public
* @param integer $project_id Project id * @param integer $project_id Project id
* @return array * @return array
*/ */
public function getAllWithTasksCount($project_id) public function getAllWithTaskCount($project_id)
{ {
return $this->db->table(self::TABLE) return $this->db->table(self::TABLE)
->columns('id', 'title', 'position', 'task_limit', 'description', 'hide_in_dashboard', 'project_id') ->columns('id', 'title', 'position', 'task_limit', 'description', 'hide_in_dashboard', 'project_id')

View File

@ -270,7 +270,7 @@ class ProjectModel extends Base
*/ */
public function getColumnStats(array &$project) public function getColumnStats(array &$project)
{ {
$project['columns'] = $this->columnModel->getAllWithTasksCount($project['id']); $project['columns'] = $this->columnModel->getAllWithTaskCount($project['id']);
$project['nb_active_tasks'] = 0; $project['nb_active_tasks'] = 0;
foreach ($project['columns'] as $column) { foreach ($project['columns'] as $column) {

View File

@ -163,6 +163,40 @@ class SwimlaneModel extends Base
return $query->findAll(); return $query->findAll();
} }
/**
* Get all swimlanes with task count
*
* @access public
* @param integer $project_id Project id
* @return array
*/
public function getAllWithTaskCount($project_id)
{
$result = array(
'active' => array(),
'inactive' => array(),
);
$swimlanes = $this->db->table(self::TABLE)
->columns('id', 'name', 'description', 'project_id', 'position', 'is_active')
->subquery("SELECT COUNT(*) FROM ".TaskModel::TABLE." WHERE swimlane_id=".self::TABLE.".id AND is_active='1'", 'nb_open_tasks')
->subquery("SELECT COUNT(*) FROM ".TaskModel::TABLE." WHERE swimlane_id=".self::TABLE.".id AND is_active='0'", 'nb_closed_tasks')
->eq('project_id', $project_id)
->asc('position')
->asc('name')
->findAll();
foreach ($swimlanes as $swimlane) {
if ($swimlane['is_active']) {
$result['active'][] = $swimlane;
} else {
$result['inactive'][] = $swimlane;
}
}
return $result;
}
/** /**
* Get list of all swimlanes * Get list of all swimlanes
* *

View File

@ -15,19 +15,31 @@
data-save-position-url="<?= $this->url->href('ColumnController', 'move', array('project_id' => $project['id'])) ?>"> data-save-position-url="<?= $this->url->href('ColumnController', 'move', array('project_id' => $project['id'])) ?>">
<thead> <thead>
<tr> <tr>
<th class="column-40"><?= t('Column') ?></th> <th><?= t('Column') ?></th>
<th class="column-10"><?= t('Task limit') ?></th> <th class="column-10"><?= t('Task limit') ?></th>
<th class="column-20"><?= t('Visible on dashboard') ?></th> <th class="column-15"><?= t('Visible on dashboard') ?></th>
<th class="column-10"><?= t('Open tasks') ?></th> <th class="column-12"><?= t('Open tasks') ?></th>
<th class="column-10"><?= t('Closed tasks') ?></th> <th class="column-12"><?= t('Closed tasks') ?></th>
<th><?= t('Actions') ?></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($columns as $column): ?> <?php foreach ($columns as $column): ?>
<tr data-column-id="<?= $column['id'] ?>"> <tr data-column-id="<?= $column['id'] ?>">
<td> <td>
<i class="fa fa-arrows-alt draggable-row-handle" title="<?= t('Change column position') ?>"></i> <i class="fa fa-arrows-alt draggable-row-handle" title="<?= t('Change column position') ?>"></i>&nbsp;
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
<?= $this->modal->medium('edit', t('Edit'), 'ColumnController', 'edit', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>
</li>
<?php if ($column['nb_open_tasks'] == 0 && $column['nb_closed_tasks'] == 0): ?>
<li>
<?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnController', 'confirm', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>
</li>
<?php endif ?>
</ul>
</div>
<?= $this->text->e($column['title']) ?> <?= $this->text->e($column['title']) ?>
<?php if (! empty($column['description'])): ?> <?php if (! empty($column['description'])): ?>
<span class="tooltip" title="<?= $this->text->markdownAttribute($column['description']) ?>"> <span class="tooltip" title="<?= $this->text->markdownAttribute($column['description']) ?>">
@ -47,21 +59,6 @@
<td> <td>
<?= $column['nb_closed_tasks'] ?> <?= $column['nb_closed_tasks'] ?>
</td> </td>
<td>
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
<?= $this->modal->medium('edit', t('Edit'), 'ColumnController', 'edit', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>
</li>
<?php if ($column['nb_open_tasks'] == 0 && $column['nb_closed_tasks'] == 0): ?>
<li>
<?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnController', 'confirm', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>
</li>
<?php endif ?>
</ul>
</div>
</td>
</tr> </tr>
<?php endforeach ?> <?php endforeach ?>
</tbody> </tbody>

View File

@ -23,6 +23,6 @@
<?= $this->render('swimlane/table', array( <?= $this->render('swimlane/table', array(
'swimlanes' => $inactive_swimlanes, 'swimlanes' => $inactive_swimlanes,
'project' => $project, 'project' => $project,
'disable_handler' => true, 'disable_handle' => true,
)) ?> )) ?>
<?php endif ?> <?php endif ?>

View File

@ -4,17 +4,39 @@
<thead> <thead>
<tr> <tr>
<th><?= t('Name') ?></th> <th><?= t('Name') ?></th>
<th class="column-8"><?= t('Actions') ?></th> <th class="column-15"><?= t('Open tasks') ?></th>
<th class="column-15"><?= t('Closed tasks') ?></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($swimlanes as $swimlane): ?> <?php foreach ($swimlanes as $swimlane): ?>
<tr data-swimlane-id="<?= $swimlane['id'] ?>"> <tr data-swimlane-id="<?= $swimlane['id'] ?>">
<td> <td>
<?php if (! isset($disable_handler)): ?> <?php if (! isset($disable_handle)): ?>
<i class="fa fa-arrows-alt draggable-row-handle" title="<?= t('Change column position') ?>"></i> <i class="fa fa-arrows-alt draggable-row-handle" title="<?= t('Change column position') ?>"></i>&nbsp;
<?php endif ?> <?php endif ?>
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
<?= $this->modal->medium('edit', t('Edit'), 'SwimlaneController', 'edit', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id'])) ?>
</li>
<li>
<?php if ($swimlane['is_active']): ?>
<?= $this->url->icon('toggle-off', t('Disable'), 'SwimlaneController', 'disable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
<?php else: ?>
<?= $this->url->icon('toggle-on', t('Enable'), 'SwimlaneController', 'enable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
<?php endif ?>
</li>
<?php if ($swimlane['nb_open_tasks'] == 0 && $swimlane['nb_closed_tasks'] == 0): ?>
<li>
<?= $this->modal->confirm('trash-o', t('Remove'), 'SwimlaneController', 'confirm', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id'])) ?>
</li>
<?php endif ?>
</ul>
</div>
<?= $this->text->e($swimlane['name']) ?> <?= $this->text->e($swimlane['name']) ?>
<?php if (! empty($swimlane['description'])): ?> <?php if (! empty($swimlane['description'])): ?>
@ -24,24 +46,10 @@
<?php endif ?> <?php endif ?>
</td> </td>
<td> <td>
<div class="dropdown"> <?= $swimlane['nb_open_tasks'] ?>
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> </td>
<ul> <td>
<li> <?= $swimlane['nb_closed_tasks'] ?>
<?= $this->modal->medium('edit', t('Edit'), 'SwimlaneController', 'edit', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id'])) ?>
</li>
<li>
<?php if ($swimlane['is_active']): ?>
<?= $this->url->icon('toggle-off', t('Disable'), 'SwimlaneController', 'disable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
<?php else: ?>
<?= $this->url->icon('toggle-on', t('Enable'), 'SwimlaneController', 'enable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
<?php endif ?>
</li>
<li>
<?= $this->modal->confirm('trash-o', t('Remove'), 'SwimlaneController', 'confirm', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id'])) ?>
</li>
</ul>
</div>
</td> </td>
</tr> </tr>
<?php endforeach ?> <?php endforeach ?>

View File

@ -105,7 +105,7 @@ class ColumnModelTest extends Base
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'UnitTest', 'project_id' => 1, 'column_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'UnitTest', 'project_id' => 1, 'column_id' => 1)));
$this->assertEquals(2, $taskCreationModel->create(array('title' => 'UnitTest', 'project_id' => 1, 'column_id' => 2, 'is_active' => 0))); $this->assertEquals(2, $taskCreationModel->create(array('title' => 'UnitTest', 'project_id' => 1, 'column_id' => 2, 'is_active' => 0)));
$columns = $columnModel->getAllWithTasksCount(1); $columns = $columnModel->getAllWithTaskCount(1);
$this->assertCount(4, $columns); $this->assertCount(4, $columns);
$this->assertEquals(1, $columns[0]['id']); $this->assertEquals(1, $columns[0]['id']);

View File

@ -324,4 +324,40 @@ class SwimlaneModelTest extends Base
$this->assertEquals(2, $swimlanes[1]['position']); $this->assertEquals(2, $swimlanes[1]['position']);
$this->assertEquals(1, $swimlanes[1]['id']); $this->assertEquals(1, $swimlanes[1]['id']);
} }
public function testGetAllWithTaskCount()
{
$projectModel = new ProjectModel($this->container);
$swimlaneModel = new SwimlaneModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Project #1')));
$this->assertEquals(2, $swimlaneModel->create(1, 'Swimlane #1'));
$this->assertEquals(3, $swimlaneModel->create(1, 'Swimlane #2'));
$this->assertEquals(4, $swimlaneModel->create(1, 'Swimlane #3'));
$this->assertEquals(5, $swimlaneModel->create(1, 'Swimlane #4'));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task #1', 'project_id' => 1, 'swimlane_id' => 2)));
$this->assertEquals(2, $taskCreationModel->create(array('title' => 'Task #1', 'project_id' => 1, 'swimlane_id' => 3, 'is_active' => 0)));
$this->assertTrue($swimlaneModel->disable(1, 2));
$this->assertTrue($swimlaneModel->disable(1, 4));
$swimlanes = $swimlaneModel->getAllWithTaskCount(1);
$this->assertCount(3, $swimlanes['active']);
$this->assertCount(2, $swimlanes['inactive']);
$this->assertEquals('Default swimlane', $swimlanes['active'][0]['name']);
$this->assertEquals('Swimlane #2', $swimlanes['active'][1]['name']);
$this->assertEquals('Swimlane #4', $swimlanes['active'][2]['name']);
$this->assertEquals(0, $swimlanes['active'][1]['nb_open_tasks']);
$this->assertEquals(1, $swimlanes['active'][1]['nb_closed_tasks']);
$this->assertEquals('Swimlane #1', $swimlanes['inactive'][0]['name']);
$this->assertEquals('Swimlane #3', $swimlanes['inactive'][1]['name']);
$this->assertEquals(1, $swimlanes['inactive'][0]['nb_open_tasks']);
$this->assertEquals(0, $swimlanes['inactive'][0]['nb_closed_tasks']);
}
} }