5 * Copyright 1999-2001, VA Linux Systems, Inc.
6 * Copyright 2002-2004, GForge, LLC
7 * Copyright 2009, Roland Mas
8 * Copyright 2009, Alcatel-Lucent
10 * This file is part of FusionForge.
12 * FusionForge is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published
14 * by the Free Software Foundation; either version 2 of the License,
15 * or (at your option) any later version.
17 * FusionForge is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with FusionForge; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 * Standard Alcatel-Lucent disclaimer for contributing to open source
31 * "The Artifact ("Contribution") has not been tested and/or
32 * validated for release as or in products, combinations with products or
33 * other commercial use. Any use of the Contribution is entirely made at
34 * the user's own responsibility and the user can not rely on any features,
35 * functionalities or performances Alcatel-Lucent has attributed to the
38 * THE CONTRIBUTION BY ALCATEL-LUCENT IS PROVIDED AS IS, WITHOUT WARRANTY
39 * OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
40 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, COMPLIANCE,
41 * NON-INTERFERENCE AND/OR INTERWORKING WITH THE SOFTWARE TO WHICH THE
42 * CONTRIBUTION HAS BEEN MADE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
43 * ALCATEL-LUCENT BE LIABLE FOR ANY DAMAGES OR OTHER LIABLITY, WHETHER IN
44 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
45 * CONTRIBUTION OR THE USE OR OTHER DEALINGS IN THE CONTRIBUTION, WHETHER
46 * TOGETHER WITH THE SOFTWARE TO WHICH THE CONTRIBUTION RELATES OR ON A STAND
49 require_once $gfcommon.'include/Error.class.php';
50 require_once $gfcommon.'tracker/ArtifactMessage.class.php';
51 require_once $gfcommon.'tracker/ArtifactExtraField.class.php';
52 require_once $gfcommon.'tracker/ArtifactWorkflow.class.php';
54 // This string is used when sending the notification mail for identifying the
56 define('ARTIFACT_MAIL_MARKER', '#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+');
59 * Factory method which creates an Artifact from an artifact ID
61 * @param int The artifact ID
62 * @param array The result array, if it's passed in
63 * @return object Artifact object
65 function &artifact_get_object($artifact_id,$data=false) {
67 if (!isset($ARTIFACT_OBJ["_".$artifact_id."_"])) {
69 //the db result handle was passed in
71 $res = db_query_params ('SELECT * FROM artifact_vw WHERE artifact_id=$1',
72 array ($artifact_id)) ;
73 if (db_numrows($res) <1 ) {
74 $ARTIFACT_OBJ["_".$artifact_id."_"]=false;
77 $data = db_fetch_array($res);
79 $ArtifactType =& artifactType_get_object($data["group_artifact_id"]);
80 $ARTIFACT_OBJ["_".$artifact_id."_"]= new Artifact($ArtifactType,$data);
82 return $ARTIFACT_OBJ["_".$artifact_id."_"];
85 class Artifact extends Error {
90 * @var int $status_res.
95 * Artifact Type object.
97 * @var object $ArtifactType.
102 * Array of artifact data.
104 * @var array $data_array.
109 * Array of artifact data for extra fields defined by Admin.
111 * @var array $extra_field_data.
113 var $extra_field_data;
116 * Array of ArtifactFile objects.
123 * Database result set of related tasks
125 * @var result $relatedtasks
130 * Artifact - constructor.
132 * @param object The ArtifactType object.
133 * @param integer (primary key from database OR complete assoc array)
134 * ONLY OPTIONAL WHEN YOU PLAN TO IMMEDIATELY CALL ->create()
135 * @return boolean success.
137 function Artifact(&$ArtifactType, $data=false) {
140 $this->ArtifactType =& $ArtifactType;
142 //was ArtifactType legit?
143 if (!$ArtifactType || !is_object($ArtifactType)) {
144 $this->setError('Artifact: No Valid ArtifactType');
148 //did ArtifactType have an error?
149 if ($ArtifactType->isError()) {
150 $this->setError('Artifact: '.$ArtifactType->getErrorMessage());
155 // make sure this person has permission to view artifacts
157 if (!forge_check_perm ('tracker', $this->ArtifactType->getID(), 'read')) {
158 $this->setError(_('Artifact: Only group members can view private artifact types'));
163 // set up data structures
166 if (is_array($data)) {
167 $this->data_array =& $data;
169 // Should verify ArtifactType ID
173 if (!$this->fetchData($data)) {
183 * create - construct a new Artifact in the database.
185 * @param string The artifact summary.
186 * @param string Details of the artifact.
187 * @param int The ID of the user to which this artifact is to be assigned.
188 * @param int The artifacts priority.
189 * @param array Array of extra fields like: array(15=>'foobar',22=>'1');
190 * @param array Array of data to change submitter and time of submit like: array('user' => 127, 'time' => 1234556789)
191 * @return id on success / false on failure.
193 function create( $summary, $details, $assigned_to=100, $priority=3, $extra_fields=array(), $importData = array()) {
195 // make sure this person has permission to add artifacts
198 if (!$this->ArtifactType->isPublic()) {
200 // Only admins can post/modify private artifacts
204 // ape: Disabled, private means only restricted to members. So, no special rules #2503.
205 // if (!forge_check_perm ('tracker_admin', $this->ArtifactType->Group->getID()) {
206 // $this->setError(_('Artifact: Only Artifact Admins Can Modify Private ArtifactTypes'));
215 if(array_key_exists('user', $importData)){
216 $user = $importData['user'];
218 if (session_loggedin()) {
221 if ($this->ArtifactType->allowsAnon()) {
224 $this->setError(_('Artifact: This ArtifactType Does Not Allow Anonymous Submissions. Please Login.'));
235 $this->setError(_('Artifact: Message Summary Is Required'));
239 $this->setError(_('Artifact: Message Body Is Required'));
248 // if (!$status_id) {
249 $status_id=1; // on creation, status is set to "open"
252 // They may be using an extra field "status" box so we have to remap
253 // the status_id based on the extra field - this keeps the counters
256 $status_id=$this->ArtifactType->remapStatus($status_id,$extra_fields);
258 $this->setError(_('Artifact: Error remapping status'));
263 if (array_key_exists('time',$importData)){
264 $time = $importData['time'];
268 $res = db_query_params ('INSERT INTO artifact
269 (group_artifact_id,status_id,priority,
270 submitted_by,assigned_to,open_date,summary,details)
271 VALUES ($1,$2,$3,$4,$5,$6,$7,$8)',
272 array ($this->ArtifactType->getID(),
278 htmlspecialchars($summary),
279 htmlspecialchars($details))) ;
281 $this->setError('Artifact: '.db_error());
286 $artifact_id=db_insertid($res,'artifact','artifact_id');
288 if (!$res || !$artifact_id) {
289 $this->setError('Artifact: '.db_error());
294 // Now set up our internal data structures
296 if (!$this->fetchData($artifact_id)) {
300 // the changes to the extra fields will be logged in this array.
301 // (we won't use it however)
302 $extra_field_changes = array();
303 if (!$this->updateExtraFields($extra_fields,$extra_field_changes)) {
309 // now send an email if appropriate
311 $this->mailFollowupEx(0, 1);
319 * fetchData - re-fetch the data for this Artifact from the database.
321 * @param int The artifact ID.
322 * @return boolean success.
324 function fetchData($artifact_id) {
325 $res = db_query_params ('SELECT * FROM artifact_vw WHERE artifact_id=$1 AND group_artifact_id=$2',
327 $this->ArtifactType->getID())) ;
328 if (!$res || db_numrows($res) < 1) {
329 $this->setError('Artifact: Invalid ArtifactID');
332 $this->data_array = db_fetch_array($res);
333 db_free_result($res);
338 * getArtifactType - get the ArtifactType Object this Artifact is associated with.
340 * @return object ArtifactType.
342 function &getArtifactType() {
343 return $this->ArtifactType;
347 * getID - get this ArtifactID.
349 * @return int The artifact_id #.
352 return $this->data_array['artifact_id'];
356 * getStatusID - get open/closed/deleted flag.
358 * @return int Status: (1) Open, (2) Closed, (3) Deleted.
360 function getStatusID() {
361 return $this->data_array['status_id'];
365 * getStatusName - get open/closed/deleted text.
367 * @return string The status name.
369 function getStatusName() {
370 return $this->data_array['status_name'];
374 * getPriority - get priority flag.
376 * @return int priority.
378 function getPriority() {
379 return $this->data_array['priority'];
383 * getSubmittedBy - get ID of submitter.
385 * @return int user_id of submitter.
387 function getSubmittedBy() {
388 return $this->data_array['submitted_by'];
392 * getSubmittedEmail - get email of submitter.
394 * @return string The email of submitter.
396 function getSubmittedEmail() {
397 return $this->data_array['submitted_email'];
401 * getSubmittedRealName - get real name of submitter.
403 * @return string The real name of submitter.
405 function getSubmittedRealName() {
406 return $this->data_array['submitted_realname'];
410 * getSubmittedUnixName - get login name of submitter.
412 * @return string The unix name of submitter.
414 function getSubmittedUnixName() {
415 return $this->data_array['submitted_unixname'];
419 * getAssignedTo - get ID of assignee.
421 * @return int user_id of assignee.
423 function getAssignedTo() {
424 return $this->data_array['assigned_to'];
428 * getAssignedEmail - get email of assignee.
430 * @return string The email of assignee.
432 function getAssignedEmail() {
433 return $this->data_array['assigned_email'];
437 * getAssignedRealName - get real name of assignee.
439 * @return string The real name of assignee.
441 function getAssignedRealName() {
442 return $this->data_array['assigned_realname'];
446 * getAssignedUnixName - get login name of assignee.
448 * @return string The unix name of assignee.
450 function getAssignedUnixName() {
451 return $this->data_array['assigned_unixname'];
455 * getOpenDate - get unix time of creation.
457 * @return int unix time.
459 function getOpenDate() {
460 return $this->data_array['open_date'];
464 * getCloseDate - get unix time of closure.
466 * @return int unix time.
468 function getCloseDate() {
469 return $this->data_array['close_date'];
473 * getLastModifiedDate - the last_modified_date of this task.
475 * @return int the last_modified_date.
477 function getLastModifiedDate() {
478 return $this->data_array['last_modified_date'];
482 * getSummary - get text summary of artifact.
484 * @return string The summary (subject).
486 function getSummary() {
487 return $this->data_array['summary'];
491 * getDetails - get text body (message) of artifact.
493 * @return string The body (message).
495 function getDetails() {
496 return $this->data_array['details'];
500 * delete - delete this tracker and all its related data.
502 * @param bool I'm Sure.
503 * @return bool true/false;
505 function delete($sure) {
507 $this->setMissingParamsError(_('Please tick all checkboxes.'));
510 if (!forge_check_perm ('tracker_admin', $this->ArtifactType->Group->getID())) {
511 $this->setPermissionDeniedError();
515 $res = db_query_params ('DELETE FROM artifact_extra_field_data WHERE artifact_id=$1',
516 array ($this->getID())) ;
518 $this->setError('Error deleting extra field data: '.db_error());
522 $res = db_query_params ('DELETE FROM artifact_file WHERE artifact_id=$1',
523 array ($this->getID())) ;
525 $this->setError('Error deleting file from db: '.db_error());
529 $res = db_query_params ('DELETE FROM artifact_message WHERE artifact_id=$1',
530 array ($this->getID())) ;
532 $this->setError('Error deleting message: '.db_error());
536 $res = db_query_params ('DELETE FROM artifact_history WHERE artifact_id=$1',
537 array ($this->getID())) ;
539 $this->setError('Error deleting history: '.db_error());
543 $res = db_query_params ('DELETE FROM artifact_monitor WHERE artifact_id=$1',
544 array ($this->getID())) ;
546 $this->setError('Error deleting monitor: '.db_error());
550 $res = db_query_params ('DELETE FROM artifact WHERE artifact_id=$1',
551 array ($this->getID())) ;
553 $this->setError('Error deleting artifact: '.db_error());
558 if ($this->getStatusID() == 1) {
559 $res = db_query_params ('UPDATE artifact_counts_agg SET count=count-1,open_count=open_count-1
560 WHERE group_artifact_id=$1',
561 array ($this->getID())) ;
563 $this->setError('Error updating artifact_counts_agg (1): '.db_error());
567 } elseif ($this->getStatusID() == 2) {
568 $res = db_query_params ('UPDATE artifact_counts_agg SET count=count-1
569 WHERE group_artifact_id=$1',
570 array ($this->getID())) ;
572 $this->setError('Error updating artifact_counts_agg (2): '.db_error());
583 * setMonitor - user can monitor this artifact.
585 * @return false - always false - always use the getErrorMessage() for feedback
587 function setMonitor() {
588 if (session_loggedin()) {
590 $user_id=user_getid();
591 $user =& user_get_object(user_getid());
594 //we don't want to include the "And email=" because
595 //a logged-in user's email may have changed
600 $this->setError(_('SetMonitor::Valid Email Address Required'));
605 $res = db_query_params ('SELECT * FROM artifact_monitor WHERE artifact_id=$1 AND user_id=$2',
606 array ($this->getID(),
609 if (!$res || db_numrows($res) < 1) {
611 $res = db_query_params ('INSERT INTO artifact_monitor (artifact_id,user_id) VALUES ($1,$2)',
612 array ($this->getID(),
615 $this->setError(db_error());
618 $this->setError(_('Now Monitoring Artifact'));
622 //already monitoring - remove their monitor
623 db_query_params ('DELETE FROM artifact_monitor
626 array ($this->getID(),
628 $this->setError(_('Artifact Monitoring Deactivated'));
633 function isMonitoring() {
634 if (!session_loggedin()) {
637 $result = db_query_params ('SELECT count(*) AS count FROM artifact_monitor WHERE user_id=$1 AND artifact_id=$2',
640 $row_count = db_fetch_array($result);
641 return $result && $row_count['count'] > 0;
645 * getMonitorIds - array of email addresses monitoring this Artifact.
647 * @return array of email addresses monitoring this Artifact.
649 function getMonitorIds() {
650 $res = db_query_params ('SELECT user_id FROM artifact_monitor WHERE artifact_id=$1',
651 array ($this->getID())) ;
652 return array_unique(array_merge($this->ArtifactType->getMonitorIds(),util_result_column_to_array($res)));
656 * getHistory - returns a result set of audit trail for this support request.
658 * @return database result set.
660 function getHistory() {
661 return db_query_params ('SELECT * FROM artifact_history_user_vw WHERE artifact_id=$1 ORDER BY entrydate DESC',
662 array ($this->getID())) ;
666 * getMessages - get the list of messages attached to this artifact.
668 * @return database result set.
670 function getMessages($asc=false) {
671 return db_query_params ('SELECT * FROM artifact_message_user_vw WHERE artifact_id=$1 ORDER BY adddate ' . ($asc ? 'ASC' : 'DESC'),
672 array ($this->getID())) ;
676 * getMessageObjects - get an array of message objects.
678 * @return array Of ArtifactMessage objects.
680 function &getMessageObjects() {
681 $res=$this->getMessages();
683 while ($arr = db_fetch_array($res)) {
684 //$return[]=new ArtifactMessage($arr['artifact_id'],$arr);
685 $return[] = new ArtifactMessage($this, $arr);
691 * getFiles - get array of ArtifactFile's.
693 * @return array of ArtifactFile's.
695 function &getFiles() {
696 if (!isset($this->files)) {
697 $res = db_query_params ('SELECT id,artifact_id,description,filename,filesize,' .
698 'filetype,adddate,submitted_by,user_name,realname
699 FROM artifact_file_user_vw WHERE artifact_id=$1',
700 array ($this->getID())) ;
701 $rows=db_numrows($res);
703 for ($i=0; $i < $rows; $i++) {
704 $this->files[$i]=new ArtifactFile($this,db_fetch_array($res));
707 $this->files=array();
714 * getRelatedTasks - get array of related tasks
716 * @return Database result set
718 function getRelatedTasks() {
719 if (!$this->relatedtasks) {
720 $this->relatedtasks = db_query_params ('SELECT pt.group_project_id,pt.project_task_id,pt.summary,pt.start_date,pt.end_date,pgl.group_id,pt.status_id,pt.percent_complete,ps.status_name
721 FROM project_task pt, project_group_list pgl, project_status ps
722 WHERE pt.group_project_id = pgl.group_project_id
723 AND ps.status_id = pt.status_id
724 AND EXISTS (SELECT project_task_id FROM project_task_artifact
725 WHERE project_task_id=pt.project_task_id
726 AND artifact_id = $1)',
727 array ($this->getID())) ;
729 return $this->relatedtasks;
733 * addMessage - attach a text message to this Artifact.
735 * @param string The message being attached.
736 * @param string Email address of message creator.
737 * @param bool Whether to email out a followup.
739 * @return boolean success.
741 function addMessage($body,$by=false,$send_followup=false) {
743 $this->setMissingParamsError();
746 if (session_loggedin()) {
747 $user_id=user_getid();
748 $user =& user_get_object($user_id);
749 if (!$user || !is_object($user)) {
750 $this->setError('ERROR - Logged In User Bug Could Not Get User Object');
753 // we'll store this email even though it will likely never be used -
754 // since we have their correct user_id, we can join the USERS table to get email
755 $by=$user->getEmail();
756 } elseif (!$this->ArtifactType->allowsAnon()) {
757 $this->setError(_('Artifact: This ArtifactType Does Not Allow Anonymous Submissions. Please Login.'));
761 if (!$by || !validate_email($by)) {
762 $this->setMissingParamsError();
768 $res = db_query_params ('INSERT INTO artifact_message (artifact_id,submitted_by,from_email,adddate,body) VALUES ($1,$2,$3,$4,$5)',
769 array ($this->getID(),
773 htmlspecialchars($body))) ;
775 $this->updateLastModifiedDate();
777 if ($send_followup) {
778 $this->mailFollowupEx($now, 2, false);
784 * addHistory - add an entry to audit trail.
786 * @param string The name of the field in the database being modified.
787 * @param string The former value of this field.
788 * @param array Array of data to change submitter and time of submit like: array('user' => 127, 'time' => 1234556789)
790 * @return boolean success.
792 function addHistory($field_name,$old_value, $importData = array()) {
793 if (array_key_exists('user', $importData)){
794 $user = $importData['user'];
796 if (!session_loggedin()) {
802 if (array_key_exists('time',$importData)){
803 $time = $importData['time'];
807 return db_query_params ('INSERT INTO artifact_history(artifact_id,field_name,old_value,mod_by,entrydate) VALUES ($1,$2,$3,$4,$5)',
808 array ($this->getID(),
810 addslashes($old_value),
816 * setStatus - set the status of this artifact.
818 * @param int The artifact status ID.
819 * @param int Closing date if status = 1
821 * @return boolean success.
823 function setStatus($status_id, $closingTime=False) {
825 $qpa = db_construct_qpa (false, 'UPDATE artifact SET status_id=$1', array ($status_id)) ;
826 if ($closingTime && $status_id != 1) {
828 $qpa = db_construct_qpa ($qpa, ', close_date=$1 ', array ($time)) ;
830 $qpa = db_construct_qpa ($qpa,
831 'WHERE artifact_id=$1 AND group_artifact_id=$2',
832 array ($this->getID(), $artifact_type_id)) ;
833 $result=db_query_qpa($qpa);
835 if (!$result || db_affected_rows($result) < 1) {
836 $this->setError('Error - update failed!'.db_error());
840 if (!$this->fetchData($this->getID())) {
853 * update - update the fields in this artifact.
855 * @param int The artifact priority.
856 * @param int The artifact status ID.
857 * @param int The person to which this artifact is to be assigned.
858 * @param string The artifact summary.
859 * @param int The canned response.
860 * @param string Attaching another comment.
861 * @param int Allows you to move an artifact to another type.
862 * @param array Array of extra fields like: array(15=>'foobar',22=>'1');
863 * @param string The description.
864 * @return boolean success.
866 function update($priority,$status_id,
867 $assigned_to,$summary,$canned_response,$details,$new_artifact_type_id,
868 $extra_fields=array(), $description='') {
871 Field-level permission checking
873 if (!forge_check_perm ('tracker', $this->ArtifactType->getID(), 'manager')) {
874 // Non-managers cannot modify these fields
875 $priority=$this->getPriority();
876 $summary=htmlspecialchars_decode($this->getSummary());
877 $description=htmlspecialchars_decode($this->getDetails());
878 $canned_response=100;
879 $new_artifact_type_id=$this->ArtifactType->getID();
880 $assigned_to=$this->getAssignedTo();
882 if (!forge_check_perm ('tracker', $this->ArtifactType->getID(), 'tech')) {
883 $this->setPermissionDeniedError();
888 // They may be using an extra field "status" box so we have to remap
889 // the status_id based on the extra field - this keeps the counters
892 if (count($extra_fields) > 0) {
893 $status_id=$this->ArtifactType->remapStatus($status_id,$extra_fields);
899 || !$new_artifact_type_id) {
900 $this->setMissingParamsError();
905 // Check that assigned_to is a tech for the tracker
906 if ($assigned_to != 100) {
907 if (!forge_check_perm ('tracker', $this->ArtifactType->getID(), 'tech')) {
908 $this->setError("Invalid assigned_to (assigned person is not a technician)");
913 // Array to record which properties were changed
920 // Get a lock on this row in the database
922 $lock = db_query_params ('SELECT * FROM artifact WHERE artifact_id=$1 FOR UPDATE',
923 array ($this->getID())) ;
924 $artifact_type_id = $this->ArtifactType->getID();
926 // Attempt to move this Artifact to a new ArtifactType
927 // need to instantiate new ArtifactType obj and test perms
929 if ($new_artifact_type_id != $artifact_type_id) {
930 $newArtifactType= new ArtifactType($this->ArtifactType->getGroup(), $new_artifact_type_id);
931 if (!is_object($newArtifactType) || $newArtifactType->isError()) {
932 $this->setError('Artifact: Could not move to new ArtifactType'. $newArtifactType->getErrorMessage());
936 // do they have perms for new ArtifactType?
937 if (!forge_check_perm ('tracker', $newArtifactType->getID(), 'manager')) {
938 $this->setPermissionDeniedError();
943 // Add a message to explain that the tracker was moved.
944 $message = 'Moved from '.$this->ArtifactType->getName().' to '.$newArtifactType->getName();
945 $this->addHistory('type', $this->ArtifactType->getName());
946 $this->addMessage($message,'',0);
948 // Fake change to send a mail when moved.
949 $changes['Type'] = 1;
951 // Try to remap extra_fields values when possible.
952 // If there is an extra_field with the same alias
953 // and if the value exist in the new one, then recode
954 // the value to keep it.
955 $new_extra_fields = array();
956 $ef = $this->ArtifactType->getExtraFields();
957 $ef_new = $newArtifactType->getExtraFields();
958 foreach($extra_fields as $extra_id => $value) {
959 $alias = preg_replace('/^@/', '', $ef[$extra_id]['alias']);
960 $type = $ef[$extra_id]['field_type'];
962 // Search if there is an extra field with the same alias.
964 foreach($ef_new as $id => $arr) {
965 if (preg_replace('/^@/', '', $arr['alias']) == $alias) {
970 // If we found one, copy for simple fields or
971 // search if there is the same value.
973 if ($type == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
974 $type == ARTIFACT_EXTRAFIELDTYPE_INTEGER ||
975 $type == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA ||
976 $type == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
977 $new_extra_fields[$new_id] = $value;
979 $values = $newArtifactType->getExtraFieldElements($new_id);
980 if (is_array($value)) {
981 foreach($value as $v) {
982 $v = $this->ArtifactType->getElementName($v);
983 foreach($values as $ev) {
984 if ($ev['element_name'] == $v) {
985 $new_extra_fields[$new_id][] = $ev['element_id'];
990 $value = $this->ArtifactType->getElementName($value);
991 foreach($values as $ev) {
992 if ($ev['element_name'] == $value) {
993 $new_extra_fields[$new_id] = $ev['element_id'];
1001 // Special case if moving to a tracker with custom status (previous has not).
1002 $custom_status_id = $newArtifactType->getCustomStatusField();
1003 if ($custom_status_id && !$new_extra_fields[$custom_status_id]) {
1004 $atw = new ArtifactWorkflow($newArtifactType, $custom_status_id);
1005 $nodes = $atw->getNextNodes(100);
1007 $new_extra_fields[$custom_status_id] = $nodes[0];
1010 $extra_fields = $new_extra_fields;
1012 $res = db_query_params ('DELETE FROM artifact_extra_field_data WHERE artifact_id=$1',
1013 array ($this->getID()));
1015 $this->setError('Removal of old artifact_extra_field_data failed: '.db_error());
1020 // Check that assigned_to is a tech in the new tracker
1021 if ($assigned_to != 100) {
1022 if (!forge_check_perm ('tracker', $newArtifactType->getID(), 'tech')) {
1027 //can't send a canned response when changing ArtifactType
1028 $canned_response=100;
1029 $this->ArtifactType =& $newArtifactType;
1033 $qpa = db_construct_qpa();
1034 $qpa = db_construct_qpa($qpa, 'UPDATE artifact SET');
1037 // handle audit trail
1040 if ($this->getStatusID() != $status_id) {
1041 $this->addHistory('status_id',$this->getStatusID());
1042 $qpa = db_construct_qpa($qpa, ' status_id=$1,', array($status_id));
1043 $changes['status'] = 1;
1046 if ($status_id != 1) {
1047 $qpa = db_construct_qpa($qpa, ' close_date=$1,', array($now));
1049 $qpa = db_construct_qpa($qpa, ' close_date=$1,', array(0));
1051 $this->addHistory('close_date', $this->getCloseDate());
1053 if ($this->getPriority() != $priority) {
1054 $this->addHistory('priority',$this->getPriority());
1055 $qpa = db_construct_qpa($qpa, ' priority=$1,', array($priority));
1056 $changes['priority'] = 1;
1060 if ($this->getAssignedTo() != $assigned_to) {
1061 $this->addHistory('assigned_to',$this->getAssignedTo());
1062 $qpa = db_construct_qpa($qpa, ' assigned_to=$1,', array($assigned_to));
1063 $changes['assigned_to'] = 1;
1066 if ($summary && ($this->getSummary() != htmlspecialchars($summary))) {
1067 $this->addHistory('summary', $this->getSummary());
1068 $qpa = db_construct_qpa($qpa, ' summary=$1,', array(htmlspecialchars($summary)));
1069 $changes['summary'] = 1;
1072 if ($description && ($this->getDetails() != htmlspecialchars($description))) {
1073 $this->addHistory('details', $this->getDetails());
1074 $qpa = db_construct_qpa($qpa, ' details=$1,', array(htmlspecialchars($description)));
1075 $changes['details'] = 1;
1079 $this->addMessage($details,'',0);
1080 $changes['details'] = 1;
1085 Finally, update the artifact itself
1088 $qpa = db_construct_qpa($qpa, ' group_artifact_id=$1
1089 WHERE artifact_id=$2 AND group_artifact_id=$3',
1090 array($new_artifact_type_id,
1091 $this->getID(), $artifact_type_id));
1092 $result = db_query_qpa($qpa);
1094 if (!$result || db_affected_rows($result) < 1) {
1095 $this->setError('Error - update failed!'.db_error());
1099 if (!$this->fetchData($this->getID())) {
1106 //extra field handling
1108 if (!$this->updateExtraFields($extra_fields,$changes)) {
1109 //TODO - see if anything actually did change
1115 handle canned responses
1117 Instantiate ArtifactCanned and get the body of the message
1119 if ($canned_response != 100) {
1120 //don't care if this response is for this group - could be hacked
1121 $acr=new ArtifactCanned($this->ArtifactType,$canned_response);
1122 if (!$acr || !is_object($acr)) {
1123 $this->setError('Artifact: Could Not Create Canned Response Object');
1124 } elseif ($acr->isError()) {
1125 $this->setError('Artifact: '.$acr->getErrorMessage());
1127 $body = $acr->getBody();
1129 if (!$this->addMessage(util_unconvert_htmlspecialchars($body),'',0)) {
1136 $this->setError('Artifact: Unable to Use Canned Response');
1142 if ($update || $send_message){
1143 if (!empty($changes)) {
1144 // Send the email with changes
1145 $this->mailFollowupEx($now, 2, false, $changes);
1150 //nothing changed, so cancel the transaction
1151 $this->setError(_('Nothing Changed - Update Cancelled'));
1159 * updateLastModifiedDate - update the last_modified_date attribute of this artifact.
1161 * @return true on success / false on failure
1163 function updateLastModifiedDate() {
1164 $res = db_query_params ('UPDATE artifact SET last_modified_date=EXTRACT(EPOCH FROM now())::integer WHERE artifact_id=$1',
1165 array ($this->getID()));
1170 * assignToMe - assigns this artifact to current user
1172 * @return true on success / false on failure
1174 function assignToMe() {
1175 if (!session_loggedin() || !($this->ArtifactType->userIsAdmin() || $this->ArtifactType->userIsTechnician())) {
1176 $this->setPermissionDeniedError();
1180 $user_id = user_getid();
1181 $res = db_query_params ('UPDATE artifact SET assigned_to=$1 WHERE artifact_id=$2',
1182 array ($user_id, $this->getID())) ;
1184 $this->setError('Error updating assigned_to in artifact: '.db_error());
1187 $this->fetchData($this->getID());
1193 * updateExtraFields - updates the extra data elements for this artifact
1194 * e.g. the extra fields created and defined by the admin.
1196 * @param array Array of extra fields like: array(15=>'foobar',22=>'1');
1197 * @param array Array where changes to the extra fields should be logged
1198 * @return true on success / false on failure
1200 function updateExtraFields($extra_fields,&$changes){
1202 This is extremely complex code - we have take the passed array
1203 and see if we need to insert it into the db, and may have to
1204 add history rows for the audit trail
1206 start by getting all the available extra fields from ArtifactType
1207 For each field from ArtifacType, check the passed array -
1208 This prevents someone from passing bogus extra field entries - they will be ignored
1209 if the passed entry is blank, may have to force a default value
1210 if the passed array is different from the existing data in db,
1211 delete old entry and insert new entries, along with possible audit trail
1213 skip it and continue to next item
1216 if (empty($extra_fields)) {
1221 //get a list of extra fields for this artifact_type
1222 $ef = $this->ArtifactType->getExtraFields();
1223 $efk=array_keys($ef);
1225 // If there is a status field, then check against the workflow.
1226 for ($i=0; $i<count($efk); $i++) {
1228 $type=$ef[$efid]['field_type'];
1229 if ($type == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
1230 // Get previous value.
1232 $res = db_query_params ('SELECT field_data FROM artifact_extra_field_data
1233 WHERE artifact_id=$1 AND extra_field_id=$2',
1234 array($this->getID(),
1236 $old = (db_numrows($res)>0) ? db_result($res,0,'field_data') : 100;
1237 if ($old != $extra_fields[$efid]) {
1238 $atw = new ArtifactWorkflow($this->ArtifactType, $efid);
1239 if (!$atw->checkEvent($old, $extra_fields[$efid])) {
1240 $this->setError('Workflow error: You are not authorized to change the Status');
1247 //now we'll update this artifact for each extra field
1248 for ($i=0; $i<count($efk); $i++) {
1250 $type=$ef[$efid]['field_type'];
1252 // check required fields
1253 if ($ef[$efid]['is_required']) {
1254 if (!array_key_exists($efid, $extra_fields)) {
1255 if ($type == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
1256 $this->setError('Status Custom Field Must Be Set');
1259 $this->setMissingParamsError($ef[$efid]['field_name']);
1264 if ($extra_fields[$efid] === '') {
1265 if ($type == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
1266 $this->setError('Status Custom Field Must Be Set');
1269 $this->setMissingParamsError($ef[$efid]['field_name']);
1274 if (($type == ARTIFACT_EXTRAFIELDTYPE_SELECT || $type == ARTIFACT_EXTRAFIELDTYPE_RADIO) &&
1275 $extra_fields[$efid] == '100') {
1276 $this->setMissingParamsError($ef[$efid]['field_name']);
1279 elseif (($type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT || $type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) &&
1280 (count($extra_fields[$efid]) == 1 && $extra_fields[$efid][0] == '100')) {
1281 $this->setMissingParamsError($ef[$efid]['field_name']);
1288 // Force each field to have some value if it is a numeric field
1289 // text fields will just be purged and skipped
1291 if (!array_key_exists($efid, $extra_fields) || $extra_fields[$efid] === '') {
1292 if (($type == ARTIFACT_EXTRAFIELDTYPE_SELECT) || ($type == ARTIFACT_EXTRAFIELDTYPE_RADIO)) {
1293 $extra_fields[$efid]='100';
1294 } elseif (($type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) || ($type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX)) {
1295 $extra_fields[$efid]=array('100');
1297 $resdel = db_query_params ('DELETE FROM artifact_extra_field_data WHERE artifact_id=$1 AND extra_field_id=$2',
1298 array ($this->getID(),
1304 // get the old rows of data
1306 $resd = db_query_params ('SELECT * FROM artifact_extra_field_data WHERE artifact_id=$1 AND extra_field_id=$2',
1307 array ($this->getID(),
1309 $rows=db_numrows($resd);
1310 if ($resd && $rows) {
1312 //POTENTIAL PROBLEM - no entry was there before, but adding one now - may need history
1315 // Compare for history purposes
1318 // these types have arrays associated to them, so they need
1319 // special handling to check for differences
1320 if ($type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT || $type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) {
1321 // check the differences between the old values and the new values
1322 $old_values = util_result_column_to_array($resd,"field_data");
1324 $added_values = array_diff($extra_fields[$efid], $old_values);
1325 $deleted_values = array_diff($old_values, $extra_fields[$efid]);
1327 if (!empty($added_values) || !empty($deleted_values)) { // there are differences...
1328 $field_name = $ef[$efid]['field_name'];
1329 if (!preg_match('/^@/', $ef[$efid]['alias'])) {
1330 $changes["extra_fields"][$efid] = 1;
1333 $this->addHistory($field_name, $this->ArtifactType->getElementName(array_reverse($old_values)));
1336 $resdel = db_query_params ('DELETE FROM artifact_extra_field_data WHERE artifact_id=$1 AND extra_field_id=$2',
1337 array ($this->getID(),
1342 } elseif (addslashes(db_result($resd,0,'field_data')) == htmlspecialchars($extra_fields[$efid])) {
1343 //element did not change
1346 //element DID change - do a history entry
1347 $field_name = $ef[$efid]['field_name'];
1348 if (!preg_match('/^@/', $ef[$efid]['alias'])) {
1349 $changes["extra_fields"][$efid] = 1;
1351 $resdel = db_query_params ('DELETE FROM artifact_extra_field_data WHERE artifact_id=$1 AND extra_field_id=$2',
1352 array ($this->getID(),
1355 // Adding history with previous value.
1356 if (($type == ARTIFACT_EXTRAFIELDTYPE_SELECT) || ($type == ARTIFACT_EXTRAFIELDTYPE_RADIO) || ($type == ARTIFACT_EXTRAFIELDTYPE_STATUS)) {
1357 $this->addHistory($field_name,$this->ArtifactType->getElementName(db_result($resd,0,'field_data')));
1359 $this->addHistory($field_name, db_result($resd,0,'field_data'));
1365 //no history for this extra field exists
1370 // Some rewrite & consistency checks on the relation type field.
1372 // 1) Convert syntax [#NNN] to NNN
1373 // 2) Allow multiple spaces as separator.
1374 // 3) Ensure that only integers are given.
1375 // 4) Ensure that id corresponds to valid tracker id.
1377 if ($type == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
1378 $value = preg_replace('/\[\#(\d+)\]/', "\\1", trim($extra_fields[$efid]));
1379 $value = preg_replace('/\\s+/', ' ', $value);
1381 foreach (explode(' ',$value) as $id) {
1382 if (preg_match('/^(\d+)$/', $id)) {
1383 // Control that the id is present in the db
1385 $res = db_query_params ('SELECT artifact_id FROM artifact WHERE artifact_id=$1',
1387 if (db_numrows($res) == 1) {
1390 $this->setError('Illegal id '.$id.', it\'s not a valid tracker id for field: '.$ef[$efid]['field_name'].'.'); // @todo: lang
1394 $this->setError('Illegal value '.$id.', only trackers id are allowed for field: '.$ef[$efid]['field_name'].'.'); // @todo: lang
1398 $extra_fields[$efid] = trim($new);
1401 // Ensure that only integer are allowed for type ARTIFACT_EXTRAFIELDTYPE_INTEGER
1402 if ($type == ARTIFACT_EXTRAFIELDTYPE_INTEGER) {
1403 $extra_fields[$efid] = trim($extra_fields[$efid]);
1404 if (!preg_match('/^[-+]?(\d+)$/', $extra_fields[$efid])) {
1405 $this->setError('Illegal value '.$extra_fields[$efid].' for field '.$ef[$efid]['field_name'].': Only integer is allowed.');
1408 if ($extra_fields[$efid] < -2147483648 || $extra_fields[$efid] > 2147483647) {
1409 $this->setError('Illegal value '.$extra_fields[$efid].' for field '.$ef[$efid]['field_name'].': Integer out of range (-2147483648 to +2147483647).');
1412 $extra_fields[$efid] = intval($extra_fields[$efid]);
1416 // See if anything was even passed for this extra_field_id
1418 if ($extra_fields[$efid] === '') {
1419 //nothing in field to update - text fields may be blank
1421 //determine the type of field and whether it should have multiple rows supporting it
1422 $type=$ef[$efid]['field_type'];
1423 if (($type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) || ($type==ARTIFACT_EXTRAFIELDTYPE_MULTISELECT)) {
1425 $count=count($extra_fields[$efid]);
1426 for ($fin=0; $fin<$count; $fin++) {
1427 $res = db_query_params ('INSERT INTO artifact_extra_field_data (artifact_id,extra_field_id,field_data) VALUES ($1,$2,$3)',
1428 array ($this->getID(),
1430 $extra_fields[$efid][$fin])) ;
1432 $this->setError(db_error());
1439 $res = db_query_params ('INSERT INTO artifact_extra_field_data (artifact_id,extra_field_id,field_data) VALUES ($1,$2,$3)',
1440 array ($this->getID(),
1442 htmlspecialchars($extra_fields[$efid]))) ;
1444 $this->setError(db_error());
1451 unset($this->extra_field_data);
1454 $this->updateLastModifiedDate();
1460 * getExtraFieldData - get an array of data for the extra fields associated with this artifact
1462 * @return array array of data
1464 function &getExtraFieldData() {
1465 if (!isset($this->extra_field_data)) {
1466 $this->extra_field_data = array();
1467 $res = db_query_params ('SELECT * FROM artifact_extra_field_data WHERE artifact_id=$1 ORDER BY extra_field_id',
1468 array ($this->getID())) ;
1469 $ef = $this->ArtifactType->getExtraFields();
1470 while ($arr = db_fetch_array($res)) {
1471 $type=$ef[$arr['extra_field_id']]['field_type'];
1472 if (($type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) || ($type==ARTIFACT_EXTRAFIELDTYPE_MULTISELECT)) {
1473 //accumulate a sub-array of values in cases where you may have multiple rows
1474 if (!array_key_exists($arr['extra_field_id'], $this->extra_field_data) || !is_array($this->extra_field_data[$arr['extra_field_id']])) {
1475 $this->extra_field_data[$arr['extra_field_id']] = array();
1477 $this->extra_field_data[$arr['extra_field_id']][]=$arr['field_data'];
1479 $this->extra_field_data[$arr['extra_field_id']]=$arr['field_data'];
1483 return $this->extra_field_data;
1487 * marker - adds the > symbol to fields that have been modified for the email message
1491 function marker($prop_name,$changes,$extra_field_id=0) {
1492 if ($prop_name == 'extra_fields' && isset($changes[$prop_name][$extra_field_id])) {
1494 } else if ($prop_name != 'extra_fields' && isset($changes[$prop_name])) {
1502 * mailFollowupEx - send out an email update for this artifact.
1504 * @param time_t Time of the change
1505 * @param int (1) initial/creation (2) update.
1506 * @param array Array of additional addresses to mail to.
1507 * @param array Array of fields changed in this update .
1509 * @return boolean success.
1511 function mailFollowupEx($tm,$type,$more_addresses=false,$changes='') {
1513 $monitor_ids = array();
1519 $sess = session_get_user() ;
1520 if ($type == 1) { // Initial opening
1522 $body = $this->ArtifactType->getName() ." item #". $this->getID() .", was opened at ". date( _('Y-m-d H:i'), $this->getOpenDate() ) . " by " . $sess->getRealName () ;
1524 $body = $this->ArtifactType->getName() ." item #". $this->getID() .", was opened at ". date( _('Y-m-d H:i'), $this->getOpenDate() ) ;
1528 $body = $this->ArtifactType->getName() .
1529 " item #" . $this->getID() .
1530 " was changed at " .
1531 date(_('Y-m-d H:i'), $tm) . " by " .
1532 $sess->getRealName();
1534 $body = $this->ArtifactType->getName() .
1535 " item #" . $this->getID() .
1536 " was changed at " .
1537 date(_('Y-m-d H:i'), $tm);
1542 $body .= "\nYou can respond by visiting: ".
1543 "\n".util_make_url ('/tracker/?func=detail&atid='. $this->ArtifactType->getID() .
1544 "&aid=". $this->getID() .
1545 "&group_id=". $this->ArtifactType->Group->getID()) .
1546 "\nOr by replying to this e-mail entering your response between the following markers: ".
1547 "\n".ARTIFACT_MAIL_MARKER.
1548 "\n(enter your response here, only in plain text format)".
1549 "\n".ARTIFACT_MAIL_MARKER.
1551 $this->marker('status',$changes).
1552 "Status: ". $this->getStatusName() ."\n".
1553 $this->marker('priority',$changes).
1554 "Priority: ". $this->getPriority() ."\n".
1555 "Submitted By: ". $this->getSubmittedRealName() .
1556 " (". $this->getSubmittedUnixName(). ")"."\n".
1557 $this->marker('assigned_to',$changes).
1558 "Assigned to: ". $this->getAssignedRealName() .
1559 " (". $this->getAssignedUnixName(). ")"."\n".
1560 $this->marker('summary',$changes).
1561 "Summary: ". util_unconvert_htmlspecialchars( $this->getSummary() )." \n";
1563 // Now display the extra fields
1564 $efd = $this->getExtraFieldDataText();
1565 foreach ($efd as $efid => $ef) {
1566 $body .= $this->marker('extra_fields', $changes, $efid);
1567 $body .= $ef["name"].": ".$ef["value"]."\n";
1570 $subject='['. $this->ArtifactType->Group->getUnixName() . '-' . $this->ArtifactType->getName() . '][' . $this->getID() .'] '. util_unconvert_htmlspecialchars( $this->getSummary() );
1573 // get all the email addresses that are monitoring this request or the ArtifactType
1574 $monitor_ids = $this->getMonitorIds();
1576 // initial creation, we just get the users monitoring the ArtifactType
1577 $monitor_ids = $this->ArtifactType->getMonitorIds();
1581 if ($more_addresses) {
1582 $emails[] = $more_addresses;
1584 //we don't email the current user
1585 if ($this->getAssignedTo() != user_getid()) {
1586 $monitor_ids[] = $this->getAssignedTo();
1588 if ($this->getSubmittedBy() != user_getid()) {
1589 $monitor_ids[] = $this->getSubmittedBy();
1591 //initial submission
1593 //if an email is set for this ArtifactType
1594 //add that address to the BCC: list
1595 if ($this->ArtifactType->getEmailAddress()) {
1596 $emails[] = $this->ArtifactType->getEmailAddress();
1600 if ($this->ArtifactType->emailAll()) {
1601 $emails[] = $this->ArtifactType->getEmailAddress();
1605 $body .= "\n\nInitial Comment:".
1606 "\n".util_unconvert_htmlspecialchars( $this->getDetails() ) .
1607 "\n\n----------------------------------------------------------------------";
1611 Now include the followups
1613 $result2=$this->getMessages();
1615 $rows=db_numrows($result2);
1617 if ($result2 && $rows > 0) {
1618 for ($i=0; $i<$rows; $i++) {
1620 // for messages posted by non-logged-in users,
1621 // we grab the email they gave us
1623 // otherwise we use the confirmed one from the users table
1625 if (db_result($result2,$i,'user_id') == 100) {
1626 $emails[] = db_result($result2,$i,'from_email');
1628 $monitor_ids[] = db_result($result2,$i,'user_id');
1634 $body .= $this->marker('details',$changes);
1636 $body .= "Comment By: ". db_result($result2,$i,'realname') . " (".db_result($result2,$i,'user_name').")".
1637 "\nDate: ". date( _('Y-m-d H:i'),db_result($result2,$i,'adddate') ).
1639 "\n".util_unconvert_htmlspecialchars( db_result($result2,$i,'body') ).
1640 "\n\n----------------------------------------------------------------------";
1646 $body .= "\n\nYou can respond by visiting: ".
1647 "\n".util_make_url ('/tracker/?func=detail&atid='. $this->ArtifactType->getID() .
1648 "&aid=". $this->getID() .
1649 "&group_id=". $this->ArtifactType->Group->getID());
1651 //only send if some recipients were found
1652 if (count($emails) < 1 && count($monitor_ids) < 1) {
1656 if (count($monitor_ids) < 1) {
1657 $monitor_ids=array();
1659 $monitor_ids=array_unique($monitor_ids);
1662 $from = $this->ArtifactType->getReturnEmailAddress();
1663 $extra_headers = 'Reply-to: '.$from;
1665 // load the e-mail addresses of the users
1666 $users =& user_get_objects($monitor_ids);
1667 if (count($users) > 0) {
1668 foreach ($users as $user) {
1669 if ($user->getStatus() == "A") { //we are only sending emails to active users
1670 $emails[] = $user->getEmail();
1675 //now remove all duplicates from the email list
1676 if (count($emails) > 0) {
1677 $BCC=implode(',',array_unique($emails));
1678 util_send_message('',$subject,$body,$from,$BCC,'',$extra_headers);
1681 $this->sendSubjectMsg = $subject;
1682 $this->sendBodyMsg = $body;
1684 //util_handle_message($monitor_ids,$subject,$body,$BCC);
1690 * getExtraFieldDataText - Return the extra fields' data in a human-readable form.
1692 * @return array Array containing field ID => field name and value associated to it for
1695 function getExtraFieldDataText() {
1696 // First we get the list of extra fields and the data
1697 // associated to the fields
1698 $efs = $this->ArtifactType->getExtraFields();
1699 $efd = $this->getExtraFieldData();
1703 foreach ($efs as $efid => $ef) {
1704 $name = $ef["field_name"];
1705 $type = $ef["field_type"];
1707 // Get the value according to the type
1710 // for these types, the associated value comes straight
1711 case ARTIFACT_EXTRAFIELDTYPE_TEXT:
1712 case ARTIFACT_EXTRAFIELDTYPE_TEXTAREA:
1713 case ARTIFACT_EXTRAFIELDTYPE_RELATION:
1714 case ARTIFACT_EXTRAFIELDTYPE_INTEGER:
1715 if (isset($efd[$efid])) {
1716 $value = $efd[$efid];
1722 // the other types have and ID or an array of IDs associated to them
1724 if (isset($efd[$efid])) {
1725 $value = $this->ArtifactType->getElementName($efd[$efid]);
1731 $return[$efid] = array("name" => $name, "value" => $value, 'type' => $type);
1739 class ArtifactComparator {
1740 var $criterion = 'artifact_id' ;
1741 var $order = 'ASC' ;
1743 function Compare ($a, $b) {
1744 if ($this->order == 'DESC') {
1745 $c = $a ; $a = $b ; $b = $c ;
1747 switch ($this->criterion) {
1749 $namecmp = strcoll ($a->getSummary(),
1751 if ($namecmp != 0) {
1756 $namecmp = strcoll (user_get_object($a->getAssignedTo())->getRealName(),
1757 user_get_object($b->getAssignedTo())->getRealName()) ;
1758 if ($namecmp != 0) {
1762 case 'submitted_by':
1763 $namecmp = strcoll (user_get_object($a->getSubmittedBy())->getRealName(),
1764 user_get_object($b->getSubmittedBy())->getRealName()) ;
1765 if ($namecmp != 0) {
1770 $a_date = $a->getOpenDate() ;
1771 $b_date = $b->getOpenDate() ;
1772 return ($a_date < $b_date) ? -1 : 1;
1775 $a_date = $a->getCloseDate() ;
1776 $b_date = $b->getCloseDate() ;
1777 return ($a_date < $b_date) ? -1 : 1;
1779 case 'last_modified_date':
1780 $a_date = $a->getLastModifiedDate() ;
1781 $b_date = $b->getLastModifiedDate() ;
1782 return ($a_date < $b_date) ? -1 : 1;
1785 $a_prority = $a->getPriority() ;
1786 $b_prority = $b->getPriority() ;
1787 return ($a_prority < $b_prority) ? -1 : 1;
1790 $aa=$a->getExtraFieldDataText();
1791 $ba=$b->getExtraFieldDataText();
1792 $af=$aa[$this->criterion]['value'];
1793 $bf=$ba[$this->criterion]['value'];
1794 $namecmp = strcoll ($af,$bf) ;
1795 if ($namecmp != 0) {
1801 // When in doubt, sort on artifact ID
1802 $aid = $a->getID() ;
1803 $bid = $b->getID() ;
1807 return ($aid < $bid) ? -1 : 1;
1811 function sortArtifactList (&$list, $criterion='name', $order='ASC') {
1812 $cmp = new ArtifactComparator () ;
1813 $cmp->criterion = $criterion ;
1814 $cmp->order = $order ;
1816 return usort ($list, array ($cmp, 'Compare')) ;
1821 // c-file-style: "bsd"