tasks/includes/t-data/dao_items.inc.php

335 lines
8.2 KiB
PHP
Raw Normal View History

<?php
class DAO_Items
extends DAO
{
private $loaded = array( );
private $tree = null;
private $treeList = null;
private $activeTasksCounted = false;
public function createBefore( $name , $before )
{
$query = $this->query( 'SELECT insert_item_before( $1 , $2 ) AS error' );
$result = $query->execute( $name , $before );
return $result[ 0 ]->error;
}
public function createUnder( $name , $under )
{
$query = $this->query( 'SELECT insert_item_under( $1 , $2 ) AS error' );
$result = $query->execute( $name , $under );
return $result[ 0 ]->error;
}
public function createLast( $name )
{
$query = $this->query( 'SELECT insert_item_last( $1 ) AS error' );
$result = $query->execute( $name );
return $result[ 0 ]->error;
}
public function get( $identifier )
{
$identifier = (int)$identifier;
if ( array_key_exists( $identifier , $this->loaded ) ) {
return $this->loaded[ $identifier ];
}
$getNameQuery = $this->query( 'SELECT item_name FROM items WHERE item_id = $1' , true );
$result = $getNameQuery->execute( $identifier );
if ( empty( $result ) ) {
$rObj = null;
} else {
$rObj = new Data_Item( $identifier , $result[ 0 ]->item_name );
}
$this->loaded[ $identifier ] = $rObj;
return $rObj;
}
public function getLineage( Data_Item $item )
{
if ( is_array( $item->lineage ) ) {
return $item->lineage;
}
$query = $this->query(
'SELECT p.item_id , p.item_name FROM items_tree pt '
. 'INNER JOIN items p '
. 'ON p.item_id = pt.item_id_parent '
. 'WHERE pt.item_id_child = $1 AND pt.pt_depth > 0 '
. 'ORDER BY pt.pt_depth DESC' );
$result = $query->execute( $item->id );
$stack = array( );
foreach ( $result as $entry ) {
if ( array_key_exists( $entry->item_id , $this->loaded ) ) {
$object = $this->loaded[ $entry->item_id ];
} else {
$object = new Data_Item( $entry->item_id , $entry->item_name );
$this->loaded[ $entry->item_id ] = $object;
}
$object->lineage = $stack;
array_push( $stack , $entry->item_id );
}
$item->lineage = $stack;
return $item->lineage;
}
private function loadTree( )
{
$query = $this->query(
'SELECT p.item_id , p.item_name , MAX( t.pt_depth ) as depth '
. 'FROM items p '
. 'INNER JOIN items_tree t ON t.item_id_child = p.item_id '
. 'GROUP BY p.item_id, p.item_name , p.item_ordering '
. 'ORDER BY p.item_ordering' );
$result = $query->execute( );
$prevEntry = null;
$stack = array( );
$stackSize = 0;
$this->tree = array( );
$this->treeList = array( );
foreach ( $result as $entry ) {
if ( $entry->depth > $stackSize ) {
array_push( $stack , $prevEntry );
$stackSize ++;
} elseif ( $entry->depth < $stackSize ) {
while ( $stackSize > $entry->depth ) {
array_pop( $stack );
$stackSize --;
}
}
if ( array_key_exists( $entry->item_id , $this->loaded ) ) {
$object = $this->loaded[ $entry->item_id ];
} else {
$object = new Data_Item( $entry->item_id , $entry->item_name );
$this->loaded[ $entry->item_id ] = $object;
}
$object->children = array( );
$object->lineage = $stack;
if ( $object->depth = $entry->depth ) {
$object->hasParent = true;
$object->parent = $stack[ $stackSize - 1 ];
array_push( $this->loaded[ $object->parent ]->children , $object->id );
} else {
$object->hasParent = false;
}
$this->loaded[ $object->id ] = $object;
if ( $object->depth == 0 ) {
array_push( $this->tree , $object );
}
array_push( $this->treeList , $object );
$prevEntry = $object->id;
}
}
public function getTree( )
{
if ( $this->tree !== null ) {
return $this->tree;
}
$this->loadTree( );
return $this->tree;
}
public function getTreeList( )
{
if ( $this->tree !== null ) {
return $this->tree;
}
$this->loadTree( );
return $this->treeList;
}
public function getAll( $input )
{
$output = array( );
foreach ( $input as $id ) {
array_push( $output , $this->get( $id ) );
}
return $output;
}
public function countActiveTasks( )
{
if ( $this->activeTasksCounted ) {
return;
}
$query = $this->query(
'SELECT p.item_id , p.item_name , COUNT(*) AS t_count '
. 'FROM items p '
. 'INNER JOIN tasks t USING( item_id ) '
. 'LEFT OUTER JOIN completed_tasks c USING( task_id ) '
. 'WHERE c.task_id IS NULL '
. 'GROUP BY item_id, p.item_name' );
$results = $query->execute( );
foreach ( $results as $entry ) {
if ( array_key_exists( $entry->item_id , $this->loaded ) ) {
$object = $this->loaded[ $entry->item_id ];
} else {
$object = new Data_Item( $entry->item_id , $entry->item_name );
$this->loaded[ $entry->item_id ] = $object;
}
$object->activeTasks = $entry->t_count;
}
$this->activeTasksCounted = true;
}
private function checkActiveTasksIn( Data_Item $item )
{
if ( (int) $item->activeTasks > 0 ) {
return true;
}
foreach ( $this->getAll( $item->children ) as $child ) {
if ( $this->checkActiveTasksIn( $child ) ) {
return true;
}
}
return false;
}
public function canDelete( Data_Item $item )
{
if ( $this->tree === null ) {
$this->loadTree( );
}
$this->countActiveTasks( );
return ! $this->checkActiveTasksIn( $item );
}
public function getMoveTargetsIn( $tree , $parent , $item )
{
$positions = array( );
$count = count( $tree );
$nameProblem = false;
for ( $i = 0 ; $i <= $count ; $i ++ ) {
// Completely skip the selected item and its children
if ( $i != $count && $tree[ $i ]->id == $item->id ) {
continue;
}
// Check for invalid positions (i.e. before/after selected item)
$invalidPos = ( $i > 0 && $tree[ $i - 1 ]->id == $item->id );
// Check for duplicate name
$nameProblem = $nameProblem || ( $i != $count && $tree[ $i ]->name == $item->name );
// Get children positions
if ( $i < $count ) {
$sub = $this->getMoveTargetsIn( $this->getAll( $tree[ $i ]->children ) , $tree[ $i ] , $item );
} else {
$sub = array( );
}
array_push( $positions , array(
'item' => ( $i < $count ) ? $tree[ $i ]->id : ( is_null( $parent ) ? null : $parent->id ) ,
'end' => ( $i == $count ) ? 1 : 0 ,
'valid' => ! $invalidPos ,
'sub' => $sub
) );
}
// Add all data to output array
$realPos = array( );
foreach ( $positions as $pos ) {
if ( $pos['valid'] && ! $nameProblem ) {
array_push( $realPos , $pos['end'] . ':' . $pos[ 'item' ] );
}
$realPos = array_merge( $realPos , $pos[ 'sub' ] );
}
return $realPos;
}
public function getMoveTargets( Data_Item $item )
{
//
// A destination is a (parent,position) couple, where the
// position corresponds to the item before which the selected
// item is to be moved.
//
// A destination is valid if:
// - there is no parent or the parent does not have the
// selected item in its lineage;
// - there is no item in the parent (or at the root if
// there is no parent) that uses the same name as the selected
// item, unless that item *is* the selected item;
// - the item at the specified position is not the selected
// item, or there is no item at the specified position;
// - the item before the specified position is not the
// selected item, or the specified position is 0.
//
$result = $this->getMoveTargetsIn( $this->getTree( ) , null , $item );
return $result;
}
public function canMove( Data_Item $item )
{
$result = $this->getMoveTargets( $item );
return ! empty( $result );
}
public function moveBefore( $item , $before )
{
$result = $this->query( 'SELECT move_item_before( $1 , $2 ) AS error' )
->execute( $item , $before );
return $result[ 0 ]->error;
}
public function moveUnder( $item , $under )
{
$result = $this->query( 'SELECT move_item_under( $1 , $2 ) AS error' )
->execute( $item , $under );
return $result[ 0 ]->error;
}
public function moveLast( $item )
{
$result = $this->query( 'SELECT move_item_last( $1 ) AS error' )
->execute( $item );
return $result[ 0 ]->error;
}
public function destroy( $item )
{
$this->query( 'SELECT delete_item( $1 )' )->execute( $item );
}
public function rename( $item , $name )
{
$result = $this->query( 'SELECT rename_item( $1 , $2 ) AS error' )
->execute( $item , $name );
return $result[ 0 ]->error;
}
}