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>
11 * This file is part of FusionForge. FusionForge is free software;
12 * you can redistribute it and/or modify it under the terms of the
13 * GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the Licence, or (at your option)
17 * FusionForge is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 require_once $gfcommon.'include/Error.class.php';
28 require_once $gfcommon.'include/Validator.class.php';
30 function &projecttask_get_object($project_task_id,$data=false) {
31 global $PROJECTTASK_OBJ;
32 if (!isset($PROJECTTASK_OBJ["_".$project_task_id."_"])) {
34 //the db result handle was passed in
36 $res = db_query_params ('SELECT * FROM project_task_vw WHERE project_task_id=$1',
37 array ($project_task_id)) ;
39 if (db_numrows($res) <1 ) {
40 $PROJECTTASK_OBJ["_".$project_task_id."_"]=false;
43 $data = db_fetch_array($res);
46 $ProjectGroup =& projectgroup_get_object($data["group_project_id"]);
47 $PROJECTTASK_OBJ["_".$project_task_id."_"]= new ProjectTask($ProjectGroup,$project_task_id,$data);
51 return $PROJECTTASK_OBJ["_".$project_task_id."_"];
55 Types of task dependencies
57 define('PM_LINK_DEFAULT','FS');
58 define('PM_LINK_START_START','SS');
59 define('PM_LINK_START_FINISH','SF');
60 define('PM_LINK_FINISH_START','FS');
61 define('PM_LINK_FINISH_FINISH','FF');
63 class ProjectTask extends Error {
66 * Associative array of data from db.
68 * @var array $data_array.
73 * The ProjectGroup object.
75 * @var object $ProjectGroup.
80 var $relatedartifacts;
85 * @param object The ProjectGroup object to which this ProjectTask is associated.
86 * @param int The project_task_id.
87 * @param array The associative array of data.
88 * @return boolean success.
90 function ProjectTask(&$ProjectGroup, $project_task_id=false, $arr=false) {
92 if (!$ProjectGroup || !is_object($ProjectGroup)) {
93 $this->setError('No Valid ProjectGroup Object');
96 if ($ProjectGroup->isError()) {
97 $this->setError($ProjectGroup->getErrorMessage());
100 $this->ProjectGroup =& $ProjectGroup;
102 if ($project_task_id) {
103 if (!$arr || !is_array($arr)) {
104 if (!$this->fetchData($project_task_id)) {
108 $this->data_array =& $arr;
110 // Verify this message truly belongs to this ProjectGroup
112 if ($this->data_array['group_project_id'] != $this->ProjectGroup->getID()) {
113 $this->setError('Group_project_id in db result does not match ProjectGroup Object');
122 * create - create a new ProjectTask in the database.
124 * @param string The summary of this task.
125 * @param string The detailed description of this task.
126 * @param int The Priority of this task.
127 * @param int The Hours estimated to complete this task.
128 * @param int The (unix) start date of this task.
129 * @param int The (unix) end date of this task.
130 * @param int The category_id of this task.
131 * @param int The percentage of completion in integer format of this task.
132 * @param array An array of user_id's that are assigned this task.
133 * @param array An array of project_task_id's that this task depends on.
134 * @param int The duration of the task in days.
135 * @param int The id of the parent task, if any.
136 * @param array An array ('user' => user_id)
137 * @return boolean success.
139 function create($summary,$details,$priority,$hours,$start_date,$end_date,
140 $category_id,$percent_complete,&$assigned_arr,&$depend_arr,$duration=0,$parent_id=0, $importData = array()) {
141 $v = new Validator();
142 $v->check($summary, _("summary"));
143 $v->check($details, _("details"));
144 $v->check($priority, _("priority"));
145 $v->check($hours, _("hours"));
146 $v->check($start_date, _("start date"));
147 $v->check($end_date, _("end date"));
148 $v->check($category_id, _("category"));
149 if (!$v->isClean()) {
150 $this->setError($v->formErrorMsg(_("Must include ")));
159 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
160 $this->setPermissionDeniedError();
164 if(array_key_exists('user', $importData)){
165 $uid = $importData['user'];
171 $res = db_query_params ('SELECT nextval($1) AS id',
172 array ('project_task_pk_seq'));
173 if (!$project_task_id=db_result($res,0,'id')) {
174 $this->setError( 'Could Not Get Next Project Task ID' );
179 $this->data_array['project_task_id']=$project_task_id;
181 $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)',
182 array ($project_task_id,
183 $this->ProjectGroup->getID(),
185 htmlspecialchars($summary),
186 htmlspecialchars($details),
197 if (!$result || db_affected_rows($result) < 1) {
198 $this->setError('Posting Failed '.db_error());
203 if (!$this->setDependentOn($depend_arr)) {
207 if (!$this->setAssignedTo($assigned_arr)) {
211 if (!$this->fetchData($project_task_id)) {
215 $this->sendNotice(1);
221 * fetchData - re-fetch the data for this ProjectTask from the database.
223 * @param int The project_task_id.
224 * @return boolean success.
226 function fetchData($project_task_id) {
227 $res = db_query_params ('SELECT * FROM project_task_vw
228 WHERE project_task_id=$1
229 AND group_project_id=$2',
230 array ($project_task_id,
231 $this->ProjectGroup->getID())) ;
232 if (!$res || db_numrows($res) < 1) {
233 $this->setError('Invalid Task ID'.db_error());
236 $this->data_array = db_fetch_array($res);
237 db_free_result($res);
242 * getProjectGroup - get the ProjectGroup object this ProjectTask is associated with.
244 * @return Object The ProjectGroup object.
246 function &getProjectGroup() {
247 return $this->ProjectGroup;
251 * getID - get this project_task_id.
253 * @return int The project_task_id.
256 return $this->data_array['project_task_id'];
260 * getSubmittedRealName - get the real name of the person who created this task.
262 * @return string The real name person who created this task.
264 function getSubmittedRealName() {
265 return $this->data_array['realname'];
269 * getDuration - the duration of the task.
271 * @return int The number of days of duration.
273 function getDuration() {
274 return $this->data_array['duration'];
278 * getParentID - the task_id of the parent task, if any.
280 * @return string The real name person who created this task.
282 function getParentID() {
283 return $this->data_array['parent_id'];
287 * getSubmittedUnixName - get the unix name of the person who created this task.
289 * @return string The unix name of the person who created this task.
291 function getSubmittedUnixName() {
292 return $this->data_array['user_name'];
296 * getSummary - get the subject/summary of this task.
298 * @return string The summary.
300 function getSummary() {
301 return $this->data_array['summary'];
305 * getDetails - get the body/details of this task.
307 * @return string The body/details.
309 function getDetails() {
310 return $this->data_array['details'];
314 * getPercentComplete - an integer between 0 and 100.
316 * @return int The percentage of completion of this task.
318 function getPercentComplete() {
319 return $this->data_array['percent_complete'];
323 * getPriority - the priority, between 1 and 9 of this task.
325 * @return int The priority.
327 function getPriority() {
328 return $this->data_array['priority'];
332 * getHours - the hours this task is expected to take.
334 * @return int The hours.
336 function getHours() {
337 return $this->data_array['hours'];
341 * getStartDate - the unix time that this task will start.
343 * @return int The unix start time of this task.
345 function getStartDate() {
346 return $this->data_array['start_date'];
350 * getEndDate - the unix time that this task will end.
352 * @return int The unix end time of this task.
354 function getEndDate() {
355 return $this->data_array['end_date'];
359 * getStatusID - the integer of the status of this task.
361 * @return int the status_id.
363 function getStatusID() {
364 return $this->data_array['status_id'];
368 * getStatusName - the string of the status of this task.
370 * @return string the status_name.
372 function getStatusName() {
373 return $this->data_array['status_name'];
377 * getCategoryID - the category_id of this task.
379 * @return int the category_id.
381 function getCategoryID() {
382 return $this->data_array['category_id'];
386 * getCategoryName - the category_name of this task.
388 * @return int the category_name.
390 function getCategoryName() {
391 return $this->data_array['category_name'];
395 * getLastModifiedDate - the last_modified_date of this task.
397 * @return int the last_modified_date.
399 function getLastModifiedDate() {
400 return $this->data_array['last_modified_date'];
404 * setExternalID - set a row in project_task_external_order which stores
405 * an id, for example an ID generated by MS Project, which needs to be restored later
407 function setExternalID($id) {
408 $res = db_query_params ('UPDATE project_task_external_order SET external_id=$1
409 WHERE project_task_id=$2',
412 if (db_affected_rows($res) < 1) {
413 $res = db_query_params ('INSERT INTO project_task_external_order (project_task_id,external_id) VALUES ($1, $2)',
414 array ($this->getID(),
420 * getExternalID - get the ID that MS Project uses to sort tasks
422 * @return int the id.
424 function getExternalID() {
425 return $this->data_array['external_id'];
429 * getRelatedArtifacts - Return a result set of artifacts which are related to this task.
431 * @returns Database result set.
433 function getRelatedArtifacts() {
434 if (!$this->relatedartifacts) {
435 $this->relatedartifacts=
436 db_query_params ('SELECT agl.group_id,agl.name,agl.group_artifact_id,a.artifact_id,a.open_date,a.summary,ast.status_name
437 FROM artifact_group_list agl, artifact a, artifact_status ast
438 WHERE a.group_artifact_id=agl.group_artifact_id
439 AND ast.id=a.status_id
440 AND EXISTS (SELECT artifact_id FROM project_task_artifact
441 WHERE artifact_id=a.artifact_id
442 AND project_task_id=$1)',
443 array ($this->getID())) ;
445 return $this->relatedartifacts;
449 * addRelatedArtifacts - take an array of artifact_id's and build relationships.
451 * @param array An array of artifact_id's to be attached to this task.
452 * @return boolean success.
454 function addRelatedArtifacts($art_array) {
455 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
456 $this->setPermissionDeniedError();
460 // SHOULD REALLY INSTANTIATE THIS ARTIFACT OBJECT TO ENSURE PROPER SECURITY - FUTURE
462 // new ArtifactFromID($id)
464 for ($i=0; $i<count($art_array); $i++) {
465 if ($art_array[$i] < 1) {
468 $res = db_query_params ('INSERT INTO project_task_artifact (project_task_id,artifact_id) VALUES ($1,$2)',
469 array ($this->getID(),
472 $this->setError('Error inserting artifact relationship: '.db_error());
480 * removeRelatedArtifacts - take an array of artifact_id's and delete relationships.
482 * @param array An array of artifact_id's to be removed from this task.
483 * @return boolean success.
485 function removeRelatedArtifacts($art_array) {
486 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
487 $this->setPermissionDeniedError();
491 for ($i=0; $i<count($art_array); $i++) {
492 $res = db_query_params ('DELETE FROM project_task_artifact
493 WHERE project_task_id=$1
495 array ($this->getID(),
498 $this->setError('Error deleting artifact relationship: '.db_error());
506 * delete - delete this tracker and all its related data.
508 * @param bool I'm Sure.
509 * @return bool true/false;
511 function delete($sure) {
513 $this->setMissingParamsError(_('Please tick all checkboxes.'));
516 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
517 $this->setPermissionDeniedError();
522 $res = db_query_params ('DELETE FROM project_assigned_to WHERE project_task_id=$1',
523 array ($this->getID())) ;
525 $this->setError('Error deleting assigned users relationship: '.db_error());
529 $res = db_query_params ('DELETE FROM project_dependencies WHERE project_task_id=$1',
530 array ($this->getID())) ;
532 $this->setError('Error deleting dependencies: '.db_error());
536 $res = db_query_params ('DELETE FROM project_history WHERE project_task_id=$1',
537 array ($this->getID())) ;
539 $this->setError('Error deleting history: '.db_error());
543 $res = db_query_params ('DELETE FROM project_messages WHERE project_task_id=$1',
544 array ($this->getID())) ;
546 $this->setError('Error deleting messages: '.db_error());
550 $res = db_query_params ('DELETE FROM project_task_artifact WHERE project_task_id=$1',
551 array ($this->getID())) ;
553 $this->setError('Error deleting artifacts: '.db_error());
557 $res = db_query_params ('DELETE FROM rep_time_tracking WHERE project_task_id=$1',
558 array ($this->getID())) ;
560 $this->setError('Error deleting time tracking report: '.db_error());
564 $res = db_query_params ('DELETE FROM project_task WHERE project_task_id=$1',
565 array ($this->getID())) ;
567 $this->setError('Error deleting task: '.db_error());
577 * getOtherTasks - Return a result set of tasks in this subproject that do not equal
578 * the current task_id.
580 * @returns Database result set.
582 function getOtherTasks () {
584 // May not yet have an ID, if we are creating a NEW task
586 if ($this->getID()) {
587 return db_query_params ('SELECT project_task_id,summary
589 WHERE group_project_id=$1
590 AND project_task_id <> $2
591 ORDER BY project_task_id DESC',
592 array ($this->ProjectGroup->getID(),
595 return db_query_params ('SELECT project_task_id,summary
597 WHERE group_project_id=$1
598 ORDER BY project_task_id DESC',
599 array ($this->ProjectGroup->getID())) ;
604 * getHistory - returns a result set of audit trail for this ProjectTask.
606 * @return database result set.
608 function getHistory() {
609 return db_query_params ('SELECT *
610 FROM project_history_user_vw
611 WHERE project_task_id=$1
612 ORDER BY mod_date DESC',
613 array ($this->getID())) ;
617 * getMessages - get the list of messages attached to this ProjectTask.
619 * @return database result set.
621 function getMessages($asc=false) {
622 return db_query_params ('SELECT *
623 FROM project_message_user_vw
624 WHERE project_task_id=$1
625 ORDER BY postdate ' . ($asc ? 'ASC' : 'DESC'),
626 array ($this->getID())) ;
630 * addMessage - Handle the addition of a followup message to this task.
632 * @param string The message.
633 * @param array Specific data for import (user id and time)
634 * @returns boolean success.
636 function addMessage($message, $importData = array()) {
637 //prevent posting the same message
638 if ($this->getDetails() == htmlspecialchars($message)) {
641 $res = db_query_params ('SELECT * FROM project_messages
642 WHERE project_task_id=$1
644 array ($this->getID(),
645 htmlspecialchars($message))) ;
646 if (!$res || db_numrows($res) < 1) {
648 if(array_key_exists('user', $importData)){
649 $uid = $importData['user'];
653 if(array_key_exists('time', $importData)){
654 $time = $importData['time'];
658 $res = db_query_params ('INSERT INTO project_messages (project_task_id,body,posted_by,postdate) VALUES ($1,$2,$3,$4)',
659 array ($this->getID(),
660 htmlspecialchars($message),
663 if (!$res || db_affected_rows($res) < 1) {
664 $this->setError(db_error());
675 * addHistory - Handle the insertion of history for these parameters.
677 * @param string The field name.
678 * @param string The old value.
679 * @param array Specific data for import (user id and time)
680 * @returns boolean success.
682 function addHistory ($field_name,$old_value,$importData=array()) {
684 if(array_key_exists('user', $importData)){
685 $uid = $importData['user'];
689 if(array_key_exists('time', $importData)){
690 $time = $importData['time'];
694 $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)',
695 array ($this->getID(),
701 $this->setError('ERROR IN AUDIT TRAIL - '.db_error());
709 * checkCircular - recursive function calls itself to look at all tasks you are dependent on.
711 * @param int The project_task_id you are dependent on.
712 * @param int The project_task_id you are checking circular dependencies for.
713 * @returns boolean success.
715 function checkCircular($depend_on_id, $original_id) {
716 //for msproject users - ms project has more complex logic than gforge
719 if ($depend_on_id == $original_id) {
720 $this->setError(_('Circular Dependency Detected\''));
724 $res = db_query_params ('SELECT is_dependent_on_task_id AS id
725 FROM project_dependencies
726 WHERE project_task_id=$1',
727 array ($depend_on_id)) ;
728 $rows=db_numrows($res);
730 for ($i=0; $i<$rows; $i++) {
731 if (!$this->checkCircular(db_result($res,$i,'id'), $original_id)) {
739 * setDependentOn - takes an array of project_task_id's and builds dependencies.
741 * @param array The array of project_task_id's.
742 * @returns boolean success.
744 function setDependentOn(&$arr_) {
745 //printr($arr_,'setDependentOn entry');
747 // IMPORTANT - MUST VERIFY NO CIRCULAR DEPENDENCY!!
749 if (!$arr_ || empty($arr_)) {
750 $arr_=array('100'=>PM_LINK_DEFAULT);
752 $arr = array_keys($arr_);
753 //get existing dependencies to diff against
754 $arr2 = array_keys($this->getDependentOn());
756 if (count($arr) || count($arr2)) {
757 $add_arr = array_values (array_diff ($arr, $arr2));
758 //echo "add arr: ".print_r($add_arr);
759 $del_arr = array_values (array_diff ($arr2, $arr));
760 //echo "del arr: ".print_r($del_arr);
761 for ($i=0; $i<count($del_arr); $i++) {
762 db_query_params ('DELETE FROM project_dependencies
763 WHERE project_task_id=$1
764 AND is_dependent_on_task_id=$2',
765 array ($this->getID(),
768 $this->setError(db_error());
772 for ($i=0; $i<count($add_arr); $i++) {
774 // Check task for circular dependencies
776 if (!$this->checkCircular($add_arr[$i],$this->getID())) {
779 $lnk = $arr_[$add_arr[$i]];
781 $lnk=PM_LINK_DEFAULT;
783 db_query_params ('INSERT INTO project_dependencies (project_task_id,is_dependent_on_task_id,link_type) VALUES ($1,$2,$3)',
784 array ($this->getID(),
788 $this->setError(db_error());
799 * convertDependentOn - converts a regular array of dependencies, such
800 * as from a multiple-select-box to an associative array with default
801 * link types. Should be called from web code as part of the create/update calls.
802 * Here we are converting an array like array(1,5,9,77) to array(1=>SS,5=>SF,9=>FS,77=>SS)
804 function &convertDependentOn($arr) {
805 $deps = $this->getDependentOn();
806 for ($i=0; $i<count($arr); $i++) {
807 if ($deps[$arr[$i]]) {
808 //use existing link_type if it exists
809 $new[$arr[$i]]=$deps[$arr[$i]];
811 //else create with default link type
812 $new[$arr[$i]]=PM_LINK_DEFAULT;
819 * getDependentOn - get an array of project_task_id's that you are dependent on.
821 * @return array The array of project_task_id's in this format:
822 * array($id=>$link_type,id2=>link_type2).
824 function getDependentOn() {
825 if (!$this->getID()) {
828 if (!$this->dependon) {
829 $res = db_query_params ('SELECT is_dependent_on_task_id,link_type
830 FROM project_dependencies
831 WHERE project_task_id=$1',
832 array ($this->getID())) ;
833 for ($i=0; $i<db_numrows($res); $i++) {
834 $this->dependon[db_result($res,$i,'is_dependent_on_task_id')] = db_result($res,$i,'link_type');
837 /* fix bug 319: if dependentlist is emtpy, set it to 100 (none) */
838 if (!$this->dependon) {
839 $this->dependon[100]=PM_LINK_DEFAULT;
841 return $this->dependon;
845 * setAssignedTo - takes an array of user_id's and builds assignments.
847 * @param array The array of user_id's.
848 * @returns boolean success.
850 function setAssignedTo(&$arr) {
851 $arr2 = $this->getAssignedTo();
852 $this->assignedto =& $arr;
854 //If no one is assigned, then assign it to "100" - NOBODY
855 if (!$arr || count($arr) < 1 || ((count($arr)==1) && ($arr[0]==''))) {
858 if (count($arr) || count($arr2)) {
859 $add_arr = array_values(array_diff ($arr, $arr2));
860 $del_arr = array_values(array_diff ($arr2, $arr));
861 for ($i=0; $i<count($del_arr); $i++) {
862 db_query_params ('DELETE FROM project_assigned_to
863 WHERE project_task_id=$1
864 AND assigned_to_id=$2',
865 array ($this->getID(),
868 $this->setError(db_error());
872 for ($i=0; $i<count($add_arr); $i++) {
873 db_query_params ('INSERT INTO project_assigned_to (project_task_id,assigned_to_id) VALUES ($1,$2)',
874 array ($this->getID(),
877 $this->setError(db_error());
888 * getAssignedTo - get an array of user_id's that you are assigned to.
890 * @return array The array of user_id's.
892 function getAssignedTo() {
893 if (!$this->getID()) {
896 if (!$this->assignedto) {
897 $this->assignedto =& util_result_column_to_array(db_query_params('SELECT assigned_to_id FROM project_assigned_to WHERE project_task_id=$1',
898 array ($this->getID()))) ;
900 return $this->assignedto;
904 * update - update this ProjectTask in the database.
906 * @param string The summary of this task.
907 * @param string The detailed description of this task.
908 * @param int The Priority of this task.
909 * @param int The Hours estimated to complete this task.
910 * @param int The (unix) start date of this task.
911 * @param int The (unix) end date of this task.
912 * @param int The status_id of this task.
913 * @param int The category_id of this task.
914 * @param int The percentage of completion in integer format of this task.
915 * @param array An array of user_id's that are assigned this task.
916 * @param array An array of project_task_id's that this task depends on.
917 * @param int The GroupProjectID of a new subproject that you want to move this Task to.
918 * @param int The duration of the task in days.
919 * @param int The id of the parent task, if any.
920 * @return boolean success.
922 function update($summary,$details,$priority,$hours,$start_date,$end_date,
923 $status_id,$category_id,$percent_complete,&$assigned_arr,&$depend_arr,
924 $new_group_project_id,$duration=0,$parent_id=0) {
925 $has_changes = false; // if any of the values passed is different from
927 $arrChangedAndInNotice = array(
936 $v = new Validator();
937 $v->check($summary, _("summary"));
938 $v->check($priority, _("priority"));
939 $v->check($hours, _("hours"));
940 $v->check($start_date, _("start date"));
941 $v->check($end_date, _("end date"));
942 $v->check($status_id, _("status"));
943 $v->check($category_id, _("category"));
944 if (!$v->isClean()) {
945 $this->setError($v->formErrorMsg(_("Must include ")));
951 if ( ($this->getParentID()) != $parent_id ) {
957 if ( ($this->getDuration()) != $duration ) {
961 if (!forge_check_perm ('pm', $this->ProjectGroup->getID(), 'manager')) {
962 $this->setPermissionDeniedError();
966 /*if ( ($this->getSummary() != $summary) || ($this->getDetails() != $details) ||
967 ($this->getPriority() != $priority) || ($this->getHours() != $hours) ||
968 ($this->getStartDate() != $start_date) || ($this->getEndDate() != $end_date) ||
969 ($this->getStatusID() != $status_id) || ($this->getCategoryID() != $category_id) ||
970 ($this->getPercentComplete() != $percent_complete) ) {
979 // Attempt to move this Task to a new Subproject
980 // need to instantiate new ProjectGroup obj and test if it works
982 $group_project_id = $this->ProjectGroup->getID();
983 if ($new_group_project_id != $group_project_id) {
984 $newProjectGroup= new ProjectGroup($this->ProjectGroup->getGroup(), $new_group_project_id);
985 if (!is_object($newProjectGroup) || $newProjectGroup->isError()) {
986 $this->setError('ProjectTask: Could not move to new ProjectGroup'. $newProjectGroup->getErrorMessage());
990 if (!forge_check_perm ('pm', $newProjectGroup->getID(), 'manager')) {
991 $this->setPermissionDeniedError();
996 // Now set ProjectGroup, Category, and Assigned to 100 in the new ProjectGroup
1000 unset($assigned_to);
1001 $assigned_to=array('100');
1002 $this->ProjectGroup =& $newProjectGroup;
1003 $this->addHistory ('group_project_id',$group_project_id);
1004 $has_changes = true;
1005 $arrChangedAndInNotice['subproject'] = ">";
1010 $has_changes = true;
1011 if($details != "" && $details != null) {$arrChangedAndInNotice['details'] = ">";}
1012 //Message vorhanden;
1013 if (!$this->addMessage($details)) {
1018 if ($this->getStatusID() != $status_id) {
1019 $this->addHistory ('status_id',$this->getStatusID());
1020 $has_changes = true;
1021 $arrChangedAndInNotice['status'] = ">";
1024 if ($this->getCategoryID() != $category_id) {
1025 $this->addHistory ('category_id',$this->getCategoryID());
1026 $has_changes = true;
1029 if ($this->getPriority() != $priority) {
1030 $this->addHistory ('priority',$this->getPriority());
1031 $has_changes = true;
1034 if ($this->getSummary() != htmlspecialchars($summary)) {
1035 $this->addHistory ('summary',$this->getSummary());
1036 $has_changes = true;
1037 $arrChangedAndInNotice['summary'] = ">";
1040 if ($this->getPercentComplete() != $percent_complete) {
1041 $this->addHistory ('percent_complete',$this->getPercentComplete());
1042 $has_changes = true;
1043 $arrChangedAndInNotice['complete'] = ">";
1046 if ($this->getHours() != $hours) {
1047 $this->addHistory ('hours',$this->getHours());
1048 $has_changes = true;
1051 if ($this->getStartDate() != $start_date) {
1052 $this->addHistory ('start_date',$this->getStartDate());
1053 $has_changes = true;
1056 if ($this->getEndDate() != $end_date) {
1057 $this->addHistory ('end_date',$this->getEndDate());
1058 $has_changes = true;
1061 $old_assigned = $this->getAssignedTo();
1062 if ($assigned_arr == '' || !$assigned_arr) {
1063 $assigned_arr = array();
1064 $assigned_arr[0] = 100;
1066 $removed=array_diff($old_assigned, $assigned_arr);
1067 $added=array_diff($assigned_arr, $old_assigned);
1068 if (count($removed)>0 || count($added)>0) {
1069 $assigned = array();
1070 foreach ($old_assigned as $user_id) {
1071 $assigned[] = user_get_object($user_id)->getRealName();
1073 $this->addHistory('assigned_to', join(', ', $assigned));
1074 $arrChangedAndInNotice['assigned'] = ">";
1075 $has_changes = true;
1078 $old_array = array_keys($this->getDependentOn());
1079 $removed=array_diff($old_array,array_keys($depend_arr));
1080 $added=array_diff(array_keys($depend_arr), $old_array);
1081 if (count($removed)>0 || count($added)>0) {
1082 $this->addHistory('dependent_on', join(', ', $old_array));
1083 $has_changes = true;
1086 if (!$this->setDependentOn($depend_arr)) {
1089 } elseif (!$this->setAssignedTo($assigned_arr)) {
1093 $res = db_query_params ('UPDATE project_task SET
1100 percent_complete=$7,
1102 group_project_id=$9,
1105 WHERE group_project_id=$12
1106 AND project_task_id=$13',
1107 array (htmlspecialchars($summary),
1115 $new_group_project_id,
1120 if (!$res || db_affected_rows($res) < 1) {
1121 $this->setError(db_error());
1125 if (!$this->fetchData($this->getID())) {
1126 $this->setError(db_error());
1130 if ($has_changes) { //only send email if there was any change
1131 $this->sendNotice(false, $arrChangedAndInNotice);
1142 * sendNotice - contains the logic for sending email/jabber updates.
1144 * @return boolean success.
1146 function sendNotice($first=false, $arrChangedAndInNotice=array()) {
1147 global $send_task_email;
1149 if ($send_task_email===false) {
1152 $ids = $this->getAssignedTo();
1155 // See if there is anyone to send messages to
1157 if (count($ids) < 1 && !$this->ProjectGroup->getSendAllPostsTo()) {
1161 if (session_loggedin()) {
1162 $user = session_get_user()->getRealName();
1167 $body = "Task #". $this->getID() ." has been updated by $user.".
1168 "\n\nProject: ". $this->ProjectGroup->Group->getPublicName();
1170 if (isset($arrChangedAndInNotice['subproject']))
1171 $body .= "\n". $arrChangedAndInNotice['subproject']."Subproject: ". $this->ProjectGroup->getName();
1173 if (isset($arrChangedAndInNotice['summary']))
1174 $body .= "\n". $arrChangedAndInNotice['summary']. "Summary: ".util_unconvert_htmlspecialchars( $this->getSummary() );
1176 if (isset($arrChangedAndInNotice['complete']))
1177 $body .= "\n". $arrChangedAndInNotice['complete']. "Complete: ". $this->getPercentComplete() ."%";
1179 if (isset($arrChangedAndInNotice['status']))
1180 $body .= "\n". $arrChangedAndInNotice['status']. "Status: ". $this->getStatusName();
1182 if (isset($arrChangedAndInNotice['assigned'])) {
1183 $assigned = array();
1184 foreach ($this->getAssignedTo() as $user_id) {
1185 $assigned[] = user_get_object($user_id)->getRealName();
1187 $body .= "\n". $arrChangedAndInNotice['assigned']. "Assigned: ". join(', ', $assigned);
1190 $body .= "\n\nDescription: ". util_unconvert_htmlspecialchars( $this->getDetails() );
1193 Now get the followups to this task
1195 $result2=$this->getMessages();
1197 $rows=db_numrows($result2);
1199 if ($result2 && $rows > 0) {
1200 $body .= "\n\nFollow-Ups:";
1201 for ($i=0; $i<$rows;$i++) {
1202 if($i===0){ $temp = $arrChangedAndInNotice['details']; } else {$temp = "";}
1203 $body .= "\n\n-------------------------------------------------------";
1204 $body .= "\nDate: ". date(_('Y-m-d H:i'),db_result($result2,$i,'postdate'));
1205 $body .= "\nBy: ".db_result($result2,$i,'user_name');
1206 $body .= "\n\n". $temp ."Comment:\n".util_unconvert_htmlspecialchars(db_result($result2,$i,'body'));
1210 $body .= "\n\n-------------------------------------------------------".
1211 "\nFor more info, visit:".
1212 "\n\n".util_make_url ('/pm/task.php?func=detailtask&project_task_id='.$this->getID().
1213 "&group_id=".$this->ProjectGroup->Group->getID().
1214 "&group_project_id=".$this->ProjectGroup->getID());
1216 $subject=sprintf (_('[%1$s - %2$s] [Task #%3$d] '),
1217 $this->ProjectGroup->Group->getUnixName(),
1218 $this->ProjectGroup->getName(),
1220 util_unconvert_htmlspecialchars( $this->getSummary() );
1222 util_handle_message(array_unique($ids),$subject,$body,$this->ProjectGroup->getSendAllPostsTo());
1230 // c-file-style: "bsd"