3 * FusionForge project manager
5 * Copyright 1999-2000, Tim Perdue/Sourceforge
6 * Copyright 2002, Tim Perdue/GForge, LLC
7 * Copyright 2009, Roland Mas
8 * Copyright 2010, Alain Peyrat - Alcatel-Lucent
9 * Copyright 2011, Thorsten Glaser <t.glaser@tarent.de>
10 * Copyright 2013, Franck Villaume - TrivialDev
12 * This file is part of FusionForge. FusionForge is free software;
13 * you can redistribute it and/or modify it under the terms of the
14 * GNU General Public License as published by the Free Software
15 * Foundation; either version 2 of the Licence, or (at your option)
18 * FusionForge is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 require_once $gfcommon.'include/Error.class.php';
29 require_once $gfcommon.'include/Validator.class.php';
31 function projecttask_get_object($project_task_id,$data=false) {
32 global $PROJECTTASK_OBJ;
33 if (!isset($PROJECTTASK_OBJ["_".$project_task_id."_"])) {
35 //the db result handle was passed in
37 $res = db_query_params ('SELECT * FROM project_task_vw WHERE project_task_id=$1',
38 array ($project_task_id)) ;
40 if (db_numrows($res) <1 ) {
41 $PROJECTTASK_OBJ["_".$project_task_id."_"]=false;
44 $data = db_fetch_array($res);
47 $ProjectGroup = projectgroup_get_object($data["group_project_id"]);
48 $PROJECTTASK_OBJ["_".$project_task_id."_"]= new ProjectTask($ProjectGroup,$project_task_id,$data);
52 return $PROJECTTASK_OBJ["_".$project_task_id."_"];
56 Types of task dependencies
58 define('PM_LINK_DEFAULT','FS');
59 define('PM_LINK_START_START','SS');
60 define('PM_LINK_START_FINISH','SF');
61 define('PM_LINK_FINISH_START','FS');
62 define('PM_LINK_FINISH_FINISH','FF');
64 class ProjectTask extends Error {
67 * Associative array of data from db.
69 * @var array $data_array.
74 * The ProjectGroup object.
76 * @var ProjectGroup $ProjectGroup.
81 var $relatedartifacts;
84 * ProjectTask - Constructor.
86 * @param object $ProjectGroup The ProjectGroup object to which this ProjectTask is associated.
87 * @param int|bool $project_task_id The project_task_id.
88 * @param array|bool $arr The associative array of data.
89 * @return boolean success.
91 function ProjectTask(&$ProjectGroup, $project_task_id=false, $arr=false) {
93 if (!$ProjectGroup || !is_object($ProjectGroup)) {
94 $this->setError('No Valid ProjectGroup Object');
97 if ($ProjectGroup->isError()) {
98 $this->setError($ProjectGroup->getErrorMessage());
101 $this->ProjectGroup =& $ProjectGroup;
103 if ($project_task_id) {
104 if (!$arr || !is_array($arr)) {
105 if (!$this->fetchData($project_task_id)) {
109 $this->data_array =& $arr;
111 // Verify this message truly belongs to this ProjectGroup
113 if ($this->data_array['group_project_id'] != $this->ProjectGroup->getID()) {
114 $this->setError('Group_project_id in db result does not match ProjectGroup Object');
123 * create - create a new ProjectTask in the database.
125 * @param string $summary The summary of this task.
126 * @param string $details The detailed description of this task.
127 * @param int $priority The Priority of this task.
128 * @param int $hours The Hours estimated to complete this task.
129 * @param int $start_date The (unix) start date of this task.
130 * @param int $end_date The (unix) end date of this task.
131 * @param int $category_id The category_id of this task.
132 * @param int $percent_complete The percentage of completion in integer format of this task.
133 * @param array $assigned_arr An array of user_id's that are assigned this task.
134 * @param array $depend_arr An array of project_task_id's that this task depends on.
135 * @param int $duration The duration of the task in days.
136 * @param int $parent_id The id of the parent task, if any.
137 * @param array $importData An array ('user' => user_id)
138 * @param array $importData An array ('user' => user_id)
139 * @return boolean success.
141 function create($summary,$details,$priority,$hours,$start_date,$end_date,
142 $category_id,$percent_complete,&$assigned_arr,&$depend_arr,$duration=0,$parent_id=0, $importData = array()) {
143 $v = new Validator();
144 $v->check($summary, _("summary"));
145 $v->check($details, _("details"));
146 $v->check($priority, _("priority"));
147 $v->check($hours, _("hours"));
148 $v->check($start_date, _("start date"));
149 $v->check($end_date, _("end date"));
150 $v->check($category_id, _("category"));
151 if (!$v->isClean()) {
152 $this->setError($v->formErrorMsg(_("Must include ")));
161 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
162 $this->setPermissionDeniedError();
166 if(array_key_exists('user', $importData)){
167 $uid = $importData['user'];
173 $res = db_query_params ('SELECT nextval($1) AS id',
174 array ('project_task_pk_seq'));
175 if (!$project_task_id=db_result($res,0,'id')) {
176 $this->setError( 'Could Not Get Next Project Task ID' );
181 $this->data_array['project_task_id']=$project_task_id;
183 $result = db_query_params ('INSERT INTO project_task (project_task_id,group_project_id,created_by,summary,details,start_date,end_date,status_id,category_id,priority,percent_complete,hours,duration,parent_id) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)',
184 array ($project_task_id,
185 $this->ProjectGroup->getID(),
187 htmlspecialchars($summary),
188 htmlspecialchars($details),
199 if (!$result || db_affected_rows($result) < 1) {
200 $this->setError('Posting Failed'.' '.db_error());
205 if (!$this->setDependentOn($depend_arr)) {
209 if (!$this->setAssignedTo($assigned_arr)) {
213 if (!$this->fetchData($project_task_id)) {
217 $this->sendNotice(1);
223 * fetchData - re-fetch the data for this ProjectTask from the database.
225 * @param int $project_task_id The project_task_id.
226 * @return boolean success.
228 function fetchData($project_task_id) {
229 $res = db_query_params ('SELECT * FROM project_task_vw
230 WHERE project_task_id=$1
231 AND group_project_id=$2',
232 array ($project_task_id,
233 $this->ProjectGroup->getID())) ;
234 if (!$res || db_numrows($res) < 1) {
235 $this->setError(_('Invalid Task ID').' '.db_error());
238 $this->data_array = db_fetch_array($res);
239 db_free_result($res);
244 * getProjectGroup - get the ProjectGroup object this ProjectTask is associated with.
246 * @return ProjectGroup The ProjectGroup object.
248 function &getProjectGroup() {
249 return $this->ProjectGroup;
253 * getID - get this project_task_id.
255 * @return int The project_task_id.
258 return $this->data_array['project_task_id'];
262 * getSubmittedRealName - get the real name of the person who created this task.
264 * @return string The real name person who created this task.
266 function getSubmittedRealName() {
267 return $this->data_array['realname'];
271 * getDuration - the duration of the task.
273 * @return int The number of days of duration.
275 function getDuration() {
276 return $this->data_array['duration'];
280 * getParentID - the task_id of the parent task, if any.
282 * @return string The real name person who created this task.
284 function getParentID() {
285 return $this->data_array['parent_id'];
289 * getSubmittedUnixName - get the unix name of the person who created this task.
291 * @return string The unix name of the person who created this task.
293 function getSubmittedUnixName() {
294 return $this->data_array['user_name'];
298 * getSummary - get the subject/summary of this task.
300 * @return string The summary.
302 function getSummary() {
303 return $this->data_array['summary'];
307 * getDetails - get the body/details of this task.
309 * @return string The body/details.
311 function getDetails() {
312 return $this->data_array['details'];
316 * getPercentComplete - an integer between 0 and 100.
318 * @return int The percentage of completion of this task.
320 function getPercentComplete() {
321 return $this->data_array['percent_complete'];
325 * getPriority - the priority, between 1 and 9 of this task.
327 * @return int The priority.
329 function getPriority() {
330 return $this->data_array['priority'];
334 * getHours - the hours this task is expected to take.
336 * @return int The hours.
338 function getHours() {
339 return $this->data_array['hours'];
343 * getStartDate - the unix time that this task will start.
345 * @return int The unix start time of this task.
347 function getStartDate() {
348 return $this->data_array['start_date'];
352 * getEndDate - the unix time that this task will end.
354 * @return int The unix end time of this task.
356 function getEndDate() {
357 return $this->data_array['end_date'];
361 * getStatusID - the integer of the status of this task.
363 * @return int the status_id.
365 function getStatusID() {
366 return $this->data_array['status_id'];
370 * getStatusName - the string of the status of this task.
372 * @return string the status_name.
374 function getStatusName() {
375 return $this->data_array['status_name'];
379 * getCategoryID - the category_id of this task.
381 * @return int the category_id.
383 function getCategoryID() {
384 return $this->data_array['category_id'];
388 * getCategoryName - the category_name of this task.
390 * @return int the category_name.
392 function getCategoryName() {
393 return $this->data_array['category_name'];
397 * getLastModifiedDate - the last_modified_date of this task.
399 * @return int the last_modified_date.
401 function getLastModifiedDate() {
402 return $this->data_array['last_modified_date'];
406 * setExternalID - set a row in project_task_external_order which stores
407 * an id, for example an ID generated by MS Project, which needs to be restored later
409 * @param int $id An external ID from another tool
411 function setExternalID($id) {
412 $res = db_query_params ('UPDATE project_task_external_order SET external_id=$1
413 WHERE project_task_id=$2',
416 if (db_affected_rows($res) < 1) {
417 db_query_params ('INSERT INTO project_task_external_order (project_task_id,external_id) VALUES ($1, $2)',
418 array ($this->getID(),
424 * getExternalID - get the ID that MS Project uses to sort tasks
426 * @return int the id.
428 function getExternalID() {
429 return $this->data_array['external_id'];
433 * getRelatedArtifacts - Return a result set of artifacts which are related to this task.
435 * @return resource Database result set.
437 function getRelatedArtifacts() {
438 if (!$this->relatedartifacts) {
439 $this->relatedartifacts=
440 db_query_params ('SELECT agl.group_id,agl.name,agl.group_artifact_id,a.artifact_id,a.open_date,a.summary,ast.status_name
441 FROM artifact_group_list agl, artifact a, artifact_status ast
442 WHERE a.group_artifact_id=agl.group_artifact_id
443 AND ast.id=a.status_id
444 AND EXISTS (SELECT artifact_id FROM project_task_artifact
445 WHERE artifact_id=a.artifact_id
446 AND project_task_id=$1)',
447 array ($this->getID())) ;
449 return $this->relatedartifacts;
453 * addRelatedArtifacts - take an array of artifact_id's and build relationships.
455 * @param array $art_array An array of artifact_id's to be attached to this task.
456 * @return boolean success.
458 function addRelatedArtifacts($art_array) {
459 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
460 $this->setPermissionDeniedError();
464 // SHOULD REALLY INSTANTIATE THIS ARTIFACT OBJECT TO ENSURE PROPER SECURITY - FUTURE
466 // new ArtifactFromID($id)
468 for ($i=0; $i<count($art_array); $i++) {
469 if ($art_array[$i] < 1) {
472 $res = db_query_params ('INSERT INTO project_task_artifact (project_task_id,artifact_id) VALUES ($1,$2)',
473 array ($this->getID(),
476 $this->setError('Error inserting artifact relationship: '.db_error());
484 * removeRelatedArtifacts - take an array of artifact_id's and delete relationships.
486 * @param array $art_array An array of artifact_id's to be removed from this task.
487 * @return boolean success.
489 function removeRelatedArtifacts($art_array) {
490 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
491 $this->setPermissionDeniedError();
495 for ($i=0; $i<count($art_array); $i++) {
496 $res = db_query_params ('DELETE FROM project_task_artifact
497 WHERE project_task_id=$1
499 array ($this->getID(),
502 $this->setError('Error deleting artifact relationship: '.db_error());
510 * delete - delete this tracker and all its related data.
512 * @param bool $sure I'm Sure.
513 * @return bool true/false;
515 function delete($sure) {
517 $this->setMissingParamsError(_('Please tick all checkboxes.'));
520 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
521 $this->setPermissionDeniedError();
526 $res = db_query_params ('DELETE FROM project_assigned_to WHERE project_task_id=$1',
527 array ($this->getID())) ;
529 $this->setError('Error deleting assigned users relationship: '.db_error());
533 $res = db_query_params ('DELETE FROM project_dependencies WHERE project_task_id=$1',
534 array ($this->getID())) ;
536 $this->setError('Error deleting dependencies: '.db_error());
540 $res = db_query_params ('DELETE FROM project_history WHERE project_task_id=$1',
541 array ($this->getID())) ;
543 $this->setError('Error deleting history: '.db_error());
547 $res = db_query_params ('DELETE FROM project_messages WHERE project_task_id=$1',
548 array ($this->getID())) ;
550 $this->setError('Error deleting messages: '.db_error());
554 $res = db_query_params ('DELETE FROM project_task_artifact WHERE project_task_id=$1',
555 array ($this->getID())) ;
557 $this->setError('Error deleting artifacts: '.db_error());
561 $res = db_query_params ('DELETE FROM rep_time_tracking WHERE project_task_id=$1',
562 array ($this->getID())) ;
564 $this->setError('Error deleting time tracking report: '.db_error());
568 $res = db_query_params ('DELETE FROM project_task WHERE project_task_id=$1',
569 array ($this->getID())) ;
571 $this->setError('Error deleting task: '.db_error());
581 * getOtherTasks - Return a result set of tasks in this subproject that do not equal
582 * the current task_id.
584 * @return resource Database result set.
586 function getOtherTasks () {
588 // May not yet have an ID, if we are creating a NEW task
590 if ($this->getID()) {
591 return db_query_params ('SELECT project_task_id,summary
593 WHERE group_project_id=$1
594 AND project_task_id <> $2
595 ORDER BY project_task_id DESC',
596 array ($this->ProjectGroup->getID(),
599 return db_query_params ('SELECT project_task_id,summary
601 WHERE group_project_id=$1
602 ORDER BY project_task_id DESC',
603 array ($this->ProjectGroup->getID())) ;
608 * getHistory - returns a result set of audit trail for this ProjectTask.
610 * @return resource database result set.
612 function getHistory() {
613 return db_query_params ('SELECT *
614 FROM project_history_user_vw
615 WHERE project_task_id=$1
616 ORDER BY mod_date DESC',
617 array ($this->getID())) ;
621 * getMessages - get the list of messages attached to this ProjectTask.
624 * @return resource database result set.
626 function getMessages($asc=false) {
627 return db_query_params ('SELECT *
628 FROM project_message_user_vw
629 WHERE project_task_id=$1
630 ORDER BY postdate ' . ($asc ? 'ASC' : 'DESC'),
631 array ($this->getID())) ;
635 * addMessage - Handle the addition of a followup message to this task.
637 * @param string $message The message.
638 * @param array $importData Specific data for import (user id and time)
639 * @return boolean success.
641 function addMessage($message, $importData = array()) {
642 //prevent posting the same message
643 if ($this->getDetails() == htmlspecialchars($message)) {
646 $res = db_query_params ('SELECT * FROM project_messages
647 WHERE project_task_id=$1
649 array ($this->getID(),
650 htmlspecialchars($message))) ;
651 if (!$res || db_numrows($res) < 1) {
653 if(array_key_exists('user', $importData)){
654 $uid = $importData['user'];
658 if(array_key_exists('time', $importData)){
659 $time = $importData['time'];
663 $res = db_query_params ('INSERT INTO project_messages (project_task_id,body,posted_by,postdate) VALUES ($1,$2,$3,$4)',
664 array ($this->getID(),
665 htmlspecialchars($message),
668 if (!$res || db_affected_rows($res) < 1) {
669 $this->setError(db_error());
680 * addHistory - Handle the insertion of history for these parameters.
682 * @param string $field_name The field name.
683 * @param string $old_value The old value.
684 * @param array $importData Specific data for import (user id and time)
687 function addHistory ($field_name,$old_value,$importData=array()) {
689 if(array_key_exists('user', $importData)){
690 $uid = $importData['user'];
694 if(array_key_exists('time', $importData)){
695 $time = $importData['time'];
699 $result = db_query_params ('INSERT INTO project_history (project_task_id,field_name,old_value,mod_by,mod_date) VALUES ($1,$2,$3,$4,$5)',
700 array ($this->getID(),
706 $this->setError('ERROR IN AUDIT TRAIL - '.db_error());
714 * checkCircular - recursive function calls itself to look at all tasks you are dependent on.
716 * @param int $depend_on_id The project_task_id you are dependent on.
717 * @param int $original_id The project_task_id you are checking circular dependencies for.
718 * @return boolean success.
720 function checkCircular($depend_on_id, $original_id) {
721 //for msproject users - ms project has more complex logic than gforge
725 if ($depend_on_id == $original_id) {
726 $this->setError(_('Circular Dependency Detected\''));
730 $res = db_query_params ('SELECT is_dependent_on_task_id AS id
731 FROM project_dependencies
732 WHERE project_task_id=$1',
733 array ($depend_on_id)) ;
734 $rows=db_numrows($res);
736 for ($i=0; $i<$rows; $i++) {
737 if (!$this->checkCircular(db_result($res,$i,'id'), $original_id)) {
746 * setDependentOn - takes an array of project_task_id's and builds dependencies.
748 * @param array $arr_ The array of project_task_id's.
749 * @return boolean success.
751 function setDependentOn(&$arr_) {
753 // IMPORTANT - MUST VERIFY NO CIRCULAR DEPENDENCY!!
755 if (!$arr_ || empty($arr_)) {
756 $arr_=array('100'=>PM_LINK_DEFAULT);
758 $arr = array_keys($arr_);
759 //get existing dependencies to diff against
760 $arr2 = array_keys($this->getDependentOn());
762 if (count($arr) || count($arr2)) {
763 $add_arr = array_values (array_diff ($arr, $arr2));
764 //echo "add arr: ".print_r($add_arr);
765 $del_arr = array_values (array_diff ($arr2, $arr));
766 //echo "del arr: ".print_r($del_arr);
767 for ($i=0; $i<count($del_arr); $i++) {
768 db_query_params ('DELETE FROM project_dependencies
769 WHERE project_task_id=$1
770 AND is_dependent_on_task_id=$2',
771 array ($this->getID(),
774 $this->setError(db_error());
778 for ($i=0; $i<count($add_arr); $i++) {
780 // Check task for circular dependencies
782 if (!$this->checkCircular($add_arr[$i],$this->getID())) {
785 $lnk = $arr_[$add_arr[$i]];
787 $lnk=PM_LINK_DEFAULT;
789 db_query_params ('INSERT INTO project_dependencies (project_task_id,is_dependent_on_task_id,link_type) VALUES ($1,$2,$3)',
790 array ($this->getID(),
794 $this->setError(db_error());
805 * convertDependentOn - converts a regular array of dependencies, such
806 * as from a multiple-select-box to an associative array with default
807 * link types. Should be called from web code as part of the create/update calls.
808 * Here we are converting an array like array(1,5,9,77) to array(1=>SS,5=>SF,9=>FS,77=>SS)
812 function &convertDependentOn($arr) {
813 $deps = $this->getDependentOn();
814 for ($i=0; $i<count($arr); $i++) {
815 if (isset($deps[$arr[$i]])) {
816 //use existing link_type if it exists
817 $new[$arr[$i]]=$deps[$arr[$i]];
819 //else create with default link type
820 $new[$arr[$i]]=PM_LINK_DEFAULT;
827 * getDependentOn - get an array of project_task_id's that you are dependent on.
829 * @return array The array of project_task_id's in this format:
830 * array($id=>$link_type,id2=>link_type2).
832 function getDependentOn() {
833 if (!$this->getID()) {
836 if (!$this->dependon) {
837 $res = db_query_params ('SELECT is_dependent_on_task_id,link_type
838 FROM project_dependencies
839 WHERE project_task_id=$1',
840 array ($this->getID())) ;
841 for ($i=0; $i<db_numrows($res); $i++) {
842 $this->dependon[db_result($res,$i,'is_dependent_on_task_id')] = db_result($res,$i,'link_type');
845 /* fix bug 319: if dependent list is empty, set it to 100 (none) */
846 if (!$this->dependon) {
847 $this->dependon[100]=PM_LINK_DEFAULT;
849 return $this->dependon;
853 * setAssignedTo - takes an array of user_id's and builds assignments.
855 * @param array $arr The array of user_id's.
856 * @return boolean success.
858 function setAssignedTo(&$arr) {
859 $arr2 = $this->getAssignedTo();
860 $this->assignedto =& $arr;
862 // If no one is assigned, then assign it to "100" - NOBODY
863 if (!$arr || count($arr) < 1 || ((count($arr)==1) && ($arr[0]==''))) {
866 if (count($arr) || count($arr2)) {
867 $add_arr = array_values(array_diff ($arr, $arr2));
868 $del_arr = array_values(array_diff ($arr2, $arr));
869 for ($i=0; $i<count($del_arr); $i++) {
870 db_query_params ('DELETE FROM project_assigned_to
871 WHERE project_task_id=$1
872 AND assigned_to_id=$2',
873 array ($this->getID(),
876 $this->setError(db_error());
880 for ($i=0; $i<count($add_arr); $i++) {
881 db_query_params ('INSERT INTO project_assigned_to (project_task_id,assigned_to_id) VALUES ($1,$2)',
882 array ($this->getID(),
885 $this->setError(db_error());
896 * getAssignedTo - get an array of user_id's that you are assigned to.
898 * @return array The array of user_id's.
900 function getAssignedTo() {
901 if (!$this->getID()) {
904 if (!$this->assignedto) {
905 $this->assignedto =& util_result_column_to_array(db_query_params('SELECT assigned_to_id FROM project_assigned_to WHERE project_task_id=$1',
906 array ($this->getID()))) ;
908 return $this->assignedto;
912 * update - update this ProjectTask in the database.
914 * @param string $summary The summary of this task.
915 * @param string $details The detailed description of this task.
916 * @param int $priority The Priority of this task.
917 * @param int $hours The Hours estimated to complete this task.
918 * @param int $start_date The (Unix) start date of this task.
919 * @param int $end_date The (Unix) end date of this task.
920 * @param int $status_id The status_id of this task.
921 * @param int $category_id The category_id of this task.
922 * @param int $percent_complete The percentage of completion in integer format of this task.
923 * @param array $assigned_arr An array of user_id's that are assigned this task.
924 * @param array $depend_arr An array of project_task_id's that this task depends on.
925 * @param int $new_group_project_id The GroupProjectID of a new subproject that you want to move this Task to.
926 * @param int $duration The duration of the task in days.
927 * @param int $parent_id The id of the parent task, if any.
928 * @return bool success.
930 function update($summary, $details, $priority, $hours, $start_date, $end_date,
931 $status_id, $category_id, $percent_complete, &$assigned_arr, &$depend_arr,
932 $new_group_project_id, $duration=0, $parent_id=0) {
933 $has_changes = false; // if any of the values passed is different from
935 $arrChangedAndInNotice = array(
944 $v = new Validator();
945 $v->check($summary, _("summary"));
946 $v->check($priority, _("priority"));
947 $v->check($hours, _("hours"));
948 $v->check($start_date, _("start date"));
949 $v->check($end_date, _("end date"));
950 $v->check($status_id, _("status"));
951 $v->check($category_id, _("category"));
952 if (!$v->isClean()) {
953 $this->setError($v->formErrorMsg(_("Must include ")));
959 if ( ($this->getParentID()) != $parent_id ) {
965 if ( ($this->getDuration()) != $duration ) {
969 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
970 $this->setPermissionDeniedError();
974 /*if ( ($this->getSummary() != $summary) || ($this->getDetails() != $details) ||
975 ($this->getPriority() != $priority) || ($this->getHours() != $hours) ||
976 ($this->getStartDate() != $start_date) || ($this->getEndDate() != $end_date) ||
977 ($this->getStatusID() != $status_id) || ($this->getCategoryID() != $category_id) ||
978 ($this->getPercentComplete() != $percent_complete) ) {
986 // Attempt to move this Task to a new Subproject
987 // need to instantiate new ProjectGroup obj and test if it works
989 $group_project_id = $this->ProjectGroup->getID();
990 if ($new_group_project_id != $group_project_id) {
991 $newProjectGroup= new ProjectGroup($this->ProjectGroup->getGroup(), $new_group_project_id);
992 if (!is_object($newProjectGroup) || $newProjectGroup->isError()) {
993 $this->setError('ProjectTask: Could not move to new ProjectGroup'. $newProjectGroup->getErrorMessage());
997 if (!forge_check_perm ('pm', $newProjectGroup->getID(), 'manager')) {
998 $this->setPermissionDeniedError();
1003 // Now set ProjectGroup, Category, and Assigned to 100 in the new ProjectGroup
1007 unset($assigned_to);
1008 $assigned_to=array('100');
1009 $this->ProjectGroup =& $newProjectGroup;
1010 $this->addHistory ('group_project_id',$group_project_id);
1011 $has_changes = true;
1012 $arrChangedAndInNotice['subproject'] = ">";
1016 $has_changes = true;
1017 if($details != "" && $details != null) {$arrChangedAndInNotice['details'] = ">";}
1018 //Message vorhanden;
1019 if (!$this->addMessage($details)) {
1024 if ($this->getStatusID() != $status_id) {
1025 $this->addHistory ('status_id',$this->getStatusID());
1026 $has_changes = true;
1027 $arrChangedAndInNotice['status'] = ">";
1030 if ($this->getCategoryID() != $category_id) {
1031 $this->addHistory ('category_id',$this->getCategoryID());
1032 $has_changes = true;
1035 if ($this->getPriority() != $priority) {
1036 $this->addHistory ('priority',$this->getPriority());
1037 $has_changes = true;
1040 if ($this->getSummary() != htmlspecialchars($summary)) {
1041 $this->addHistory ('summary',$this->getSummary());
1042 $has_changes = true;
1043 $arrChangedAndInNotice['summary'] = ">";
1046 if ($this->getPercentComplete() != $percent_complete) {
1047 $this->addHistory ('percent_complete',$this->getPercentComplete());
1048 $has_changes = true;
1049 $arrChangedAndInNotice['complete'] = ">";
1052 if ($this->getHours() != $hours) {
1053 $this->addHistory ('hours',$this->getHours());
1054 $has_changes = true;
1057 if ($this->getStartDate() != $start_date) {
1058 $this->addHistory ('start_date',$this->getStartDate());
1059 $has_changes = true;
1062 if ($this->getEndDate() != $end_date) {
1063 $this->addHistory ('end_date',$this->getEndDate());
1064 $has_changes = true;
1067 $old_assigned = $this->getAssignedTo();
1068 if ($assigned_arr == '' || !$assigned_arr) {
1069 $assigned_arr = array();
1070 $assigned_arr[0] = 100;
1072 $removed=array_diff($old_assigned, $assigned_arr);
1073 $added=array_diff($assigned_arr, $old_assigned);
1074 if (count($removed)>0 || count($added)>0) {
1075 $assigned = array();
1076 foreach ($old_assigned as $user_id) {
1077 $assigned[] = user_get_object($user_id)->getRealName();
1079 $this->addHistory('assigned_to', join(', ', $assigned));
1080 $arrChangedAndInNotice['assigned'] = ">";
1081 $has_changes = true;
1084 $old_array = array_keys($this->getDependentOn());
1085 if (!is_array($depend_arr)) {
1086 $depend_arr = array();
1088 $removed=array_diff($old_array,array_keys($depend_arr));
1089 $added=array_diff(array_keys($depend_arr), $old_array);
1090 if (count($removed)>0 || count($added)>0) {
1091 $this->addHistory('dependent_on', join(', ', $old_array));
1092 $has_changes = true;
1095 if (!$this->setDependentOn($depend_arr)) {
1098 } elseif (!$this->setAssignedTo($assigned_arr)) {
1102 $res = db_query_params ('UPDATE project_task SET
1109 percent_complete=$7,
1111 group_project_id=$9,
1114 WHERE group_project_id=$12
1115 AND project_task_id=$13',
1116 array (htmlspecialchars($summary),
1124 $new_group_project_id,
1129 if (!$res || db_affected_rows($res) < 1) {
1130 $this->setError(db_error());
1134 if (!$this->fetchData($this->getID())) {
1135 $this->setError(db_error());
1139 if ($has_changes) { //only send email if there was any change
1140 $this->sendNotice(false, $arrChangedAndInNotice);
1151 * sendNotice - contains the logic for sending email updates.
1153 * @param bool $first
1154 * @param array $arrChangedAndInNotice
1155 * @return bool success.
1157 function sendNotice($first=false, $arrChangedAndInNotice=array()) {
1158 global $send_task_email;
1160 if ($send_task_email===false) {
1163 $ids = $this->getAssignedTo();
1166 // See if there is anyone to send messages to
1168 if (count($ids) < 1 && !$this->ProjectGroup->getSendAllPostsTo()) {
1172 if (session_loggedin()) {
1173 $user = session_get_user()->getRealName();
1178 $body = "Task #". $this->getID() ." has been updated by $user.".
1179 "\n\nProject: ". $this->ProjectGroup->Group->getPublicName();
1181 if (isset($arrChangedAndInNotice['subproject']))
1182 $body .= "\n". $arrChangedAndInNotice['subproject']."Subproject: ". $this->ProjectGroup->getName();
1184 if (isset($arrChangedAndInNotice['summary']))
1185 $body .= "\n". $arrChangedAndInNotice['summary']. "Summary: ".util_unconvert_htmlspecialchars( $this->getSummary() );
1187 if (isset($arrChangedAndInNotice['complete']))
1188 $body .= "\n". $arrChangedAndInNotice['complete']. "Complete: ". $this->getPercentComplete() ."%";
1190 if (isset($arrChangedAndInNotice['status']))
1191 $body .= "\n". $arrChangedAndInNotice['status']. "Status: ". $this->getStatusName();
1193 if (isset($arrChangedAndInNotice['assigned'])) {
1194 $assigned = array();
1195 foreach ($this->getAssignedTo() as $user_id) {
1196 $assigned[] = user_get_object($user_id)->getRealName();
1198 $body .= "\n". $arrChangedAndInNotice['assigned']. "Assigned: ". join(', ', $assigned);
1201 $body .= "\n\nDescription: ". util_unconvert_htmlspecialchars( $this->getDetails() );
1204 Now get the followups to this task
1206 $result2=$this->getMessages();
1208 $rows=db_numrows($result2);
1210 if ($result2 && $rows > 0) {
1211 $body .= "\n\nFollow-Ups:";
1212 for ($i=0; $i<$rows;$i++) {
1213 if($i===0){ $temp = $arrChangedAndInNotice['details']; } else {$temp = "";}
1214 $body .= "\n\n-------------------------------------------------------";
1215 $body .= "\nDate: ". date(_('Y-m-d H:i'),db_result($result2,$i,'postdate'));
1216 $body .= "\nBy: ".db_result($result2,$i,'user_name');
1217 $body .= "\n\n". $temp ."Comment:\n".util_unconvert_htmlspecialchars(db_result($result2,$i,'body'));
1221 $body .= "\n\n-------------------------------------------------------".
1222 "\nFor more info, visit:".
1223 "\n\n".util_make_url ('/pm/task.php?func=detailtask&project_task_id='.$this->getID().
1224 "&group_id=".$this->ProjectGroup->Group->getID().
1225 "&group_project_id=".$this->ProjectGroup->getID());
1227 $subject=sprintf (_('[%1$s - %2$s] [Task #%3$d] '),
1228 $this->ProjectGroup->Group->getUnixName(),
1229 $this->ProjectGroup->getName(),
1231 util_unconvert_htmlspecialchars( $this->getSummary() );
1233 util_handle_message(array_unique($ids),$subject,$body,$this->ProjectGroup->getSendAllPostsTo());
1241 // c-file-style: "bsd"