Initial commit of the dependency list filtering
Adding dependencies now includes filtering options on name, state, and items.
This commit is contained in:
parent
b8db2ae5b6
commit
525497e1f4
4 changed files with 254 additions and 57 deletions
|
@ -171,8 +171,10 @@ class DAO_Tasks
|
||||||
. 'ORDER BY i.item_name , t.task_priority DESC , t.task_title' )->execute( $id );
|
. 'ORDER BY i.item_name , t.task_priority DESC , t.task_title' )->execute( $id );
|
||||||
$task->possibleDependencies = $this->query(
|
$task->possibleDependencies = $this->query(
|
||||||
'SELECT t.task_id AS id , t.task_title AS title , t.item_id AS item , '
|
'SELECT t.task_id AS id , t.task_title AS title , t.item_id AS item , '
|
||||||
. 'i.item_name AS item_name '
|
. 'i.item_name AS item_name , l.badness <> 0 AS blocked , '
|
||||||
|
. 'l.completed_at IS NOT NULL AS completed '
|
||||||
. 'FROM tasks_possible_dependencies( $1 ) t '
|
. 'FROM tasks_possible_dependencies( $1 ) t '
|
||||||
|
. 'INNER JOIN tasks_list l ON t.task_id = l.id '
|
||||||
. 'LEFT OUTER JOIN items i USING ( item_id ) '
|
. 'LEFT OUTER JOIN items i USING ( item_id ) '
|
||||||
. 'ORDER BY i.item_name , t.task_priority , t.task_title' )->execute( $id );
|
. 'ORDER BY i.item_name , t.task_priority , t.task_title' )->execute( $id );
|
||||||
$task->lineage = null;
|
$task->lineage = null;
|
||||||
|
|
|
@ -527,6 +527,188 @@ class Ctrl_DependencyAdd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Ctrl_DependencyAddFiltering
|
||||||
|
extends Controller
|
||||||
|
implements FormAware
|
||||||
|
{
|
||||||
|
private $filtering;
|
||||||
|
private $selector;
|
||||||
|
private $task;
|
||||||
|
|
||||||
|
private $dependencies;
|
||||||
|
|
||||||
|
public function __construct( Form $selector , $task )
|
||||||
|
{
|
||||||
|
$this->selector = $selector;
|
||||||
|
$this->task = $task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setForm( Form $form )
|
||||||
|
{
|
||||||
|
$this->filtering = $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle( Page $page )
|
||||||
|
{
|
||||||
|
$this->filterTaskDependencies( );
|
||||||
|
$this->addDependencySelector( );
|
||||||
|
$this->copyFiltersToSelector( );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function filterTaskDependencies( )
|
||||||
|
{
|
||||||
|
$this->dependencies = array( );
|
||||||
|
$text = trim( $this->getField( 'text' ) );
|
||||||
|
if ( $text == '' ) {
|
||||||
|
$text = array( );
|
||||||
|
} else {
|
||||||
|
$text = array_unique( preg_split( '/\s+/' , $text ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$state = $this->getField( 'state' );
|
||||||
|
$sActive = ( $state == '' || strstr( $state , 'a' ) !== false );
|
||||||
|
$sBlocked = ( $state == '' || strstr( $state , 'b' ) !== false );
|
||||||
|
$sCompleted = ( $state == '' || strstr( $state , 'c' ) !== false );
|
||||||
|
|
||||||
|
foreach ( $this->task->possibleDependencies as $dep ) {
|
||||||
|
// Check for text
|
||||||
|
$ok = true;
|
||||||
|
foreach ( $text as $tCheck ) {
|
||||||
|
$ok = stristr( $dep->title , $tCheck );
|
||||||
|
if ( !$ok ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !$ok ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check state
|
||||||
|
$isBlocked = ( $dep->blocked === 't' );
|
||||||
|
$isCompleted = ( $dep->completed === 't' );
|
||||||
|
if ( $isBlocked && !$sBlocked || $isCompleted && !$sCompleted
|
||||||
|
|| !( $isBlocked || $isCompleted ) && !$sActive ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dependencies[] = $dep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addDependencySelector( )
|
||||||
|
{
|
||||||
|
$this->selector->addField( $select = Loader::Create( 'Field' , 'dependency' , 'select' )
|
||||||
|
->setDescription( 'Dependency to add:' )
|
||||||
|
->addOption( '' , '(please select a task)' ) );
|
||||||
|
|
||||||
|
if ( $this->task->parent_task === null ) {
|
||||||
|
$depsByItem = $this->getDependenciesByItem( );
|
||||||
|
$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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ( $this->dependencies as $task ) {
|
||||||
|
$select->addOption( $task->id , $task->title );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getItemsToDisplay( $depsByItem )
|
||||||
|
{
|
||||||
|
$dao = Loader::DAO( 'items' );
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fByItem = $this->getField( 'items' );
|
||||||
|
$fByItem = ( $fByItem == '' ) ? null : ( (int) $fByItem );
|
||||||
|
$fChildren = ( $this->getField( 'item-children' ) === '1' );
|
||||||
|
$fOKChildren = false;
|
||||||
|
$fDepth = -1;
|
||||||
|
|
||||||
|
$result = array( );
|
||||||
|
foreach ( $dao->getTreeList( ) as $item ) {
|
||||||
|
if ( $fByItem !== null && $fChildren ){
|
||||||
|
if ( $item->id == $fByItem ) {
|
||||||
|
$fOKChildren = true;
|
||||||
|
$fDepth = $item->depth;
|
||||||
|
} else if ( $fOKChildren && $item->depth <= $fDepth ) {
|
||||||
|
$fOKChildren = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $fByItem !== null && $item->id != $fByItem && !$fOKChildren ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( array_key_exists( $item->id , $found ) ) {
|
||||||
|
array_push( $result , $item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDependenciesByItem( )
|
||||||
|
{
|
||||||
|
$dbi = array( );
|
||||||
|
foreach ( $this->dependencies as $pDep ) {
|
||||||
|
$dbi[ $pDep->item ][] = $pDep;
|
||||||
|
}
|
||||||
|
return $dbi;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function copyFiltersToSelector( )
|
||||||
|
{
|
||||||
|
$fields = array( 'text' , 'state' , 'items' , 'item-children' );
|
||||||
|
foreach ( $fields as $f ) {
|
||||||
|
$v = $this->getField( $f );
|
||||||
|
$this->selector->addField(
|
||||||
|
Loader::Create( 'Field' , 'filters-' . $f , 'hidden' )
|
||||||
|
->setMandatory( false )
|
||||||
|
->setDefaultValue( $v ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFiltersFromSelector( )
|
||||||
|
{
|
||||||
|
$fields = array( 'text' , 'state' , 'items' , 'item-children' );
|
||||||
|
foreach ( $fields as $f ) {
|
||||||
|
$field = $this->filtering->field( $f );
|
||||||
|
if ( $field !== null ) {
|
||||||
|
$fv = $this->getParameter( 'filters-' . $f , 'POST' );
|
||||||
|
$field->setFormValue( $fv );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getField( $name )
|
||||||
|
{
|
||||||
|
$fld = $this->filtering->field( $name );
|
||||||
|
return $fld ? $fld->value( ) : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Ctrl_DependencyDelete
|
class Ctrl_DependencyDelete
|
||||||
extends Controller
|
extends Controller
|
||||||
implements FormAware
|
implements FormAware
|
||||||
|
|
|
@ -20,6 +20,7 @@ $package[ 'ctrls' ][] = 'delete_task_form';
|
||||||
$package[ 'ctrls' ][] = 'delete_task';
|
$package[ 'ctrls' ][] = 'delete_task';
|
||||||
$package[ 'ctrls' ][] = 'dependency_add';
|
$package[ 'ctrls' ][] = 'dependency_add';
|
||||||
$package[ 'ctrls' ][] = 'dependency_add_form';
|
$package[ 'ctrls' ][] = 'dependency_add_form';
|
||||||
|
$package[ 'ctrls' ][] = 'dependency_add_filtering';
|
||||||
$package[ 'ctrls' ][] = 'dependency_delete';
|
$package[ 'ctrls' ][] = 'dependency_delete';
|
||||||
$package[ 'ctrls' ][] = 'dependency_delete_form';
|
$package[ 'ctrls' ][] = 'dependency_delete_form';
|
||||||
$package[ 'ctrls' ][] = 'edit_note_form';
|
$package[ 'ctrls' ][] = 'edit_note_form';
|
||||||
|
|
|
@ -454,78 +454,90 @@ class Ctrl_DependencyAddForm
|
||||||
$page->setTitle( $task->title . ' (task)' );
|
$page->setTitle( $task->title . ' (task)' );
|
||||||
|
|
||||||
// Generate form
|
// Generate form
|
||||||
$form = Loader::Create( 'Form' , 'Add dependency' , 'add-dep' )
|
$form = Loader::Create( 'Form' , 'Add dependency' , 'add-dep' , 'Select dependency' )
|
||||||
->addField( Loader::Create( 'Field' , 'to' , 'hidden' )
|
->addField( Loader::Create( 'Field' , 'to' , 'hidden' )
|
||||||
->setDefaultValue( $id ) );
|
->setDefaultValue( $id ) )
|
||||||
$this->addDependencySelector( $form , $task->possibleDependencies , $task->parent_task === null );
|
->setURL( 'tasks/view?id=' . $id )
|
||||||
return $form->setURL( 'tasks/view?id=' . $id )
|
->addController( Loader::Ctrl( 'dependency_add' ) );
|
||||||
->addController( Loader::Ctrl( 'dependency_add' ) )
|
|
||||||
->controller( );
|
|
||||||
|
|
||||||
|
$filters = $this->handleFiltering( $page , $form , $task );
|
||||||
|
|
||||||
|
return array( $form->controller( ) , $filters );
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addDependencySelector( $form , $possibleDependencies , $topLevel )
|
private function handleFiltering( Page $page , Form $form , $task )
|
||||||
{
|
{
|
||||||
$form->addField( $select = Loader::Create( 'Field' , 'dependency' , 'select' )
|
$fCtrl = Loader::Ctrl( 'dependency_add_filtering' , $form , $task );
|
||||||
->setDescription( 'Dependency to add:' )
|
$filters = $this->makeFilteringForm( $form , $fCtrl , $task );
|
||||||
->addOption( '' , '(please select a task)' ) );
|
|
||||||
|
|
||||||
if ( $topLevel ) {
|
// Was the filters form submitted?
|
||||||
$depsByItem = $this->getDependenciesByItem( $possibleDependencies );
|
try {
|
||||||
$items = $this->getItemsToDisplay( $depsByItem );
|
$submitted = $this->getParameter( 'filter-deps-submit' , 'POST' );
|
||||||
|
} catch ( ParameterException $e ) {
|
||||||
|
$submitted = null;
|
||||||
|
}
|
||||||
|
if ( $submitted !== null ) {
|
||||||
|
return $filters->controller( )->handle( $page );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Was the main form submitted?
|
||||||
|
try {
|
||||||
|
$submitted = $this->getParameter( 'to' , 'POST' );
|
||||||
|
} catch ( ParameterException $e ) {
|
||||||
|
$submitted = null;
|
||||||
|
}
|
||||||
|
if ( $submitted !== null ) {
|
||||||
|
$fCtrl->getFiltersFromSelector( );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fake handling the form
|
||||||
|
$fCtrl->handle( $page );
|
||||||
|
return $filters->view( );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeFilteringForm( Form $form , Controller $ctrl , $task )
|
||||||
|
{
|
||||||
|
// Generate filtering form, and handle it immediately
|
||||||
|
$filters = Loader::Create( 'Form' , 'Apply' , 'filter-deps' , 'Filter dependencies' )
|
||||||
|
->addController( $ctrl )
|
||||||
|
->setAction( '?' )
|
||||||
|
->addField( Loader::Create( 'Field' , 'to' , 'hidden' )
|
||||||
|
->setDefaultValue( $task->id ) )
|
||||||
|
->addField( Loader::Create( 'Field' , 'text' , 'text' )
|
||||||
|
->setDescription( 'Name must contain:' )
|
||||||
|
->setMandatory( false ) )
|
||||||
|
->addField( Loader::Create( 'Field' , 'state' , 'select' )
|
||||||
|
->setDescription( 'Task state:' )
|
||||||
|
->setMandatory( false )
|
||||||
|
->addOption( 'abc' , 'Indifferent' )
|
||||||
|
->addOption( 'ab' , 'Active or blocked' )
|
||||||
|
->addOption( 'a' , 'Active' )
|
||||||
|
->addOption( 'b' , 'Blocked' )
|
||||||
|
->addOption( 'c' , 'Completed' ) );
|
||||||
|
if ( $task->parent_task === null ) {
|
||||||
|
$itemSelect = Loader::Create( 'Field' , 'items' , 'select' )
|
||||||
|
->setDescription( 'Limit to items:' )
|
||||||
|
->setMandatory( false )
|
||||||
|
->addOption( '' , '(Any item)' );
|
||||||
|
$this->addItemSelector( $itemSelect );
|
||||||
|
$filters->addField( $itemSelect )
|
||||||
|
->addField( Loader::Create( 'Field' , 'item-children' , 'select' )
|
||||||
|
->setDescription( 'Include child items:' )
|
||||||
|
->setMandatory( false )
|
||||||
|
->addOption( '1' , 'Yes' )
|
||||||
|
->addOption( '0' , 'No' ) );
|
||||||
|
}
|
||||||
|
return $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: duplicate code
|
||||||
|
private function addItemSelector( $select )
|
||||||
|
{
|
||||||
|
$items = Loader::DAO( 'items' )->getTreeList( );
|
||||||
foreach ( $items as $item ) {
|
foreach ( $items as $item ) {
|
||||||
$prefix = '-' . str_repeat( '--' , $item->depth );
|
$name = '-' . str_repeat( '--' , $item->depth ) . ' ' . $item->name;
|
||||||
$name = $prefix . ' ' . $item->name;
|
$select->addOption( $item->id , $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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foreach ( $possibleDependencies as $task ) {
|
|
||||||
$select->addOption( $task->id , $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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue