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,2021, 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);
440 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SLA) {
441 $str = $this->renderSLAField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $attrs);
443 $template = str_replace('{$PostName:'.$efarr[$i]['field_name'].'}', $post_name, $template);
444 $template = str_replace('{$'.$efarr[$i]['field_name'].'}', $str, $template);
446 if($template != NULL){
447 if ($mode == 'UPDATE' || $mode == 'NEW' || $mode == 'QUERY') {
448 echo $this->javascript();
454 function renderRelatedTasks($group, $ah, $formid = null) {
456 if (!$group->usesPM()) {
462 $tasks = $ah->getRelatedTasks();
463 $taskcount = db_numrows($tasks);
465 if (forge_check_perm('tracker_admin', $ah->ArtifactType->Group->getID())) {
470 $totalPercentage = 0;
471 if ($taskcount > 0) {
472 $title_arr = array();
473 $title_arr[] = _('Task Id and Summary');
474 $title_arr[] = _('Progress');
475 $title_arr[] = _('Start Date');
476 $title_arr[] = _('End Date');
477 $title_arr[] = _('Status');
478 (($is_admin) ? $title_arr[]=_('Remove Relation') : '');
479 $return .= $HTML->listTableTop($title_arr);
481 while ($taskinfo = db_fetch_array($tasks)) {
482 $totalPercentage += $taskinfo['percent_complete'];
483 $taskid = $taskinfo['project_task_id'];
484 $projectid = $taskinfo['group_project_id'];
485 $groupid = $taskinfo['group_id'];
486 if (forge_check_perm('pm', $projectid, 'read')) {
487 $summary = util_unconvert_htmlspecialchars($taskinfo['summary']);
488 $startdate = date(_('Y-m-d H:i'), $taskinfo['start_date']);
489 $enddate = date(_('Y-m-d H:i'), $taskinfo['end_date']);
490 $status = $taskinfo['status_name'];
492 <td>'.util_make_link('/pm/t_follow.php/'.$taskid, '[T'.$taskid.'] '.$summary).'</td>
493 <td><div class="percentbar" style="width: 100px;">
494 <div style="width:'.round($taskinfo['percent_complete']).'px;"></div></div></td>
495 <td>'.$startdate.'</td>
496 <td>'.$enddate.'</td>
497 <td>'.$status.' ('.$taskinfo['percent_complete'].'%)</td>'.
498 (($is_admin) ? '<td><input type="checkbox" '.(($formid) ? 'form="'.$formid.'"' : '').' name="remlink[]" value="'.$taskid.'" /></td>' : '').
502 $return .= $HTML->listTableBottom();
504 $return .= "\n<hr /><p style=\"text-align:right;\">";
505 $return .= _('Average completion rate')._(': ').(int)($totalPercentage/$taskcount).'%';
508 $return .= $HTML->information(_('No related tasks'));
513 function renderFiles($group_id, $ah, $formid = null) {
515 $file_list =& $ah->getFiles();
516 $count=count($file_list);
520 $return .= '<strong>'._("Attachments")._(':').'</strong>'.'<br/>';
522 $title_arr[] = _('Size');
523 $title_arr[] = _('Name');
524 $title_arr[] = _('Date');
525 $title_arr[] = _('By');
526 $title_arr[] = _('Download');
527 if (forge_check_perm('tracker', $this->getID(), 'tech')) {
530 $return .= $HTML->listTableTop($title_arr);
532 foreach ($file_list as $file) {
534 $return .= '<td>'.human_readable_bytes($file->getSize()).'</td>';
535 $return .= '<td>'.htmlspecialchars($file->getName()).'</td>';
536 $return .= '<td>'.date(_('Y-m-d H:i'), $file->getDate()).'</td>';
537 $return .= '<td>'.util_display_user($file->getSubmittedUnixName(), $file->getSubmittedBy(), $file->getSubmittedRealName()).'</td>';
538 $return .= '<td>'.util_make_link('/tracker/download.php/'.$group_id.'/'. $this->getID().'/'. $ah->getID() .'/'.$file->getID().'/'.$file->getName(), htmlspecialchars($file->getName())).'</td>';
539 if (forge_check_perm('tracker', $this->getID(), 'tech')) {
540 $return .= '<td><input type="checkbox" name="delete_file[]" '.(($formid) ? 'form="'.$formid.'"' : '').' value="'. $file->getID() .'">'._('Delete').'</td>';
545 $return .= $HTML->listTableBottom();
547 $return .= $HTML->information(_('No attached documents'));
555 * @param array $types
556 * @param string $mode
557 * @return string HTML template.
559 function getRenderHTML($types=array(), $mode='') {
560 // Use template only for the browse (not for query or mass update)
561 if (($mode === 'DISPLAY' || $mode === 'DETAIL' || $mode === 'UPDATE' || $mode == 'NEW')
562 && $this->data_array['custom_renderer']) {
563 return preg_replace('/<!--(\S+.*?)-->/','{$\\1}', $this->data_array['custom_renderer']);
565 return $this->generateRenderHTML($types, $mode);
572 * @param array $types
573 * @param string $mode Display mode (QUERY OR DISPLAY OR NEW)
574 * @return string HTML template.
576 function generateRenderHTML($types, $mode) {
577 if ($mode == 'NEW') {
578 $efarr = $this->getExtraFields($types, false, false);
580 $efarr = $this->getExtraFields($types);
582 //each two columns, we'll reset this and start a new row
585 <!-- Start Extra Fields Rendering -->
589 $keys=array_keys($efarr);
594 for ($k=0; $k<$count; $k++) {
597 // Do not show the required star in query mode (creating/updating a query).
598 $is_required = ($mode == 'QUERY' || $mode == 'DISPLAY') ? 0 : $efarr[$i]['is_required'];
599 if ($mode == 'QUERY' && ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME || $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATE)) {
600 $name = sprintf(_('%s range'), $efarr[$i]['field_name']).($is_required ? utils_requiredField() : '')._(': ');
602 $name = $efarr[$i]['field_name'].($is_required ? utils_requiredField() : '')._(': ');
604 $name = '<strong>'.$name.'</strong>';
606 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
607 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
608 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
609 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT ||
610 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
611 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER ||
612 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE ) {
615 <td class="halfwidth top">'.$name.'<br />{$'.$efarr[$i]['field_name'].'}</td>';
617 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
618 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER ||
619 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION ||
620 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_PARENT ||
621 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME ||
622 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
624 //text fields might be really wide, so need a row to themselves.
625 if (($col_count == 1) && ($efarr[$i]['attribute1'] > 30)) {
635 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
637 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
639 //text areas might be really wide, so need a row to themselves.
640 if (($col_count == 1) && ($efarr[$i]['attribute2'] > 30)) {
650 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
655 //we've done two columns - if there are more to do, start a new row
656 if (($col_count == 2) && ($k != $count-1)) {
663 if ($col_count == 1) {
669 <!-- End Extra Fields Rendering -->';
674 * renderSelect - this function builds pop up box with choices.
676 * @param int $extra_field_id The ID of this field.
677 * @param string $checked The item that should be checked
678 * @param bool|string $show_100 Whether to show the '100 row'
679 * @param string $text_100 What to call the '100 row'
680 * @param bool $show_any
681 * @param string $text_any
682 * @param bool $allowed
683 * @param array $attrs
684 * @return string HTML code for the box and choices
686 function renderSelect($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
687 if ($text_100 == 'none'){
690 $arr = $this->getExtraFieldElements($extra_field_id);
691 $aef = new ArtifactExtraField($this, $extra_field_id);
692 $aefChildren = $aef->getChildren();
693 if (!empty($aefChildren)) {
694 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
696 if ($aef->isAutoAssign()) {
697 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'autoassign';
701 $opt_attrs = array();
702 $attrs_100 = array();
704 for ($i=0; $i<count($arr); $i++) {
705 $vals[$i]=$arr[$i]['element_id'];
706 $texts[$i]=$arr[$i]['element_name'];
707 $opt_attrs[$i]=array();
708 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
709 if (!empty($aefChildren)) {
710 $cElmntArr = $aefe->getChildrenElements();
711 if (!empty($cElmntArr))
714 foreach ($cElmntArr as $key=>$cElmnt) {
715 $childField = new ArtifactExtraField($this, $key);
716 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
718 $dependency = '['.$dependency.']';
719 $opt_attrs[$i]= array('data-dependency'=>$dependency);
722 if ($aef->isAutoAssign()) {
723 $autoAssignTo = UserManager::instance()->getUserById($aefe->getAutoAssignto());
724 $opt_attrs[$i]=array_merge(isset($opt_attrs[$i]) ? $opt_attrs[$i] : array(), array('data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
728 if ($show_100 && !empty($aefChildren)) {
729 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
731 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);
735 * renderUserField - this function builds pop up box with users.
737 * @param int $extra_field_id The ID of this field.
738 * @param string $checked The item that should be checked
739 * @param bool|string $show_100 Whether to show the '100 row'
740 * @param string $text_100 What to call the '100 row'
741 * @param bool $show_any
742 * @param string $text_any
743 * @param bool $allowed
744 * @param array $attrs
745 * @return string HTML code for the box and choices
747 function renderUserField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
748 if ($text_100 == 'none' || $text_100 == 'nobody'){
749 $text_100=_('Nobody');
751 $arr = $this->getExtraFieldElements($extra_field_id);
752 $selectedRolesId = array();
753 for ($i=0; $i<count($arr); $i++) {
754 $selectedRolesId[$i]=$arr[$i]['element_name'];
756 $roles = $this->getGroup()->getRoles();
757 $userArray = array();
758 foreach ($roles as $role) {
759 if (in_array($role->getID(), $selectedRolesId)) {
760 foreach ($role->getUsers() as $user) {
761 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
765 if (is_integer($checked) && !isset($userArray[$checked])) {
766 $checkedUser = user_get_object($checked);
767 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
769 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
770 $keys = array_keys($userArray);
771 $vals = array_values($userArray);
772 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);
776 * renderMultiUserField - this function builds pop up box with users.
778 * @param int $extra_field_id The ID of this field.
779 * @param string $checked The item that should be checked
780 * @param bool|string $show_100 Whether to show the '100 row'
781 * @param string $text_100 What to call the '100 row'
782 * @param bool $show_any
783 * @param string $text_any
784 * @param bool $allowed
785 * @param array $attrs
786 * @return string HTML code for the box and choices
788 function renderMultiUserField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
789 if ($text_100 == 'none' || $text_100 == 'nobody'){
790 $text_100=_('Nobody');
796 if (!is_array($checked)) {
797 $checked = explode(',', $checked);
800 $arr = $this->getExtraFieldElements($extra_field_id);
801 $selectedRolesId = array();
802 for ($i=0; $i<count($arr); $i++) {
803 $selectedRolesId[$i]=$arr[$i]['element_name'];
805 $roles = $this->getGroup()->getRoles();
806 $userArray = array();
807 foreach ($roles as $role) {
808 if (in_array($role->getID(), $selectedRolesId)) {
809 foreach ($role->getUsers() as $user) {
810 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
814 if (is_integer($checked) && !isset($userArray[$checked])) {
815 $checkedUser = user_get_object($checked);
816 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
818 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
819 $size = min(count($userArray)+1, 15);
820 $keys = array_keys($userArray);
821 $vals = array_values($userArray);
823 return html_build_multiple_select_box_from_arrays($keys, $vals,'extra_fields['.$extra_field_id.'][]', $checked, $size, $show_100, $text_100, $allowed, $attrs);
827 * renderReleaseField - this function builds 2 pop up boxes with packages & releases.
829 * @param int $extra_field_id The ID of this field.
830 * @param string $checked The item that should be checked
831 * @param bool|string $show_100 Whether to show the '100 row'
832 * @param string $text_100 What to call the '100 row'
833 * @param bool $show_any
834 * @param string $text_any
835 * @param bool $allowed
836 * @param array $attrs
837 * @return string HTML code for the box and choices
839 function renderReleaseField($extra_field_id, $checked = 'xzxz', $show_100 = false, $text_100 = 'none', $show_any = false, $text_any = 'Any', $allowed = false, $attrs = array()) {
840 if ($text_100 == 'none'){
844 $releasesArray = array();
845 $releasesAttrs = array();
848 $arr = $this->getExtraFieldElements($extra_field_id);
849 $selectedPackagesId = array();
850 for ($i=0; $i<count($arr); $i++) {
851 $selectedPackagesId[$i]=$arr[$i]['element_name'];
854 $fpFactory = new FRSPackageFactory($this->getGroup());
855 if ($fpFactory && is_object($fpFactory) && !$fpFactory->isError()) {
856 $packages = $fpFactory->getFRSs(true);
857 uasort($packages, 'compareObjectName');
858 foreach ($packages as $package) {
859 $releases = $package->getReleases(false);
860 uasort($releases, 'compareObjectName');
861 foreach ($releases as $release) {
862 $optGroup[] = $package->getName();
863 $releasesArray[$release->getID()] = $release->getName();
867 $keys = array_keys($releasesArray);
868 $vals = array_values($releasesArray);
869 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);
872 return $HTML->error_msg(_('Unable to get FRS Package Factory'));
877 * renderMultiReleaseField - this function builds 2 pop up boxes with packages & releases.
879 * @param int $extra_field_id The ID of this field.
880 * @param string $checked The item that should be checked
881 * @param bool|string $show_100 Whether to show the '100 row'
882 * @param string $text_100 What to call the '100 row'
883 * @param bool $show_any
884 * @param string $text_any
885 * @param bool $allowed
886 * @param array $attrs
887 * @return string HTML code for the box and choices
889 function renderMultiReleaseField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs=array()) {
890 if ($text_100 == 'none'){
896 if (!is_array($checked)) {
897 $checked = explode(',', $checked);
900 $releasesArray = array();
901 $releasesAttrs = array();
904 $arr = $this->getExtraFieldElements($extra_field_id);
905 $selectedPackagesId = array();
906 for ($i=0; $i<count($arr); $i++) {
907 $selectedPackagesId[$i]=$arr[$i]['element_name'];
910 $packages = get_frs_packages($this->getGroup());
911 uasort($packages, 'compareObjectName');
912 foreach ($packages as $package) {
913 if (in_array($package->getID(), $selectedPackagesId)) {
914 $releases = $package->getReleases();
915 uasort($releases, 'compareObjectName');
916 foreach ($releases as $release) {
917 $optGroup[] = $package->getName();
918 $releasesArray[$release->getID()] = $release->getName();
922 $size = min(count($releasesArray) + count($optGroup) + 1, 15);
923 $keys = array_keys($releasesArray);
924 $vals = array_values($releasesArray);
925 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);
929 * renderRadio - this function builds radio buttons.
931 * @param int $extra_field_id The $int ID of this field.
932 * @param string $checked The $string item that should be checked
933 * @param bool $show_100 Whether $string to show the '100 row'
934 * @param string $text_100 What $string to call the '100 row'
935 * @param bool $show_any
936 * @param string $text_any
937 * @param bool $allowed
938 * @param array $attrs Array of other attributes
939 * @return string HTML code using radio buttons
941 function renderRadio($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed = false, $attrs = array()) {
943 $arr = $this->getExtraFieldElements($extra_field_id);
944 $aef = new ArtifactExtraField($this, $extra_field_id);
945 $aefChildren = $aef->getChildren();
946 if (!empty($aefChildren)) {
947 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
952 $radios_attrs = array();
953 $attrs_100 = array();
955 for ($i=0; $i<count($arr); $i++) {
956 $vals[$i]=$arr[$i]['element_id'];
957 $texts[$i]=$arr[$i]['element_name'];
958 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
960 if (!empty($aefChildren)) {
961 $cElmntArr = $aefe->getChildrenElements();
962 if (!empty($cElmntArr))
964 foreach ($cElmntArr as $key=>$cElmnt) {
965 $childField = new ArtifactExtraField($this, $key);
966 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
968 $dependency = '['.$dependency.']';
969 $radios_attrs[$i]['data-dependency']=$dependency;
972 if ($aef->isAutoAssign()) {
973 $autoAssignTo = UserManager::instance()->getUserById($aefe->getAutoAssignto());
974 $radios_attrs[$i]=array_merge(isset($radios_attrs[$i]) ? $radios_attrs[$i] : array(), array('data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
977 if ($show_100 && !empty($aefChildren)) {
978 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
980 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);
984 * renderCheckbox - this function builds checkboxes.
986 * @param int $extra_field_id The ID of this field.
987 * @param array $checked The items that should be checked
988 * @param bool|string $show_100 Whether to show the '100 row'
989 * @param string $text_100 What to call the '100 row'
990 * @param bool $allowed
991 * @param array $attrs Array of other attributes
992 * @return string radio buttons
994 function renderCheckbox($extra_field_id, $checked=array(), $show_100=false, $text_100='none', $allowed=false, $attrs = array()) {
995 if ($text_100 == 'none'){
999 if (!$checked || !is_array($checked)) {
1002 if (!empty($attrs['title'])) {
1003 $attrs['title'] = util_html_secure($attrs['title']);
1006 $arr = $this->getExtraFieldElements($extra_field_id);
1007 $aef = new ArtifactExtraField($this, $extra_field_id);
1008 $aefChildren = $aef->getChildren();
1009 if (!empty($aefChildren)) {
1010 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
1015 $chk_attrs = array();
1016 $attrs_100 = array();
1018 for ($i=0; $i<count($arr); $i++) {
1019 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
1020 $vals[$i] = $arr[$i]['element_id'];
1021 $texts[$i] = $arr[$i]['element_name'];
1022 $chk_attrs[$i] = array();
1024 if (!empty($aefChildren)) {
1025 $cElmntArr = $aefe->getChildrenElements();
1026 if (!empty($cElmntArr))
1028 foreach ($cElmntArr as $key=>$cElmnt) {
1029 $childField = new ArtifactExtraField($this, $key);
1030 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1032 $dependency = '['.$dependency.']';
1033 $chk_attrs[$i]['data-dependency']=$dependency;
1037 if ($show_100 && !empty($aefChildren)) {
1038 $attrs_100['data-dependency'] ='{"fields": ['.implode(', ', $aefChildren).']}';
1040 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);
1044 * renderMultiSelectBox - this function builds checkboxes.
1046 * @param int $extra_field_id The ID of this field.
1047 * @param array $checked The items that should be checked
1048 * @param bool|string $show_100 Whether to show the '100 row'
1049 * @param string $text_100 What to call the '100 row'
1050 * @param bool $allowed
1051 * @param array $attrs Array of other attributes
1052 * @return string radio multiselectbox
1054 function renderMultiSelectBox($extra_field_id, $checked=array(), $show_100=false, $text_100='none', $allowed=false, $attrs=array()) {
1058 if (!is_array($checked)) {
1059 $checked = explode(',', $checked);
1061 $arr = $this->getExtraFieldElements($extra_field_id);
1062 $aef = new ArtifactExtraField($this, $extra_field_id);
1063 $aefChildren = $aef->getChildren();
1064 if (!empty($aefChildren)) {
1065 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
1069 $opt_attrs = array();
1070 $attrs_100 = array();
1072 for ($i=0; $i<count($arr); $i++) {
1073 $vals[$i]=$arr[$i]['element_id'];
1074 $texts[$i]=$arr[$i]['element_name'];
1075 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
1076 if (!empty($aefChildren)) {
1077 $cElmntArr = $aefe->getChildrenElements();
1078 if (!empty($cElmntArr))
1081 foreach ($cElmntArr as $key=>$cElmnt) {
1082 $childField = new ArtifactExtraField($this, $key);
1083 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1085 $dependency = '['.$dependency.']';
1086 $opt_attrs[$i]= array('data-dependency'=>$dependency);
1090 $size = min(count($arr)+1, 15);
1091 if ($show_100 && !empty($aefChildren)) {
1092 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
1094 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);
1098 * renderTextField - this function builds a text field.
1100 * @param int $extra_field_id The ID of this field.
1101 * @param string $contents The data for this field.
1102 * @param string $size
1103 * @param string $maxlength
1104 * @param array $attrs Array of other attributes
1105 * @return string HTML code of corresponding input tag.
1107 function renderTextField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1108 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength), $attrs));
1112 * renderIntegerField - this function builds a text field.
1114 * @param int $extra_field_id The ID of this field.
1115 * @param string $contents The data for this field.
1116 * @param string $size
1117 * @param string $maxlength
1118 * @param array $attrs Array of other attributes
1119 * @return string HTML code of corresponding input tag.
1121 function renderIntegerField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1122 $intAttrs = array('type'=>'number', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0);
1123 $newattrs = array_merge($intAttrs, $attrs);
1124 return html_e('input', $newattrs);
1128 * renderRelationField - this function builds a relation field.
1130 * @param int $extra_field_id The ID of this field.
1131 * @param string $contents The data for this field.
1132 * @param string $size
1133 * @param string $maxlength
1134 * @param array $attrs Array of other attributes
1135 * @return string text area and data.
1137 function renderRelationField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1138 $arr = $this->getExtraFieldElements($extra_field_id);
1139 for ($i=0; $i<count($arr); $i++) {
1140 $keys[$i]=$arr[$i]['element_id'];
1141 $vals[$i]=$arr[$i]['element_name'];
1143 // Convert artifact id to links.
1144 $attrsTxt = array();
1145 if (isset($attrs['form'])) {
1146 $attrsTxt['form'] = $attrs['form'];
1148 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1149 $edit_contents = $this->renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrsTxt);
1150 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)
1151 .html_e('div',array_merge(array('id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1155 * renderParentField - this function builds a parent field.
1157 * @param int $extra_field_id The ID of this field.
1158 * @param string $contents The data for this field.
1159 * @param string $size
1160 * @param string $maxlength
1161 * @param array $attrs Array of other attributes
1162 * @return string text area and data.
1164 function renderParentField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1166 $arr = $this->getExtraFieldElements($extra_field_id);
1167 for ($i=0; $i<count($arr); $i++) {
1168 $keys[$i]=$arr[$i]['element_id'];
1169 $vals[$i]=$arr[$i]['element_name'];
1171 $attrsTxt = array();
1172 if (isset($attrs['form'])) {
1173 $attrsTxt['form'] = $attrs['form'];
1175 if (is_object($ah)) {
1176 $attrsTxt['pattern']='^(?!'.$ah->getID().'$)\d*$';
1178 $attrsTxt['pattern']='^\d*$';
1180 // Convert artifact id to links.
1181 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1182 $edit_contents = $this->renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrsTxt);
1183 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)
1184 .html_e('div',array_merge(array('id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1188 * renderTextArea - this function builds a text area.
1190 * @param int $extra_field_id The ID of this field.
1191 * @param string $contents The data for this field.
1192 * @param string $rows
1193 * @param string $cols
1194 * @param array $attrs Array of other attributes
1195 * @return string text area and data.
1197 function renderTextArea($extra_field_id, $contents, $rows, $cols, $attrs = array()) {
1198 return html_e('textarea', array_merge(array('name'=>'extra_fields['.$extra_field_id.']', 'rows'=>$rows, 'cols'=>$cols), $attrs), $contents, false);
1202 * renderDatetime - this function builds a Datetime field.
1204 * @param int $extra_field_id The ID of this field.
1205 * @param string $datetime datetime for this field.
1206 * @param array $attrs Array of other attributes
1207 * @return string datetime.
1209 function renderDatetime($extra_field_id, $datetime, $attrs = array()) {
1210 if (!$datetime=='') {
1211 $datetime_format = _('Y-m-d H:i');
1212 $datetime = date($datetime_format, $datetime);
1214 if (isset($attrs['class'])) {
1215 $attrs['class'] = $attrs['class'] . ' datetimepicker';
1217 $attrs['class'] = 'datetimepicker';
1219 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$datetime), $attrs));
1222 function renderDateRange($extra_field_id, $dateRange, $attrs = array()) {
1223 // http://html5pattern.com/Dates
1224 // Date with leapyear-check
1225 $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)))';
1226 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));
1230 * renderEffort - this function builds a Effort extra field.
1232 * @param int $extra_field_id The ID of this field.
1233 * @param string $contents The data for this field.
1234 * @param string $size
1235 * @param string $maxlength
1236 * @param array $attrs Array of other attributes
1237 * @return string text area and data.
1239 function renderEffort($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1240 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1241 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
1242 $units = $effortUnitFactory->getUnits();
1246 foreach ($units as $unit) {
1247 $vals [] = $unit->getID();
1248 $texts [] = $unit->getName();
1249 $opts_attrs []['data-factor'] = $unit->getConversionFactorForBaseUnit();
1251 $valueInUnitBase = $effortUnitFactory->encodedToValueInBaseUnit($contents);
1252 $value = $effortUnitFactory->encodedToValue($contents);
1253 $unitId = $effortUnitFactory->encodedToUnitId($contents);
1254 if (isset($attrs['class'])) {
1255 $attrs['class'] .= ' effort';
1257 $attrs['class'] = 'effort';
1259 $attrs['data-effortid'] = $extra_field_id;
1260 $hiddenAttrs = array();
1261 if (isset($attrs['form'])) {
1262 $hiddenAttrs['form'] = $attrs['form'];
1264 $return = html_e('input', array_merge(array('type'=>'hidden', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$valueInUnitBase.'U'.$unitId), $hiddenAttrs));
1265 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value['.$extra_field_id.']', 'value'=>$value, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrs));
1266 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit['.$extra_field_id.']', $unitId, false, '', false, '', false, $attrs, $opts_attrs);
1270 function renderEffortRange($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1271 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1272 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
1273 $units = $effortUnitFactory->getUnits();
1276 $opts_attrs = array();
1277 foreach ($units as $unit) {
1278 $vals [] = $unit->getID();
1279 $texts [] = $unit->getName();
1280 $opts_attrs []['data-factor'] = $unit->getConversionFactorForBaseUnit();
1282 $contentsFrom = '0U'.$vals[0];
1283 $contentsTo = '0U'.$vals[0];
1284 if (preg_match('/^(\d+U\d+) ?(\d+U\d+)?$/', $contents, $matches)) {
1285 $contentsFrom = $matches[1];
1286 $contentsTo = $matches[2];
1288 $valueInUnitBaseFrom = $effortUnitFactory->encodedToValueInBaseUnit($contentsFrom);
1289 $valueInUnitBaseTo = $effortUnitFactory->encodedToValueInBaseUnit($contentsTo);
1290 $valueFrom = $effortUnitFactory->encodedToValue($contentsFrom);
1291 $valueTo = $effortUnitFactory->encodedToValue($contentsTo);
1292 $unitIdFrom = $effortUnitFactory->encodedToUnitId($contentsFrom);
1293 $unitIdTo = $effortUnitFactory->encodedToUnitId($contentsTo);
1294 $attrs['data-effortid'] = $extra_field_id;
1295 if (isset($attrs['class'])) {
1296 $attrs['class'] .= ' effort-range';
1298 $attrs['class'] = 'effort-range';
1300 $attrsFrom = $attrs;
1302 $hiddenAttrs = array();
1303 if (isset($attrs['form'])) {
1304 $hiddenAttrs['form'] = $attrs['form'];
1306 $return = html_e('input', array_merge(array('type'=>'hidden', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$valueInUnitBaseFrom.'U'.$unitIdFrom.' '.$valueInUnitBaseTo.'U'.$unitIdTo), $hiddenAttrs));
1307 $return .= _('Between')._(':').html_e('br');
1308 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value_from['.$extra_field_id.']', 'value'=>$valueFrom, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrsFrom));
1309 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit_from['.$extra_field_id.']', $unitIdFrom, false, '', false, '', false, $attrsFrom, $opts_attrs);
1310 $return .= html_e('br');
1311 $return .= _('and')._(':').html_e('br');
1312 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value_to['.$extra_field_id.']', 'value'=>$valueTo, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrsTo));
1313 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit_to['.$extra_field_id.']', $unitIdTo, false, '', false, '', false, $attrsTo, $opts_attrs);
1318 * renderFormulaField - this function builds a formula field (RO).
1320 * @param string $contents The data for this field.
1323 function renderFormulaField($contents) {
1328 * renderSLAField - this function builds a formula field (RO).
1330 * @param int $extra_field_id The ID of this field.
1331 * @param string $contents The data for this field.
1335 function renderSLAField($extra_field_id, $contents, $ef) {
1338 if (!isset($aid) ||!$aid) {
1342 $config = parse_ini_string($ef["attributes"]);
1343 $field_name = $config['field'];
1344 $rule = explode(':', $config['values']);
1346 if (!isset($config['start_id']) || $aid >= $config['start_id']) {
1347 // Get Max value from
1348 // @todo use global variable $aid !!!
1349 $artifact = artifact_get_object($aid);
1350 $value = $artifact->getFieldDataByKey('alias', $field_name);
1351 $field = $artifact->getFieldTypeByKey('alias', $field_name);
1352 $elements = $artifact->ArtifactType->getExtraFieldElements($field['extra_field_id']);
1353 for ($i = 0; $i < count($elements); $i++) {
1354 if ($elements[$i]['element_name'] == $value) {
1355 $max = 60 * $rule[$i];
1361 $value = '<span class="label label-warning">Not Set</span>';
1362 } elseif ($ef['alias'] == 'response_time') {
1363 // Implement Response SLA: Time from create to first comment.
1364 $messages = $artifact->getMessageObjects();
1365 if (!empty($messages)) {
1366 $message = $messages[count($messages) - 1];
1367 $current_time = $message->getAddDate();
1368 $timer_is_running = false;
1369 } elseif ($artifact->getStatusID() == 2) {
1370 $current_time = $artifact->getCloseDate();
1371 $timer_is_running = false;
1373 $current_time = time();
1374 $timer_is_running = true;
1376 $time = $current_time - $artifact->getOpenDate();
1377 $percent = round(100 * $time / $max);
1378 $remaining = $max - $time;
1379 if ($remaining >= 0) {
1380 $value = gmdate("G\h i", $remaining);
1382 $value = '<b>-' . gmdate("G\h i", -$remaining) . "</b>";
1385 if (!$timer_is_running) {
1386 if ($remaining >= 0) {
1387 $value = '<span class="label label-success">' . _('OK') . '</span>';
1389 $value = '<span class="label label-danger">' . _('Missed') . '</span>';
1392 if ($percent > 90) {
1393 $value = '<span class="sla-alert">' . $value . '</span>';
1394 } elseif ($percent > 80) {
1395 $value = '<span class="sla-warning">' . $value . '</span>';
1397 $value = '<span class="sla-normal">' . $value . '</span>';
1400 } elseif ($ef['alias'] == 'resolution_time') {
1401 // Implement Resolution SLA: Time from create to closed state.
1402 list($last_status_change, $timer, $timer_is_running) = json_decode($contents);
1405 if ($timer_is_running) {
1406 $time += time() - $last_status_change;
1409 $percent = round(100 * $time / $max);
1410 $remaining = $max - $time;
1411 if ($remaining >= 0) {
1412 $value = gmdate("G\h i", $remaining);
1414 $value = '<b>-' . gmdate("G\h i", -$remaining) . "</b>";
1416 if ($artifact->getStatusID() == 2) {
1417 if ($remaining >= 0) {
1418 $value = '<span class="label label-success">' . _('OK') . '</span>';
1420 $value = '<span class="label label-danger">' . _('Missed') . '</span>';
1423 if (!$timer_is_running) {
1424 $value = ' <span class="label label-info">' . _('Suspended') . '</span>';
1425 } elseif ($percent > 90) {
1426 $value = '<span class="sla-alert">' . $value . '</span>';
1427 } elseif ($percent > 80) {
1428 $value = '<span class="sla-warning">' . $value . '</span>';
1430 $value = '<span class="sla-normal">' . $value . '</span>';
1435 $value = '<span class="label label-default">' . _('Not Available') . '</span>';
1440 function technicianBox($name = 'assigned_to[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false, $attrs = array()) {
1441 if ($text_100=='none'){
1442 $text_100=_('Nobody');
1444 $engine = RBACEngine::getInstance();
1445 $techs = $engine->getUsersByAllowedAction('tracker', $this->getID(), 'tech') ;
1450 sortUserList($techs);
1452 foreach ($techs as $tech) {
1453 $ids[] = $tech->getID() ;
1454 $names[] = $tech->getRealName() ;
1457 if ($extra_id != '-1') {
1459 $names[]=$extra_name;
1463 if (!is_array($checked)) {
1464 $checked = explode(',', $checked);
1466 $size = min(count($ids)+1, 15);
1467 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100, false, $attrs);
1469 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100, false, false, false, $attrs);
1473 function submitterBox($name = 'submitted_by[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false, $attrs = array()) {
1474 if ($text_100=='none'){
1475 $text_100=_('Nobody');
1477 $result = $this->getSubmitters();
1478 $ids =& util_result_column_to_array($result,0);
1479 $names =& util_result_column_to_array($result,1);
1480 if ($extra_id != '-1') {
1482 $names[]=$extra_name;
1486 if (!is_array($checked)) {
1487 $checked = explode(',', $checked);
1489 $size = min(count($ids)+1, 15);
1490 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100, false, $attrs);
1492 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100, false, $attrs);
1496 function lastModifierBox($name = 'last_modified_by[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false) {
1497 if ($text_100=='none'){
1498 $text_100=_('Nobody');
1500 $result = $this->getLastModifiers();
1501 $ids =& util_result_column_to_array($result, 0);
1502 $names =& util_result_column_to_array($result, 1);
1503 if ($extra_id != '-1') {
1505 $names[] = $extra_name;
1509 if (!is_array($checked)) {
1510 $checked = explode(',', $checked);
1512 $size = min(count($ids)+1, 15);
1513 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100);
1515 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100);
1519 function cannedResponseBox($name = 'canned_response', $checked = 'xzxz', $attrs = array()) {
1520 if (!isset($attrs['id'])) {
1521 $attrs['id'] = $name;
1523 return html_build_select_box($this->getCannedResponses(), $name, $checked, true, 'none', false, '', false, $attrs);
1527 * statusBox - show the statuses - automatically shows the "custom statuses" if they exist
1529 * @param string $name
1530 * @param string $checked
1531 * @param bool $show_100
1532 * @param string $text_100
1533 * @param array $attrs
1534 * @return string HTML code
1536 function statusBox($name = 'status_id', $checked = 'xzxz', $show_100 = false, $text_100 = 'none', $attrs = array()) {
1537 if ($text_100=='none'){
1538 $text_100=_('None');
1540 return html_build_select_box($this->getStatuses(), $name, $checked, $show_100, $text_100, false, '', false, $attrs);
1544 * priorityBox - show the priorities
1546 * @param string $name
1547 * @param string $checked_val
1548 * @param bool $nochange
1549 * @param array $attrs
1550 * @param bool $show_any
1551 * @return string HTML code
1553 function priorityBox($name = 'priority', $checked_val = '3', $nochange = false, $attrs = array(), $show_any = false){
1554 return html_build_priority_select_box($name, $checked_val, $nochange, $attrs, $show_any);
1557 function javascript() {
1559 var invalidSelectMsg = '"._("One or more of the selected options is not allowed")."';
1560 var invalidInputMsg = '". _("This choice is not allowed")."';
1561 var groupId =".$this->Group->getID().";
1562 var atId = ".$this->getID().";";
1563 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1564 if ($effortUnitSet->isAutoconvert()) {
1565 $jseffort = <<<'EOS'
1566 $("select.effort").change(function(){
1567 var effortid = $(this).data("effortid");
1568 var value = parseInt(parseInt($("input[name='extra_fields["+effortid+"]']").val())/$("select[name='unit["+effortid+"]'] option:selected").data('factor'));
1569 $("input[name='value["+effortid+"]']").val(value);
1570 $("input[name='extra_fields["+effortid+"]']").val(value*$("select[name='unit["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit["+effortid+"]']").val());
1572 $("input.effort").change(function(){
1573 var effortid = $(this).data("effortid");
1574 var value = $("input[name='value["+effortid+"]']").val();
1575 $("input[name='extra_fields["+effortid+"]']").val(value*$("select[name='unit["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit["+effortid+"]']").val());
1577 $(".effort-range").change(function(){
1578 var effortid = $(this).data("effortid");
1579 $("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());
1583 $jseffort = <<<'EOS'
1584 $(".effort").change(function(){
1585 var effortid = $(this).data("effortid");
1586 $("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());
1588 $(".effort-range").change(function(){
1589 var effortid = $(this).data("effortid");
1590 $("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());
1594 $javascript = <<<'EOS'
1595 function showMessage( msg_text, msg_class) {
1596 $("#maindiv h1").append($("<p>", { "class": msg_class }).html( msg_text )).show();
1598 $.expr[':'].invalid = function(elem, index, match) {
1599 var invalids = document.querySelectorAll(':invalid'), result = false, len = invalids.length;
1601 for (var i=0; i<len; i++) {
1602 if (elem === invalids[i]) {
1611 $("input[type='radio'].readonly, input[type='checkbox'].readonly").on('click', function(){
1613 }).on('keydown', function(event){
1614 if(event.keyCode !== 9) return false;
1616 $(".in-formula").change(function(){
1620 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(),
1623 success: function(answer){
1624 if(answer['message']) {
1625 showMessage(answer['message'], 'error');
1627 fields = answer['fields'];
1628 $.each(fields, function (index, field) {
1629 if (field.error!=null){
1630 showMessage(field.error, 'error');
1632 fieldObj = $("[name^='extra_fields["+field.id+"]']");
1633 if (fieldObj.is("input[type='checkbox']")){
1634 fieldObj.each(function() {
1636 for (var key in field.value) {
1637 if (field.value[key] == $(this).val()) {
1642 if (in_array > -1) {
1643 $(this).prop("checked",true);
1645 $(this).prop("checked",false);
1648 } else if (fieldObj.is("input[type='radio']")){
1649 fieldObj.each(function() {
1651 for (var key in field.value) {
1652 if (field.value[key] == $(this).val()) {
1657 if (in_array > -1) {
1658 $(this).prop("checked",true);
1660 $(this).prop("checked",false);
1663 } else if (fieldObj.is("input")){
1664 fieldObj.val(field.value);
1665 } else if (fieldObj.is("select")){
1666 fieldObj.val(field.value);
1667 } else if (fieldObj.is("textarea")){
1668 fieldObj.val(field.value);
1676 $("img.addparent").click(function(){
1680 data: 'rtype=ajax&function=add_parent&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&parent_id='+$("input#parent_id").val(),
1683 success: function(answer){
1684 if(answer['message']) {
1685 showMessage(answer['message'], 'error');
1687 $("table.parent").replaceWith(answer['parent']);
1688 $("input[name='extra_fields["+answer['parent_efid']+"]']").val(answer['parent_id']);
1689 $("div#show"+answer['parent_efid']).html(answer['parent_link']);
1690 $("input#parent_id").val('');
1691 $("div.addparent").addClass('hide');
1692 $("img.removeparent").click(function(){
1693 removeParent($(this).data("id"));
1701 $("img.addchild").click(function(){
1705 data: 'rtype=ajax&function=add_child&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&child_id='+$("input#child_id").val(),
1708 success: function(answer){
1709 if(answer['message']) {
1710 showMessage(answer['message'], 'error');
1712 $("table.children").replaceWith(answer['children']);
1713 $("input#child_id").val('');
1714 $("img.removechild").click(function(){
1715 removeChild($(this).data("id"));
1724 $("img.removeparent").click(function(){
1725 removeParent($(this).data("id"));
1728 function removeParent(id) {
1732 data: 'rtype=ajax&function=remove_parent&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&parent_id='+id,
1735 success: function(answer){
1736 if(answer['message']) {
1737 showMessage(answer['message'], 'error');
1739 $("table#parent"+id).remove();
1740 $("div.addparent").removeClass('hide');
1747 $("img.removechild").click(function(){
1748 removeChild($(this).data("id"));
1751 function removeChild(id) {
1755 data: 'rtype=ajax&function=remove_child&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&child_id='+id,
1758 success: function(answer){
1759 if(answer['message']) {
1760 showMessage(answer['message'], 'error');
1762 $("span#child"+id).remove();
1768 $(".autoassign[name^='extra_fields']").change(function(){
1769 if ($(this).prop('tagName') == 'SELECT') {
1770 var elmnts = $(this).children('option:selected');
1772 var elmnts = $(this).siblings('input:checked');
1774 elmnts.each(function(i){
1775 var aat = $(this).data("autoassignto");
1776 $("select#tracker-assigned_to option[value="+aat.id+"]").prop('selected', true);
1777 $("span#tracker-assigned_to").text(aat.name);
1780 $(".with-depcy[name^='extra_fields']").change(function(){
1781 if ($(this).prop('tagName') == 'SELECT') {
1782 var elmnts = $(this).children('option:selected');
1784 var elmnts = $(this).siblings('input:checked');
1786 elmnts.each(function(i){
1787 var dep = $(this).data("dependency");
1788 if (this.value!='100') {
1789 $(dep).each(function(j, val) {
1790 $("select[name^='extra_fields["+val.field+"]']:invalid, input[name^='extra_fields["+val.field+"]']:invalid").each(function() {
1791 this.setCustomValidity("");
1792 $(this).off("change.invalid");
1794 $("select[name^='extra_fields["+val.field+"]'] option").each(function(k,opt){
1795 if (this.value!='100') {
1796 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1797 $(this).prop('disabled', false).removeClass('option_disabled');
1799 $(this).prop('disabled', true);
1800 $(this).addClass('option_disabled');
1804 $("input[name^='extra_fields["+val.field+"]']").each(function(k,opt){
1805 if (this.value!='100') {
1806 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1807 $(this).prop('disabled', false).removeClass($(this).attr('type')+'_disabled');
1809 $(this).prop('disabled', true);
1810 $(this).addClass($(this).attr('type')+'_disabled');
1816 $(dep.fields).each(function(j, val) {
1817 $("select[name^='extra_fields["+val+"]']:invalid, input[name^='extra_fields["+val+"]']:invalid").each(function() {
1818 this.setCustomValidity("");
1820 $("select[name^='extra_fields["+val+"]'] option.option_disabled").each(function() {
1821 $(this).prop('disabled', false).removeClass('option_disabled');
1823 $("input.radio_disable[name^='extra_fields["+val+"]']").each(function() {
1824 $(this).prop('disabled', false).removeClass('radio_disabled');
1826 $("input.checkbox_disabled[name^='extra_fields["+val+"]']").each(function() {
1827 $(this).prop('disabled', false).removeClass('checkbox_disabled');
1832 $("select[name^='extra_fields'] option:selected:disabled").parent().each(function() {
1833 $(this).children('option:selected:disabled').prop('disabled', false);
1834 this.setCustomValidity(invalidSelectMsg);
1835 $(this).on("change.invalid", function() {
1836 $(this).children('option.option_disabled:not(:disabled):not(:selected)').prop('disabled', true);
1837 if (!$(this).children('option.option_disabled:selected').length) {
1838 this.setCustomValidity("");
1839 $(this).off("change.invalid");
1843 $("input[name^='extra_fields']:checked:disabled").each(function() {
1844 $(this).prop('disabled', false);
1845 this.setCustomValidity(invalidInputMsg);
1846 if ($(this).attr('type') == 'radio') {
1847 $(this).siblings('input[type="radio"]').on("change.invalid", function() {
1848 $(this).siblings('input[type="radio"]:invalid').prop('disabled', true).addClass('input_disabled').each(function() {
1849 this.setCustomValidity("");
1851 $(this).siblings('input[type="radio"]').off("change.invalid");
1852 $(this).off("change.invalid");
1855 $(this).on("change.invalid", function() {
1856 $(this).prop('disabled', true);
1857 this.setCustomValidity("");
1858 $(this).off("change.invalid");
1864 return html_e('script', array('type'=>'text/javascript'), '//<![CDATA['."\n".'$(function(){'.$jsvariable."\n".$jseffort."\n".$javascript.'});'."\n".'//]]>');
1871 // c-file-style: "bsd"