3 * FusionForge Generic Tracker facility
5 * Copyright 1999-2001 (c) VA Linux Systems - Sourceforge
6 * Copyright 2010 (c) Fusionforge Team
7 * Copyright 2011, Franck Villaume - Capgemini
8 * Copyright (C) 2011 Alain Peyrat - Alcatel-Lucent
9 * Copyright 2014,2016-2017, Franck Villaume - TrivialDev
10 * Copyright 2016-2017, Stéphane-Eymeric Bredthauer - TrivialDev
11 * http://fusionforge.org
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.'tracker/ArtifactType.class.php';
30 require_once $gfcommon.'tracker/ArtifactExtraField.class.php';
31 require_once $gfcommon.'tracker/ArtifactExtraFieldElement.class.php';
32 require_once $gfcommon.'tracker/ArtifactWorkflow.class.php';
33 require_once $gfcommon.'tracker/EffortUnitFactory.class.php';
34 require_once $gfcommon.'include/utils_crossref.php';
35 require_once $gfcommon.'include/UserManager.class.php';
36 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
38 class ArtifactTypeHtml extends ArtifactType {
40 function header($params) {
42 if (!forge_get_config('use_tracker')) {
46 $group_id = $this->Group->getID();
48 //required by new site_project_header
49 $params['group'] = $group_id;
50 $params['toptab'] = 'tracker';
51 $params['tabtext'] = $this->getName();
57 $labels[] = _("View Trackers");
58 $links[] = '/tracker/?group_id='.$group_id;
59 $attr[] = array('title' => _('Get the list of available trackers'));
60 $labels[] = $this->getName();
61 $links[] = '/tracker/?func=browse&group_id='.$group_id.'&atid='. $this->getID();
62 $attr[] = array('title' => _('Browse this tracker.'));
63 $labels[] = _('Export CSV');
64 $links[] = '/tracker/?func=csv&group_id='.$group_id.'&atid='. $this->getID();
65 $attr[] = array('title' => _('Download data from this tracker as csv file.'));
66 if (forge_check_perm('tracker', $this->getID(),'submit')) {
67 $labels[] = _('Submit New');
68 $links[] = '/tracker/?func=add&group_id='.$group_id.'&atid='. $this->getID();
69 $attr[] = array('title' => _('Add a new issue.'));
72 if (session_loggedin()) {
73 $labels[] = _('Reporting');
74 $links[] = '/tracker/reporting/?group_id='.$group_id.'&atid='. $this->getID();
75 $attr[] = array('title' => _('Various graph about statistics.'));
76 if ($this->isMonitoring()) {
77 $labels[] = _('Stop Monitor');
78 $links[] = '/tracker/?group_id='.$group_id.'&atid='. $this->getID().'&func=monitor&stopmonitor=1';
79 $attr[] = array('title' => _('Remove this tracker from your monitoring.'));
81 $labels[] = _('Monitor');
82 $links[] = '/tracker/?group_id='.$group_id.'&atid='. $this->getID().'&func=monitor&startmonitor=1';
83 $attr[] = array('title' => _('Add this tracker from your monitoring.'));
86 if (forge_check_perm('tracker_admin', $group_id)) {
87 $labels[] = _('Administration');
88 $links[] = '/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID();
89 $attr[] = array('title' => _('Global administration for trackers. Create, clone, workflow, fields ...'));
91 if (forge_get_config('use_tracker_widget_display')) {
92 $sql = "SELECT l.* FROM layouts AS l INNER JOIN owner_layouts AS o ON(l.id = o.layout_id)
93 WHERE o.owner_type = $1
95 AND o.is_default = 1";
96 $res = db_query_params($sql,array('t', $this->getID()));
97 if($res && db_numrows($res) < 1) {
98 $lm = new WidgetLayoutManager();
99 $lm->createDefaultLayoutForTracker($this->getID());
100 $res = db_query_params($sql,array('t', $this->getID()));
102 $id = db_result($res, 0 , 'id');
103 $url = '/widgets/widgets.php?owner=t'.$this->getID().'&layout_id='.$id;
104 $labels[] = _('Add widgets');
105 $labels[] = _('Customize Layout');
106 $attr[] = array('title' => _('Customfields must be linked to a widget to be displayed. Use “Add widgets” to create new widget to link and organize your customfields.'));
107 $attr[] = array('title' => _('General layout to display “Submit New” form or detailed view of an existing artifact can be customize. Use “Customize Layout” to that purpose.'));
109 $links[] = $url.'&update=layout';
114 $params['submenu'] = $HTML->subMenu($labels, $links, $attr);
115 site_project_header($params);
118 plugin_hook("blocks", "tracker_".$this->getName());
122 function footer($params = array()) {
123 site_project_footer($params);
126 function adminHeader($params) {
128 $this->header($params);
129 $group_id= $this->Group->getID();
130 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id, _('New Tracker'), array('title'=>_('Create a new tracker.'))));
131 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&update_type=1', _('Update Settings'), array('title'=>_('Set up preferences like expiration times, email addresses.'))));
132 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&effort_units=1', _('Manage Effort Units'), array('title'=>_('Manage Effort Units for Effort custom extra field.'))));
133 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&add_extrafield=1', _('Manage Custom Fields'), array('title'=>_('Add new boxes like Phases, Quality Metrics, Components, etc. Once added they can be used with other selection boxes (for example, Categories or Groups) to describe and browse bugs or other artifact types.'))));
134 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&workflow=1', _('Manage Workflow'), array('title'=>_('Edit tracker workflow.'))));
135 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&customize_list=1', _('Customize List'), array('title'=>_('Customize display for the tracker.'))));
136 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&add_canned=1', _('Manage Canned Responses'), array('title'=>_('Create/change generic response messages for the tracker.'))));
137 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&clone_tracker=1', _('Apply Template Tracker'), array('title'=>_('Duplicate parameters and fields from a template trackers in this one.'))));
138 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&delete=1', _('Delete'), array('title'=>_('Permanently delete this tracker.'))));
139 echo $HTML->html_list($elementLi);
142 function adminFooter($params) {
143 $this->footer($params);
146 function renderSubmitInstructions() {
147 $msg = $this->getSubmitInstructions();
148 return str_replace("\n","<br />", $msg);
151 function renderBrowseInstructions() {
152 $msg = $this->getBrowseInstructions();
153 return str_replace("\n","<br />", $msg);
157 * renderExtraFields - ???
159 * @param array $selected
160 * @param bool $show_100 Display the specific '100' value. Default is false.
161 * @param string $text_100 Label displayed for the '100' value. Default is 'none'
162 * @param bool $show_any
163 * @param string $text_any
164 * @param array $types
165 * @param bool $status_show_100 Force display of the '100' value if needed. Default is false.
166 * @param string $mode QUERY, DISPLAY, UPDATE, NEW
168 function renderExtraFields($selected = array(),
169 $show_100 = false, $text_100 = 'none',
170 $show_any = false, $text_any = 'Any',
172 $status_show_100 = false,
177 if ($mode == 'NEW') {
178 $efarr = $this->getExtraFields($types, false, false);
180 $efarr = $this->getExtraFields($types);
182 //each two columns, we'll reset this and start a new row
183 $template = $this->getRenderHTML($types, $mode);
185 if ($mode=='QUERY') {
186 $keys=array_keys($efarr);
187 for ($k=0; $k<count($keys); $k++) {
189 $type = $efarr[$i]['field_type'];
190 if ($type == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
191 $type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
192 $type == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
193 $type == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
194 $type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
195 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTISELECT;
196 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_USER ||
197 $type == ARTIFACT_EXTRAFIELDTYPE_MULTIUSER) {
198 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTIUSER;
199 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_RELEASE ||
200 $type == ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE) {
201 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE;
202 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_DATETIME ||
203 $type == ARTIFACT_EXTRAFIELDTYPE_DATE) {
204 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_DATERANGE;
205 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
206 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_EFFORTRANGE;
208 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_TEXT;
213 // 'DISPLAY' mode is for rendering in 'read-only' mode (for detail view).
214 if ($mode === 'DISPLAY') {
215 $keys=array_keys($efarr);
216 for ($k=0; $k<count($keys); $k++) {
220 if (!isset($selected[$efarr[$i]['extra_field_id']]))
221 $selected[$efarr[$i]['extra_field_id']] = '';
223 $value = $selected[$efarr[$i]['extra_field_id']];
224 $type = $efarr[$i]['field_type'];
226 if ($type == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
227 $type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
228 $type == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
229 $type == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
230 $type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
232 $value = $efarr[$i]['show100label'];
234 $arr = $this->getExtraFieldElements($efarr[$i]['extra_field_id']);
236 // Convert the values (ids) to names in the ids order.
238 for ($j=0; $j<count($arr); $j++) {
239 if (is_array($value) && in_array($arr[$j]['element_id'], $value)) {
240 $new[]= $arr[$j]['element_name'];
241 } elseif ($arr[$j]['element_id'] === $value) {
242 $new[] = $arr[$j]['element_name'];
245 $value = join('<br />', $new);
247 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
248 $type == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
249 $value = preg_replace('/((http|https|ftp):\/\/\S+)/',
250 "<a href=\"\\1\" target=\"_blank\">\\1</a>", $value);
251 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_RELATION || $type == ARTIFACT_EXTRAFIELDTYPE_PARENT) {
252 // Convert artifact id to links.
253 $value = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $value);
254 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_DATETIME && $value!='') {
255 $value = date('Y-m-d H:i', $value);
256 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
257 if (!isset($effortUnitSet)) {
258 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
259 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
261 $value = $effortUnitFactory->encodedToString($value);
263 $template = str_replace('{$PostName:'.$efarr[$i]['field_name'].'}', $post_name, $template);
264 $template = str_replace('{$'.$efarr[$i]['field_name'].'}', $value, $template);
269 if ($mode == 'UPDATE' || $mode == 'NEW') {
270 if ($mode == 'NEW') {
271 $efInFormula = $this->getExtraFieldsInFormula($types, false, false);
272 $efWithFormula = $this->getExtraFieldsWithFormula($types, false, false);
274 $efInFormula = $this->getExtraFieldsInFormula($types);
275 $efWithFormula = $this->getExtraFieldsWithFormula($types);
279 $keys = array_keys($efarr);
280 for ($k = 0; $k < count($keys); $k++) {
285 if (!empty($efarr[$i]['description'])) {
286 $attrs['title'] = $efarr[$i]['description'];
288 if ($efarr[$i]['is_required'] == 1 && $mode != 'QUERY') {
289 $attrs['required'] = 'required';
292 if ($mode == 'UPDATE' || $mode == 'NEW') {
293 if (in_array($efarr[$i]['extra_field_id'], $efInFormula)) {
294 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'in-formula';
296 if (in_array($efarr[$i]['extra_field_id'], $efWithFormula)) {
297 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-formula readonly';
298 $attrs['readonly'] = 'readonly';
302 if (!isset($selected[$efarr[$i]['extra_field_id']]))
303 $selected[$efarr[$i]['extra_field_id']] = '';
305 if ($status_show_100) {
306 $efarr[$i]['show100'] = $status_show_100;
311 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
312 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
313 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
314 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
315 if (!is_null($efarr[$i]['parent']) && !empty($efarr[$i]['parent']) && $efarr[$i]['parent']!='100') {
316 $aefParentId = $efarr[$i]['parent'];
317 $selectedElmnts = (isset($selected[$aefParentId]) ? $selected[$aefParentId] : '');
318 $aef = new ArtifactExtraField($this, $efarr[$i]['extra_field_id']);
319 $allowed = $aef->getAllowedValues($selectedElmnts);
323 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT) {
324 $str = $this->renderSelect($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $show_any, $text_any, $allowed, $attrs);
326 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) {
328 $str = $this->renderCheckbox($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $allowed, $attrs);
330 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO) {
332 $str = $this->renderRadio($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $show_any, $text_any, $allowed, $attrs);
334 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
335 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER) {
337 if (!empty($efarr[$i]['pattern'])) {
338 $attrs['pattern'] = $efarr[$i]['pattern'];
340 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT) {
341 $str = $this->renderTextField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
343 $str = $this->renderIntegerField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
345 if ($mode == 'QUERY') {
346 $post_name = ' <i>'._('(%% for wildcards)').'</i> ';
349 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
351 $str = $this->renderTextArea($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
352 if ($mode == 'QUERY') {
353 $post_name = ' <i>'._('(%% for wildcards)').'</i>';
356 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
358 $str = $this->renderMultiSelectBox($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $allowed, $attrs);
360 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
361 // parent artifact can't be close if a child is still open
362 if ($mode == 'UPDATE' &&
363 $efarr[$i]['aggregation_rule'] == ARTIFACT_EXTRAFIELD_AGGREGATION_RULE_STATUS_CLOSE_RESTRICTED &&
364 $ah->hasChildren()) {
365 $children = $ah->getChildren();
367 foreach ($children as $child) {
368 if ($child['status_id'] == 1) {
374 $aef = new ArtifactExtraField($this, $efarr[$i]['extra_field_id']);
375 $statusArr = $aef->getAvailableValues();
376 $openStatus = array();
377 foreach ($statusArr as $status) {
378 if ($status['status_id']==1) {
379 $openStatus[] = $status['element_id'];
383 $allowed = array_intersect($allowed, $openStatus);
385 $allowed = $openStatus;
390 // Get the allowed values from the workflow.
391 $atw = new ArtifactWorkflow($this, $efarr[$i]['extra_field_id']);
393 // Special treatment for the initial step (Submit).
394 // In this case, the initial value is the first value.
395 if (isset($selected[$efarr[$i]['extra_field_id']]) && $selected[$efarr[$i]['extra_field_id']]) {
396 $selected_node = $selected[$efarr[$i]['extra_field_id']];
398 $selected_node = 100;
402 $allowedWF = $atw->getNextNodes($selected_node);
404 $allowed = array_intersect($allowed, $allowedWF);
406 $allowed = $allowedWF;
408 $allowed[] = $selected_node;
409 $str = $this->renderSelect($efarr[$i]['extra_field_id'], $selected_node, $status_show_100, $text_100, $show_any, $text_any, $allowed, $attrs);
411 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
412 $str = $this->renderRelationField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
413 if ($mode == 'UPDATE' || $mode == 'NEW') {
414 $post_name = $HTML->getEditFieldPic(_('Click to edit'), $alt = _('Click to edit'), array('onclick'=>"switch2edit(this, 'show$i', 'edit$i')"));
416 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_PARENT) {
417 $str = $this->renderParentField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
418 if ($mode == 'UPDATE' || $mode == 'NEW') {
419 $post_name = $HTML->getEditFieldPic(_('Click to edit'), $alt = _('Click to edit'), array('onclick'=>"switch2edit(this, 'show$i', 'edit$i')"));
421 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME) {
422 $str = $this->renderDatetime($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $attrs);
423 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER) {
424 $str = $this->renderUserField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $show_any, $text_any,false, $attrs);
425 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTIUSER) {
426 $str = $this->renderMultiUserField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $show_any, $text_any,false, $attrs);
427 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE) {
428 $str = $this->renderReleaseField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $show_any, $text_any,false, $attrs);
429 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE) {
430 $str = $this->renderMultiReleaseField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $show_any, $text_any,false, $attrs);
431 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATERANGE) {
432 if ($mode == 'QUERY') {
433 $post_name = ' <i>'._('(YYYY-MM-DD YYYY-MM-DD Format)').'</i>';
435 $str = $this->renderDateRange($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $attrs);
436 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
437 $str = $this->renderEffort($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
438 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_EFFORTRANGE) {
439 $str = $this->renderEffortRange($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
441 $template = str_replace('{$PostName:'.$efarr[$i]['field_name'].'}', $post_name, $template);
442 $template = str_replace('{$'.$efarr[$i]['field_name'].'}', $str, $template);
444 if($template != NULL){
445 if ($mode == 'UPDATE' || $mode == 'NEW' || $mode == 'QUERY') {
446 echo $this->javascript();
452 function renderRelatedTasks($group, $ah, $formid = null) {
454 if (!$group->usesPM()) {
460 $taskcount = db_numrows($ah->getRelatedTasks());
461 db_result_reset($ah->getRelatedTasks());
463 if (forge_check_perm('tracker_admin', $ah->ArtifactType->Group->getID())) {
468 $totalPercentage = 0;
469 if ($taskcount > 0) {
470 $title_arr = array();
471 $title_arr[] = _('Task Id and Summary');
472 $title_arr[] = _('Progress');
473 $title_arr[] = _('Start Date');
474 $title_arr[] = _('End Date');
475 $title_arr[] = _('Status');
476 (($is_admin) ? $title_arr[]=_('Remove Relation') : '');
477 $return .= $HTML->listTableTop($title_arr);
479 for ($i = 0; $i < $taskcount; $i++) {
480 $taskinfo = db_fetch_array_by_row($ah->getRelatedTasks(), $i);
481 $totalPercentage += $taskinfo['percent_complete'];
482 $taskid = $taskinfo['project_task_id'];
483 $projectid = $taskinfo['group_project_id'];
484 $groupid = $taskinfo['group_id'];
485 if (forge_check_perm('pm', $projectid, 'read')) {
486 $summary = util_unconvert_htmlspecialchars($taskinfo['summary']);
487 $startdate = date(_('Y-m-d H:i'), $taskinfo['start_date']);
488 $enddate = date(_('Y-m-d H:i'), $taskinfo['end_date']);
489 $status = $taskinfo['status_name'];
491 <td>'.util_make_link('/pm/t_follow.php/'.$taskid, '[T'.$taskid.'] '.$summary).'</td>
492 <td><div class="percentbar" style="width: 100px;">
493 <div style="width:'.round($taskinfo['percent_complete']).'px;"></div></div></td>
494 <td>'.$startdate.'</td>
495 <td>'.$enddate.'</td>
496 <td>'.$status.' ('.$taskinfo['percent_complete'].'%)</td>'.
497 (($is_admin) ? '<td><input type="checkbox" '.(($formid) ? 'form="'.$formid.'"' : '').' name="remlink[]" value="'.$taskid.'" /></td>' : '').
501 $return .= $HTML->listTableBottom();
503 $return .= "\n<hr /><p style=\"text-align:right;\">";
504 $return .= _('Average completion rate')._(': ').(int)($totalPercentage/$taskcount).'%';
507 $return .= $HTML->information(_('No related tasks'));
512 function renderFiles($group_id, $ah) {
514 $file_list =& $ah->getFiles();
515 $count=count($file_list);
519 $return .= '<strong>'._("Attachments")._(':').'</strong>'.'<br/>';
521 $title_arr[] = _('Size');
522 $title_arr[] = _('Name');
523 $title_arr[] = _('Date');
524 $title_arr[] = _('By');
525 $title_arr[] = _('Download');
526 if (forge_check_perm('tracker', $this->getID(), 'tech')) {
529 $return .= $HTML->listTableTop($title_arr);
531 foreach ($file_list as $file) {
533 $return .= '<td>'.human_readable_bytes($file->getSize()).'</td>';
534 $return .= '<td>'.htmlspecialchars($file->getName()).'</td>';
535 $return .= '<td>'.date(_('Y-m-d H:i'), $file->getDate()).'</td>';
536 $return .= '<td>'.util_display_user($file->getSubmittedUnixName(), $file->getSubmittedBy(), $file->getSubmittedRealName()).'</td>';
537 $return .= '<td>'.util_make_link('/tracker/download.php/'.$group_id.'/'. $this->getID().'/'. $ah->getID() .'/'.$file->getID().'/'.$file->getName(), htmlspecialchars($file->getName())).'</td>';
538 if (forge_check_perm('tracker', $this->getID(), 'tech')) {
539 $return .= '<td><input type="checkbox" name="delete_file[]" value="'. $file->getID() .'">'._('Delete').'</td>';
544 $return .= $HTML->listTableBottom();
546 $return .= $HTML->information(_('No attached documents'));
554 * @param array $types
555 * @param string $mode
556 * @return string HTML template.
558 function getRenderHTML($types=array(), $mode='') {
559 // Use template only for the browse (not for query or mass update)
560 if (($mode === 'DISPLAY' || $mode === 'DETAIL' || $mode === 'UPDATE' || $mode == 'NEW')
561 && $this->data_array['custom_renderer']) {
562 return preg_replace('/<!--(\S+.*?)-->/','{$\\1}', $this->data_array['custom_renderer']);
564 return $this->generateRenderHTML($types, $mode);
571 * @param array $types
572 * @param string $mode Display mode (QUERY OR DISPLAY OR NEW)
573 * @return string HTML template.
575 function generateRenderHTML($types=array(), $mode) {
576 if ($mode == 'NEW') {
577 $efarr = $this->getExtraFields($types, false, false);
579 $efarr = $this->getExtraFields($types);
581 //each two columns, we'll reset this and start a new row
584 <!-- Start Extra Fields Rendering -->
588 $keys=array_keys($efarr);
590 if ($count == 0) return '';
592 for ($k=0; $k<$count; $k++) {
595 // Do not show the required star in query mode (creating/updating a query).
596 $is_required = ($mode == 'QUERY' || $mode == 'DISPLAY') ? 0 : $efarr[$i]['is_required'];
597 if ($mode == 'QUERY' && ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME || $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATE)) {
598 $name = sprintf(_('%s range'), $efarr[$i]['field_name']).($is_required ? utils_requiredField() : '')._(': ');
600 $name = $efarr[$i]['field_name'].($is_required ? utils_requiredField() : '')._(': ');
602 $name = '<strong>'.$name.'</strong>';
604 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
605 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
606 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
607 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT ||
608 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
609 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER ||
610 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE ) {
613 <td class="halfwidth top">'.$name.'<br />{$'.$efarr[$i]['field_name'].'}</td>';
615 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
616 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER ||
617 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION ||
618 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_PARENT ||
619 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME ||
620 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
622 //text fields might be really wide, so need a row to themselves.
623 if (($col_count == 1) && ($efarr[$i]['attribute1'] > 30)) {
633 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
635 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
637 //text areas might be really wide, so need a row to themselves.
638 if (($col_count == 1) && ($efarr[$i]['attribute2'] > 30)) {
648 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
653 //we've done two columns - if there are more to do, start a new row
654 if (($col_count == 2) && ($k != $count-1)) {
661 if ($col_count == 1) {
667 <!-- End Extra Fields Rendering -->';
672 * renderSelect - this function builds pop up box with choices.
674 * @param int $extra_field_id The ID of this field.
675 * @param string $checked The item that should be checked
676 * @param bool|string $show_100 Whether to show the '100 row'
677 * @param string $text_100 What to call the '100 row'
678 * @param bool $show_any
679 * @param string $text_any
680 * @param bool $allowed
681 * @param array $attrs
682 * @return string HTML code for the box and choices
684 function renderSelect($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
685 if ($text_100 == 'none'){
688 $arr = $this->getExtraFieldElements($extra_field_id);
689 $aef = new ArtifactExtraField($this, $extra_field_id);
690 $aefChildren = $aef->getChildren();
691 if (!empty($aefChildren)) {
692 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
694 if ($aef->isAutoAssign()) {
695 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'autoassign';
699 $opt_attrs = array();
700 $attrs_100 = array();
702 for ($i=0; $i<count($arr); $i++) {
703 $vals[$i]=$arr[$i]['element_id'];
704 $texts[$i]=$arr[$i]['element_name'];
705 $opt_attrs[$i]=array();
706 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
707 if (!empty($aefChildren)) {
708 $cElmntArr = $aefe->getChildrenElements();
709 if (!empty($cElmntArr))
712 foreach ($cElmntArr as $key=>$cElmnt) {
713 $childField = new ArtifactExtraField($this, $key);
714 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
716 $dependency = '['.$dependency.']';
717 $opt_attrs[$i]= array('data-dependency'=>$dependency);
720 if ($aef->isAutoAssign()) {
721 $autoAssignTo = UserManager::instance()->getUserById($aefe->getAutoAssignto());
722 $opt_attrs[$i]=array_merge(isset($opt_attrs[$i]) ? $opt_attrs[$i] : array(), array('data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
726 if ($show_100 && !empty($aefChildren)) {
727 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
729 return html_build_select_box_from_arrays($vals, $texts,'extra_fields['.$extra_field_id.']', $checked, $show_100, $text_100, $show_any, $text_any, $allowed, $attrs, $opt_attrs, $attrs_100);
733 * renderUserField - this function builds pop up box with users.
735 * @param int $extra_field_id The ID of this field.
736 * @param string $checked The item that should be checked
737 * @param bool|string $show_100 Whether to show the '100 row'
738 * @param string $text_100 What to call the '100 row'
739 * @param bool $show_any
740 * @param string $text_any
741 * @param bool $allowed
742 * @param array $attrs
743 * @return string HTML code for the box and choices
745 function renderUserField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
746 if ($text_100 == 'none' || $text_100 == 'nobody'){
747 $text_100=_('Nobody');
749 $arr = $this->getExtraFieldElements($extra_field_id);
750 $selectedRolesId = array();
751 for ($i=0; $i<count($arr); $i++) {
752 $selectedRolesId[$i]=$arr[$i]['element_name'];
754 $roles = $this->getGroup()->getRoles();
755 $userArray = array();
756 foreach ($roles as $role) {
757 if (in_array($role->getID(), $selectedRolesId)) {
758 foreach ($role->getUsers() as $user) {
759 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
763 if (is_integer($checked) && !isset($userArray[$checked])) {
764 $checkedUser = user_get_object($checked);
765 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
767 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
768 $keys = array_keys($userArray);
769 $vals = array_values($userArray);
770 return html_build_select_box_from_arrays($keys, $vals,'extra_fields['.$extra_field_id.']', $checked, $show_100, $text_100, $show_any, $text_any, $allowed, $attrs);
774 * renderMultiUserField - this function builds pop up box with users.
776 * @param int $extra_field_id The ID of this field.
777 * @param string $checked The item that should be checked
778 * @param bool|string $show_100 Whether to show the '100 row'
779 * @param string $text_100 What to call the '100 row'
780 * @param bool $show_any
781 * @param string $text_any
782 * @param bool $allowed
783 * @param array $attrs
784 * @return string HTML code for the box and choices
786 function renderMultiUserField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
787 if ($text_100 == 'none' || $text_100 == 'nobody'){
788 $text_100=_('Nobody');
794 if (!is_array($checked)) {
795 $checked = explode(',', $checked);
798 $arr = $this->getExtraFieldElements($extra_field_id);
799 $selectedRolesId = array();
800 for ($i=0; $i<count($arr); $i++) {
801 $selectedRolesId[$i]=$arr[$i]['element_name'];
803 $roles = $this->getGroup()->getRoles();
804 $userArray = array();
805 foreach ($roles as $role) {
806 if (in_array($role->getID(), $selectedRolesId)) {
807 foreach ($role->getUsers() as $user) {
808 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
812 if (is_integer($checked) && !isset($userArray[$checked])) {
813 $checkedUser = user_get_object($checked);
814 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
816 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
817 $size = min(count($userArray)+1, 15);
818 $keys = array_keys($userArray);
819 $vals = array_values($userArray);
821 return html_build_multiple_select_box_from_arrays($keys, $vals,'extra_fields['.$extra_field_id.'][]', $checked, $size, $show_100, $text_100, $allowed, $attrs);
825 * renderReleaseField - this function builds 2 pop up boxes with packages & releases.
827 * @param int $extra_field_id The ID of this field.
828 * @param string $checked The item that should be checked
829 * @param bool|string $show_100 Whether to show the '100 row'
830 * @param string $text_100 What to call the '100 row'
831 * @param bool $show_any
832 * @param string $text_any
833 * @param bool $allowed
834 * @param array $attrs
835 * @return string HTML code for the box and choices
837 function renderReleaseField($extra_field_id, $checked = 'xzxz', $show_100 = false, $text_100 = 'none', $show_any = false, $text_any = 'Any', $allowed = false, $attrs = array()) {
838 if ($text_100 == 'none'){
842 $releasesArray = array();
843 $releasesAttrs = array();
846 $arr = $this->getExtraFieldElements($extra_field_id);
847 $selectedPackagesId = array();
848 for ($i=0; $i<count($arr); $i++) {
849 $selectedPackagesId[$i]=$arr[$i]['element_name'];
852 $fpFactory = new FRSPackageFactory($this->getGroup());
853 $packages = $fpFactory->getFRSs(true);
854 uasort($packages, 'compareObjectName');
855 foreach ($packages as $package) {
856 $releases = $package->getReleases(false);
857 uasort($releases, 'compareObjectName');
858 foreach ($releases as $release) {
859 $optGroup[] = $package->getName();
860 $releasesArray[$release->getID()] = $release->getName();
864 $keys = array_keys($releasesArray);
865 $vals = array_values($releasesArray);
866 return html_build_select_box_from_arrays($keys, $vals, 'extra_fields['.$extra_field_id.']', $checked, $show_100, $text_100, $show_any, $text_any, $allowed, $attrs, $releasesAttrs, array(), $optGroup);
870 * renderMultiReleaseField - this function builds 2 pop up boxes with packages & releases.
872 * @param int $extra_field_id The ID of this field.
873 * @param string $checked The item that should be checked
874 * @param bool|string $show_100 Whether to show the '100 row'
875 * @param string $text_100 What to call the '100 row'
876 * @param bool $show_any
877 * @param string $text_any
878 * @param bool $allowed
879 * @param array $attrs
880 * @return string HTML code for the box and choices
882 function renderMultiReleaseField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs=array()) {
883 if ($text_100 == 'none'){
889 if (!is_array($checked)) {
890 $checked = explode(',', $checked);
893 $releasesArray = array();
894 $releasesAttrs = array();
897 $arr = $this->getExtraFieldElements($extra_field_id);
898 $selectedPackagesId = array();
899 for ($i=0; $i<count($arr); $i++) {
900 $selectedPackagesId[$i]=$arr[$i]['element_name'];
903 $packages = get_frs_packages($this->getGroup());
904 uasort($packages, 'compareObjectName');
905 foreach ($packages as $package) {
906 if (in_array($package->getID(), $selectedPackagesId)) {
907 $releases = $package->getReleases();
908 uasort($releases, 'compareObjectName');
909 foreach ($releases as $release) {
910 $optGroup[] = $package->getName();
911 $releasesArray[$release->getID()] = $release->getName();
915 $size = min(count($releasesArray) + count($optGroup) + 1, 15);
916 $keys = array_keys($releasesArray);
917 $vals = array_values($releasesArray);
918 return html_build_multiple_select_box_from_arrays($keys, $vals,'extra_fields['.$extra_field_id.'][]', $checked, $size, $show_100, $text_100, $allowed, $attrs, $releasesAttrs, array(), $optGroup);
922 * renderRadio - this function builds radio buttons.
924 * @param int $extra_field_id The $int ID of this field.
925 * @param string $checked The $string item that should be checked
926 * @param bool $show_100 Whether $string to show the '100 row'
927 * @param string $text_100 What $string to call the '100 row'
928 * @param bool $show_any
929 * @param string $text_any
930 * @param array $attrs Array of other attributes
931 * @return string HTML code using radio buttons
933 function renderRadio($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed = false, $attrs = array()) {
935 $arr = $this->getExtraFieldElements($extra_field_id);
936 $aef = new ArtifactExtraField($this, $extra_field_id);
937 $aefChildren = $aef->getChildren();
938 if (!empty($aefChildren)) {
939 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
944 $radios_attrs = array();
945 $attrs_100 = array();
947 for ($i=0; $i<count($arr); $i++) {
948 $vals[$i]=$arr[$i]['element_id'];
949 $texts[$i]=$arr[$i]['element_name'];
950 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
952 if (!empty($aefChildren)) {
953 $cElmntArr = $aefe->getChildrenElements();
954 if (!empty($cElmntArr))
956 foreach ($cElmntArr as $key=>$cElmnt) {
957 $childField = new ArtifactExtraField($this, $key);
958 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
960 $dependency = '['.$dependency.']';
961 $radios_attrs[$i]['data-dependency']=$dependency;
964 if ($aef->isAutoAssign()) {
965 $autoAssignTo = UserManager::instance()->getUserById($aefe->getAutoAssignto());
966 $radios_attrs[$i]=array_merge(isset($radios_attrs[$i]) ? $radios_attrs[$i] : array(), array('data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
969 if ($show_100 && !empty($aefChildren)) {
970 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
972 return html_build_radio_buttons_from_arrays($vals, $texts,'extra_fields['.$extra_field_id.']', $checked, $show_100, $text_100, $show_any, $text_any, $allowed, $attrs, $radios_attrs, $attrs_100);
976 * renderCheckbox - this function builds checkboxes.
978 * @param int $extra_field_id The ID of this field.
979 * @param array $checked The items that should be checked
980 * @param bool|string $show_100 Whether to show the '100 row'
981 * @param string $text_100 What to call the '100 row'
982 * @param array $attrs Array of other attributes
983 * @return string radio buttons
985 function renderCheckbox($extra_field_id, $checked=array(), $show_100=false, $text_100='none', $allowed=false, $attrs = array()) {
986 if ($text_100 == 'none'){
990 if (!$checked || !is_array($checked)) {
993 if (!empty($attrs['title'])) {
994 $attrs['title'] = util_html_secure($attrs['title']);
997 $arr = $this->getExtraFieldElements($extra_field_id);
998 $aef = new ArtifactExtraField($this, $extra_field_id);
999 $aefChildren = $aef->getChildren();
1000 if (!empty($aefChildren)) {
1001 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
1006 $chk_attrs = array();
1007 $attrs_100 = array();
1009 for ($i=0; $i<count($arr); $i++) {
1010 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
1011 $vals[$i] = $arr[$i]['element_id'];
1012 $texts[$i] = $arr[$i]['element_name'];
1013 $chk_attrs[$i] = array();
1015 if (!empty($aefChildren)) {
1016 $cElmntArr = $aefe->getChildrenElements();
1017 if (!empty($cElmntArr))
1019 foreach ($cElmntArr as $key=>$cElmnt) {
1020 $childField = new ArtifactExtraField($this, $key);
1021 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1023 $dependency = '['.$dependency.']';
1024 $chk_attrs[$i]['data-dependency']=$dependency;
1028 if ($show_100 && !empty($aefChildren)) {
1029 $attrs_100['data-dependency'] ='{"fields": ['.implode(', ', $aefChildren).']}';
1031 return html_build_checkboxes_from_arrays($vals, $texts,'extra_fields['.$extra_field_id.']', $checked,false, $show_100, $text_100, $allowed, $attrs, $chk_attrs, $attrs_100);
1035 * renderMultiSelectBox - this function builds checkboxes.
1037 * @param int $extra_field_id The ID of this field.
1038 * @param array $checked The items that should be checked
1039 * @param bool|string $show_100 Whether to show the '100 row'
1040 * @param string $text_100 What to call the '100 row'
1041 * @param array $attrs Array of other attributes
1042 * @return string radio multiselectbox
1044 function renderMultiSelectBox($extra_field_id, $checked=array(), $show_100=false, $text_100='none', $allowed=false, $attrs=array()) {
1048 if (!is_array($checked)) {
1049 $checked = explode(',', $checked);
1051 $arr = $this->getExtraFieldElements($extra_field_id);
1052 $aef = new ArtifactExtraField($this, $extra_field_id);
1053 $aefChildren = $aef->getChildren();
1054 if (!empty($aefChildren)) {
1055 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
1059 $opt_attrs = array();
1060 $attrs_100 = array();
1062 for ($i=0; $i<count($arr); $i++) {
1063 $vals[$i]=$arr[$i]['element_id'];
1064 $texts[$i]=$arr[$i]['element_name'];
1065 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
1066 if (!empty($aefChildren)) {
1067 $cElmntArr = $aefe->getChildrenElements();
1068 if (!empty($cElmntArr))
1071 foreach ($cElmntArr as $key=>$cElmnt) {
1072 $childField = new ArtifactExtraField($this, $key);
1073 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1075 $dependency = '['.$dependency.']';
1076 $opt_attrs[$i]= array('data-dependency'=>$dependency);
1080 $size = min(count($arr)+1, 15);
1081 if ($show_100 && !empty($aefChildren)) {
1082 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
1084 return html_build_multiple_select_box_from_arrays($vals, $texts,"extra_fields[$extra_field_id][]", $checked, $size, $show_100, $text_100, $allowed, $attrs, $opt_attrs, $attrs_100);
1088 * renderTextField - this function builds a text field.
1090 * @param int $extra_field_id The ID of this field.
1091 * @param string $contents The data for this field.
1092 * @param string $size
1093 * @param string $maxlength
1094 * @param array $attrs Array of other attributes
1095 * @return string HTML code of corresponding input tag.
1097 function renderTextField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1098 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength), $attrs));
1102 * renderIntegerField - this function builds a text field.
1104 * @param int $extra_field_id The ID of this field.
1105 * @param string $contents The data for this field.
1106 * @param string $size
1107 * @param string $maxlength
1108 * @param array $attrs Array of other attributes
1109 * @return string HTML code of corresponding input tag.
1111 function renderIntegerField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1112 $intAttrs = array('type'=>'number', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0);
1113 $newattrs = array_merge($intAttrs, $attrs);
1114 return html_e('input', $newattrs);
1118 * renderRelationField - this function builds a relation field.
1120 * @param int $extra_field_id The ID of this field.
1121 * @param string $contents The data for this field.
1122 * @param string $size
1123 * @param string $maxlength
1124 * @param array $attrs Array of other attributes
1125 * @return string text area and data.
1127 function renderRelationField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1128 $arr = $this->getExtraFieldElements($extra_field_id);
1129 for ($i=0; $i<count($arr); $i++) {
1130 $keys[$i]=$arr[$i]['element_id'];
1131 $vals[$i]=$arr[$i]['element_name'];
1133 // Convert artifact id to links.
1134 $attrsTxt = array();
1135 if (isset($attrs['form'])) {
1136 $attrsTxt['form'] = $attrs['form'];
1138 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1139 $edit_contents = $this->renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrsTxt);
1140 return html_e('div',array_merge(array('id'=>'edit'.$extra_field_id, 'style'=>'display: none', 'title'=>_('Tip: Enter a space-separated list of artifact ids ([#NNN] also accepted)')), $attrs), $edit_contents)
1141 .html_e('div',array_merge(array('id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1145 * renderParentField - this function builds a parent field.
1147 * @param int $extra_field_id The ID of this field.
1148 * @param string $contents The data for this field.
1149 * @param string $size
1150 * @param string $maxlength
1151 * @param array $attrs Array of other attributes
1152 * @return string text area and data.
1154 function renderParentField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1156 $arr = $this->getExtraFieldElements($extra_field_id);
1157 for ($i=0; $i<count($arr); $i++) {
1158 $keys[$i]=$arr[$i]['element_id'];
1159 $vals[$i]=$arr[$i]['element_name'];
1161 $attrsTxt = array();
1162 if (isset($attrs['form'])) {
1163 $attrsTxt['form'] = $attrs['form'];
1165 if (is_object($ah)) {
1166 $attrsTxt['pattern']='^(?!'.$ah->getID().'$)\d*$';
1168 $attrsTxt['pattern']='^\d*$';
1170 // Convert artifact id to links.
1171 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1172 $edit_contents = $this->renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrsTxt);
1173 return html_e('div',array_merge(array('id'=>'edit'.$extra_field_id, 'style'=>'display: none', 'title'=>_('Tip: Enter a space-separated list of artifact ids ([#NNN] also accepted)')), $attrs), $edit_contents)
1174 .html_e('div',array_merge(array('id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1178 * renderTextArea - this function builds a text area.
1180 * @param int $extra_field_id The ID of this field.
1181 * @param string $contents The data for this field.
1182 * @param string $rows
1183 * @param string $cols
1184 * @param array $attrs Array of other attributes
1185 * @return string text area and data.
1187 function renderTextArea($extra_field_id, $contents, $rows, $cols, $attrs = array()) {
1188 return html_e('textarea', array_merge(array('name'=>'extra_fields['.$extra_field_id.']', 'rows'=>$rows, 'cols'=>$cols), $attrs), $contents, false);
1192 * renderDatetime - this function builds a Datetime field.
1194 * @param int $extra_field_id The ID of this field.
1195 * @param string $datetime datetime for this field.
1196 * @param array $attrs Array of other attributes
1197 * @return string datetime.
1199 function renderDatetime($extra_field_id, $datetime, $attrs = array()) {
1200 if (!$datetime=='') {
1201 $datetime_format = _('Y-m-d H:i');
1202 $datetime = date($datetime_format, $datetime);
1204 if (isset($attrs['class'])) {
1205 $attrs['class'] = $attrs['class'] . ' datetimepicker';
1207 $attrs['class'] = 'datetimepicker';
1209 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$datetime), $attrs));
1212 function renderDateRange($extra_field_id, $dateRange, $attrs = array()) {
1213 // http://html5pattern.com/Dates
1214 // Date with leapyear-check
1215 $datepattern = '(?:19|20)(?:(?:[13579][26]|[02468][048])-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))|(?:[0-9]{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:29|30))|(?:(?:0[13578]|1[02])-31)))';
1216 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'pattern'=>$datepattern.' '.$datepattern, 'maxlength'=>21, 'size'=>21, 'value'=>$dateRange), $attrs));
1220 * renderEffort - this function builds a Effort extra field.
1222 * @param int $extra_field_id The ID of this field.
1223 * @param string $contents The data for this field.
1224 * @param string $size
1225 * @param string $maxlength
1226 * @param array $attrs Array of other attributes
1227 * @return string text area and data.
1229 function renderEffort($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1230 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1231 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
1232 $units = $effortUnitFactory->getUnits();
1236 foreach ($units as $unit) {
1237 $vals [] = $unit->getID();
1238 $texts [] = $unit->getName();
1239 $opts_attrs []['data-factor'] = $unit->getConversionFactorForBaseUnit();
1241 $valueInUnitBase = $effortUnitFactory->encodedToValueInBaseUnit($contents);
1242 $value = $effortUnitFactory->encodedToValue($contents);
1243 $unitId = $effortUnitFactory->encodedToUnitId($contents);
1244 if (isset($attrs['class'])) {
1245 $attrs['class'] .= ' effort';
1247 $attrs['class'] = 'effort';
1249 $attrs['data-effortid'] = $extra_field_id;
1250 $hiddenAttrs = array();
1251 if (isset($attrs['form'])) {
1252 $hiddenAttrs['form'] = $attrs['form'];
1254 $return = html_e('input', array_merge(array('type'=>'hidden', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$valueInUnitBase.'U'.$unitId), $hiddenAttrs));
1255 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value['.$extra_field_id.']', 'value'=>$value, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrs));
1256 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit['.$extra_field_id.']', $unitId, false, '', false, '', false, $attrs, $opts_attrs);
1260 function renderEffortRange($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1261 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1262 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
1263 $units = $effortUnitFactory->getUnits();
1266 $opts_attrs = array();
1267 foreach ($units as $unit) {
1268 $vals [] = $unit->getID();
1269 $texts [] = $unit->getName();
1270 $opts_attrs []['data-factor'] = $unit->getConversionFactorForBaseUnit();
1272 $contentsFrom = '0U'.$vals[0];
1273 $contentsTo = '0U'.$vals[0];
1274 if (preg_match('/^(\d+U\d+) ?(\d+U\d+)?$/', $contents, $matches)) {
1275 $contentsFrom = $matches[1];
1276 $contentsTo = $matches[2];
1278 $valueInUnitBaseFrom = $effortUnitFactory->encodedToValueInBaseUnit($contentsFrom);
1279 $valueInUnitBaseTo = $effortUnitFactory->encodedToValueInBaseUnit($contentsTo);
1280 $valueFrom = $effortUnitFactory->encodedToValue($contentsFrom);
1281 $valueTo = $effortUnitFactory->encodedToValue($contentsTo);
1282 $unitIdFrom = $effortUnitFactory->encodedToUnitId($contentsFrom);
1283 $unitIdTo = $effortUnitFactory->encodedToUnitId($contentsTo);
1284 $attrs['data-effortid'] = $extra_field_id;
1285 if (isset($attrs['class'])) {
1286 $attrs['class'] .= ' effort-range';
1288 $attrs['class'] = 'effort-range';
1290 $attrsFrom = $attrs;
1292 $hiddenAttrs = array();
1293 if (isset($attrs['form'])) {
1294 $hiddenAttrs['form'] = $attrs['form'];
1296 $return = html_e('input', array_merge(array('type'=>'hidden', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$valueInUnitBaseFrom.'U'.$unitIdFrom.' '.$valueInUnitBaseTo.'U'.$unitIdTo), $hiddenAttrs));
1297 $return .= _('Between')._(':').html_e('br');
1298 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value_from['.$extra_field_id.']', 'value'=>$valueFrom, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrsFrom));
1299 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit_from['.$extra_field_id.']', $unitIdFrom, false, '', false, '', false, $attrsFrom, $opts_attrs);
1300 $return .= html_e('br');
1301 $return .= _('and')._(':').html_e('br');
1302 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value_to['.$extra_field_id.']', 'value'=>$valueTo, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrsTo));
1303 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit_to['.$extra_field_id.']', $unitIdTo, false, '', false, '', false, $attrsTo, $opts_attrs);
1308 * renderFormulaField - this function builds a formula field (RO).
1310 * @param string $contents The data for this field.
1313 function renderFormulaField($contents) {
1318 * renderSLAField - this function builds a formula field (RO).
1320 * @param int $extra_field_id The ID of this field.
1321 * @param string $contents The data for this field.
1324 function renderSLAField($extra_field_id, $contents, $ef) {
1327 if (!isset($aid) ||!$aid) {
1331 $config = parse_ini_string($ef["attributes"]);
1332 $field_name = $config['field'];
1333 $rule = explode(':', $config['values']);
1335 if (!isset($config['start_id']) || $aid >= $config['start_id']) {
1336 // Get Max value from
1337 // @todo use global variable $aid !!!
1338 $artifact = artifact_get_object($aid);
1339 $value = $artifact->getFieldDataByKey('alias', $field_name);
1340 $field = $artifact->getFieldTypeByKey('alias', $field_name);
1341 $elements = $artifact->ArtifactType->getExtraFieldElements($field['extra_field_id']);
1342 for ($i = 0; $i < count($elements); $i++) {
1343 if ($elements[$i]['element_name'] == $value) {
1344 $max = 60 * $rule[$i];
1350 $value = '<span class="label label-warning">Not Set</span>';
1351 } elseif ($ef['alias'] == 'response_time') {
1352 // Implement Response SLA: Time from create to first comment.
1353 $messages = $artifact->getMessageObjects();
1354 if (count($messages) > 0) {
1355 $message = $messages[count($messages) - 1];
1356 $current_time = $message->getAddDate();
1357 $timer_is_running = false;
1358 } elseif ($artifact->getStatusID() == 2) {
1359 $current_time = $artifact->getCloseDate();
1360 $timer_is_running = false;
1362 $current_time = time();
1363 $timer_is_running = true;
1365 $time = $current_time - $artifact->getOpenDate();
1366 $percent = round(100 * $time / $max);
1367 $remaining = $max - $time;
1368 if ($remaining >= 0) {
1369 $value = gmdate("G\h i", $remaining);
1371 $value = '<b>-' . gmdate("G\h i", -$remaining) . "</b>";
1374 if (!$timer_is_running) {
1375 if ($remaining >= 0) {
1376 $value = '<span class="label label-success">' . _('OK') . '</span>';
1378 $value = '<span class="label label-danger">' . _('Missed') . '</span>';
1381 if ($percent > 90) {
1382 $value = '<span class="sla-alert">' . $value . '</span>';
1383 } elseif ($percent > 80) {
1384 $value = '<span class="sla-warning">' . $value . '</span>';
1386 $value = '<span class="sla-normal">' . $value . '</span>';
1389 } elseif ($ef['alias'] == 'resolution_time') {
1390 // Implement Resolution SLA: Time from create to closed state.
1391 list($last_status_change, $timer, $timer_is_running) = json_decode($contents);
1394 if ($timer_is_running) {
1395 $time += time() - $last_status_change;
1398 $percent = round(100 * $time / $max);
1399 $remaining = $max - $time;
1400 if ($remaining >= 0) {
1401 $value = gmdate("G\h i", $remaining);
1403 $value = '<b>-' . gmdate("G\h i", -$remaining) . "</b>";
1405 if ($artifact->getStatusID() == 2) {
1406 if ($remaining >= 0) {
1407 $value = '<span class="label label-success">' . _('OK') . '</span>';
1409 $value = '<span class="label label-danger">' . _('Missed') . '</span>';
1412 if (!$timer_is_running) {
1413 $value = ' <span class="label label-info">' . _('Suspended') . '</span>';
1414 } elseif ($percent > 90) {
1415 $value = '<span class="sla-alert">' . $value . '</span>';
1416 } elseif ($percent > 80) {
1417 $value = '<span class="sla-warning">' . $value . '</span>';
1419 $value = '<span class="sla-normal">' . $value . '</span>';
1424 $value = '<span class="label label-default">' . _('Not Available') . '</span>';
1429 function technicianBox($name = 'assigned_to[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false, $attrs = array()) {
1430 if ($text_100=='none'){
1431 $text_100=_('Nobody');
1433 $engine = RBACEngine::getInstance();
1434 $techs = $engine->getUsersByAllowedAction('tracker', $this->getID(), 'tech') ;
1439 sortUserList($techs);
1441 foreach ($techs as $tech) {
1442 $ids[] = $tech->getID() ;
1443 $names[] = $tech->getRealName() ;
1446 if ($extra_id != '-1') {
1448 $names[]=$extra_name;
1452 if (!is_array($checked)) {
1453 $checked = explode(',', $checked);
1455 $size = min(count($ids)+1, 15);
1456 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100, false, $attrs);
1458 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100, false, false, false, $attrs);
1462 function submitterBox($name = 'submitted_by[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false, $attrs = array()) {
1463 if ($text_100=='none'){
1464 $text_100=_('Nobody');
1466 $result = $this->getSubmitters();
1467 $ids =& util_result_column_to_array($result,0);
1468 $names =& util_result_column_to_array($result,1);
1469 if ($extra_id != '-1') {
1471 $names[]=$extra_name;
1475 if (!is_array($checked)) {
1476 $checked = explode(',', $checked);
1478 $size = min(count($ids)+1, 15);
1479 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100, false, $attrs);
1481 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100, false, $attrs);
1485 function lastModifierBox($name='last_modified_by[]', $checked='xzxz', $show_100=true, $text_100='none', $extra_id='-1', $extra_name='', $multiple=false) {
1486 if ($text_100=='none'){
1487 $text_100=_('Nobody');
1489 $result = $this->getLastModifiers();
1490 $ids =& util_result_column_to_array($result,0);
1491 $names =& util_result_column_to_array($result,1);
1492 if ($extra_id != '-1') {
1494 $names[]=$extra_name;
1498 if (!is_array($checked)) {
1499 $checked = explode(',', $checked);
1501 $size = min(count($ids)+1, 15);
1502 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100);
1504 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100);
1508 function cannedResponseBox($name = 'canned_response', $checked = 'xzxz', $attrs = array()) {
1509 if (!isset($attrs['id'])) {
1510 $attrs['id'] = $name;
1512 return html_build_select_box($this->getCannedResponses(), $name, $checked, true, 'none', false, '', false, $attrs);
1516 * statusBox - show the statuses - automatically shows the "custom statuses" if they exist
1518 * @return string HTML code
1520 function statusBox($name = 'status_id', $checked = 'xzxz', $show_100 = false, $text_100 = 'none', $attrs = array()) {
1521 if ($text_100=='none'){
1522 $text_100=_('None');
1524 return html_build_select_box($this->getStatuses(), $name, $checked, $show_100, $text_100, false, '', false, $attrs);
1528 * priorityBox - show the priorities
1530 * @return string HTML code
1532 function priorityBox($name = 'priority', $checked_val = '3', $nochange = false, $attrs = array(), $show_any = false){
1533 return html_build_priority_select_box($name, $checked_val, $nochange, $attrs, $show_any);
1536 function javascript() {
1538 var invalidSelectMsg = '"._("One or more of the selected options is not allowed")."';
1539 var invalidInputMsg = '". _("This choice is not allowed")."';
1540 var groupId =".$this->Group->getID().";
1541 var atId = ".$this->getID().";";
1542 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1543 if ($effortUnitSet->isAutoconvert()) {
1544 $jseffort = <<<'EOS'
1545 $("select.effort").change(function(){
1546 var effortid = $(this).data("effortid");
1547 var value = parseInt(parseInt($("input[name='extra_fields["+effortid+"]']").val())/$("select[name='unit["+effortid+"]'] option:selected").data('factor'));
1548 $("input[name='value["+effortid+"]']").val(value);
1549 $("input[name='extra_fields["+effortid+"]']").val(value*$("select[name='unit["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit["+effortid+"]']").val());
1551 $("input.effort").change(function(){
1552 var effortid = $(this).data("effortid");
1553 var value = $("input[name='value["+effortid+"]']").val();
1554 $("input[name='extra_fields["+effortid+"]']").val(value*$("select[name='unit["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit["+effortid+"]']").val());
1556 $(".effort-range").change(function(){
1557 var effortid = $(this).data("effortid");
1558 $("input[name='extra_fields["+effortid+"]']").val($("input[name='value_from["+effortid+"]']").val()*$("select[name='unit_from["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit_from["+effortid+"]']").val()+' '+$("input[name='value_to["+effortid+"]']").val()*$("select[name='unit_to["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit_to["+effortid+"]']").val());
1562 $jseffort = <<<'EOS'
1563 $(".effort").change(function(){
1564 var effortid = $(this).data("effortid");
1565 $("input[name='extra_fields["+effortid+"]']").val($("input[name='value["+effortid+"]']").val()*$("select[name='unit["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit["+effortid+"]']").val());
1567 $(".effort-range").change(function(){
1568 var effortid = $(this).data("effortid");
1569 $("input[name='extra_fields["+effortid+"]']").val($("input[name='value_from["+effortid+"]']").val()*$("select[name='unit_from["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit_from["+effortid+"]']").val()+' '+$("input[name='value_to["+effortid+"]']").val()*$("select[name='unit_to["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit_to["+effortid+"]']").val());
1573 $javascript = <<<'EOS'
1574 function showMessage( msg_text, msg_class) {
1575 $("#maindiv h1").append($("<p>", { "class": msg_class }).html( msg_text )).show();
1577 $.expr[':'].invalid = function(elem, index, match) {
1578 var invalids = document.querySelectorAll(':invalid'), result = false, len = invalids.length;
1580 for (var i=0; i<len; i++) {
1581 if (elem === invalids[i]) {
1590 $("input[type='radio'].readonly, input[type='checkbox'].readonly").on('click', function(){
1592 }).on('keydown', function(event){
1593 if(event.keyCode !== 9) return false;
1595 $(".in-formula").change(function(){
1599 data: 'rtype=ajax&function=get_formulas_results&group_id='+groupId+'&atid='+atId+'&status='+$("select[name='status_id'] option:selected").text()+'&assigned_to='+$("select[name='assigned_to'] option:selected").text()+'&'+$("[name^='extra_fields'], #tracker-summary, #tracker-description, [name='priority']").serialize(),
1602 success: function(answer){
1603 if(answer['message']) {
1604 showMessage(answer['message'], 'error');
1606 fields = answer['fields'];
1607 $.each(fields, function (index, field) {
1608 if (field.error!=null){
1609 showMessage(field.error, 'error');
1611 fieldObj = $("[name^='extra_fields["+field.id+"]']");
1612 if (fieldObj.is("input[type='checkbox']")){
1613 fieldObj.each(function() {
1615 for (var key in field.value) {
1616 if (field.value[key] == $(this).val()) {
1621 if (in_array > -1) {
1622 $(this).prop("checked",true);
1624 $(this).prop("checked",false);
1627 } else if (fieldObj.is("input[type='radio']")){
1628 fieldObj.each(function() {
1630 for (var key in field.value) {
1631 if (field.value[key] == $(this).val()) {
1636 if (in_array > -1) {
1637 $(this).prop("checked",true);
1639 $(this).prop("checked",false);
1642 } else if (fieldObj.is("input")){
1643 fieldObj.val(field.value);
1644 } else if (fieldObj.is("select")){
1645 fieldObj.val(field.value);
1646 } else if (fieldObj.is("textarea")){
1647 fieldObj.val(field.value);
1655 $("img.addparent").click(function(){
1659 data: 'rtype=ajax&function=add_parent&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&parent_id='+$("input#parent_id").val(),
1662 success: function(answer){
1663 if(answer['message']) {
1664 showMessage(answer['message'], 'error');
1666 $("table.parent").replaceWith(answer['parent']);
1667 $("input[name='extra_fields["+answer['parent_efid']+"]']").val(answer['parent_id']);
1668 $("div#show"+answer['parent_efid']).html(answer['parent_link']);
1669 $("input#parent_id").val('');
1670 $("div.addparent").addClass('hide');
1671 $("img.removeparent").click(function(){
1672 removeParent($(this).data("id"));
1680 $("img.addchild").click(function(){
1684 data: 'rtype=ajax&function=add_child&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&child_id='+$("input#child_id").val(),
1687 success: function(answer){
1688 if(answer['message']) {
1689 showMessage(answer['message'], 'error');
1691 $("table.children").replaceWith(answer['children']);
1692 $("input#child_id").val('');
1693 $("img.removechild").click(function(){
1694 removeChild($(this).data("id"));
1703 $("img.removeparent").click(function(){
1704 removeParent($(this).data("id"));
1707 function removeParent(id) {
1711 data: 'rtype=ajax&function=remove_parent&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&parent_id='+id,
1714 success: function(answer){
1715 if(answer['message']) {
1716 showMessage(answer['message'], 'error');
1718 $("table#parent"+id).remove();
1719 $("div.addparent").removeClass('hide');
1726 $("img.removechild").click(function(){
1727 removeChild($(this).data("id"));
1730 function removeChild(id) {
1734 data: 'rtype=ajax&function=remove_child&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&child_id='+id,
1737 success: function(answer){
1738 if(answer['message']) {
1739 showMessage(answer['message'], 'error');
1741 $("span#child"+id).remove();
1747 $(".autoassign[name^='extra_fields']").change(function(){
1748 if ($(this).prop('tagName') == 'SELECT') {
1749 var elmnts = $(this).children('option:selected');
1751 var elmnts = $(this).siblings('input:checked');
1753 elmnts.each(function(i){
1754 var aat = $(this).data("autoassignto");
1755 $("select#tracker-assigned_to option[value="+aat.id+"]").prop('selected', true);
1756 $("span#tracker-assigned_to").text(aat.name);
1759 $(".with-depcy[name^='extra_fields']").change(function(){
1760 if ($(this).prop('tagName') == 'SELECT') {
1761 var elmnts = $(this).children('option:selected');
1763 var elmnts = $(this).siblings('input:checked');
1765 elmnts.each(function(i){
1766 var dep = $(this).data("dependency");
1767 if (this.value!='100') {
1768 $(dep).each(function(j, val) {
1769 $("select[name^='extra_fields["+val.field+"]']:invalid, input[name^='extra_fields["+val.field+"]']:invalid").each(function() {
1770 this.setCustomValidity("");
1771 $(this).off("change.invalid");
1773 $("select[name^='extra_fields["+val.field+"]'] option").each(function(k,opt){
1774 if (this.value!='100') {
1775 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1776 $(this).prop('disabled', false).removeClass('option_disabled');
1778 $(this).prop('disabled', true);
1779 $(this).addClass('option_disabled');
1783 $("input[name^='extra_fields["+val.field+"]']").each(function(k,opt){
1784 if (this.value!='100') {
1785 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1786 $(this).prop('disabled', false).removeClass($(this).attr('type')+'_disabled');
1788 $(this).prop('disabled', true);
1789 $(this).addClass($(this).attr('type')+'_disabled');
1795 $(dep.fields).each(function(j, val) {
1796 $("select[name^='extra_fields["+val+"]']:invalid, input[name^='extra_fields["+val+"]']:invalid").each(function() {
1797 this.setCustomValidity("");
1799 $("select[name^='extra_fields["+val+"]'] option.option_disabled").each(function() {
1800 $(this).prop('disabled', false).removeClass('option_disabled');
1802 $("input.radio_disable[name^='extra_fields["+val+"]']").each(function() {
1803 $(this).prop('disabled', false).removeClass('radio_disabled');
1805 $("input.checkbox_disabled[name^='extra_fields["+val+"]']").each(function() {
1806 $(this).prop('disabled', false).removeClass('checkbox_disabled');
1811 $("select[name^='extra_fields'] option:selected:disabled").parent().each(function() {
1812 $(this).children('option:selected:disabled').prop('disabled', false);
1813 this.setCustomValidity(invalidSelectMsg);
1814 $(this).on("change.invalid", function() {
1815 $(this).children('option.option_disabled:not(:disabled):not(:selected)').prop('disabled', true);
1816 if (!$(this).children('option.option_disabled:selected').length) {
1817 this.setCustomValidity("");
1818 $(this).off("change.invalid");
1822 $("input[name^='extra_fields']:checked:disabled").each(function() {
1823 $(this).prop('disabled', false);
1824 this.setCustomValidity(invalidInputMsg);
1825 if ($(this).attr('type') == 'radio') {
1826 $(this).siblings('input[type="radio"]').on("change.invalid", function() {
1827 $(this).siblings('input[type="radio"]:invalid').prop('disabled', true).addClass('input_disabled').each(function() {
1828 this.setCustomValidity("");
1830 $(this).siblings('input[type="radio"]').off("change.invalid");
1831 $(this).off("change.invalid");
1834 $(this).on("change.invalid", function() {
1835 $(this).prop('disabled', true);
1836 this.setCustomValidity("");
1837 $(this).off("change.invalid");
1843 return html_e('script', array('type'=>'text/javascript'), '//<![CDATA['."\n".'$(function(){'.$jsvariable."\n".$jseffort."\n".$javascript.'});'."\n".'//]]>');
1850 // c-file-style: "bsd"