Initial sub-tasks support
In addition to normal dependencies, the application now supports sub-tasks. Sub-tasks can be added into any existing task (including other sub-tasks, Inception-style). Dependencies can only be added between global tasks, or between sub-tasks of the same task. It is no longer possible to mark a task as finished if it has incomplete sub-tasks, and conversedly, it is not possible to reactivate a sub-task if its parent is marked as completed. A pair of buttons allowing tasks to be moved up and down in the task hierarachy have been added.
This commit is contained in:
parent
49cc53e31f
commit
d28f5741fe
18 changed files with 1658 additions and 132 deletions
|
@ -13,6 +13,15 @@ class Ctrl_AddTask
|
|||
}
|
||||
|
||||
public function handle( Page $page )
|
||||
{
|
||||
$nested = $this->form->field( 'nested' )->value( );
|
||||
if ( 0 === (int) $nested ) {
|
||||
return $this->addTopLevelTask( );
|
||||
}
|
||||
return $this->addNestedTask( );
|
||||
}
|
||||
|
||||
private function addTopLevelTask( )
|
||||
{
|
||||
$item = $this->form->field( 'item' );
|
||||
$name = $this->form->field( 'title' );
|
||||
|
@ -41,6 +50,32 @@ class Ctrl_AddTask
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function addNestedTask( )
|
||||
{
|
||||
$parent = $this->form->field( 'parent' );
|
||||
$name = $this->form->field( 'title' );
|
||||
$priority = $this->form->field( 'priority' );
|
||||
$description = $this->form->field( 'description' );
|
||||
|
||||
$error = Loader::DAO( 'tasks' )->addNestedTask( (int) $parent->value( ) ,
|
||||
$name->value( ) , (int) $priority->value( ) , $description->value( ) );
|
||||
switch ( $error ) {
|
||||
|
||||
case 0:
|
||||
return true;
|
||||
|
||||
case 1:
|
||||
$name->putError( 'Duplicate sub-task name.' );
|
||||
break;
|
||||
|
||||
default:
|
||||
$name->putError( "An unknown error occurred ($error)" );
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -62,8 +97,13 @@ class Ctrl_TaskDetails
|
|||
} else {
|
||||
$bTitle = "Active task";
|
||||
}
|
||||
$items = Loader::DAO( 'items' );
|
||||
$items->getLineage( $this->task->item = $items->get( $this->task->item ) );
|
||||
|
||||
if ( $this->task->item !== null ) {
|
||||
$items = Loader::DAO( 'items' );
|
||||
$items->getLineage( $this->task->item = $items->get( $this->task->item ) );
|
||||
} else {
|
||||
$this->task->parent_task = Loader::DAO( 'tasks' )->get( $this->task->parent_task );
|
||||
}
|
||||
|
||||
$box = Loader::View( 'box' , $bTitle , Loader::View( 'task_details' , $this->task ) );
|
||||
|
||||
|
@ -81,6 +121,15 @@ class Ctrl_TaskDetails
|
|||
$box->addButton( BoxButton::create( 'Claim task' , 'tasks/claim?id=' . $this->task->id )
|
||||
->setClass( 'icon claim' ) );
|
||||
}
|
||||
|
||||
if ( $this->task->can_move_up == 't' ) {
|
||||
$box->addButton( BoxButton::create( 'Move task to grandparent' ,
|
||||
'tasks/move/up?id=' . $this->task->id )->setClass( 'icon move-up' ) );
|
||||
}
|
||||
if ( ! empty( $this->task->moveDownTargets ) ) {
|
||||
$box->addButton( BoxButton::create( 'Move task to sibling' ,
|
||||
'tasks/move/down?id=' . $this->task->id )->setClass( 'icon move-down' ) );
|
||||
}
|
||||
} else {
|
||||
if ( $tasks->canRestart( $this->task ) ) {
|
||||
$box->addButton( BoxButton::create( 'Re-activate' , 'tasks/restart?id=' . $this->task->id )
|
||||
|
@ -99,6 +148,32 @@ class Ctrl_TaskDetails
|
|||
}
|
||||
|
||||
|
||||
class Ctrl_TaskListSubtasks
|
||||
extends Controller
|
||||
{
|
||||
private $task;
|
||||
|
||||
public function __construct( $task )
|
||||
{
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
|
||||
public function handle( Page $page )
|
||||
{
|
||||
$box = Loader::View( 'box' , 'Sub-tasks' ,
|
||||
Loader::View( 'tasks_list' , $this->task->subtasks , array( 'deps' , 'assigned' , 'completed' ) ) );
|
||||
|
||||
if ( $this->task->completed_by === null ) {
|
||||
$box->addButton( BoxButton::create( 'Add sub-task' , 'tasks/add?parent=' . $this->task->id )
|
||||
->setClass( 'list-add' ) );
|
||||
}
|
||||
|
||||
return $box;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Ctrl_TaskDependencies
|
||||
extends Controller
|
||||
{
|
||||
|
@ -112,14 +187,17 @@ class Ctrl_TaskDependencies
|
|||
|
||||
public function handle( Page $page )
|
||||
{
|
||||
$views = array(
|
||||
$depBox = Loader::View( 'box' , 'Dependencies' ,
|
||||
Loader::View( 'task_dependencies' , $this->task , false ) )
|
||||
);
|
||||
$views = array( );
|
||||
|
||||
if ( ! empty( $this->task->possibleDependencies ) ) {
|
||||
$depBox->addButton( BoxButton::create( 'Add dependency' , 'tasks/deps/add?to=' . $this->task->id )
|
||||
->setClass( 'list-add' ) );
|
||||
if ( ! empty( $this->task->dependencies )
|
||||
|| ( $this->task->completed_by === null && ! empty( $this->task->possibleDependencies ) ) ) {
|
||||
$views[] = ( $depBox = Loader::View( 'box' , 'Dependencies' ,
|
||||
Loader::View( 'task_dependencies' , $this->task , false ) ) );
|
||||
|
||||
if ( ! empty( $this->task->possibleDependencies ) ) {
|
||||
$depBox->addButton( BoxButton::create( 'Add dependency' , 'tasks/deps/add?to=' . $this->task->id )
|
||||
->setClass( 'list-add' ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->task->reverseDependencies ) ) {
|
||||
|
@ -230,12 +308,54 @@ class Ctrl_EditTask
|
|||
public function handle( Page $page )
|
||||
{
|
||||
$id = $this->form->field( 'id' );
|
||||
$item = $this->form->field( 'item' );
|
||||
|
||||
$nested = $this->form->field( 'nested' )->value( );
|
||||
if ( 0 == (int) $nested ) {
|
||||
$item = $this->form->field( 'item' );
|
||||
} else {
|
||||
$item = null;
|
||||
}
|
||||
|
||||
$name = $this->form->field( 'title' );
|
||||
$priority = $this->form->field( 'priority' );
|
||||
$description = $this->form->field( 'description' );
|
||||
$assignee = $this->form->field( 'assigned-to' );
|
||||
|
||||
if ( $item != null ) {
|
||||
return $this->handleTopLevelTask( $id , $item , $name , $priority , $description , $assignee );
|
||||
}
|
||||
return $this->handleNestedTask( $id , $name , $priority , $description , $assignee );
|
||||
}
|
||||
|
||||
private function handleNestedTask( $id , $name , $priority , $description , $assignee )
|
||||
{
|
||||
$error = Loader::DAO( 'tasks' )->updateNestedTask( (int) $id->value( ) , $name->value( ) ,
|
||||
(int) $priority->value( ) , $description->value( ) ,
|
||||
(int) $assignee->value( ) );
|
||||
|
||||
switch ( $error ) {
|
||||
|
||||
case 0:
|
||||
return true;
|
||||
|
||||
case 1:
|
||||
$name->putError( 'Another sub-task already uses this title.' );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$assignee->putError( 'This user has been deleted.' );
|
||||
break;
|
||||
|
||||
default:
|
||||
$name->putError( "An unknown error occurred ($error)" );
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function handleTopLevelTask( $id , $item , $name , $priority , $description , $assignee )
|
||||
{
|
||||
$error = Loader::DAO( 'tasks' )->updateTask( (int) $id->value( ) ,
|
||||
(int) $item->value( ) , $name->value( ) ,
|
||||
(int) $priority->value( ) , $description->value( ) ,
|
||||
|
@ -373,8 +493,8 @@ class Ctrl_DependencyAdd
|
|||
public function handle( Page $page )
|
||||
{
|
||||
$id = (int) $this->form->field( 'to' )->value( );
|
||||
$dependency = $this->form->field( 'dependency' )->value( );
|
||||
$error = Loader::DAO( 'tasks' )->addDependency( $id , $dependency );
|
||||
$dependency = $this->form->field( 'dependency' );
|
||||
$error = Loader::DAO( 'tasks' )->addDependency( $id , $dependency->value( ) );
|
||||
|
||||
switch ( $error ) {
|
||||
|
||||
|
@ -382,15 +502,19 @@ class Ctrl_DependencyAdd
|
|||
return true;
|
||||
|
||||
case 1:
|
||||
$name->putError( 'The task you selected has been deleted.' );
|
||||
$dependency->putError( 'The task you selected has been deleted.' );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$item->putError( 'This dependency is no longer possible.' );
|
||||
$dependency->putError( 'This dependency is no longer possible.' );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$dependency->putError( 'These tasks are no longer at the same level.' );
|
||||
break;
|
||||
|
||||
default:
|
||||
$name->putError( "An unknown error occurred ($error)" );
|
||||
$dependency->putError( "An unknown error occurred ($error)" );
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,9 +26,12 @@ $package[ 'ctrls' ][] = 'edit_note_form';
|
|||
$package[ 'ctrls' ][] = 'edit_note';
|
||||
$package[ 'ctrls' ][] = 'edit_task_form';
|
||||
$package[ 'ctrls' ][] = 'edit_task';
|
||||
$package[ 'ctrls' ][] = 'task_claim';
|
||||
$package[ 'ctrls' ][] = 'task_dependencies';
|
||||
$package[ 'ctrls' ][] = 'task_details';
|
||||
$package[ 'ctrls' ][] = 'task_claim';
|
||||
$package[ 'ctrls' ][] = 'task_list_subtasks';
|
||||
$package[ 'ctrls' ][] = 'task_move_down';
|
||||
$package[ 'ctrls' ][] = 'task_move_up';
|
||||
$package[ 'ctrls' ][] = 'task_notes';
|
||||
$package[ 'ctrls' ][] = 'toggle_task';
|
||||
$package[ 'ctrls' ][] = 'view_task';
|
||||
|
|
|
@ -75,8 +75,15 @@ class Ctrl_AddTaskForm
|
|||
{
|
||||
try {
|
||||
$target = (int) $this->getParameter( 'to' );
|
||||
$targetIsItem = true;
|
||||
} catch ( ParameterException $e ) {
|
||||
$target = null;
|
||||
try {
|
||||
$target = (int) $this->getParameter( 'parent' );
|
||||
$targetIsItem = false;
|
||||
} catch ( ParameterException $e ) {
|
||||
$target = null;
|
||||
$targetIsItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
$form = Loader::Create( 'Form' , 'Add this task' , 'create-task' );
|
||||
|
@ -86,20 +93,43 @@ class Ctrl_AddTaskForm
|
|||
if ( ! $this->addItemSelector( $form ) ) {
|
||||
return 'items';
|
||||
}
|
||||
} else {
|
||||
$form->addField( Loader::Create( 'Field' , 'nested' , 'hidden' )
|
||||
->setDefaultValue( 0 ) );
|
||||
} elseif ( $targetIsItem ) {
|
||||
$item = Loader::DAO( 'items' )->get( $target );
|
||||
if ( $item === null ) {
|
||||
return 'items';
|
||||
return 'tasks';
|
||||
}
|
||||
$returnURL = 'items/view?id=' . $target;
|
||||
|
||||
$form->addField( Loader::Create( 'Field' , 'to' , 'hidden' )
|
||||
->setDefaultValue( $target ) )
|
||||
->addField( Loader::Create( 'Field' , 'nested' , 'hidden' )
|
||||
->setDefaultValue( 0 ) )
|
||||
->addField( Loader::Create( 'Field' , 'item' , 'hidden' )
|
||||
->setDefaultValue( $target ) )
|
||||
->addField( Loader::Create( 'Field' , 'item-name' , 'label' )
|
||||
->setMandatory( false )
|
||||
->setDescription( 'Item:' )
|
||||
->setDefaultValue( $item->name ) );
|
||||
} else {
|
||||
$parent = Loader::DAO( 'tasks' )->get( $target );
|
||||
if ( $parent === null ) {
|
||||
return 'tasks';
|
||||
}
|
||||
$returnURL = 'tasks/view?id=' . $target;
|
||||
if ( $parent->completed_by !== null ) {
|
||||
return $returnURL;
|
||||
}
|
||||
|
||||
$form->addField( Loader::Create( 'Field' , 'parent' , 'hidden' )
|
||||
->setDefaultValue( $target ) )
|
||||
->addField( Loader::Create( 'Field' , 'nested' , 'hidden' )
|
||||
->setDefaultValue( 1 ) )
|
||||
->addField( Loader::Create( 'Field' , 'item-name' , 'label' )
|
||||
->setMandatory( false )
|
||||
->setDescription( 'Sub-task of:' )
|
||||
->setDefaultValue( $parent->title ) );
|
||||
}
|
||||
|
||||
$page->setTitle( 'New task' );
|
||||
|
@ -159,10 +189,13 @@ class Ctrl_ViewTask
|
|||
$page->setTitle( $task->title . ' (task)' );
|
||||
|
||||
$result = array(
|
||||
Loader::Ctrl( 'task_details' , $task ) ,
|
||||
Loader::Ctrl( 'task_dependencies' , $task ) ,
|
||||
Loader::Ctrl( 'task_details' , $task )
|
||||
);
|
||||
|
||||
if ( $task->completed_by === null || ! empty( $task->subtasks ) ) {
|
||||
$result[] = Loader::Ctrl( 'task_list_subtasks' , $task );
|
||||
}
|
||||
$result[] = Loader::Ctrl( 'task_dependencies' , $task );
|
||||
if ( $task->completed_by === null ) {
|
||||
array_push( $result , Loader::Ctrl( 'add_task_note_form' , $task ) );
|
||||
}
|
||||
|
@ -197,6 +230,13 @@ class Ctrl_DeleteTaskForm
|
|||
}
|
||||
$page->setTitle( $task->title . ' (task)' );
|
||||
|
||||
// Create parent URL from either the item or parent task
|
||||
if ( $task->parent_task === null ) {
|
||||
$parentURL = 'items/view?id=' . $task->item;
|
||||
} else {
|
||||
$parentURL = 'tasks/view?id=' . $task->parent_task;
|
||||
}
|
||||
|
||||
// Generate confirmation text
|
||||
$confText = HTML::make( 'div' )
|
||||
->appendElement( HTML::make( 'p' )
|
||||
|
@ -210,7 +250,7 @@ class Ctrl_DeleteTaskForm
|
|||
->setDefaultValue( $task->id ) )
|
||||
->addField( Loader::Create( 'Field' , 'confirm' , 'html' )->setDefaultValue( $confText ) )
|
||||
->setCancelURL( 'tasks/view?id=' . $task->id )
|
||||
->setSuccessURL( 'items/view?id=' . $task->item )
|
||||
->setSuccessURL( $parentURL )
|
||||
->addController( Loader::Ctrl( 'delete_task' ) )
|
||||
->controller( );
|
||||
|
||||
|
@ -242,13 +282,19 @@ class Ctrl_EditTaskForm
|
|||
$page->setTitle( $task->title . ' (task)' );
|
||||
|
||||
|
||||
return Loader::Create( 'Form' , 'Update task' , 'edit-task' , 'Editing task' )
|
||||
$form = Loader::Create( 'Form' , 'Update task' , 'edit-task' , 'Editing task' )
|
||||
->setURL( 'tasks/view?id=' . $task->id )
|
||||
->addField( Loader::Create( 'Field' , 'id' , 'hidden' )
|
||||
->setDefaultValue( $task->id ) )
|
||||
->addField( $this->createItemSelector( )
|
||||
->setDefaultValue( $task->item ) )
|
||||
->addField( Loader::Create( 'Field' , 'title' , 'text' )
|
||||
->addField( Loader::Create( 'Field' , 'nested' , 'hidden' )
|
||||
->setDefaultValue( $task->item === null ? 1 : 0 ) );
|
||||
|
||||
if ( $task->item !== null ) {
|
||||
$form->addField( $this->createItemSelector( )
|
||||
->setDefaultValue( $task->item ) );
|
||||
}
|
||||
|
||||
return $form->addField( Loader::Create( 'Field' , 'title' , 'text' )
|
||||
->setDescription( 'Title:' )
|
||||
->setModifier( Loader::Create( 'Modifier_TrimString' ) )
|
||||
->setValidator( Loader::Create( 'Validator_StringLength' , 'This title' , 5 , 256 ) )
|
||||
|
@ -411,31 +457,37 @@ class Ctrl_DependencyAddForm
|
|||
$form = Loader::Create( 'Form' , 'Add dependency' , 'add-dep' )
|
||||
->addField( Loader::Create( 'Field' , 'to' , 'hidden' )
|
||||
->setDefaultValue( $id ) );
|
||||
$this->addDependencySelector( $form , $task->possibleDependencies );
|
||||
$this->addDependencySelector( $form , $task->possibleDependencies , $task->item !== null );
|
||||
return $form->setURL( 'tasks/view?id=' . $id )
|
||||
->addController( Loader::Ctrl( 'dependency_add' ) )
|
||||
->controller( );
|
||||
|
||||
}
|
||||
|
||||
private function addDependencySelector( $form , $possibleDependencies )
|
||||
private function addDependencySelector( $form , $possibleDependencies , $topLevel )
|
||||
{
|
||||
$form->addField( $select = Loader::Create( 'Field' , 'dependency' , 'select' )
|
||||
->setDescription( 'Dependency to add:' )
|
||||
->addOption( '' , '(please select a task)' ) );
|
||||
|
||||
$depsByItem = $this->getDependenciesByItem( $possibleDependencies );
|
||||
$items = $this->getItemsToDisplay( $depsByItem );
|
||||
foreach ( $items as $item ) {
|
||||
$prefix = '-' . str_repeat( '--' , $item->depth );
|
||||
$name = $prefix . ' ' . $item->name;
|
||||
$select->addOption( 'I' . $item->id , $name , true );
|
||||
if ( ! array_key_exists( $item->id , $depsByItem ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( $topLevel ) {
|
||||
$depsByItem = $this->getDependenciesByItem( $possibleDependencies );
|
||||
$items = $this->getItemsToDisplay( $depsByItem );
|
||||
foreach ( $items as $item ) {
|
||||
$prefix = '-' . str_repeat( '--' , $item->depth );
|
||||
$name = $prefix . ' ' . $item->name;
|
||||
$select->addOption( 'I' . $item->id , $name , true );
|
||||
if ( ! array_key_exists( $item->id , $depsByItem ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $depsByItem[ $item->id ] as $task ) {
|
||||
$select->addOption( $task->id , $prefix . '-> ' . $task->title );
|
||||
foreach ( $depsByItem[ $item->id ] as $task ) {
|
||||
$select->addOption( $task->id , $prefix . '-> ' . $task->title );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ( $possibleDependencies as $task ) {
|
||||
$select->addOption( $task->id , $task->title );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -546,3 +598,155 @@ class Ctrl_DependencyDeleteForm
|
|||
}
|
||||
|
||||
|
||||
|
||||
class Ctrl_TaskMoveDown
|
||||
extends Controller
|
||||
{
|
||||
|
||||
public function __construct( )
|
||||
{
|
||||
$this->dao = Loader::DAO( 'tasks' );
|
||||
}
|
||||
|
||||
public function handle( Page $page )
|
||||
{
|
||||
try {
|
||||
$id = (int) $this->getParameter( 'id' );
|
||||
} catch ( ParameterException $e ) {
|
||||
return 'tasks';
|
||||
}
|
||||
|
||||
$task = $this->dao->get( $id );
|
||||
if ( $task === null ) {
|
||||
return 'tasks';
|
||||
}
|
||||
|
||||
if ( empty( $task->moveDownTargets ) ) {
|
||||
return 'tasks/view?id=' . $id;
|
||||
}
|
||||
|
||||
$page->setTitle( $task->title . ' (task)' );
|
||||
$sibling = $this->getSibling( $task );
|
||||
if ( $sibling != null ) {
|
||||
if ( $this->handleSelectedSibling( $task , $sibling ) ) {
|
||||
return 'tasks/view?id=' . $id;
|
||||
} else {
|
||||
return $this->confirmationForm( $task , $sibling );
|
||||
}
|
||||
}
|
||||
return $this->siblingSelectionForm( $task );
|
||||
}
|
||||
|
||||
private function getSibling( $task )
|
||||
{
|
||||
try {
|
||||
$sibling = (int) $this->getParameter( 'sibling' );
|
||||
$okSiblings = array_map( function( $item ) { return $item->target_id; } , $task->moveDownTargets );
|
||||
if ( ! in_array( $sibling , $okSiblings ) ) {
|
||||
$sibling = null;
|
||||
}
|
||||
} catch ( ParameterException $e ) {
|
||||
$sibling = null;
|
||||
}
|
||||
return $sibling;
|
||||
}
|
||||
|
||||
private function handleSelectedSibling( $task , $sibling )
|
||||
{
|
||||
try {
|
||||
$force = (bool) $this->getParameter( 'force' );
|
||||
} catch ( ParameterException $e ) {
|
||||
$force = false;
|
||||
}
|
||||
|
||||
return $this->dao->moveDown( $task , $sibling , $force );
|
||||
}
|
||||
|
||||
private function confirmationForm( $task , $sibling )
|
||||
{
|
||||
$sibling = $this->dao->get( $sibling );
|
||||
$confText = HTML::make( 'div' )
|
||||
->appendElement( HTML::make( 'p' )
|
||||
->appendText( 'All dependencies and reverse dependencies of the '
|
||||
. 'selected task will be lost when it is moved into ' )
|
||||
->appendElement( HTML::make( 'strong' )->appendText( $sibling->title ) )
|
||||
->appendText( '.' ) )
|
||||
->appendElement( HTML::make( 'p' )
|
||||
->appendText( 'Please confirm.' ) );
|
||||
|
||||
return Loader::Create( 'Form' , 'Move task' , 'move-down' )
|
||||
->addField( Loader::Create( 'Field' , 'id' , 'hidden' )
|
||||
->setDefaultValue( $task->id ) )
|
||||
->addField( Loader::Create( 'Field' , 'sibling' , 'hidden' )
|
||||
->setDefaultValue( $sibling->id ) )
|
||||
->addField( Loader::Create( 'Field' , 'force' , 'hidden' )
|
||||
->setDefaultValue( 1 ) )
|
||||
->addField( Loader::Create( 'Field' , 'confirm' , 'html' )->setDefaultValue( $confText ) )
|
||||
->setURL( 'tasks/view?id=' . $task->id )
|
||||
->controller( );
|
||||
}
|
||||
|
||||
private function siblingSelectionForm( $task )
|
||||
{
|
||||
$selector = Loader::Create( 'Field' , 'sibling' , 'select' )
|
||||
->setDescription( 'Move task into: ' );
|
||||
foreach ( $task->moveDownTargets as $target ) {
|
||||
$selector->addOption( $target->target_id , $target->target_title );
|
||||
}
|
||||
return Loader::Create( 'Form' , 'Move task' , 'move-down' , 'Move task to sibling' )
|
||||
->addField( Loader::Create( 'Field' , 'id' , 'hidden' )
|
||||
->setDefaultValue( $task->id ) )
|
||||
->addField( $selector )
|
||||
->setURL( 'tasks/view?id=' . $task->id )
|
||||
->controller( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Ctrl_TaskMoveUp
|
||||
extends Controller
|
||||
{
|
||||
|
||||
public function handle( Page $page )
|
||||
{
|
||||
try {
|
||||
$id = (int) $this->getParameter( 'id' );
|
||||
} catch ( ParameterException $e ) {
|
||||
return 'tasks';
|
||||
}
|
||||
|
||||
$dao = Loader::DAO( 'tasks' );
|
||||
$task = $dao->get( $id );
|
||||
if ( $task === null ) {
|
||||
return 'tasks';
|
||||
}
|
||||
|
||||
try {
|
||||
$force = (bool) $this->getParameter( 'force' );
|
||||
} catch ( ParameterException $e ) {
|
||||
$force = false;
|
||||
}
|
||||
|
||||
if ( ! $task->can_move_up || $dao->moveUp( $task , $force ) ) {
|
||||
return 'tasks/view?id=' . $id;
|
||||
}
|
||||
|
||||
$confText = HTML::make( 'div' )
|
||||
->appendElement( HTML::make( 'p' )
|
||||
->appendText( 'All dependencies and reverse dependencies of the '
|
||||
. 'selected task will be lost when it is moved.' ) )
|
||||
->appendElement( HTML::make( 'p' )
|
||||
->appendText( 'Please confirm.' ) );
|
||||
|
||||
$page->setTitle( $task->title . ' (task)' );
|
||||
return Loader::Create( 'Form' , 'Move task' , 'move-up' )
|
||||
->addField( Loader::Create( 'Field' , 'id' , 'hidden' )
|
||||
->setDefaultValue( $id ) )
|
||||
->addField( Loader::Create( 'Field' , 'force' , 'hidden' )
|
||||
->setDefaultValue( 1 ) )
|
||||
->addField( Loader::Create( 'Field' , 'confirm' , 'html' )->setDefaultValue( $confText ) )
|
||||
->setURL( 'tasks/view?id=' . $id )
|
||||
->controller( );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,10 +15,12 @@ class Page_TasksTasks
|
|||
'finish' => array( 'toggle_task' , false ) ,
|
||||
'restart' => array( 'toggle_task' , true ) ,
|
||||
'view' => 'view_task' ,
|
||||
'notes/edit' => 'edit_note_form' ,
|
||||
'notes/delete' => 'delete_note_form' ,
|
||||
'deps/add' => 'dependency_add_form' ,
|
||||
'deps/delete' => 'dependency_delete_form' ,
|
||||
'move/down' => 'task_move_down' ,
|
||||
'move/up' => 'task_move_up' ,
|
||||
'notes/edit' => 'edit_note_form' ,
|
||||
'notes/delete' => 'delete_note_form' ,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ class View_TasksList
|
|||
->appendElement( HTML::make( 'a' )
|
||||
->setAttribute( 'href' , $this->base . '/tasks/view?id=' . $task->id )
|
||||
->appendText( $task->title ) ) );
|
||||
$this->addItem( $cell , $task );
|
||||
$this->addParent( $cell , $task );
|
||||
$classes = array( );
|
||||
|
||||
$addedAt = strtotime( $task->added_at );
|
||||
|
@ -75,6 +75,9 @@ class View_TasksList
|
|||
if ( $task->missing_dependencies !== null ) {
|
||||
$this->generateMissingDependencies( $cell , $classes , $task );
|
||||
}
|
||||
if ( $task->missing_subtasks !== null ) {
|
||||
$this->generateMissingSubtasks( $cell , $classes , $task );
|
||||
}
|
||||
if ( $task->assigned_to !== null ) {
|
||||
$this->generateAssignedTask( $cell , $classes , $task );
|
||||
}
|
||||
|
@ -82,19 +85,28 @@ class View_TasksList
|
|||
|
||||
if ( ! empty( $classes ) ) {
|
||||
foreach ( $cell as $entry ) {
|
||||
$entry->setAttribute( 'class' , join( ' ' , $classes ) );
|
||||
$entry->setAttribute( 'class' , join( ' ' , array_unique( $classes ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $cell;
|
||||
}
|
||||
|
||||
protected function addItem( &$cell , $task )
|
||||
protected function addParent( &$cell , $task )
|
||||
{
|
||||
if ( ! array_key_exists( 'item' , $this->features ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $task->item !== null ) {
|
||||
$this->addItem( $cell , $task );
|
||||
} else {
|
||||
$this->addParentTask( $cell , $task );
|
||||
}
|
||||
}
|
||||
|
||||
protected function addItem( &$cell , $task )
|
||||
{
|
||||
$itemsDao = Loader::DAO( 'items' );
|
||||
$item = $itemsDao->get( $task->item );
|
||||
$lineage = $itemsDao->getLineage( $item );
|
||||
|
@ -114,6 +126,17 @@ class View_TasksList
|
|||
array_push( $cell , HTML::make( 'dd' )->append( $contents ) );
|
||||
}
|
||||
|
||||
protected function addParentTask( &$cell , $task )
|
||||
{
|
||||
$parent = $this->dao->get( $task->parent_task );
|
||||
|
||||
array_push( $cell , HTML::make( 'dd' )
|
||||
->appendText( 'Sub-task of ' )
|
||||
->appendElement( HTML::make( 'a' )
|
||||
->setAttribute( 'href' , $this->base . '/tasks/view?id=' . $parent->id )
|
||||
->appendText( $parent->title ) ) );
|
||||
}
|
||||
|
||||
protected function generateMissingDependencies( &$cell , &$classes , $task )
|
||||
{
|
||||
if ( ! array_key_exists( 'deps' , $this->features ) ) {
|
||||
|
@ -134,6 +157,23 @@ class View_TasksList
|
|||
array_push( $classes , 'missing-deps' );
|
||||
}
|
||||
|
||||
protected function generateMissingSubtasks( &$cell , &$classes , $task )
|
||||
{
|
||||
if ( ! array_key_exists( 'deps' , $this->features ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $task->missing_subtasks > 1 ) {
|
||||
$end = 's';
|
||||
} else {
|
||||
$end = '';
|
||||
}
|
||||
array_push( $cell ,
|
||||
$md = HTML::make( 'dd' )->appendText( "{$task->missing_subtasks} incomplete sub-task$end" ) );
|
||||
|
||||
array_push( $classes , 'missing-deps' );
|
||||
}
|
||||
|
||||
protected function generateAssignedTask( &$cell , &$classes , $task )
|
||||
{
|
||||
if ( ! array_key_exists( 'assigned' , $this->features ) ) {
|
||||
|
@ -173,11 +213,22 @@ class View_TaskDetails
|
|||
public function render( )
|
||||
{
|
||||
$list = HTML::make( 'dl' )
|
||||
->setAttribute( 'class' , 'tasks' )
|
||||
->appendElement( HTML::make( 'dt' )
|
||||
->appendText( 'On item:' ) )
|
||||
->appendElement( HTML::make( 'dd' )
|
||||
->append( $this->formatPlaceLineage( $this->task->item ) ) );
|
||||
->setAttribute( 'class' , 'tasks' );
|
||||
|
||||
if ( $this->task->item !== null ) {
|
||||
$list->appendElement( HTML::make( 'dt' )
|
||||
->appendText( 'On item:' ) )
|
||||
->appendElement( HTML::make( 'dd' )
|
||||
->append( $this->formatPlaceLineage( $this->task->item ) ) );
|
||||
} else {
|
||||
$list->appendElement( HTML::make( 'dt' )
|
||||
->appendText( 'Sub-task of:' ) )
|
||||
->appendElement( HTML::make( 'dd' )
|
||||
->appendElement( HTML::make( 'a' )
|
||||
->setAttribute( 'href' , $this->base .
|
||||
'/tasks/view?id=' . $this->task->parent_task->id )
|
||||
->appendText( $this->task->parent_task->title ) ) );
|
||||
}
|
||||
|
||||
if ( $this->task->description != '' ) {
|
||||
$list->appendElement( HTML::make( 'dt' )
|
||||
|
@ -317,6 +368,8 @@ class View_TaskDependencies
|
|||
->appendText( $dependency->item_name ) )
|
||||
->appendElement( $itemList ) );
|
||||
$prevItem = $dependency->item;
|
||||
} elseif ( $itemList === null ) {
|
||||
$itemList = $list;
|
||||
}
|
||||
|
||||
$entry = HTML::make( 'li' )->appendElement(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue