3 * The ArtifactWorkflow class manages workflow for trackers.
4 * Previous Copyright, FusionForge Team
5 * Copyright 2016, Franck Villaume - TrivialDev
7 * This file is part of FusionForge. FusionForge is free software;
8 * you can redistribute it and/or modify it under the terms of the
9 * GNU General Public License as published by the Free Software
10 * Foundation; either version 2 of the Licence, or (at your option)
13 * FusionForge is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * The workflow is attached to custom status field only.
27 * Associated tables are:
28 * - artifact_workflow_event : to track allowed events.
29 * - artifact_workflow_roles : to track roles allowed to perform an event.
30 * - artifact_workflow_notify : to track notification associated to an event (not implemented).
32 * An event is a transition from one value to another.
34 * NOTE: Code should be improved to manage any kind of custom fields not only of type
35 * 'Status' but maybe also for the 'select' also.
39 * @todo: the getAllowedRoles should be replaced by getRealAllowedRoles code. (to be tested).
41 require_once $gfcommon.'include/FFError.class.php';
43 class ArtifactWorkflow extends FFError {
49 function __construct($artifact, $field_id) {
50 $this->ath = $artifact;
51 $this->artifact_id = (int)$artifact->getID();
52 $this->field_id = (int)$field_id;
55 // Check if the following event is allowed or not.
56 // return true is allowed, false if not.
57 function checkEvent($from, $to) {
61 $res = db_query_params ('SELECT event_id FROM artifact_workflow_event
62 WHERE group_artifact_id=$1
66 array($this->artifact_id,
70 $event_id = db_result($res, 0, 'event_id');
72 // No role based checks for the initial transition.
76 // There is a transition, now check if current role is allowed.
78 $available_roles = RBACEngine::getInstance()->getAvailableRoles() ;
79 $project_role_ids = $this->ath->Group->getRolesId () ;
80 foreach ($available_roles as $role) {
81 if (in_array($role->getID(),$project_role_ids)) {
82 $rids[] = $role->getID() ;
86 $res = db_query_params ('SELECT event_id
87 FROM artifact_workflow_roles
91 db_int_array_to_any_clause($rids)));
92 return db_result($res, 0, 'event_id') ? true : false;
97 function getNotifyFromWorkFlow() {
102 * When a new element is created, add all the new events in the workflow.
104 function addNode($element_id) {
106 $this->ath->fetchData($this->ath->getID());
107 $elearray = $this->ath->getExtraFieldElements($this->field_id);
108 foreach ($elearray as $e) {
109 if ($element_id !== $e['element_id']) {
110 $this->_addEvent($e['element_id'], $element_id);
111 $this->_addEvent($element_id, $e['element_id']);
115 // Allow the new element for the Submit form (Initial values).
116 $this->_addEvent('100', $element_id);
120 * When a new element is removed, remove all the events in the workflow.
122 function removeNode($element_id) {
123 $elearray = $this->ath->getExtraFieldElements($this->field_id);
124 foreach ($elearray as $e) {
125 if ($element_id !== $e['element_id']) {
126 $this->_removeEvent($e['element_id'], $element_id);
127 $this->_removeEvent($element_id, $e['element_id']);
130 // Allow the new element for the Submit form (Initial values).
131 $this->_removeEvent('100', $element_id);
134 // Returns all the possible following nodes (no roles involved).
135 function getNextNodes($from) {
136 $res = db_query_params ('SELECT to_value_id FROM artifact_workflow_event
137 WHERE group_artifact_id=$1
139 AND from_value_id=$3',
140 array($this->artifact_id,
143 return util_result_column_to_array($res);
146 function saveNextNodes($from, $nodes) {
147 // Get All possible nodes.
148 $current = $this->getNextNodes($from);
150 // Remove events no longer present.
151 foreach ($current as $node) {
152 if (!in_array($node, $nodes)) {
153 if ($from != $node) {
154 $this->_removeEvent($from, $node);
159 // Add missing events.
160 foreach ($nodes as $node) {
161 if (!in_array($node, $current)) {
162 $this->_addEvent($from, $node);
168 function getAllowedRoles($from, $to) {
169 $values = $this->_getRealAllowedRoles($from, $to);
171 // If no values, then no roles defined, all roles are allowed.
172 if (empty($values)) {
173 $roles = $this->ath->Group->getRoles() ;
174 sortRoleList($roles, $this->ath->Group) ;
175 foreach ($roles as $r) {
176 $values[] = $r->getID() ;
182 function saveAllowedRoles($from, $to, $roles) {
184 $event_id = $this->_getEventId($from, $to);
186 // Get All possible roles.
187 $current = $this->_getRealAllowedRoles($from, $to);
189 // Remove roles no longer present.
190 foreach ($current as $role) {
191 if (!in_array($role, $roles)) {
192 $this->_removeRole($event_id, $role);
196 // Add missing roles.
197 foreach ($roles as $role) {
198 if (!in_array($role, $current)) {
199 $this->_addRole($event_id, $role);
205 function getRequiredFields($from, $to) {
206 $res = db_query_params ('SELECT extra_field_id
207 FROM artifact_workflow_required_fields NATURAL INNER JOIN artifact_workflow_event
208 WHERE group_artifact_id=$1
212 array($this->artifact_id,
216 return util_result_column_to_array($res);
219 function saveRequiredFields($from, $to, $extra_fields) {
220 $event_id = $this->_getEventId($from, $to);
221 // Get required fields.
222 $current = $this->getRequiredFields($from, $to);
223 // Remove required fields no longer present.
224 foreach ($current as $extra_field) {
225 if (!in_array($extra_field, $extra_fields)) {
226 $this->_removeRequiredField($event_id, $extra_field);
230 // Add missing required fields.
231 foreach ($extra_fields as $extra_field) {
232 if (!in_array($extra_field, $current)) {
233 $this->_addRequiredField($event_id, $extra_field);
239 function _getEventId($from, $to) {
240 $res = db_query_params ('SELECT event_id FROM artifact_workflow_event
241 WHERE group_artifact_id=$1
245 array($this->artifact_id,
250 $this->setError('Unable to get Event Id ($from, $to): '.db_error());
253 return db_result($res, 0, 'event_id');
257 function _addEvent($from, $to) {
258 $event_id = $this->_getEventId($from, $to);
260 $res = db_query_params ('INSERT INTO artifact_workflow_event
261 (group_artifact_id, field_id, from_value_id, to_value_id)
262 VALUES ($1, $2, $3, $4)',
263 array($this->artifact_id,
268 $this->setError('Unable to add Event($from, $to): '.db_error());
271 $event_id = $this->_getEventId($from, $to);
275 // By default, all roles are allowed on a new event.
276 foreach ($this->ath->Group->getRoles() as $r) {
277 $this->_addRole($event_id, $r->getID());
283 function _removeEvent($from, $to) {
284 $event_id = $this->_getEventId($from, $to);
286 $res = db_query_params ('DELETE FROM artifact_workflow_event
287 WHERE group_artifact_id=$1
291 array($this->artifact_id,
296 $this->setError('Unable to remove Event($from, $to): '.db_error());
302 function _getRealAllowedRoles($from, $to) {
304 $res = db_query_params ('SELECT role_id
305 FROM artifact_workflow_roles, artifact_workflow_event
306 WHERE artifact_workflow_roles.event_id = artifact_workflow_event.event_id
307 AND group_artifact_id=$1
311 array($this->artifact_id,
315 return util_result_column_to_array($res);
318 function _addRole($event_id, $role_id) {
320 $res = db_query_params ('INSERT INTO artifact_workflow_roles
326 $this->setError('Unable to add Role ($role_id): '.db_error());
333 function _removeRole($event_id, $role_id) {
335 $res = db_query_params ('DELETE FROM artifact_workflow_roles
336 WHERE event_id=$1 AND role_id=$2',
340 $this->setError('Unable to remove Role ($role_id): '.db_error());
346 function _addRequiredField($event_id, $extra_field_id) {
347 $res = db_query_params ('INSERT INTO artifact_workflow_required_fields
348 (event_id, extra_field_id)
353 $this->setError('Unable to add Extra Field ($extra_field_id): '.db_error());
359 function _removeRequiredField($event_id, $extra_field_id) {
360 $res = db_query_params ('DELETE FROM artifact_workflow_required_fields
361 WHERE event_id=$1 AND extra_field_id=$2',
365 $this->setError('Unable to remove Extra Field ($extra_field_id): '.db_error());