diff --git a/database/create-tables.sql b/database/create-tables.sql index b3d7756..b274367 100644 --- a/database/create-tables.sql +++ b/database/create-tables.sql @@ -22,6 +22,7 @@ GRANT SELECT,UPDATE ON notes_note_id_seq TO :webapp_user; CREATE SEQUENCE task_dependencies_taskdep_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1; +GRANT SELECT,UPDATE ON task_dependencies_taskdep_id_seq TO :webapp_user; -- Tables diff --git a/database/task-dependencies.sql b/database/task-dependencies.sql index aa8bc64..312139b 100644 --- a/database/task-dependencies.sql +++ b/database/task-dependencies.sql @@ -287,7 +287,7 @@ CREATE OR REPLACE FUNCTION tasks_add_dependency( t_id INT , t_dependency INT ) SECURITY INVOKER AS $tasks_add_dependency$ BEGIN - INSERT INTO task_dependencies( task_id , task_id_depends_on ) + INSERT INTO task_dependencies( task_id , task_id_depends ) VALUES ( t_id , t_dependency ); RETURN 0; EXCEPTION diff --git a/includes/t-data/dao_items.inc.php b/includes/t-data/dao_items.inc.php index c99ce79..92d0f9f 100644 --- a/includes/t-data/dao_items.inc.php +++ b/includes/t-data/dao_items.inc.php @@ -57,7 +57,7 @@ class DAO_Items public function getLineage( Data_Item $item ) { if ( is_array( $item->lineage ) ) { - return; + return $item->lineage; } $query = $this->query( @@ -80,6 +80,7 @@ class DAO_Items array_push( $stack , $entry->item_id ); } $item->lineage = $stack; + return $item->lineage; } diff --git a/includes/t-data/dao_tasks.inc.php b/includes/t-data/dao_tasks.inc.php index 301c922..0c33450 100644 --- a/includes/t-data/dao_tasks.inc.php +++ b/includes/t-data/dao_tasks.inc.php @@ -107,7 +107,7 @@ class DAO_Tasks . 'INNER JOIN items i USING ( item_id ) ' . 'LEFT OUTER JOIN completed_tasks ct ON ct.task_id = t.task_id ' . 'WHERE td.task_id = $1 ' - . 'ORDER BY i.item_name , t.task_priority , t.task_title' )->execute( $id ); + . 'ORDER BY i.item_name , t.task_priority DESC , t.task_title' )->execute( $id ); $task->reverseDependencies = $this->query( 'SELECT t.task_id AS id , t.task_title AS title , t.item_id AS item , ' . 'i.item_name AS item_name , ' @@ -117,6 +117,12 @@ class DAO_Tasks . 'INNER JOIN items i USING ( item_id ) ' . 'LEFT OUTER JOIN completed_tasks ct USING ( task_id ) ' . 'WHERE td.task_id_depends = $1 ' + . 'ORDER BY i.item_name , t.task_priority DESC , t.task_title' )->execute( $id ); + $task->possibleDependencies = $this->query( + 'SELECT t.task_id AS id , t.task_title AS title , t.item_id AS item , ' + . 'i.item_name AS item_name ' + . 'FROM tasks_possible_dependencies( $1 ) t ' + . 'INNER JOIN items i USING ( item_id ) ' . 'ORDER BY i.item_name , t.task_priority , t.task_title' )->execute( $id ); return $task; @@ -218,4 +224,11 @@ class DAO_Tasks ->execute( $id , $text ); } + public function addDependency( $id , $dependency ) + { + $result = $this->query( 'SELECT tasks_add_dependency( $1 , $2 ) AS error' ) + ->execute( $id , $dependency ); + return $result[0]->error; + } + } diff --git a/includes/t-tasks/controllers.inc.php b/includes/t-tasks/controllers.inc.php index 2a11bf0..72bb8d0 100644 --- a/includes/t-tasks/controllers.inc.php +++ b/includes/t-tasks/controllers.inc.php @@ -107,10 +107,15 @@ class Ctrl_TaskDependencies public function handle( Page $page ) { $views = array( - Loader::View( 'box' , 'Dependencies' , + $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 ) ) { array_push( $views , Loader::View( 'box' , 'Reverse dependencies' , Loader::View( 'task_dependencies' , $this->task , true ) ) ); @@ -340,3 +345,43 @@ class Ctrl_EditNote return true; } } + + +class Ctrl_DependencyAdd + extends Controller + implements FormAware +{ + private $form; + + public function setForm( Form $form ) + { + $this->form = $form; + } + + 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 ); + + switch ( $error ) { + + case 0: + return true; + + case 1: + $name->putError( 'The task you selected has been deleted.' ); + break; + + case 2: + $item->putError( 'This dependency is no longer possible.' ); + break; + + default: + $name->putError( "An unknown error occurred ($error)" ); + break; + } + + return null; + } +} diff --git a/includes/t-tasks/package.inc.php b/includes/t-tasks/package.inc.php index 7c63925..c7956a2 100644 --- a/includes/t-tasks/package.inc.php +++ b/includes/t-tasks/package.inc.php @@ -18,6 +18,8 @@ $package[ 'ctrls' ][] = 'delete_note_form'; $package[ 'ctrls' ][] = 'delete_note'; $package[ 'ctrls' ][] = 'delete_task_form'; $package[ 'ctrls' ][] = 'delete_task'; +$package[ 'ctrls' ][] = 'dependency_add'; +$package[ 'ctrls' ][] = 'dependency_add_form'; $package[ 'ctrls' ][] = 'edit_note_form'; $package[ 'ctrls' ][] = 'edit_note'; $package[ 'ctrls' ][] = 'edit_task_form'; diff --git a/includes/t-tasks/page_controllers.inc.php b/includes/t-tasks/page_controllers.inc.php index a2e9495..6f304be 100644 --- a/includes/t-tasks/page_controllers.inc.php +++ b/includes/t-tasks/page_controllers.inc.php @@ -353,3 +353,97 @@ class Ctrl_EditNoteForm } } + + +class Ctrl_DependencyAddForm + extends Controller +{ + + public function handle( Page $page ) + { + // Check selected note + try { + $id = (int) $this->getParameter( 'to' ); + } catch ( ParameterException $e ) { + return 'tasks'; + } + + $tasks = Loader::DAO( 'tasks' ); + $task = $tasks->get( $id ); + if ( $task === null ) { + return 'tasks'; + } + if ( $task->completed_at !== null || empty( $task->possibleDependencies ) ) { + return 'tasks/view?id=' . $id; + } + $page->setTitle( $task->title . ' (task)' ); + + // Generate form + $form = Loader::Create( 'Form' , 'Add dependency' , 'add-dep' ) + ->addField( Loader::Create( 'Field' , 'to' , 'hidden' ) + ->setDefaultValue( $id ) ); + $this->addDependencySelector( $form , $task->possibleDependencies ); + return $form->setURL( 'tasks/view?id=' . $id ) + ->addController( Loader::Ctrl( 'dependency_add' ) ) + ->controller( ); + + } + + private function addDependencySelector( $form , $possibleDependencies ) + { + $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; + } + + foreach ( $depsByItem[ $item->id ] as $task ) { + $select->addOption( $task->id , $prefix . '-> ' . $task->title ); + } + } + return true; + + } + + private function getDependenciesByItem( $possibleDependencies ) + { + $dbi = array( ); + foreach ( $possibleDependencies as $pDep ) { + $dbi[ $pDep->item ][] = $pDep; + } + return $dbi; + } + + private function getItemsToDisplay( $depsByItem ) + { + $dao = Loader::DAO( 'items' ); + $allItems = $dao->getTreeList( ); + $found = array( ); + foreach ( array_keys( $depsByItem ) as $id ) { + if ( array_key_exists( $id , $found ) ) { + continue; + } + $item = $dao->get( $id ); + foreach ( $dao->getLineage( $item ) as $parent ) { + $found[ $parent ] = 1; + } + $found[ $id ] = 1; + } + + $result = array( ); + foreach ( $allItems as $item ) { + if ( array_key_exists( $item->id , $found ) ) { + array_push( $result , $item ); + } + } + return $result; + } +} diff --git a/includes/t-tasks/pages.inc.php b/includes/t-tasks/pages.inc.php index 54566bf..67ebe12 100644 --- a/includes/t-tasks/pages.inc.php +++ b/includes/t-tasks/pages.inc.php @@ -16,6 +16,7 @@ class Page_TasksTasks 'view' => 'view_task' , 'notes/edit' => 'edit_note_form' , 'notes/delete' => 'delete_note_form' , + 'deps/add' => 'dependency_add_form' , )); }