[ Index ]

Source Code Reference for V1.00

title

Body

[close]

/modules/tasks/ -> tasks.class.php (source)

   1  <?php /* $Id: tasks.class.php 220 2008-08-29 15:23:20Z pedroix $ $URL: https://web2project.svn.sourceforge.net/svnroot/web2project/trunk/modules/tasks/tasks.class.php $ */
   2  if (!defined('W2P_BASE_DIR')) {
   3      die('You should not access this file directly.');
   4  }
   5  
   6  require_once ($AppUI->getSystemClass('libmail'));
   7  require_once ($AppUI->getSystemClass('w2p'));
   8  require_once ($AppUI->getModuleClass('projects'));
   9  require_once ($AppUI->getSystemClass('event_queue'));
  10  require_once ($AppUI->getSystemClass('date'));
  11  
  12  $percent = array(0 => '0', 5 => '5', 10 => '10', 15 => '15', 20 => '20', 25 => '25', 30 => '30', 35 => '35', 40 => '40', 45 => '45', 50 => '50', 55 => '55', 60 => '60', 65 => '65', 70 => '70', 75 => '75', 80 => '80', 85 => '85', 90 => '90', 95 => '95', 100 => '100');
  13  
  14  // patch 2.12.04 add all finished last 7 days, my finished last 7 days
  15  $filters = array('my' => 'My Tasks', 'myunfinished' => 'My Unfinished Tasks', 'allunfinished' => 'All Unfinished Tasks', 'myproj' => 'My Projects', 'mycomp' => 'All Tasks for my Company', 'unassigned' => 'All Tasks (unassigned)', 'taskcreated' => 'All Tasks I Have Created', 'all' => 'All Tasks', 'allfinished7days' => 'All Tasks Finished Last 7 Days', 'myfinished7days' => 'My Tasks Finished Last 7 Days');
  16  
  17  $status = w2PgetSysVal('TaskStatus');
  18  
  19  $priority = w2PgetSysVal('TaskPriority');
  20  
  21  // user based access
  22  $task_access = array('0' => 'Public', '1' => 'Protected', '2' => 'Participant', '3' => 'Private');
  23  
  24  /*
  25  * TASK DYNAMIC VALUE:
  26  * 0  = default(OFF), no dep tracking of others, others do track
  27  * 1  = dynamic, umbrella task, no dep tracking, others do track
  28  * 11 = OFF, no dep tracking, others do not track
  29  * 21 = FEATURE, dep tracking, others do not track
  30  * 31 = ON, dep tracking, others do track
  31  */
  32  
  33  // When calculating a task's start date only consider
  34  // end dates of tasks with these dynamic values.
  35  $tracked_dynamics = array('0' => '0', '1' => '1', '2' => '31');
  36  // Tasks with these dynamics have their dates updated when
  37  // one of their dependencies changes. (They track dependencies)
  38  $tracking_dynamics = array('0' => '21', '1' => '31');
  39  
  40  /*
  41  * CTask Class
  42  */
  43  class CTask extends CW2pObject {
  44      /**
  45       @var int */
  46      var $task_id = null;
  47      /**
  48       @var string */
  49      var $task_name = null;
  50      /**
  51       @var int */
  52      var $task_parent = null;
  53      var $task_milestone = null;
  54      var $task_project = null;
  55      var $task_owner = null;
  56      var $task_start_date = null;
  57      var $task_duration = null;
  58      var $task_duration_type = null;
  59      /**
  60       @deprecated */
  61      var $task_hours_worked = null;
  62      var $task_end_date = null;
  63      var $task_status = null;
  64      var $task_priority = null;
  65      var $task_percent_complete = null;
  66      var $task_description = null;
  67      var $task_target_budget = null;
  68      var $task_related_url = null;
  69      var $task_creator = null;
  70  
  71      var $task_order = null;
  72      var $task_client_publish = null;
  73      var $task_dynamic = null;
  74      var $task_access = null;
  75      var $task_notify = null;
  76      var $task_departments = null;
  77      var $task_contacts = null;
  78      var $task_custom = null;
  79      var $task_type = null;
  80  
  81  	function CTask() {
  82          $this->CW2pObject('tasks', 'task_id');
  83      }
  84  
  85  	function __toString() {
  86          return $this->link . '/' . $this->type . '/' . $this->length;
  87      }
  88  
  89      // overload check
  90  	function check() {
  91          global $AppUI;
  92  
  93          if ($this->task_id === null) {
  94              return 'task id is NULL';
  95          }
  96          // ensure changes to checkboxes are honoured
  97          $this->task_milestone = intval($this->task_milestone);
  98          $this->task_dynamic = intval($this->task_dynamic);
  99  
 100          $this->task_percent_complete = intval($this->task_percent_complete);
 101  
 102          $this->task_target_budget = $this->task_target_budget ? $this->task_target_budget : 0.00;
 103  
 104          if (!$this->task_duration || $this->task_milestone) {
 105              $this->task_duration = '0';
 106          }
 107          if ($this->task_milestone) {
 108              if ($this->task_start_date && $this->task_start_date != '0000-00-00 00:00:00') {
 109                  $this->task_end_date = $this->task_start_date;
 110              } else {
 111                  $this->task_start_date = $this->task_end_date;
 112              }
 113          }
 114          if (!$this->task_creator) {
 115              $this->task_creator = $AppUI->user_id;
 116          }
 117          if (!$this->task_duration_type) {
 118              $this->task_duration_type = 1;
 119          }
 120          if (!$this->task_related_url) {
 121              $this->task_related_url = '';
 122          }
 123          if (!$this->task_notify) {
 124              $this->task_notify = 0;
 125          }
 126  
 127          /*
 128          * Check for bad or circular task relationships (dep or child-parent).
 129          * These checks are definately not exhaustive it is still quite possible
 130          * to get things in a knot.
 131          * Note: some of these checks may be problematic and might have to be removed
 132          */
 133          static $addedit;
 134          if (!isset($addedit)) {
 135              $addedit = w2PgetParam($_POST, 'dosql', '') == 'do_task_aed' ? true : false;
 136          }
 137          $this_dependencies = array();
 138  
 139          /*
 140          * If we are called from addedit then we want to use the incoming
 141          * list of dependencies and attempt to stop bad deps from being created
 142          */
 143          if ($addedit) {
 144              $hdependencies = w2PgetParam($_POST, 'hdependencies', '0');
 145              if ($hdependencies) {
 146                  $this_dependencies = explode(',', $hdependencies);
 147              }
 148          } else {
 149              $this_dependencies = explode(',', $this->getDependencies());
 150          }
 151          // Set to false for recursive updateDynamic calls etc.
 152          $addedit = false;
 153  
 154          // Have deps
 155          if (array_sum($this_dependencies)) {
 156              if ($this->task_dynamic == 1) {
 157                  return 'BadDep_DynNoDep';
 158              }
 159  
 160              $this_dependents = $this->task_id ? explode(',', $this->dependentTasks()) : array();
 161              $more_dependents = array();
 162              // If the dependents' have parents add them to list of dependents
 163              foreach ($this_dependents as $dependent) {
 164                  $dependent_task = new CTask();
 165                  $dependent_task->load($dependent);
 166                  if ($dependent_task->task_id != $dependent_task->task_parent) {
 167                      $more_dependents = explode(',', $this->dependentTasks($dependent_task->task_parent));
 168                  }
 169              }
 170              $this_dependents = array_merge($this_dependents, $more_dependents);
 171  
 172              // Task dependencies can not be dependent on this task
 173              $intersect = array_intersect($this_dependencies, $this_dependents);
 174              if (array_sum($intersect)) {
 175                  $ids = '(' . implode(',', $intersect) . ')';
 176                  return array('BadDep_CircularDep', $ids);
 177              }
 178          }
 179  
 180          // Has a parent
 181          if ($this->task_id && $this->task_id != $this->task_parent) {
 182              $this_children = $this->getChildren();
 183              $this_parent = new CTask();
 184              $this_parent->load($this->task_parent);
 185              $parents_dependents = explode(',', $this_parent->dependentTasks());
 186  
 187              if (in_array($this_parent->task_id, $this_dependencies)) {
 188                  return 'BadDep_CannotDependOnParent';
 189              }
 190              // Task parent cannot be child of this task
 191              if (in_array($this_parent->task_id, $this_children)) {
 192                  return 'BadParent_CircularParent';
 193              }
 194  
 195              if ($this_parent->task_parent != $this_parent->task_id) {
 196                  // ... or parent's parent, cannot be child of this task. Could go on ...
 197                  if (in_array($this_parent->task_parent, $this_children)) {
 198                      return array('BadParent_CircularGrandParent', '(' . $this_parent->task_parent . ')');
 199                  }
 200                  // parent's parent cannot be one of this task's dependencies
 201                  if (in_array($this_parent->task_parent, $this_dependencies)) {
 202                      return array('BadDep_CircularGrandParent', '(' . $this_parent->task_parent . ')');
 203                  }
 204              } // grand parent
 205  
 206              if ($this_parent->task_dynamic == 1) {
 207                  $intersect = array_intersect($this_dependencies, $parents_dependents);
 208                  if (array_sum($intersect)) {
 209                      $ids = '(' . implode(',', $intersect) . ')';
 210                      return array('BadDep_CircularDepOnParentDependent', $ids);
 211                  }
 212              }
 213              if ($this->task_dynamic == 1) {
 214                  // then task's children can not be dependent on parent
 215                  $intersect = array_intersect($this_children, $parents_dependents);
 216                  if (array_sum($intersect)) {
 217                      return 'BadParent_ChildDepOnParent';
 218                  }
 219              }
 220          } // parent
 221  
 222          return null;
 223      }
 224  
 225      /*
 226      * overload the load function
 227      * We need to update dynamic tasks of type '1' on each load process!
 228      * @param int $oid optional argument, if not specifed then the value of current key is used
 229      * @return any result from the database operation
 230      */
 231  
 232  	function load($oid = null, $strip = false, $skipUpdate = false) {
 233          // use parent function to load the given object
 234          $loaded = parent::load($oid, $strip);
 235  
 236          /*
 237          ** Update the values of a dynamic task from
 238          ** the children's properties each time the
 239          ** dynamic task is loaded.
 240          ** Additionally store the values in the db.
 241          ** Only treat umbrella tasks of dynamics '1'.
 242          */
 243          if ($this->task_dynamic == 1 && !($skipUpdate)) {
 244              // update task from children
 245              $this->htmlDecode();
 246              $this->updateDynamics(true);
 247  
 248              /*
 249              ** Use parent function to store the updated values in the db
 250              ** instead of store function of this object in order to
 251              ** prevent from infinite loops.
 252              */
 253              parent::store();
 254              $loaded = parent::load($oid, $strip);
 255          }
 256  
 257          // return whether the object load process has been successful or not
 258          return $loaded;
 259      }
 260  
 261      /*
 262      * call the load function but don't update dynamics
 263      */
 264  	function peek($oid = null, $strip = false) {
 265          $loadme = $this->load($oid, $strip, true);
 266          return $loadme;
 267      }
 268  
 269  	function updateDynamics($fromChildren = false) {
 270          //Has a parent or children, we will check if it is dynamic so that it's info is updated also
 271          $q = &new DBQuery;
 272          $modified_task = new CTask();
 273  
 274          if ($fromChildren) {
 275              $modified_task = &$this;
 276          } else {
 277              $modified_task->load($this->task_parent);
 278              $modified_task->htmlDecode();
 279          }
 280  
 281          if ($modified_task->task_dynamic == '1') {
 282              //Update allocated hours based on children with duration type of 'hours'
 283              $q->addTable($this->_tbl);
 284              $q->addQuery('SUM(task_duration * task_duration_type)');
 285              $q->addWhere('task_parent = ' . (int)$modified_task->task_id . ' AND task_id <> ' . $modified_task->task_id . ' AND task_duration_type = 1 ');
 286              $q->addGroup('task_parent');
 287              $children_allocated_hours1 = (float)$q->loadResult();
 288              $q->clear();
 289  
 290              /*
 291              * Update allocated hours based on children with duration type of 'days'
 292              * use the daily working hours instead of the full 24 hours to calculate 
 293              * dynamic task duration!
 294              */
 295              $q->addTable($this->_tbl);
 296              $q->addQuery(' SUM(task_duration * ' . w2PgetConfig('daily_working_hours') . ')');
 297              $q->addWhere('task_parent = ' . (int)$modified_task->task_id . ' AND task_id <> ' . $modified_task->task_id . ' AND task_duration_type <> 1 ');
 298              $q->addGroup('task_parent');
 299              $children_allocated_hours2 = (float)$q->loadResult();
 300              $q->clear();
 301  
 302              // sum up the two distinct duration values for the children with duration type 'hrs'
 303              // and for those with the duration type 'day'
 304              $children_allocated_hours = $children_allocated_hours1 + $children_allocated_hours2;
 305  
 306              if ($modified_task->task_duration_type == 1) {
 307                  $modified_task->task_duration = round($children_allocated_hours, 2);
 308              } else {
 309                  $modified_task->task_duration = round($children_allocated_hours / w2PgetConfig('daily_working_hours'), 2);
 310              }
 311  
 312              //Update worked hours based on children
 313              $q->addTable('tasks', 't');
 314              $q->innerJoin('task_log', 'tl', 't.task_id = tl.task_log_task');
 315              $q->addQuery('SUM(task_log_hours)');
 316              $q->addWhere('task_parent = ' . (int)$modified_task->task_id . ' AND task_id <> ' . $modified_task->task_id . ' AND task_dynamic <> 1 ');
 317              $children_hours_worked = (float)$q->loadResult();
 318              $q->clear();
 319  
 320              //Update worked hours based on dynamic children tasks
 321              $q->addTable('tasks');
 322              $q->addQuery('SUM(task_hours_worked)');
 323              $q->addWhere('task_parent = ' . (int)$modified_task->task_id . ' AND task_id <> ' . $modified_task->task_id . ' AND task_dynamic = 1 ');
 324              $children_hours_worked += (float)$q->loadResult();
 325              $q->clear();
 326  
 327              $modified_task->task_hours_worked = $children_hours_worked;
 328  
 329              //Update percent complete
 330              //hours
 331              $q->addTable('tasks');
 332              $q->addQuery('SUM(task_percent_complete * task_duration * task_duration_type)');
 333              $q->addWhere('task_parent = ' . (int)$modified_task->task_id . ' AND task_id <> ' . $modified_task->task_id . ' AND task_duration_type = 1 ');
 334              $real_children_hours_worked = (float)$q->loadResult();
 335              $q->clear();
 336  
 337              //days
 338              $q->addTable('tasks');
 339              $q->addQuery('SUM(task_percent_complete * task_duration * ' . w2PgetConfig('daily_working_hours') . ')');
 340              $q->addWhere('task_parent = ' . (int)$modified_task->task_id . ' AND task_id <> ' . $modified_task->task_id . ' AND task_duration_type <> 1 ');
 341              $real_children_hours_worked += (float)$q->loadResult();
 342              $q->clear();
 343  
 344              $total_hours_allocated = (float)($modified_task->task_duration * (($modified_task->task_duration_type > 1) ? w2PgetConfig('daily_working_hours') : 1));
 345              if ($total_hours_allocated > 0) {
 346                  $modified_task->task_percent_complete = ceil($real_children_hours_worked / $total_hours_allocated);
 347              } else {
 348                  $q->addTable('tasks');
 349                  $q->addQuery('AVG(task_percent_complete)');
 350                  $q->addWhere('task_parent = ' . (int)$modified_task->task_id . ' AND task_id <> ' . $modified_task->task_id);
 351                  $modified_task->task_percent_complete = $q->loadResult();
 352                  $q->clear();
 353              }
 354  
 355              //Update start date
 356              $q->addTable('tasks');
 357              $q->addQuery('MIN(task_start_date)');
 358              $q->addWhere('task_parent = ' . (int)$modified_task->task_id . ' AND task_id <> ' . $modified_task->task_id . ' AND NOT ISNULL(task_start_date)' . ' AND task_start_date <>    \'0000-00-00 00:00:00\'');
 359              $d = $q->loadResult();
 360              $q->clear();
 361              if ($d) {
 362                  $modified_task->task_start_date = $d;
 363              } else {
 364                  $modified_task->task_start_date = '0000-00-00 00:00:00';
 365              }
 366  
 367              //Update end date
 368              $q->addTable('tasks');
 369              $q->addQuery('MAX(task_end_date)');
 370              $q->addWhere('task_parent = ' . (int)$modified_task->task_id . ' AND task_id <> ' . $modified_task->task_id . ' AND NOT ISNULL(task_end_date)');
 371              $modified_task->task_end_date = $q->loadResult();
 372              $q->clear();
 373  
 374              //If we are updating a dynamic task from its children we don't want to store() it
 375              //when the method exists the next line in the store calling function will do that
 376              if ($fromChildren == false) {
 377                  $modified_task->store();
 378              }
 379          }
 380      }
 381  
 382      /*
 383      * Copy the current task
 384      *
 385      * @author handco <handco@users.sourceforge.net>
 386      * @param int id of the destination project
 387      * @return object The new record object or null if error
 388      */
 389  	function copy($destProject_id = 0, $destTask_id = -1) {
 390          $newObj = $this->duplicate();
 391  
 392          // Copy this task to another project if it's specified
 393          if ($destProject_id != 0) {
 394              $newObj->task_project = $destProject_id;
 395          }
 396  
 397          if ($destTask_id == 0) {
 398              $newObj->task_parent = $newObj->task_id;
 399          } else
 400              if ($destTask_id > 0) {
 401                  $newObj->task_parent = $destTask_id;
 402              }
 403  
 404          if ($newObj->task_parent == $this->task_id) {
 405              $newObj->task_parent = '';
 406          }
 407          $newObj->store();
 408  
 409          return $newObj;
 410      } // end of copy()
 411  
 412  	function deepCopy($destProject_id = 0, $destTask_id = 0) {
 413          $children = $this->getChildren();
 414          $newObj = $this->copy($destProject_id, $destTask_id);
 415          $new_id = $newObj->task_id;
 416          if (!empty($children)) {
 417              $tempTask = &new CTask();
 418              foreach ($children as $child) {
 419