Moving tasks - Preliminary version

* Works when moving tasks without dependencies. Crashes with a SQL error
due to FK violation when moving tasks with interdependencies (LTC ID?)

* The form is unable to force removal of external dependencies at this
time.

* Related PL/PgSQL code stored in database/temp.sql at this time.
This commit is contained in:
Emmanuel BENOîT 2016-01-04 11:01:28 +01:00
parent fd39819304
commit 59fec5529f
7 changed files with 395 additions and 8 deletions

View file

@ -29,6 +29,25 @@ class DAO_Tasks
}
public function getActiveTasksAssoc( )
{
$qr = $this->query(
'SELECT id , item , parent_task , title FROM tasks_list '
. 'WHERE completed_at IS NULL '
. 'ORDER BY priority DESC , badness , added_at DESC' )->execute( );
$result = array( );
foreach ( $qr as $task ) {
$container = $task->parent_task === null ? 'I' : 'T';
$container .= $container == 'I' ? $task->item : $task->parent_task;
if ( !array_key_exists( $container , $result ) ) {
$result[ $container ] = array( );
}
array_push( $result[ $container ] , $task );
}
return $result;
}
public function getAllTasks( )
{
return $this->query(
@ -326,4 +345,15 @@ class DAO_Tasks
->execute( $task->id , $sibling , $force ? 't' : 'f' );
return ( $result[0]->success == 't' );
}
public function moveTasks( $fromTask , $parentId , $toTask , $targetId , $tasks , $force )
{
$result = $this->query( 'SELECT tasks_move( $1 , $2 , $3 , $4 , $5 , $6::int[] ) AS error' )
->execute( $fromTask ? 't' : 'f' , $parentId ,
$toTask ? 't' : 'f' , $targetId ,
$force ? 't' : 'f' ,
'{' . join( ',' , $tasks ) . '}' );
return $result[0]->error;
}
}

View file

@ -566,3 +566,125 @@ class Ctrl_TaskClaim
}
}
class Ctrl_TaskMove
extends Controller
implements FormAware
{
private $form;
public function setForm( Form $form )
{
$this->form = $form;
}
public function handle( Page $page )
{
$type = $this->form->field( 'type' );
$id = $this->form->field( 'id' );
$target = $this->form->field( 'target' );
$tasks = $this->form->field( 'tasks[]' );
$tFull = $target->value( );
if ( strlen( $tFull ) < 2 ) {
$target->putError( 'Invalid target.' );
return null;
}
$toTask = ( substr( $tFull , 0 , 1 ) == 'T' );
$toId = (int) substr( $tFull , 1 );
$error = Loader::DAO( 'tasks' )->moveTasks(
$type->value( ) === 's' , (int) $id->value( ) ,
$toTask , $toId , $tasks->value( ) ,
false ); // FIXME: should be read from the form
switch ( $error ) {
case 0:
return true;
case 1:
$tasks->putError( 'Selected tasks deleted.' );
break;
case 2:
$tasks->putError( 'Selected tasks moved.' );
break;
case 3:
$target->putError( 'Target has been deleted.' );
break;
case 4:
$target->putError( 'This is a child of a selected task.' );
break;
case 5:
$tasks->putError( 'Dependencies would be broken (FIXME: force mode)' );
break;
default:
$target->putError( "An unknown error occurred ($error)" );
break;
}
return null;
}
private function addTopLevelTask( )
{
$item = $this->form->field( 'item' );
$name = $this->form->field( 'title' );
$priority = $this->form->field( 'priority' );
$description = $this->form->field( 'description' );
$error = Loader::DAO( 'tasks' )->addTask( (int) $item->value( ) , $name->value( ) ,
(int) $priority->value( ) , $description->value( ) );
switch ( $error ) {
case 0:
return true;
case 1:
$name->putError( 'Duplicate task name for this item.' );
break;
case 2:
$item->putError( 'This item has been deleted' );
break;
default:
$name->putError( "An unknown error occurred ($error)" );
break;
}
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;
}
}

View file

@ -32,6 +32,7 @@ $package[ 'ctrls' ][] = 'task_details';
$package[ 'ctrls' ][] = 'task_list_subtasks';
$package[ 'ctrls' ][] = 'task_move_down';
$package[ 'ctrls' ][] = 'task_move_up';
$package[ 'ctrls' ][] = 'task_move_form';
$package[ 'ctrls' ][] = 'task_move';
$package[ 'ctrls' ][] = 'task_notes';
$package[ 'ctrls' ][] = 'toggle_task';

View file

@ -788,7 +788,7 @@ class Ctrl_TaskMoveUp
}
class Ctrl_TaskMove
class Ctrl_TaskMoveForm
extends Controller
{
@ -833,16 +833,62 @@ class Ctrl_TaskMove
return $failure;
}
// Generate form
// Form header
$page->setTitle( $name . ': move tasks' );
$form = Loader::Create( 'Form' , 'Move tasks' , 'move-tasks' )
->setURL( $failure )
->addController( Loader::Ctrl( 'task_move' ) )
->addField( Loader::Create( 'Field' , 'type' , 'hidden' )
->setDefaultValue( $subtasks ? 's' : 'i' ) )
->addField( Loader::Create( 'Field' , 'id' , 'hidden' )
->setDefaultValue( $id ) ) ;
// $this->addDependencySelector( $form , $task->possibleDependencies , $task->parent_task === null );
return $form->setURL( $failure )
// ->addController( Loader::Ctrl( 'dependency_add' ) )
->controller( );
->setDefaultValue( $id ) );
// List of targets
$tSel = Loader::Create( 'Field' , 'target' , 'select' )
->setDescription( 'Move to:' )
->addOption( '' , '(please select a target)' );
$this->addTargets( $tSel , $subtasks , $id );
$form->addField( $tSel );
// List of tasks
$tSel = Loader::Create( 'Field' , 'tasks[]' , 'select' , array( 'multiple' ) )
->setDescription( 'Tasks to move:' );
foreach ( $tasks as $t ) {
$tSel->addOption( $t->id , $t->title );
}
$form->addField( $tSel );
return $form->controller( );
}
private function addTargets( Field $field , $isTask , $id )
{
$items = $this->dItems->getTreeList( );
$tasks = $this->dTasks->getActiveTasksAssoc( );
foreach ( $items as $item ) {
$title = str_repeat( '--' , $item->depth ) . ' ' . strtoupper( $item->name );
$disabled = !$isTask && $item->id == $id;
$iid = 'I' . $item->id;
$field->addOption( $iid , $title , $disabled );
$this->addTargetTasks( $field , $iid , $tasks , $isTask , $id , $item->depth + 1 );
}
}
private function addTargetTasks( Field $field , $iid , $tasks , $isTask , $id , $depth )
{
if ( !array_key_exists( $iid , $tasks ) ) {
return;
}
foreach ( $tasks[ $iid ] as $task ) {
$title = str_repeat( '--' , $depth ) . '> ' . $task->title;
$disabled = $isTask && $task->id == $id;
$tid = 'T' . $task->id;
$field->addOption( $tid , $title , $disabled );
$this->addTargetTasks( $field , $tid , $tasks , $isTask , $id , $depth + 1 );
}
}
}

View file

@ -17,7 +17,7 @@ class Page_TasksTasks
'view' => 'view_task' ,
'deps/add' => 'dependency_add_form' ,
'deps/delete' => 'dependency_delete_form' ,
'move' => 'task_move' ,
'move' => 'task_move_form' ,
'move/down' => 'task_move_down' ,
'move/up' => 'task_move_up' ,
'notes/edit' => 'edit_note_form' ,