5 * Copyright 2004, Anthony J. Pugliese
6 * Copyright 2009, Roland Mas
7 * Copyright 2014, Franck Villaume - TrivialDev
9 * This file is part of FusionForge. FusionForge is free software;
10 * you can redistribute it and/or modify it under the terms of the
11 * GNU General Public License as published by the Free Software
12 * Foundation; either version 2 of the Licence, or (at your option)
15 * FusionForge is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 require_once $gfcommon.'include/Error.class.php';
27 define('ARTIFACT_EXTRAFIELD_FILTER_INT','1,2,3,5,7');
28 define('ARTIFACT_EXTRAFIELDTYPE_SELECT',1);
29 define('ARTIFACT_EXTRAFIELDTYPE_CHECKBOX',2);
30 define('ARTIFACT_EXTRAFIELDTYPE_RADIO',3);
31 define('ARTIFACT_EXTRAFIELDTYPE_TEXT',4);
32 define('ARTIFACT_EXTRAFIELDTYPE_MULTISELECT',5);
33 define('ARTIFACT_EXTRAFIELDTYPE_TEXTAREA',6);
34 define('ARTIFACT_EXTRAFIELDTYPE_STATUS',7);
35 //define('ARTIFACT_EXTRAFIELDTYPE_ASSIGNEE',8);
36 define('ARTIFACT_EXTRAFIELDTYPE_RELATION',9);
37 define('ARTIFACT_EXTRAFIELDTYPE_INTEGER',10);
38 /* reserved for aljeux extension, for merge into FusionForge */
39 define('ARTIFACT_EXTRAFIELDTYPE_FORMULA',11);
40 /* reserved for Evolvis extension, for merge into FusionForge */
41 define('ARTIFACT_EXTRAFIELDTYPE_DATETIME',12);
43 class ArtifactExtraField extends Error {
46 * The artifact type object.
48 * @var object $ArtifactType.
50 var $ArtifactType; //object
53 * Array of artifact data.
55 * @var array $data_array.
60 * __construct - Constructor
61 * @param $ArtifactType
64 function __construct(&$ArtifactType, $data=false) {
67 //was ArtifactType legit?
68 if (!$ArtifactType || !is_object($ArtifactType)) {
69 $this->setError(_('Invalid Artifact Type'));
72 //did ArtifactType have an error?
73 if ($ArtifactType->isError()) {
74 $this->setError('ArtifactExtraField: '.$ArtifactType->getErrorMessage());
77 $this->ArtifactType =& $ArtifactType;
80 if (is_array($data)) {
81 $this->data_array =& $data;
83 $this->fetchData($data);
89 * create - create a row in the table that stores box names for a
90 * a tracker. This function is only used to create rows for boxes
91 * configured by the admin.
93 * @param string $name Name of the extra field.
94 * @param int $field_type The type of field - radio, select, text, textarea
95 * @param int $attribute1 For text (size) and textarea (rows)
96 * @param int $attribute2 For text (maxlength) and textarea (cols)
97 * @param int $is_required True or false whether this is a required field or not.
98 * @param string $alias Alias for this extra field (optional)
99 * @param int $show100 True or false whether the 100 value is displayed or not
100 * @param string $show100label The label used for the 100 value if displayed
101 * @return bool true on success / false on failure.
103 function create($name, $field_type, $attribute1, $attribute2, $is_required = 0, $alias = '', $show100 = true, $show100label = 'none') {
108 $this->setError(_('A field name is required'));
112 $this->setError(_('Type of custom field not selected'));
115 if (!forge_check_perm ('tracker_admin', $this->ArtifactType->Group->getID())) {
116 $this->setPermissionDeniedError();
120 $res = db_query_params ('SELECT field_name FROM artifact_extra_field_list WHERE field_name=$1 AND group_artifact_id=$2',
122 $this->ArtifactType->getID()));
123 if (db_numrows($res) > 0) {
124 $this->setError(_('Field name already exists'));
127 if ($field_type == ARTIFACT_EXTRAFIELDTYPE_TEXT || $field_type == ARTIFACT_EXTRAFIELDTYPE_INTEGER) {
128 if (!$attribute1 || !$attribute2 || $attribute2 < $attribute1) {
129 $this->setError(_('Invalid size/maxlength for text field'));
133 if ($field_type == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
134 if (!$attribute1 || !$attribute2) {
135 $this->setError(_('Invalid rows/cols for textarea field'));
138 } elseif ($field_type == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
139 if ($this->ArtifactType->getCustomStatusField()) {
140 $this->setError(_('This Tracker already uses custom statuses'));
151 if (!($alias = $this->generateAlias($alias,$name))) {
156 $result = db_query_params ('INSERT INTO artifact_extra_field_list (group_artifact_id, field_name, field_type, attribute1, attribute2, is_required, alias, show100, show100label)
157 VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)',
158 array ($this->ArtifactType->getID(),
159 htmlspecialchars($name),
168 if ($result && db_affected_rows($result) > 0) {
170 $id=db_insertid($result,'artifact_extra_field_list','extra_field_id');
172 // Now set up our internal data structures
174 if (!$this->fetchData($id)) {
178 if ($field_type == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
179 if (!$this->ArtifactType->setCustomStatusField($id)) {
184 // Must insert some default statuses for each artifact
186 $ao = new ArtifactExtraFieldElement($this);
187 if (!$ao || !is_object($ao)) {
188 $feedback .= 'Unable to create ArtifactExtraFieldElement Object';
192 if (!$ao->create('Open', '1')) {
193 $feedback .= _('Insert Error')._(': ').$ao->getErrorMessage();
198 if (!$ao->create('Closed', '2')) {
199 $feedback .= _('Insert Error')._(': ').$ao->getErrorMessage();
207 if (strstr(ARTIFACT_EXTRAFIELD_FILTER_INT,$field_type) !== false) {
209 // Must insert some default 100 rows for the data table so None queries will work right
211 $resdefault = db_query_params ('INSERT INTO artifact_extra_field_data(artifact_id,field_data,extra_field_id)
212 SELECT artifact_id,100,$1 FROM artifact WHERE group_artifact_id=$2',
214 $this->ArtifactType->getID())) ;
222 $this->setError(db_error());
229 * fetchData - re-fetch the data for this ArtifactExtraField from the database.
231 * @param int $id ID of the Box.
232 * @return boolean success.
234 function fetchData($id) {
236 $res = db_query_params ('SELECT * FROM artifact_extra_field_list WHERE extra_field_id=$1',
239 if (!$res || db_numrows($res) < 1) {
240 $this->setError(_('Invalid ArtifactExtraField ID'));
243 $this->data_array = db_fetch_array($res);
244 db_free_result($res);
249 * getArtifactType - get the ArtifactType Object this ArtifactExtraField is associated with.
251 * @return object ArtifactType.
253 function &getArtifactType() {
254 return $this->ArtifactType;
258 * getID - get this ArtifactExtraField ID.
260 * @return int The id #.
263 return $this->data_array['extra_field_id'];
267 * getName - get the name.
269 * @return string The name.
272 return $this->data_array['field_name'];
276 * getAttribute1 - get the attribute1 field.
278 * @return int The first attribute.
280 function getAttribute1() {
281 return $this->data_array['attribute1'];
285 * getAttribute2 - get the attribute2 field.
287 * @return int The second attribute.
289 function getAttribute2() {
290 return $this->data_array['attribute2'];
294 * getShow100 - get the show100 field.
296 * @return int The show100 attribute.
298 function getShow100() {
299 return $this->data_array['show100'];
303 * getShow100label - get the show100label field.
305 * @return int The show100label attribute.
307 function getShow100label() {
308 return $this->data_array['show100label'];
312 * getType - the type of field.
317 return $this->data_array['field_type'];
321 * getTypeName - the name of type of field.
323 * @return string type.
325 function getTypeName() {
326 $arr = $this->getAvailableTypes();
327 return $arr[$this->data_array['field_type']];
331 * isRequired - whether this field is required or not.
333 * @return boolean required.
335 function isRequired() {
336 return $this->data_array['is_required'];
340 * getAvailableTypes - the types of text fields and their names available.
342 * @return array types.
344 static function getAvailableTypes() {
346 ARTIFACT_EXTRAFIELDTYPE_SELECT => _('Select Box'),
347 ARTIFACT_EXTRAFIELDTYPE_CHECKBOX => _('Check Box'),
348 ARTIFACT_EXTRAFIELDTYPE_RADIO => _('Radio Buttons'),
349 ARTIFACT_EXTRAFIELDTYPE_TEXT => _('Text Field'),
350 ARTIFACT_EXTRAFIELDTYPE_MULTISELECT => _('Multi-Select Box'),
351 ARTIFACT_EXTRAFIELDTYPE_TEXTAREA => _('Text Area'),
352 ARTIFACT_EXTRAFIELDTYPE_STATUS => _('Status'),
353 ARTIFACT_EXTRAFIELDTYPE_RELATION => _('Relation between artifacts'),
354 ARTIFACT_EXTRAFIELDTYPE_INTEGER => _('Integer')
359 * getAlias - the alias that is used for this field
361 * @return string alias
363 function getAlias() {
364 return $this->data_array['alias'];
368 * getAvailableValues - Get the list of available values for this extra field
372 function getAvailableValues() {
373 $res = db_query_params ('SELECT * FROM artifact_extra_field_elements WHERE extra_field_id=$1',
374 array ($this->getID()));
376 while ($row = db_fetch_array($res)) {
383 * update - update a row in the table used to store box names
384 * for a tracker. This function is only to update rowsf
385 * for boxes configured by the admin.
387 * @param string $name Name of the field.
388 * @param int $attribute1 For text (size) and textarea (rows)
389 * @param int $attribute2 For text (maxlength) and textarea (cols)
390 * @param int $is_required True or false whether this is a required field or not.
391 * @param string $alias Alias for this field
392 * @param int $show100 True or false whether the 100 value is displayed or not
393 * @param string $show100label The label used for the 100 value if displayed
394 * @return bool success.
396 function update($name, $attribute1, $attribute2, $is_required = 0, $alias = "", $show100 = true, $show100label = 'none') {
397 if (!forge_check_perm ('tracker_admin', $this->ArtifactType->Group->getID())) {
398 $this->setPermissionDeniedError();
405 $this->setError(_('A field name is required'));
408 $res = db_query_params ('SELECT field_name FROM artifact_extra_field_list
409 WHERE field_name=$1 AND group_artifact_id=$2 AND extra_field_id !=$3',
411 $this->ArtifactType->getID(),
413 if (db_numrows($res) > 0) {
414 $this->setError(_('Field name already exists'));
423 if (!($alias = $this->generateAlias($alias,$name))) {
427 $result = db_query_params ('UPDATE artifact_extra_field_list
435 WHERE extra_field_id = $8
436 AND group_artifact_id = $9',
437 array (htmlspecialchars($name),
445 $this->ArtifactType->getID())) ;
446 if ($result && db_affected_rows($result) > 0) {
449 $this->setError(db_error());
458 function delete($sure, $really_sure) {
459 if (!$sure || !$really_sure) {
460 $this->setMissingParamsError(_('Please tick all checkboxes.'));
463 if (!forge_check_perm ('tracker_admin', $this->ArtifactType->Group->getID())) {
464 $this->setPermissionDeniedError();
468 $result = db_query_params ('DELETE FROM artifact_extra_field_data WHERE extra_field_id=$1',
469 array ($this->getID())) ;
471 $result = db_query_params ('DELETE FROM artifact_extra_field_elements WHERE extra_field_id=$1',
472 array ($this->getID())) ;
474 $result = db_query_params ('DELETE FROM artifact_extra_field_list WHERE extra_field_id=$1',
475 array ($this->getID())) ;
477 if ($this->getType() == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
478 if (!$this->ArtifactType->setCustomStatusField(0)) {
486 $this->setError(db_error());
491 $this->setError(db_error());
496 $this->setError(db_error());
505 * Note that this function does not check for conflicts.
507 * @param string alias - alias to validate
508 * @return bool true if alias is valid, false otherwise and it sets the corresponding error
510 function validateAlias($alias) {
511 // these are reserved alias names
512 static $reserved_alias = array(
524 if (strlen($alias) == 0) return true; // empty alias
527 if (preg_match("/[^[:alnum:]_@\\-]/", $alias)) {
528 $this->setError(_('The alias contains invalid characters. Only letters, numbers, hypens (-), at sign (@) and underscores (_) allowed.'));
530 } elseif (in_array($alias, $reserved_alias)) { // alias is reserved?
531 $this->setError(sprintf(_('ā%sā is a reserved alias. Please provide another name.'), $alias));
539 * Generate an alias for this field. The alias can be entered by the user or
540 * be generated automatically from the name of the field.
542 * @param string $alias Alias entered by the user
543 * @param string $name Name of the field entered by the user (it'll be used when $alias is empty)
546 function generateAlias($alias, $name) {
547 $alias = strtolower(trim($alias));
548 if (strlen($alias) == 0) { // no alias was entered, generate alias from $name
549 $name = strtolower(trim($name));
550 // Convert the original name to a valid alias (i.e., if the extra field is
551 // called "Quality test", make an alias called "quality_test").
552 // The alias can be seen as a "unix name" for this field
553 $alias = preg_replace("/ /", "_", $name);
554 $alias = preg_replace("/[^[:alnum:]_@]/", "", $alias);
555 $alias = strtolower($alias);
556 } elseif (!$this->validateAlias($alias)) {
557 // alias is invalid...
560 // check if the name conflicts with another alias in the same artifact type
561 // in that case append a serial number to the alias
565 if ($this->data_array['extra_field_id']) {
566 $res = db_query_params ('SELECT * FROM artifact_extra_field_list
567 WHERE LOWER (alias)=$1
568 AND group_artifact_id=$2
569 AND extra_field_id <> $3',
571 $this->ArtifactType->getID(),
572 $this->data_array['extra_field_id'])) ;
574 $res = db_query_params ('SELECT * FROM artifact_extra_field_list WHERE LOWER (alias)=$1 AND group_artifact_id=$2',
576 $this->ArtifactType->getID()));
579 $this->setError(db_error());
581 } elseif (db_numrows($res) > 0) { // found another field with the same alias
584 $alias = $alias.$serial;
590 // at this point, the alias is valid and unique
594 function updateOrder($element_id, $order) {
596 $result=db_query_params ('UPDATE artifact_extra_field_elements
598 WHERE element_id=$2',
601 if ($result && db_affected_rows($result) > 0) {
605 $this->setError(db_error());
610 function reorderValues($element_id, $new_pos) {
612 $res = db_query_params ('SELECT element_id FROM artifact_extra_field_elements WHERE extra_field_id=$1 ORDER BY element_pos ASC, element_id ASC',
613 array($this->getID()));
614 $max = db_numrows($res);
615 if ($new_pos < 1 || $new_pos > $max) {
616 $this->setError(_('Out of range value'));
622 if ($i == $new_pos) {
623 $data[] = $element_id;
626 if (($row = db_fetch_array($res)) && $row['element_id'] != $element_id) {
627 $data[] = $row['element_id'];
631 for ($i = 0; $i < count($data); $i++) {
632 if (! $this->updateOrder($data[$i], $i + 1))
639 function alphaorderValues() {
641 $res = db_query_params ('SELECT element_id FROM artifact_extra_field_elements WHERE extra_field_id=$1 ORDER BY element_name ASC',
642 array($this->getID()));
644 while ($row = db_fetch_array($res)) {
645 if (! $this->updateOrder($row['element_id'], $i))
657 // c-file-style: "bsd"