5 * Copyright 1999-2001, VA Linux Systems, Inc.
6 * Copyright 2002-2004, GForge, LLC
7 * Copyright 2009, Roland Mas
8 * Copyright (C) 2011 Alain Peyrat - Alcatel-Lucent
9 * Copyright 2012, Thorsten “mirabilos” Glaser <t.glaser@tarent.de>
10 * Copyright 2014,2016, Franck Villaume - TrivialDev
11 * Copyright 2016, Stéphane-Eymeric Bredthauer - TrivialDev
13 * This file is part of FusionForge. FusionForge is free software;
14 * you can redistribute it and/or modify it under the terms of the
15 * GNU General Public License as published by the Free Software
16 * Foundation; either version 2 of the Licence, or (at your option)
19 * FusionForge is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 require_once $gfcommon.'include/FFError.class.php';
30 require_once $gfcommon.'tracker/ArtifactExtraFieldElement.class.php';
31 require_once $gfcommon.'tracker/ArtifactStorage.class.php';
32 require_once $gfcommon.'include/MonitorElement.class.php';
35 * Gets an ArtifactType object from the artifact type id
37 * @param int $artType_id The ArtifactType id
38 * @param resource|bool $res The DB handle if passed in (optional)
39 * @return ArtifactType The ArtifactType object
41 function &artifactType_get_object($artType_id, $res = false) {
42 global $ARTIFACTTYPE_OBJ;
43 if (!isset($ARTIFACTTYPE_OBJ["_".$artType_id."_"])) {
45 //the db result handle was passed in
47 $res = db_query_params('SELECT * FROM artifact_group_list_vw WHERE group_artifact_id=$1',
50 if (!$res || db_numrows($res) < 1) {
51 $ARTIFACTTYPE_OBJ["_".$artType_id."_"] = false;
53 $data = db_fetch_array($res);
54 $Group = group_get_object($data["group_id"]);
55 $ARTIFACTTYPE_OBJ["_".$artType_id."_"] = new ArtifactType($Group, $data["group_artifact_id"], $data);
58 return $ARTIFACTTYPE_OBJ["_".$artType_id."_"];
61 function artifacttype_get_groupid($artifact_type_id) {
62 global $ARTIFACTTYPE_OBJ;
63 if (isset($ARTIFACTTYPE_OBJ["_".$artifact_type_id."_"])) {
64 return $ARTIFACTTYPE_OBJ["_".$artifact_type_id."_"]->Group->getID();
67 $res = db_query_params('SELECT group_id FROM artifact_group_list WHERE group_artifact_id=$1',
68 array($artifact_type_id));
69 if (!$res || db_numrows($res) < 1) {
72 $arr = db_fetch_array($res);
73 return $arr['group_id'];
76 class ArtifactType extends FFError {
86 * extra_fields 3d array - the IDs and Names of the extra fields
88 * @var array extra_fields;
90 var $extra_fields = array();
93 * extra_field[extra_field_id] array - the IDs and Names of elements on the extra fields
95 * @var array extra_field
100 * Technicians db resource ID.
102 * @var int $technicians_res.
104 var $technicians_res;
107 * Submitters db resource ID.
109 * @var int $submitters_res.
114 * Status db resource ID.
116 * @var int $status_res.
121 * Canned responses resource ID.
123 * @var int $canned_responses_res.
125 var $canned_responses_res;
128 * Array of artifact data.
130 * @var array $data_array.
135 * Array of element names so they only have to be fetched once from db.
137 * @var array $data_array.
142 * Array of element status so they only have to be fetched once from db.
144 * @var array $data_array.
149 * cached return value of getVoters
150 * @var int|bool $voters
155 * @param Group $Group The Group object.
156 * @param int|bool $artifact_type_id The id # assigned to this artifact type in the db.
157 * @param array|bool $arr The associative array of data.
159 function __construct($Group, $artifact_type_id = false, $arr = false) {
160 parent::__construct();
161 if (!$Group || !is_object($Group)) {
162 $this->setError(_('No Valid Group Object'));
165 if ($Group->isError()) {
166 $this->setError('ArtifactType: '.$Group->getErrorMessage());
169 $this->Group = $Group;
170 if ($artifact_type_id) {
171 if (!$arr || !is_array($arr)) {
172 if (!$this->fetchData($artifact_type_id)) {
176 $this->data_array =& $arr;
177 if ($this->data_array['group_id'] != $this->Group->getID()) {
178 $this->setError('Group_id in db result does not match Group Object');
179 $this->data_array = null;
184 // Make sure they can even access this object
186 if (!forge_check_perm ('tracker', $this->getID(), 'read')) {
187 $this->setPermissionDeniedError();
188 $this->data_array = null;
195 * create - use this to create a new ArtifactType in the database.
197 * @param string $name The type name.
198 * @param string $description The type description.
199 * @param bool $email_all (1) true (0) false - whether to email on all updates.
200 * @param string $email_address The address to send new entries and updates to.
201 * @param int $due_period Days before this item is considered overdue.
202 * @param bool $use_resolution (1) true (0) false - whether the resolution box should be shown.
203 * @param string $submit_instructions Free-form string that project admins can place on the submit page.
204 * @param string $browse_instructions Free-form string that project admins can place on the browse page.
205 * @param int $datatype (1) bug tracker, (2) Support Tracker, (3) Patch Tracker, (4) features (0) other.
206 * @return int id on success, false on failure.
208 function create($name, $description, $email_all, $email_address,
209 $due_period, $use_resolution, $submit_instructions, $browse_instructions, $datatype = 0) {
211 if (!forge_check_perm('tracker_admin', $this->Group->getID())) {
212 $this->setPermissionDeniedError();
216 if (!$name || !$description || !$due_period) {
217 $this->setError(_('ArtifactType: Name, Description, Due Period, and Status Timeout are required'));
221 if ($email_address) {
222 $invalid_emails = validate_emails($email_address);
223 if (count($invalid_emails) > 0) {
224 $this->setError(_('E-mail address(es) appeared invalid')._(': ').implode(',', $invalid_emails));
229 $use_resolution = ((!$use_resolution) ? 0 : $use_resolution);
230 $email_all = ((!$email_all) ? 0 : $email_all);
234 $res = db_query_params('INSERT INTO
247 ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)',
248 array($this->Group->getID(),
249 htmlspecialchars($name),
250 htmlspecialchars($description),
253 $due_period*(60*60*24),
255 htmlspecialchars($submit_instructions),
256 htmlspecialchars($browse_instructions),
259 $id = db_insertid($res, 'artifact_group_list', 'group_artifact_id');
262 $this->setError('ArtifactType: '.db_error());
266 if (!$this->fetchData($id)) {
270 $this->Group->normalizeAllRoles();
278 * fetchData - re-fetch the data for this ArtifactType from the database.
280 * @param int $artifact_type_id The artifact type ID.
281 * @return boolean success.
283 function fetchData($artifact_type_id) {
284 $this->voters = false;
285 $this->extra_field = false;
286 $this->extra_fields = false;
287 $res = db_query_params('SELECT * FROM artifact_group_list_vw
288 WHERE group_artifact_id=$1
290 array($artifact_type_id,
291 $this->Group->getID()));
292 if (!$res || db_numrows($res) < 1) {
293 $this->setError('ArtifactType: Invalid ArtifactTypeID');
296 $this->data_array = db_fetch_array($res);
297 db_free_result($res);
302 * getGroup - get the Group object this ArtifactType is associated with.
304 * @return Object The Group object.
306 function &getGroup() {
311 * getID - get this ArtifactTypeID.
313 * @return int The group_artifact_id #.
316 return $this->data_array['group_artifact_id'];
320 * getOpenCount - get the count of open tracker items in this tracker type.
322 * @return int The count.
324 function getOpenCount() {
325 return $this->data_array['open_count'];
329 * getTotalCount - get the total number of tracker items in this tracker type.
331 * @return int The total count.
333 function getTotalCount() {
334 return $this->data_array['count'];
338 * getSubmitInstructions - get the free-form string strings.
340 * @return string instructions.
342 function getSubmitInstructions() {
343 return $this->data_array['submit_instructions'];
347 * getBrowseInstructions - get the free-form string strings.
349 * @return string instructions.
351 function getBrowseInstructions() {
352 return $this->data_array['browse_instructions'];
356 * emailAll - determine if we're supposed to email on every event.
358 * @return boolean email_all.
360 function emailAll() {
361 return $this->data_array['email_all_updates'];
365 * emailAddress - defined email address to send events to.
367 * @return string email.
369 function getEmailAddress() {
370 return $this->data_array['email_address'];
374 * getName - the name of this ArtifactType.
376 * @return string name.
379 return $this->data_array['name'];
383 * getFormattedName - formatted name of this ArtifactType
385 * @return string formatted name
387 function getFormattedName() {
388 $name = preg_replace('/[^[:alnum:]]/', '', $this->getName());
389 $name = strtolower($name);
394 * getUnixName - returns the name used by email gateway
396 * @return string unix name
398 function getUnixName() {
399 return strtolower($this->Group->getUnixName()).'-'.$this->getFormattedName();
403 * getReturnEmailAddress() - return the return email address for notification emails
405 * @return string return email address
407 function getReturnEmailAddress() {
410 if (forge_get_config('use_gateways')) {
411 $address .= strtolower($this->getUnixName());
413 $address .= 'noreply';
415 $address .= '@'.forge_get_config('web_host');
420 * getDescription - the description of this ArtifactType.
422 * @return string description.
424 function getDescription() {
425 return $this->data_array['description'];
429 * getDuePeriod - how many seconds until it's considered overdue.
431 * @return int seconds.
433 function getDuePeriod() {
434 return $this->data_array['due_period'];
438 * getStatusTimeout - how many seconds until an item is stale.
440 * @return int seconds.
442 function getStatusTimeout() {
443 return $this->data_array['status_timeout'];
447 * getCustomStatusField - return the extra_field_id of the field containing the custom status.
449 * @return int extra_field_id.
451 function getCustomStatusField() {
452 return $this->data_array['custom_status_field'];
456 * setCustomStatusField - set the extra_field_id of the field containing the custom status.
458 * @param int $extra_field_id The extra field id.
459 * @return boolean success.
461 function setCustomStatusField($extra_field_id) {
462 $res = db_query_params('UPDATE artifact_group_list SET custom_status_field=$1
463 WHERE group_artifact_id=$2',
464 array ($extra_field_id,
470 * getAutoAssignField - get the extra_field_id of the field that triggers auto-assignment rules.
472 * @return int extra_field_id.
474 function getAutoAssignField() {
475 return $this->data_array['auto_assign_field'];
479 * setAutoAssignField - set the extra_field_id of the field that triggers auto-assignment rules.
481 * @param int $extra_field_id The extra field id.
482 * @return boolean success.
484 function setAutoAssignField($extra_field_id) {
485 $res = db_query_params('UPDATE artifact_group_list SET auto_assign_field=$1
486 WHERE group_artifact_id=$2',
487 array ($extra_field_id,
493 * usesCustomStatuses - boolean
495 * @return boolean use_custom_statues.
497 function usesCustomStatuses() {
498 return $this->getCustomStatusField();
502 * remapStatus - pass the extra_fields array and return the status_id, either open/closed
504 * @param int $status_id The status_id
505 * @param array $extra_fields Complex array of extra_field_data
506 * @return int status_id.
508 function remapStatus($status_id, $extra_fields) {
509 if ($this->usesCustomStatuses()) {
510 //get the selected element for the extra_field_status element
511 $csfield = $this->getCustomStatusField();
512 if (array_key_exists($csfield, $extra_fields)) {
513 $element_id = $extra_fields[$csfield];
515 //convert that element_id into the status_id
516 $res = db_query_params('SELECT status_id FROM artifact_extra_field_elements WHERE element_id=$1',
519 $this->setError('Error Remapping Status: '.db_error());
522 $status_id = db_result($res, 0, 'status_id');
523 if ($status_id < 1 || $status_id > 4) {
524 $this->setError('INVALID STATUS REMAP: '.$status_id.' FROM SELECTED ELEMENT: '.$element_id);
528 // custom status was not passed... use the first status from the database
529 $res = db_query_params('SELECT status_id FROM artifact_extra_field_elements WHERE extra_field_id=$1 ORDER BY element_id ASC LIMIT 1 OFFSET 0',
531 if (db_numrows($res) == 0) { // No values available
532 $this->setError('Error Remapping Status');
535 $status_id = db_result($res, 0, 'status_id');
544 * getDataType - flag that is generally unused but can mark the difference between bugs, patches, etc.
546 * @return int The type (1) bug (2) support (3) patch (4) feature (0) other.
548 function getDataType() {
549 return $this->data_array['datatype'];
553 * setMonitor - user can monitor this artifact.
555 * @return bool false - always false - always use the getErrorMessage() for feedback
557 function setMonitor($user_id = -1) {
559 if ($user_id == -1) {
560 if (!session_loggedin()) {
561 $this->setError(_('You can only monitor if you are logged in.'));
564 $user_id = user_getid();
566 $MonitorElementObject = new MonitorElement('artifact_type');
567 if (!$this->isMonitoring()) {
568 if (!$MonitorElementObject->enableMonitoringByUserId($this->getID(), $user_id)) {
569 $this->setError($MonitorElementObject->getErrorMessage());
572 $feedback = _('Monitoring Started');
575 if (!$MonitorElementObject->disableMonitoringByUserId($this->getID(), $user_id)) {
576 $this->setError($MonitorElementObject->getErrorMessage());
579 $feedback = _('Monitoring Stopped');
585 function isMonitoring() {
586 if (!session_loggedin()) {
589 $MonitorElementObject = new MonitorElement('artifact_type');
590 return $MonitorElementObject->isMonitoredByUserId($this->getID(), user_getid());
594 * getMonitorIds - array of id of users monitoring this Artifact.
596 * @return array array of id of users monitoring this Artifact.
598 function getMonitorIds() {
599 $MonitorElementObject = new MonitorElement('artifact_type');
600 return $MonitorElementObject->getMonitorUsersIdsInArray($this->getID());
604 * getExtraFields - List of possible user built extra fields
605 * set up for this artifact type.
607 * @param array $types
608 * @param bool $get_is_disabled
609 * @param bool $get_is_hidden_on_submit
610 * @return array arrays of data;
612 function getExtraFields($types = array(), $get_is_disabled = false, $get_is_hidden_on_submit = true) {
614 if (!$get_is_disabled) {
615 $where = ' AND is_disabled = 0';
617 if (!$get_is_hidden_on_submit) {
618 $where = ' AND is_hidden_on_submit = 0';
621 $filter = implode(',', $types);
622 $types = explode(',', $filter);
626 if (!isset($this->extra_fields[$filter])) {
627 $this->extra_fields[$filter] = array();
629 $res = db_query_params('SELECT *
630 FROM artifact_extra_field_list
631 WHERE group_artifact_id=$1
632 AND field_type = ANY ($2)'.
634 'ORDER BY field_type ASC',
635 array($this->getID(),
636 db_int_array_to_any_clause($types)));
638 $res = db_query_params('SELECT *
639 FROM artifact_extra_field_list
640 WHERE group_artifact_id=$1'.
642 'ORDER BY field_type ASC',
643 array($this->getID()));
645 while ($arr = db_fetch_array($res)) {
646 $this->extra_fields[$filter][$arr['extra_field_id']] = $arr;
650 return $this->extra_fields[$filter];
654 * cloneFieldsFrom - clone all the fields and elements from another tracker
656 * @param int $clone_tracker_id id of the cloned tracker
657 * @param int $group_id id of the project template to use.
658 * @param array $id_mappings array mapping between template objects and new project objects
659 * @return boolean true/false on success
661 function cloneFieldsFrom($clone_tracker_id, $group_id = null, $id_mappings = array()) {
663 $g = group_get_object($group_id);
665 $g = group_get_object(forge_get_config('template_group'));
667 if (!$g || !is_object($g)) {
668 $this->setError(_('Could Not Get Template Group'));
670 } elseif ($g->isError()) {
671 $this->setError(_('Template Group Error').' '.$g->getErrorMessage());
674 $at = new ArtifactType($g,$clone_tracker_id);
675 if (!$at || !is_object($at)) {
676 $this->setError(_('Could Not Get Tracker To Clone'));
678 } elseif ($at->isError()) {
679 $this->setError(_('Clone Tracker Error').' '.$at->getErrorMessage());
682 // do not filter and get disabled fields as well
683 $efs = $at->getExtraFields(array(), true);
685 // get current getExtraFields if any
686 $current_efs = $this->getExtraFields();
689 // Iterate list of extra fields
693 $newEFElIds = array();
694 foreach ($efs as $ef) {
695 //new field in this tracker
696 $nef = new ArtifactExtraField($this);
697 foreach ($current_efs as $current_ef) {
698 if ($current_ef['field_name'] == $ef['field_name']) {
699 // we delete the current extra field and use the template one...
700 $current_ef_todelete = new ArtifactExtraField($this, $current_ef);
701 $current_ef_todelete->delete(true,true);
704 if (!$nef->create(util_unconvert_htmlspecialchars($ef['field_name']), $ef['field_type'], $ef['attribute1'], $ef['attribute2'], $ef['is_required'], $ef['alias'], $ef['show100'], $ef['show100label'], $ef['description'], $ef['pattern'], 100, 0, $ef['is_hidden_on_submit'], $ef['is_disabled'])) {
705 $this->setError(_('Error Creating New Extra Field')._(':').' '.$nef->getErrorMessage());
709 $newEFIds[$ef['extra_field_id']] = $nef->getID();
710 $newEFElIds[$ef['extra_field_id']] = array();
712 // Iterate the elements
714 $resel = db_query_params('SELECT * FROM artifact_extra_field_elements WHERE extra_field_id=$1',
715 array($ef['extra_field_id']));
717 //by default extrafield status is created with default values: 'Open' & 'Closed'
718 if ($nef->getType() == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
719 $existingElements = $nef->getAvailableValues();
720 foreach($existingElements as $existingElement) {
721 $existingElement = new ArtifactExtraFieldElement($nef, $existingElement);
722 $existingElement->delete();
725 while ($el = db_fetch_array($resel)) {
727 $nel = new ArtifactExtraFieldElement($nef);
728 if (!$nel->create(util_unconvert_htmlspecialchars($el['element_name']), $el['status_id'])) {
730 $this->setError(_('Error Creating New Extra Field Element')._(':').' '.$nel->getErrorMessage());
733 $newEFElIds[$ef['extra_field_id']][$el['element_id']] = $nel->getID();
737 foreach ($newEFIds as $oldEFId => $newEFId) {
738 $oef = new ArtifactExtraField($at, $oldEFId);
739 $nef = new ArtifactExtraField($this, $newEFId);
740 // update Dependency between extrafield
741 if ($oef->getParent() != 100) {
742 if (!$nef->update($nef->getName(), $nef->getAttribute1(), $nef->getAttribute2(), $nef->isRequired(), $nef->getAlias(), $nef->getShow100(), $nef->getShow100label(), $nef->getDescription(), $nef->getPattern(), $newEFIds[$oef->getParent()], $nef->isAutoAssign(), $nef->is_hidden_on_submit(), $nef->is_disabled())) {
744 $this->setError(_('Error Updating New Extra Field Parent')._(':').' '.$nef->getErrorMessage());
748 foreach ($newEFElIds[$oldEFId] as $oldEFElId => $newEFElId) {
749 $oel = new ArtifactExtraFieldElement($oef,$oldEFElId);
750 if ($oel->isError()) {
752 $this->setError($oel->getErrorMessage());
755 $nel = new ArtifactExtraFieldElement($nef,$newEFElId);
756 if ($nel->isError()) {
758 $this->setError($nel->getErrorMessage());
761 $oPEls = $oel->getParentElements();
763 foreach ($oPEls as $oPEl) {
764 $nPEls[]=$newEFElIds[$oef->getParent()][$oPEl];
766 $nel->saveParentElements($nPEls);
767 if ($nel->isError()) {
769 $this->setError(_('Error Saving New Extra Field Parent Elements').' '.$nel->getErrorMessage());
775 if ($nef->getType() == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
776 // update the allowed init values
777 $oatw = new ArtifactWorkflow($at, $oldEFId);
778 $natw = new ArtifactWorkflow($this, $newEFId);
779 // template allowed init values
780 $oaivs = $oatw->getNextNodes('100');
782 foreach ($oaivs as $oaiv) {
783 $naivs[] = $newEFElIds[$oldEFId][$oaiv];
785 $natw->saveNextNodes('100', $naivs);
787 //implement role based of the workflow
788 if (sizeof($id_mappings) && isset($id_mappings['role'])) {
789 $oefelements = $at->getExtraFieldElements($oldEFId);
790 foreach ($oefelements as $oefelement) {
791 // retrieve the allowed values for the old element
792 $onexts = $oatw->getNextNodes($oefelement['element_id']);
794 foreach ($onexts as $onext) {
795 $naivs[] = $newEFElIds[$oldEFId][$onext];
796 //retrieve the allowed old roles from old element to old next value
797 $oars = $oatw->getAllowedRoles($oefelement['element_id'], $onext);
798 //map old roles into new roles id
800 foreach ($oars as $oar) {
801 if (array_key_exists($oar, $id_mappings['role'])) {
802 $nar[] = $id_mappings['role'][$oar];
805 $natw->saveAllowedRoles($newEFElIds[$oldEFId][$oefelement['element_id']], $newEFElIds[$oldEFId][$onext], $nar);
807 $natw->saveNextNodes($newEFElIds[$oldEFId][$oefelement['element_id']], $naivs);
818 * getExtraFieldName - Get a box name using the box ID
820 * @param int $extra_field_id id of an extra field.
821 * @return string name of extra field.
823 function getExtraFieldName($extra_field_id) {
824 $arr = $this->getExtraFields();
825 return $arr[$extra_field_id]['field_name'];
829 * getExtraFieldElements - List of possible admin configured
830 * extra field elements. This function is used to
831 * present the boxes and choices on the main Add/Update page.
833 * @param int $id id of the extra field
834 * @return array of elements for this extra field.
836 function getExtraFieldElements($id) {
841 if (!isset($this->extra_field[$id])) {
842 $this->extra_field[$id] = array();
843 $res = db_query_params('SELECT element_id, element_name, status_id
844 FROM artifact_extra_field_elements
845 WHERE extra_field_id = $1
846 ORDER BY element_pos ASC, element_id ASC',
849 while ($arr = db_fetch_array($res)) {
850 $this->extra_field[$id][$i++] = $arr;
852 // if (count($this->extra_field[$id]) == 0) {
857 return $this->extra_field[$id];
861 * getElementName - get the name of a particular element.
864 * @return string The name.
866 function getElementName($choice_id) {
870 if (is_array($choice_id)) {
871 $choice_id = implode(',', array_map('intval', $choice_id));
873 $choice_id = intval($choice_id);
875 if ($choice_id == 100) {
878 if (!isset($this->element_name[$choice_id])) {
879 $res = db_query_params('SELECT element_id,extra_field_id,element_name
880 FROM artifact_extra_field_elements
881 WHERE element_id = ANY ($1)',
882 array(db_int_array_to_any_clause(explode(',', $choice_id))));
883 if (db_numrows($res) > 1) {
884 $arr = util_result_column_to_array($res, 2);
885 $this->element_name[$choice_id] = implode(',', $arr);
887 $this->element_name[$choice_id] = db_result($res, 0, 'element_name');
890 return $this->element_name[$choice_id];
894 * getElementStatusID - get the status of a particular element.
896 * @param int|array $choice_id
897 * @return int The status
899 function getElementStatusID($choice_id) {
903 if (is_array($choice_id)) {
904 $choice_id = implode(',',$choice_id);
906 if ($choice_id == 100) {
909 if (!$this->element_status[$choice_id]) {
910 $res = db_query_params('SELECT element_id,extra_field_id,status_id
911 FROM artifact_extra_field_elements
912 WHERE element_id = ANY ($1)',
913 array(db_int_array_to_any_clause(explode(',', $choice_id))));
914 if (db_numrows($res) > 1) {
915 $arr = util_result_column_to_array($res, 2);
916 $this->element_status[$choice_id] = implode(',', $arr);
918 $this->element_status[$choice_id] = db_result($res, 0, 'status_id');
921 return $this->element_status[$choice_id];
925 * delete - delete this tracker and all its related data.
927 * @param bool $sure I'm Sure.
928 * @param bool $really_sure I'm REALLY sure.
929 * @return bool true/false;
931 function delete($sure, $really_sure) {
932 if (!$sure || !$really_sure) {
933 $this->setMissingParamsError(_('Please tick all checkboxes.'));
936 if (!forge_check_perm ('tracker_admin', $this->Group->getID())) {
937 $this->setPermissionDeniedError();
941 db_query_params('DELETE FROM artifact_extra_field_data
942 WHERE EXISTS (SELECT artifact_id FROM artifact
943 WHERE group_artifact_id=$1
944 AND artifact.artifact_id=artifact_extra_field_data.artifact_id)',
945 array($this->getID()));
946 //echo '0.1'.db_error();
947 db_query_params('DELETE FROM artifact_extra_field_elements
948 WHERE EXISTS (SELECT extra_field_id FROM artifact_extra_field_list
949 WHERE group_artifact_id=$1
950 AND artifact_extra_field_list.extra_field_id = artifact_extra_field_elements.extra_field_id)',
951 array ($this->getID()));
952 //echo '0.2'.db_error();
953 db_query_params('DELETE FROM artifact_extra_field_list
954 WHERE group_artifact_id=$1',
955 array ($this->getID()));
956 //echo '0.3'.db_error();
957 db_query_params('DELETE FROM artifact_canned_responses
958 WHERE group_artifact_id=$1',
959 array ($this->getID()));
960 //echo '1'.db_error();
961 db_query_params('DELETE FROM artifact_counts_agg
962 WHERE group_artifact_id=$1',
963 array ($this->getID()));
964 //echo '5'.db_error();
966 ArtifactStorage::instance()->deleteFromQuery('SELECT id FROM artifact_file
967 WHERE EXISTS (SELECT artifact_id FROM artifact
968 WHERE group_artifact_id=$1
969 AND artifact.artifact_id=artifact_file.artifact_id)',
970 array($this->getID()));
972 db_query_params('DELETE FROM artifact_file
973 WHERE EXISTS (SELECT artifact_id FROM artifact
974 WHERE group_artifact_id=$1
975 AND artifact.artifact_id=artifact_file.artifact_id)',
976 array($this->getID()));
977 //echo '6'.db_error();
978 db_query_params('DELETE FROM artifact_message
979 WHERE EXISTS (SELECT artifact_id FROM artifact
980 WHERE group_artifact_id=$1
981 AND artifact.artifact_id=artifact_message.artifact_id)',
982 array($this->getID()));
983 //echo '7'.db_error();
984 db_query_params('DELETE FROM artifact_history
985 WHERE EXISTS (SELECT artifact_id FROM artifact
986 WHERE group_artifact_id=$1
987 AND artifact.artifact_id=artifact_history.artifact_id)',
988 array($this->getID()));
989 //echo '8'.db_error();
990 db_query_params('DELETE FROM artifact_monitor
991 WHERE EXISTS (SELECT artifact_id FROM artifact
992 WHERE group_artifact_id=$1
993 AND artifact.artifact_id=artifact_monitor.artifact_id)',
994 array($this->getID()));
995 //echo '9'.db_error();
996 db_query_params('DELETE FROM artifact
997 WHERE group_artifact_id=$1',
998 array($this->getID()));
999 //echo '4'.db_error();
1000 db_query_params('DELETE FROM artifact_group_list
1001 WHERE group_artifact_id=$1',
1002 array($this->getID()));
1003 //echo '11'.db_error();
1005 $MonitorElementObject = new MonitorElement('artifact_type');
1006 $MonitorElementObject->clearMonitor($this->getID());
1009 ArtifactStorage::instance()->commit();
1011 $this->Group->normalizeAllRoles();
1017 * getSubmitters - returns a result set of submitters.
1019 * @return resource database result set.
1021 function getSubmitters() {
1022 if (!isset($this->submitters_res)) {
1023 $this->submitters_res = db_query_params('SELECT DISTINCT submitted_by, submitted_realname
1025 WHERE group_artifact_id=$1
1026 ORDER BY submitted_realname',
1027 array($this->getID()));
1029 return $this->submitters_res;
1033 * getCannedResponses - returns a result set of canned responses.
1035 * @return resource database result set.
1037 function getCannedResponses() {
1038 if (!isset($this->cannedresponses_res)) {
1039 $this->cannedresponses_res = db_query_params('SELECT id,title
1040 FROM artifact_canned_responses
1041 WHERE group_artifact_id=$1',
1042 array($this->getID()));
1044 return $this->cannedresponses_res;
1048 * getStatuses - returns a result set of statuses.
1050 * These statuses are either the default open/closed or any number of
1051 * custom statuses that are stored in the extra fields. On insert/update
1052 * to an artifact the status_id is remapped from the extra_field_element_id to
1053 * the standard open/closed id.
1055 * @return resource database result set.
1057 function getStatuses() {
1058 if (!isset($this->status_res)) {
1059 $this->status_res = db_query_params('SELECT * FROM artifact_status', array());
1061 return $this->status_res;
1065 * getStatusName - returns the name of this status.
1067 * @param int $id The status ID.
1068 * @return string name.
1070 function getStatusName($id) {
1071 $result = db_query_params('select status_name from artifact_status WHERE id=$1',
1073 if ($result && db_numrows($result) > 0) {
1074 return db_result($result, 0, 'status_name');
1076 return 'Error: Not Found';
1081 * update - use this to update this ArtifactType in the database.
1083 * @param string $name The item name.
1084 * @param string $description The item description.
1085 * @param bool $email_all (1) true (0) false - whether to email on all updates.
1086 * @param string $email_address The address to send new entries and updates to.
1087 * @param int $due_period Days before this item is considered overdue.
1088 * @param int $status_timeout Days before stale items time out.
1089 * @param bool $use_resolution (1) true (0) false - whether the resolution box should be shown.
1090 * @param string $submit_instructions Free-form string that project admins can place on the submit page.
1091 * @param string $browse_instructions Free-form string that project admins can place on the browse page.
1092 * @return bool true on success, false on failure.
1094 function update($name, $description, $email_all, $email_address,
1095 $due_period, $status_timeout, $use_resolution, $submit_instructions, $browse_instructions) {
1097 if (!forge_check_perm ('tracker_admin', $this->Group->getID())) {
1098 $this->setPermissionDeniedError();
1102 if ($this->getDataType()) {
1103 $name=$this->getName();
1104 $description=$this->getDescription();
1107 if (!$name || !$description || !$due_period || !$status_timeout) {
1108 $this->setError(_('ArtifactType: Name, Description, Due Period, and Status Timeout are required'));
1112 $result = db_query_params('SELECT count(*) AS count FROM artifact_group_list WHERE group_id=$1 AND name=$2 AND group_artifact_id!=$3',
1113 array($this->Group->getID(), $name, $this->getID()));
1115 $this->setError('ArtifactType::Update(): '.db_error());
1118 if (db_result($result, 0, 'count')) {
1119 $this->setError(_('Tracker name already used'));
1123 if ($email_address) {
1124 $invalid_emails = validate_emails($email_address);
1125 if (count($invalid_emails) > 0) {
1126 $this->setError(_('E-mail address(es) appeared invalid')._(': ').implode(',', $invalid_emails));
1131 $email_all = ((!$email_all) ? 0 : $email_all);
1132 $use_resolution = ((!$use_resolution) ? 0 : $use_resolution);
1134 $res = db_query_params('UPDATE artifact_group_list SET
1137 email_all_updates=$3,
1141 submit_instructions=$7,
1142 browse_instructions=$8
1143 WHERE group_artifact_id=$9 AND group_id=$10',
1145 htmlspecialchars($name),
1146 htmlspecialchars($description),
1149 $due_period * (60*60*24),
1150 $status_timeout * (60*60*24),
1151 htmlspecialchars($submit_instructions),
1152 htmlspecialchars($browse_instructions),
1154 $this->Group->getID()));
1156 if (!$res || db_affected_rows($res) < 1) {
1157 $this->setError('ArtifactType::Update(): '.db_error());
1160 $this->fetchData($this->getID());
1166 * getBrowseList - get the free-form string strings.
1168 * @return string instructions.
1170 function getBrowseList() {
1171 $list = $this->data_array['browse_list'];
1173 // remove status_id in the browse list if a custom status exists
1174 if (count($this->getExtraFields(array(ARTIFACT_EXTRAFIELDTYPE_STATUS))) > 0) {
1175 $arr = explode(',', $list);
1176 $idx = array_search('status_id', $arr);
1177 if ($idx !== False) {
1178 array_splice($arr, $idx, 1);
1180 return join(',', $arr);
1187 * setCustomStatusField - set the extra_field_id of the field containing the custom status.
1189 * @param int $list The extra field id.
1190 * @return boolean success.
1192 function setBrowseList($list) {
1193 $res = db_query_params('UPDATE artifact_group_list
1195 WHERE group_artifact_id=$2',
1198 $this->fetchData($this->getID());
1203 * canVote - check whether the current user can vote on
1204 * items in this tracker
1206 * @return bool true if they can
1208 function canVote() {
1209 return forge_check_perm('tracker', $this->getID(), 'vote');
1213 * getVoters - get IDs of users that may vote on
1214 * items in this tracker
1216 * @return array list of user IDs
1218 function getVoters() {
1219 if ($this->voters !== false) {
1220 return $this->voters;
1223 $this->voters = array();
1224 if (($engine = RBACEngine::getInstance())
1225 && ($voters = $engine->getUsersByAllowedAction('tracker', $this->getID(), 'vote'))
1226 && (count($voters) > 0)) {
1227 foreach ($voters as $voter) {
1228 $voter_id = $voter->getID();
1229 $this->voters[$voter_id] = $voter_id;
1232 return $this->voters;
1238 // c-file-style: "bsd"