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 $tasks = $ah->getRelatedTasks();
461 $taskcount = db_numrows($tasks);
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 while ($taskinfo = db_fetch_array($tasks)) {
480 $totalPercentage += $taskinfo['percent_complete'];
481 $taskid = $taskinfo['project_task_id'];
482 $projectid = $taskinfo['group_project_id'];
483 $groupid = $taskinfo['group_id'];
484 if (forge_check_perm('pm', $projectid, 'read')) {
485 $summary = util_unconvert_htmlspecialchars($taskinfo['summary']);
486 $startdate = date(_('Y-m-d H:i'), $taskinfo['start_date']);
487 $enddate = date(_('Y-m-d H:i'), $taskinfo['end_date']);
488 $status = $taskinfo['status_name'];
490 <td>'.util_make_link('/pm/t_follow.php/'.$taskid, '[T'.$taskid.'] '.$summary).'</td>
491 <td><div class="percentbar" style="width: 100px;">
492 <div style="width:'.round($taskinfo['percent_complete']).'px;"></div></div></td>
493 <td>'.$startdate.'</td>
494 <td>'.$enddate.'</td>
495 <td>'.$status.' ('.$taskinfo['percent_complete'].'%)</td>'.
496 (($is_admin) ? '<td><input type="checkbox" '.(($formid) ? 'form="'.$formid.'"' : '').' name="remlink[]" value="'.$taskid.'" /></td>' : '').
500 $return .= $HTML->listTableBottom();
502 $return .= "\n<hr /><p style=\"text-align:right;\">";
503 $return .= _('Average completion rate')._(': ').(int)($totalPercentage/$taskcount).'%';
506 $return .= $HTML->information(_('No related tasks'));
511 function renderFiles($group_id, $ah) {
513 $file_list =& $ah->getFiles();
514 $count=count($file_list);
518 $return .= '<strong>'._("Attachments")._(':').'</strong>'.'<br/>';
520 $title_arr[] = _('Size');
521 $title_arr[] = _('Name');
522 $title_arr[] = _('Date');
523 $title_arr[] = _('By');
524 $title_arr[] = _('Download');
525 if (forge_check_perm('tracker', $this->getID(), 'tech')) {
528 $return .= $HTML->listTableTop($title_arr);
530 foreach ($file_list as $file) {
532 $return .= '<td>'.human_readable_bytes($file->getSize()).'</td>';
533 $return .= '<td>'.htmlspecialchars($file->getName()).'</td>';
534 $return .= '<td>'.date(_('Y-m-d H:i'), $file->getDate()).'</td>';
535 $return .= '<td>'.util_display_user($file->getSubmittedUnixName(), $file->getSubmittedBy(), $file->getSubmittedRealName()).'</td>';
536 $return .= '<td>'.util_make_link('/tracker/download.php/'.$group_id.'/'. $this->getID().'/'. $ah->getID() .'/'.$file->getID().'/'.$file->getName(), htmlspecialchars($file->getName())).'</td>';
537 if (forge_check_perm('tracker', $this->getID(), 'tech')) {
538 $return .= '<td><input type="checkbox" name="delete_file[]" value="'. $file->getID() .'">'._('Delete').'</td>';
543 $return .= $HTML->listTableBottom();
545 $return .= $HTML->information(_('No attached documents'));
553 * @param array $types
554 * @param string $mode
555 * @return string HTML template.
557 function getRenderHTML($types=array(), $mode='') {
558 // Use template only for the browse (not for query or mass update)
559 if (($mode === 'DISPLAY' || $mode === 'DETAIL' || $mode === 'UPDATE' || $mode == 'NEW')
560 && $this->data_array['custom_renderer']) {
561 return preg_replace('/<!--(\S+.*?)-->/','{$\\1}', $this->data_array['custom_renderer']);
563 return $this->generateRenderHTML($types, $mode);
570 * @param array $types
571 * @param string $mode Display mode (QUERY OR DISPLAY OR NEW)
572 * @return string HTML template.
574 function generateRenderHTML($types=array(), $mode) {
575 if ($mode == 'NEW') {
576 $efarr = $this->getExtraFields($types, false, false);
578 $efarr = $this->getExtraFields($types);
580 //each two columns, we'll reset this and start a new row
583 <!-- Start Extra Fields Rendering -->
587 $keys=array_keys($efarr);
589 if ($count == 0) return '';
591 for ($k=0; $k<$count; $k++) {
594 // Do not show the required star in query mode (creating/updating a query).
595 $is_required = ($mode == 'QUERY' || $mode == 'DISPLAY') ? 0 : $efarr[$i]['is_required'];
596 if ($mode == 'QUERY' && ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME || $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATE)) {
597 $name = sprintf(_('%s range'), $efarr[$i]['field_name']).($is_required ? utils_requiredField() : '')._(': ');
599 $name = $efarr[$i]['field_name'].($is_required ? utils_requiredField() : '')._(': ');
601 $name = '<strong>'.$name.'</strong>';
603 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
604 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
605 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
606 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT ||
607 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
608 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER ||
609 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE ) {
612 <td class="halfwidth top">'.$name.'<br />{$'.$efarr[$i]['field_name'].'}</td>';
614 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
615 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER ||
616 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION ||
617 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_PARENT ||
618 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME ||
619 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
621 //text fields might be really wide, so need a row to themselves.
622 if (($col_count == 1) && ($efarr[$i]['attribute1'] > 30)) {
632 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
634 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
636 //text areas might be really wide, so need a row to themselves.
637 if (($col_count == 1) && ($efarr[$i]['attribute2'] > 30)) {
647 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
652 //we've done two columns - if there are more to do, start a new row
653 if (($col_count == 2) && ($k != $count-1)) {
660 if ($col_count == 1) {
666 <!-- End Extra Fields Rendering -->';
671 * renderSelect - this function builds pop up box with choices.
673 * @param int $extra_field_id The ID of this field.
674 * @param string $checked The item that should be checked
675 * @param bool|string $show_100 Whether to show the '100 row'
676 * @param string $text_100 What to call the '100 row'
677 * @param bool $show_any
678 * @param string $text_any
679 * @param bool $allowed
680 * @param array $attrs
681 * @return string HTML code for the box and choices
683 function renderSelect($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
684 if ($text_100 == 'none'){
687 $arr = $this->getExtraFieldElements($extra_field_id);
688 $aef = new ArtifactExtraField($this, $extra_field_id);
689 $aefChildren = $aef->getChildren();
690 if (!empty($aefChildren)) {
691 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
693 if ($aef->isAutoAssign()) {
694 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'autoassign';
698 $opt_attrs = array();
699 $attrs_100 = array();
701 for ($i=0; $i<count($arr); $i++) {
702 $vals[$i]=$arr[$i]['element_id'];
703 $texts[$i]=$arr[$i]['element_name'];
704 $opt_attrs[$i]=array();
705 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
706 if (!empty($aefChildren)) {
707 $cElmntArr = $aefe->getChildrenElements();
708 if (!empty($cElmntArr))
711 foreach ($cElmntArr as $key=>$cElmnt) {
712 $childField = new ArtifactExtraField($this, $key);
713 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
715 $dependency = '['.$dependency.']';
716 $opt_attrs[$i]= array('data-dependency'=>$dependency);
719 if ($aef->isAutoAssign()) {
720 $autoAssignTo = UserManager::instance()->getUserById($aefe->getAutoAssignto());
721 $opt_attrs[$i]=array_merge(isset($opt_attrs[$i]) ? $opt_attrs[$i] : array(), array('data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
725 if ($show_100 && !empty($aefChildren)) {
726 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
728 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);
732 * renderUserField - this function builds pop up box with users.
734 * @param int $extra_field_id The ID of this field.
735 * @param string $checked The item that should be checked
736 * @param bool|string $show_100 Whether to show the '100 row'
737 * @param string $text_100 What to call the '100 row'
738 * @param bool $show_any
739 * @param string $text_any
740 * @param bool $allowed
741 * @param array $attrs
742 * @return string HTML code for the box and choices
744 function renderUserField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
745 if ($text_100 == 'none' || $text_100 == 'nobody'){
746 $text_100=_('Nobody');
748 $arr = $this->getExtraFieldElements($extra_field_id);
749 $selectedRolesId = array();
750 for ($i=0; $i<count($arr); $i++) {
751 $selectedRolesId[$i]=$arr[$i]['element_name'];
753 $roles = $this->getGroup()->getRoles();
754 $userArray = array();
755 foreach ($roles as $role) {
756 if (in_array($role->getID(), $selectedRolesId)) {
757 foreach ($role->getUsers() as $user) {
758 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
762 if (is_integer($checked) && !isset($userArray[$checked])) {
763 $checkedUser = user_get_object($checked);
764 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
766 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
767 $keys = array_keys($userArray);
768 $vals = array_values($userArray);
769 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);
773 * renderMultiUserField - this function builds pop up box with users.
775 * @param int $extra_field_id The ID of this field.
776 * @param string $checked The item that should be checked
777 * @param bool|string $show_100 Whether to show the '100 row'
778 * @param string $text_100 What to call the '100 row'
779 * @param bool $show_any
780 * @param string $text_any
781 * @param bool $allowed
782 * @param array $attrs
783 * @return string HTML code for the box and choices
785 function renderMultiUserField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
786 if ($text_100 == 'none' || $text_100 == 'nobody'){
787 $text_100=_('Nobody');
793 if (!is_array($checked)) {
794 $checked = explode(',', $checked);
797 $arr = $this->getExtraFieldElements($extra_field_id);
798 $selectedRolesId = array();
799 for ($i=0; $i<count($arr); $i++) {
800 $selectedRolesId[$i]=$arr[$i]['element_name'];
802 $roles = $this->getGroup()->getRoles();
803 $userArray = array();
804 foreach ($roles as $role) {
805 if (in_array($role->getID(), $selectedRolesId)) {
806 foreach ($role->getUsers() as $user) {
807 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
811 if (is_integer($checked) && !isset($userArray[$checked])) {
812 $checkedUser = user_get_object($checked);
813 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
815 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
816 $size = min(count($userArray)+1, 15);
817 $keys = array_keys($userArray);
818 $vals = array_values($userArray);
820 return html_build_multiple_select_box_from_arrays($keys, $vals,'extra_fields['.$extra_field_id.'][]', $checked, $size, $show_100, $text_100, $allowed, $attrs);
824 * renderReleaseField - this function builds 2 pop up boxes with packages & releases.
826 * @param int $extra_field_id The ID of this field.
827 * @param string $checked The item that should be checked
828 * @param bool|string $show_100 Whether to show the '100 row'
829 * @param string $text_100 What to call the '100 row'
830 * @param bool $show_any
831 * @param string $text_any
832 * @param bool $allowed
833 * @param array $attrs
834 * @return string HTML code for the box and choices
836 function renderReleaseField($extra_field_id, $checked = 'xzxz', $show_100 = false, $text_100 = 'none', $show_any = false, $text_any = 'Any', $allowed = false, $attrs = array()) {
837 if ($text_100 == 'none'){
841 $releasesArray = array();
842 $releasesAttrs = array();
845 $arr = $this->getExtraFieldElements($extra_field_id);
846 $selectedPackagesId = array();
847 for ($i=0; $i<count($arr); $i++) {
848 $selectedPackagesId[$i]=$arr[$i]['element_name'];
851 $fpFactory = new FRSPackageFactory($this->getGroup());
852 $packages = $fpFactory->getFRSs(true);
853 uasort($packages, 'compareObjectName');
854 foreach ($packages as $package) {
855 $releases = $package->getReleases(false);
856 uasort($releases, 'compareObjectName');
857 foreach ($releases as $release) {
858 $optGroup[] = $package->getName();
859 $releasesArray[$release->getID()] = $release->getName();
863 $keys = array_keys($releasesArray);
864 $vals = array_values($releasesArray);
865 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);
869 * renderMultiReleaseField - this function builds 2 pop up boxes with packages & releases.
871 * @param int $extra_field_id The ID of this field.
872 * @param string $checked The item that should be checked
873 * @param bool|string $show_100 Whether to show the '100 row'
874 * @param string $text_100 What to call the '100 row'
875 * @param bool $show_any
876 * @param string $text_any
877 * @param bool $allowed
878 * @param array $attrs
879 * @return string HTML code for the box and choices
881 function renderMultiReleaseField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs=array()) {
882 if ($text_100 == 'none'){
888 if (!is_array($checked)) {
889 $checked = explode(',', $checked);
892 $releasesArray = array();
893 $releasesAttrs = array();
896 $arr = $this->getExtraFieldElements($extra_field_id);
897 $selectedPackagesId = array();
898 for ($i=0; $i<count($arr); $i++) {
899 $selectedPackagesId[$i]=$arr[$i]['element_name'];
902 $packages = get_frs_packages($this->getGroup());
903 uasort($packages, 'compareObjectName');
904 foreach ($packages as $package) {
905 if (in_array($package->getID(), $selectedPackagesId)) {
906 $releases = $package->getReleases();
907 uasort($releases, 'compareObjectName');
908 foreach ($releases as $release) {
909 $optGroup[] = $package->getName();
910 $releasesArray[$release->getID()] = $release->getName();
914 $size = min(count($releasesArray) + count($optGroup) + 1, 15);
915 $keys = array_keys($releasesArray);
916 $vals = array_values($releasesArray);
917 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);
921 * renderRadio - this function builds radio buttons.
923 * @param int $extra_field_id The $int ID of this field.
924 * @param string $checked The $string item that should be checked
925 * @param bool $show_100 Whether $string to show the '100 row'
926 * @param string $text_100 What $string to call the '100 row'
927 * @param bool $show_any
928 * @param string $text_any
929 * @param bool $allowed
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 bool $allowed
983 * @param array $attrs Array of other attributes
984 * @return string radio buttons
986 function renderCheckbox($extra_field_id, $checked=array(), $show_100=false, $text_100='none', $allowed=false, $attrs = array()) {
987 if ($text_100 == 'none'){
991 if (!$checked || !is_array($checked)) {
994 if (!empty($attrs['title'])) {
995 $attrs['title'] = util_html_secure($attrs['title']);
998 $arr = $this->getExtraFieldElements($extra_field_id);
999 $aef = new ArtifactExtraField($this, $extra_field_id);
1000 $aefChildren = $aef->getChildren();
1001 if (!empty($aefChildren)) {
1002 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
1007 $chk_attrs = array();
1008 $attrs_100 = array();
1010 for ($i=0; $i<count($arr); $i++) {
1011 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
1012 $vals[$i] = $arr[$i]['element_id'];
1013 $texts[$i] = $arr[$i]['element_name'];
1014 $chk_attrs[$i] = array();
1016 if (!empty($aefChildren)) {
1017 $cElmntArr = $aefe->getChildrenElements();
1018 if (!empty($cElmntArr))
1020 foreach ($cElmntArr as $key=>$cElmnt) {
1021 $childField = new ArtifactExtraField($this, $key);
1022 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1024 $dependency = '['.$dependency.']';
1025 $chk_attrs[$i]['data-dependency']=$dependency;
1029 if ($show_100 && !empty($aefChildren)) {
1030 $attrs_100['data-dependency'] ='{"fields": ['.implode(', ', $aefChildren).']}';
1032 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);
1036 * renderMultiSelectBox - this function builds checkboxes.
1038 * @param int $extra_field_id The ID of this field.
1039 * @param array $checked The items that should be checked
1040 * @param bool|string $show_100 Whether to show the '100 row'
1041 * @param string $text_100 What to call the '100 row'
1042 * @param bool $allowed
1043 * @param array $attrs Array of other attributes
1044 * @return string radio multiselectbox
1046 function renderMultiSelectBox($extra_field_id, $checked=array(), $show_100=false, $text_100='none', $allowed=false, $attrs=array()) {
1050 if (!is_array($checked)) {
1051 $checked = explode(',', $checked);
1053 $arr = $this->getExtraFieldElements($extra_field_id);
1054 $aef = new ArtifactExtraField($this, $extra_field_id);
1055 $aefChildren = $aef->getChildren();
1056 if (!empty($aefChildren)) {
1057 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
1061 $opt_attrs = array();
1062 $attrs_100 = array();
1064 for ($i=0; $i<count($arr); $i++) {
1065 $vals[$i]=$arr[$i]['element_id'];
1066 $texts[$i]=$arr[$i]['element_name'];
1067 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
1068 if (!empty($aefChildren)) {
1069 $cElmntArr = $aefe->getChildrenElements();
1070 if (!empty($cElmntArr))
1073 foreach ($cElmntArr as $key=>$cElmnt) {
1074 $childField = new ArtifactExtraField($this, $key);
1075 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1077 $dependency = '['.$dependency.']';
1078 $opt_attrs[$i]= array('data-dependency'=>$dependency);
1082 $size = min(count($arr)+1, 15);
1083 if ($show_100 && !empty($aefChildren)) {
1084 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
1086 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);
1090 * renderTextField - this function builds a text field.
1092 * @param int $extra_field_id The ID of this field.
1093 * @param string $contents The data for this field.
1094 * @param string $size
1095 * @param string $maxlength
1096 * @param array $attrs Array of other attributes
1097 * @return string HTML code of corresponding input tag.
1099 function renderTextField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1100 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength), $attrs));
1104 * renderIntegerField - this function builds a text field.
1106 * @param int $extra_field_id The ID of this field.
1107 * @param string $contents The data for this field.
1108 * @param string $size
1109 * @param string $maxlength
1110 * @param array $attrs Array of other attributes
1111 * @return string HTML code of corresponding input tag.
1113 function renderIntegerField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1114 $intAttrs = array('type'=>'number', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0);
1115 $newattrs = array_merge($intAttrs, $attrs);
1116 return html_e('input', $newattrs);
1120 * renderRelationField - this function builds a relation field.
1122 * @param int $extra_field_id The ID of this field.
1123 * @param string $contents The data for this field.
1124 * @param string $size
1125 * @param string $maxlength
1126 * @param array $attrs Array of other attributes
1127 * @return string text area and data.
1129 function renderRelationField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1130 $arr = $this->getExtraFieldElements($extra_field_id);
1131 for ($i=0; $i<count($arr); $i++) {
1132 $keys[$i]=$arr[$i]['element_id'];
1133 $vals[$i]=$arr[$i]['element_name'];
1135 // Convert artifact id to links.
1136 $attrsTxt = array();
1137 if (isset($attrs['form'])) {
1138 $attrsTxt['form'] = $attrs['form'];
1140 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1141 $edit_contents = $this->renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrsTxt);
1142 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)
1143 .html_e('div',array_merge(array('id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1147 * renderParentField - this function builds a parent field.
1149 * @param int $extra_field_id The ID of this field.
1150 * @param string $contents The data for this field.
1151 * @param string $size
1152 * @param string $maxlength
1153 * @param array $attrs Array of other attributes
1154 * @return string text area and data.
1156 function renderParentField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1158 $arr = $this->getExtraFieldElements($extra_field_id);
1159 for ($i=0; $i<count($arr); $i++) {
1160 $keys[$i]=$arr[$i]['element_id'];
1161 $vals[$i]=$arr[$i]['element_name'];
1163 $attrsTxt = array();
1164 if (isset($attrs['form'])) {
1165 $attrsTxt['form'] = $attrs['form'];
1167 if (is_object($ah)) {
1168 $attrsTxt['pattern']='^(?!'.$ah->getID().'$)\d*$';
1170 $attrsTxt['pattern']='^\d*$';
1172 // Convert artifact id to links.
1173 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1174 $edit_contents = $this->renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrsTxt);
1175 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)
1176 .html_e('div',array_merge(array('id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1180 * renderTextArea - this function builds a text area.
1182 * @param int $extra_field_id The ID of this field.
1183 * @param string $contents The data for this field.
1184 * @param string $rows
1185 * @param string $cols
1186 * @param array $attrs Array of other attributes
1187 * @return string text area and data.
1189 function renderTextArea($extra_field_id, $contents, $rows, $cols, $attrs = array()) {
1190 return html_e('textarea', array_merge(array('name'=>'extra_fields['.$extra_field_id.']', 'rows'=>$rows, 'cols'=>$cols), $attrs), $contents, false);
1194 * renderDatetime - this function builds a Datetime field.
1196 * @param int $extra_field_id The ID of this field.
1197 * @param string $datetime datetime for this field.
1198 * @param array $attrs Array of other attributes
1199 * @return string datetime.
1201 function renderDatetime($extra_field_id, $datetime, $attrs = array()) {
1202 if (!$datetime=='') {
1203 $datetime_format = _('Y-m-d H:i');
1204 $datetime = date($datetime_format, $datetime);
1206 if (isset($attrs['class'])) {
1207 $attrs['class'] = $attrs['class'] . ' datetimepicker';
1209 $attrs['class'] = 'datetimepicker';
1211 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$datetime), $attrs));
1214 function renderDateRange($extra_field_id, $dateRange, $attrs = array()) {
1215 // http://html5pattern.com/Dates
1216 // Date with leapyear-check
1217 $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)))';
1218 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));
1222 * renderEffort - this function builds a Effort extra field.
1224 * @param int $extra_field_id The ID of this field.
1225 * @param string $contents The data for this field.
1226 * @param string $size
1227 * @param string $maxlength
1228 * @param array $attrs Array of other attributes
1229 * @return string text area and data.
1231 function renderEffort($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1232 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1233 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
1234 $units = $effortUnitFactory->getUnits();
1238 foreach ($units as $unit) {
1239 $vals [] = $unit->getID();
1240 $texts [] = $unit->getName();
1241 $opts_attrs []['data-factor'] = $unit->getConversionFactorForBaseUnit();
1243 $valueInUnitBase = $effortUnitFactory->encodedToValueInBaseUnit($contents);
1244 $value = $effortUnitFactory->encodedToValue($contents);
1245 $unitId = $effortUnitFactory->encodedToUnitId($contents);
1246 if (isset($attrs['class'])) {
1247 $attrs['class'] .= ' effort';
1249 $attrs['class'] = 'effort';
1251 $attrs['data-effortid'] = $extra_field_id;
1252 $hiddenAttrs = array();
1253 if (isset($attrs['form'])) {
1254 $hiddenAttrs['form'] = $attrs['form'];
1256 $return = html_e('input', array_merge(array('type'=>'hidden', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$valueInUnitBase.'U'.$unitId), $hiddenAttrs));
1257 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value['.$extra_field_id.']', 'value'=>$value, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrs));
1258 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit['.$extra_field_id.']', $unitId, false, '', false, '', false, $attrs, $opts_attrs);
1262 function renderEffortRange($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1263 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1264 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
1265 $units = $effortUnitFactory->getUnits();
1268 $opts_attrs = array();
1269 foreach ($units as $unit) {
1270 $vals [] = $unit->getID();
1271 $texts [] = $unit->getName();
1272 $opts_attrs []['data-factor'] = $unit->getConversionFactorForBaseUnit();
1274 $contentsFrom = '0U'.$vals[0];
1275 $contentsTo = '0U'.$vals[0];
1276 if (preg_match('/^(\d+U\d+) ?(\d+U\d+)?$/', $contents, $matches)) {
1277 $contentsFrom = $matches[1];
1278 $contentsTo = $matches[2];
1280 $valueInUnitBaseFrom = $effortUnitFactory->encodedToValueInBaseUnit($contentsFrom);
1281 $valueInUnitBaseTo = $effortUnitFactory->encodedToValueInBaseUnit($contentsTo);
1282 $valueFrom = $effortUnitFactory->encodedToValue($contentsFrom);
1283 $valueTo = $effortUnitFactory->encodedToValue($contentsTo);
1284 $unitIdFrom = $effortUnitFactory->encodedToUnitId($contentsFrom);
1285 $unitIdTo = $effortUnitFactory->encodedToUnitId($contentsTo);
1286 $attrs['data-effortid'] = $extra_field_id;
1287 if (isset($attrs['class'])) {
1288 $attrs['class'] .= ' effort-range';
1290 $attrs['class'] = 'effort-range';
1292 $attrsFrom = $attrs;
1294 $hiddenAttrs = array();
1295 if (isset($attrs['form'])) {
1296 $hiddenAttrs['form'] = $attrs['form'];
1298 $return = html_e('input', array_merge(array('type'=>'hidden', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$valueInUnitBaseFrom.'U'.$unitIdFrom.' '.$valueInUnitBaseTo.'U'.$unitIdTo), $hiddenAttrs));
1299 $return .= _('Between')._(':').html_e('br');
1300 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value_from['.$extra_field_id.']', 'value'=>$valueFrom, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrsFrom));
1301 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit_from['.$extra_field_id.']', $unitIdFrom, false, '', false, '', false, $attrsFrom, $opts_attrs);
1302 $return .= html_e('br');
1303 $return .= _('and')._(':').html_e('br');
1304 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value_to['.$extra_field_id.']', 'value'=>$valueTo, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrsTo));
1305 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit_to['.$extra_field_id.']', $unitIdTo, false, '', false, '', false, $attrsTo, $opts_attrs);
1310 * renderFormulaField - this function builds a formula field (RO).
1312 * @param string $contents The data for this field.
1315 function renderFormulaField($contents) {
1320 * renderSLAField - this function builds a formula field (RO).
1322 * @param int $extra_field_id The ID of this field.
1323 * @param string $contents The data for this field.
1327 function renderSLAField($extra_field_id, $contents, $ef) {
1330 if (!isset($aid) ||!$aid) {
1334 $config = parse_ini_string($ef["attributes"]);
1335 $field_name = $config['field'];
1336 $rule = explode(':', $config['values']);
1338 if (!isset($config['start_id']) || $aid >= $config['start_id']) {
1339 // Get Max value from
1340 // @todo use global variable $aid !!!
1341 $artifact = artifact_get_object($aid);
1342 $value = $artifact->getFieldDataByKey('alias', $field_name);
1343 $field = $artifact->getFieldTypeByKey('alias', $field_name);
1344 $elements = $artifact->ArtifactType->getExtraFieldElements($field['extra_field_id']);
1345 for ($i = 0; $i < count($elements); $i++) {
1346 if ($elements[$i]['element_name'] == $value) {
1347 $max = 60 * $rule[$i];
1353 $value = '<span class="label label-warning">Not Set</span>';
1354 } elseif ($ef['alias'] == 'response_time') {
1355 // Implement Response SLA: Time from create to first comment.
1356 $messages = $artifact->getMessageObjects();
1357 if (count($messages) > 0) {
1358 $message = $messages[count($messages) - 1];
1359 $current_time = $message->getAddDate();
1360 $timer_is_running = false;
1361 } elseif ($artifact->getStatusID() == 2) {
1362 $current_time = $artifact->getCloseDate();
1363 $timer_is_running = false;
1365 $current_time = time();
1366 $timer_is_running = true;
1368 $time = $current_time - $artifact->getOpenDate();
1369 $percent = round(100 * $time / $max);
1370 $remaining = $max - $time;
1371 if ($remaining >= 0) {
1372 $value = gmdate("G\h i", $remaining);
1374 $value = '<b>-' . gmdate("G\h i", -$remaining) . "</b>";
1377 if (!$timer_is_running) {
1378 if ($remaining >= 0) {
1379 $value = '<span class="label label-success">' . _('OK') . '</span>';
1381 $value = '<span class="label label-danger">' . _('Missed') . '</span>';
1384 if ($percent > 90) {
1385 $value = '<span class="sla-alert">' . $value . '</span>';
1386 } elseif ($percent > 80) {
1387 $value = '<span class="sla-warning">' . $value . '</span>';
1389 $value = '<span class="sla-normal">' . $value . '</span>';
1392 } elseif ($ef['alias'] == 'resolution_time') {
1393 // Implement Resolution SLA: Time from create to closed state.
1394 list($last_status_change, $timer, $timer_is_running) = json_decode($contents);
1397 if ($timer_is_running) {
1398 $time += time() - $last_status_change;
1401 $percent = round(100 * $time / $max);
1402 $remaining = $max - $time;
1403 if ($remaining >= 0) {
1404 $value = gmdate("G\h i", $remaining);
1406 $value = '<b>-' . gmdate("G\h i", -$remaining) . "</b>";
1408 if ($artifact->getStatusID() == 2) {
1409 if ($remaining >= 0) {
1410 $value = '<span class="label label-success">' . _('OK') . '</span>';
1412 $value = '<span class="label label-danger">' . _('Missed') . '</span>';
1415 if (!$timer_is_running) {
1416 $value = ' <span class="label label-info">' . _('Suspended') . '</span>';
1417 } elseif ($percent > 90) {
1418 $value = '<span class="sla-alert">' . $value . '</span>';
1419 } elseif ($percent > 80) {
1420 $value = '<span class="sla-warning">' . $value . '</span>';
1422 $value = '<span class="sla-normal">' . $value . '</span>';
1427 $value = '<span class="label label-default">' . _('Not Available') . '</span>';
1432 function technicianBox($name = 'assigned_to[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false, $attrs = array()) {
1433 if ($text_100=='none'){
1434 $text_100=_('Nobody');
1436 $engine = RBACEngine::getInstance();
1437 $techs = $engine->getUsersByAllowedAction('tracker', $this->getID(), 'tech') ;
1442 sortUserList($techs);
1444 foreach ($techs as $tech) {
1445 $ids[] = $tech->getID() ;
1446 $names[] = $tech->getRealName() ;
1449 if ($extra_id != '-1') {
1451 $names[]=$extra_name;
1455 if (!is_array($checked)) {
1456 $checked = explode(',', $checked);
1458 $size = min(count($ids)+1, 15);
1459 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100, false, $attrs);
1461 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100, false, false, false, $attrs);
1465 function submitterBox($name = 'submitted_by[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false, $attrs = array()) {
1466 if ($text_100=='none'){
1467 $text_100=_('Nobody');
1469 $result = $this->getSubmitters();
1470 $ids =& util_result_column_to_array($result,0);
1471 $names =& util_result_column_to_array($result,1);
1472 if ($extra_id != '-1') {
1474 $names[]=$extra_name;
1478 if (!is_array($checked)) {
1479 $checked = explode(',', $checked);
1481 $size = min(count($ids)+1, 15);
1482 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100, false, $attrs);
1484 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100, false, $attrs);
1488 function lastModifierBox($name='last_modified_by[]', $checked='xzxz', $show_100=true, $text_100='none', $extra_id='-1', $extra_name='', $multiple=false) {
1489 if ($text_100=='none'){
1490 $text_100=_('Nobody');
1492 $result = $this->getLastModifiers();
1493 $ids =& util_result_column_to_array($result,0);
1494 $names =& util_result_column_to_array($result,1);
1495 if ($extra_id != '-1') {
1497 $names[]=$extra_name;
1501 if (!is_array($checked)) {
1502 $checked = explode(',', $checked);
1504 $size = min(count($ids)+1, 15);
1505 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100);
1507 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100);
1511 function cannedResponseBox($name = 'canned_response', $checked = 'xzxz', $attrs = array()) {
1512 if (!isset($attrs['id'])) {
1513 $attrs['id'] = $name;
1515 return html_build_select_box($this->getCannedResponses(), $name, $checked, true, 'none', false, '', false, $attrs);
1519 * statusBox - show the statuses - automatically shows the "custom statuses" if they exist
1521 * @param string $name
1522 * @param string $checked
1523 * @param bool $show_100
1524 * @param string $text_100
1525 * @param array $attrs
1526 * @return string HTML code
1528 function statusBox($name = 'status_id', $checked = 'xzxz', $show_100 = false, $text_100 = 'none', $attrs = array()) {
1529 if ($text_100=='none'){
1530 $text_100=_('None');
1532 return html_build_select_box($this->getStatuses(), $name, $checked, $show_100, $text_100, false, '', false, $attrs);
1536 * priorityBox - show the priorities
1538 * @param string $name
1539 * @param string $checked_val
1540 * @param bool $nochange
1541 * @param array $attrs
1542 * @param bool $show_any
1543 * @return string HTML code
1545 function priorityBox($name = 'priority', $checked_val = '3', $nochange = false, $attrs = array(), $show_any = false){
1546 return html_build_priority_select_box($name, $checked_val, $nochange, $attrs, $show_any);
1549 function javascript() {
1551 var invalidSelectMsg = '"._("One or more of the selected options is not allowed")."';
1552 var invalidInputMsg = '". _("This choice is not allowed")."';
1553 var groupId =".$this->Group->getID().";
1554 var atId = ".$this->getID().";";
1555 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1556 if ($effortUnitSet->isAutoconvert()) {
1557 $jseffort = <<<'EOS'
1558 $("select.effort").change(function(){
1559 var effortid = $(this).data("effortid");
1560 var value = parseInt(parseInt($("input[name='extra_fields["+effortid+"]']").val())/$("select[name='unit["+effortid+"]'] option:selected").data('factor'));
1561 $("input[name='value["+effortid+"]']").val(value);
1562 $("input[name='extra_fields["+effortid+"]']").val(value*$("select[name='unit["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit["+effortid+"]']").val());
1564 $("input.effort").change(function(){
1565 var effortid = $(this).data("effortid");
1566 var value = $("input[name='value["+effortid+"]']").val();
1567 $("input[name='extra_fields["+effortid+"]']").val(value*$("select[name='unit["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit["+effortid+"]']").val());
1569 $(".effort-range").change(function(){
1570 var effortid = $(this).data("effortid");
1571 $("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());
1575 $jseffort = <<<'EOS'
1576 $(".effort").change(function(){
1577 var effortid = $(this).data("effortid");
1578 $("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());
1580 $(".effort-range").change(function(){
1581 var effortid = $(this).data("effortid");
1582 $("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());
1586 $javascript = <<<'EOS'
1587 function showMessage( msg_text, msg_class) {
1588 $("#maindiv h1").append($("<p>", { "class": msg_class }).html( msg_text )).show();
1590 $.expr[':'].invalid = function(elem, index, match) {
1591 var invalids = document.querySelectorAll(':invalid'), result = false, len = invalids.length;
1593 for (var i=0; i<len; i++) {
1594 if (elem === invalids[i]) {
1603 $("input[type='radio'].readonly, input[type='checkbox'].readonly").on('click', function(){
1605 }).on('keydown', function(event){
1606 if(event.keyCode !== 9) return false;
1608 $(".in-formula").change(function(){
1612 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(),
1615 success: function(answer){
1616 if(answer['message']) {
1617 showMessage(answer['message'], 'error');
1619 fields = answer['fields'];
1620 $.each(fields, function (index, field) {
1621 if (field.error!=null){
1622 showMessage(field.error, 'error');
1624 fieldObj = $("[name^='extra_fields["+field.id+"]']");
1625 if (fieldObj.is("input[type='checkbox']")){
1626 fieldObj.each(function() {
1628 for (var key in field.value) {
1629 if (field.value[key] == $(this).val()) {
1634 if (in_array > -1) {
1635 $(this).prop("checked",true);
1637 $(this).prop("checked",false);
1640 } else if (fieldObj.is("input[type='radio']")){
1641 fieldObj.each(function() {
1643 for (var key in field.value) {
1644 if (field.value[key] == $(this).val()) {
1649 if (in_array > -1) {
1650 $(this).prop("checked",true);
1652 $(this).prop("checked",false);
1655 } else if (fieldObj.is("input")){
1656 fieldObj.val(field.value);
1657 } else if (fieldObj.is("select")){
1658 fieldObj.val(field.value);
1659 } else if (fieldObj.is("textarea")){
1660 fieldObj.val(field.value);
1668 $("img.addparent").click(function(){
1672 data: 'rtype=ajax&function=add_parent&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&parent_id='+$("input#parent_id").val(),
1675 success: function(answer){
1676 if(answer['message']) {
1677 showMessage(answer['message'], 'error');
1679 $("table.parent").replaceWith(answer['parent']);
1680 $("input[name='extra_fields["+answer['parent_efid']+"]']").val(answer['parent_id']);
1681 $("div#show"+answer['parent_efid']).html(answer['parent_link']);
1682 $("input#parent_id").val('');
1683 $("div.addparent").addClass('hide');
1684 $("img.removeparent").click(function(){
1685 removeParent($(this).data("id"));
1693 $("img.addchild").click(function(){
1697 data: 'rtype=ajax&function=add_child&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&child_id='+$("input#child_id").val(),
1700 success: function(answer){
1701 if(answer['message']) {
1702 showMessage(answer['message'], 'error');
1704 $("table.children").replaceWith(answer['children']);
1705 $("input#child_id").val('');
1706 $("img.removechild").click(function(){
1707 removeChild($(this).data("id"));
1716 $("img.removeparent").click(function(){
1717 removeParent($(this).data("id"));
1720 function removeParent(id) {
1724 data: 'rtype=ajax&function=remove_parent&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&parent_id='+id,
1727 success: function(answer){
1728 if(answer['message']) {
1729 showMessage(answer['message'], 'error');
1731 $("table#parent"+id).remove();
1732 $("div.addparent").removeClass('hide');
1739 $("img.removechild").click(function(){
1740 removeChild($(this).data("id"));
1743 function removeChild(id) {
1747 data: 'rtype=ajax&function=remove_child&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&child_id='+id,
1750 success: function(answer){
1751 if(answer['message']) {
1752 showMessage(answer['message'], 'error');
1754 $("span#child"+id).remove();
1760 $(".autoassign[name^='extra_fields']").change(function(){
1761 if ($(this).prop('tagName') == 'SELECT') {
1762 var elmnts = $(this).children('option:selected');
1764 var elmnts = $(this).siblings('input:checked');
1766 elmnts.each(function(i){
1767 var aat = $(this).data("autoassignto");
1768 $("select#tracker-assigned_to option[value="+aat.id+"]").prop('selected', true);
1769 $("span#tracker-assigned_to").text(aat.name);
1772 $(".with-depcy[name^='extra_fields']").change(function(){
1773 if ($(this).prop('tagName') == 'SELECT') {
1774 var elmnts = $(this).children('option:selected');
1776 var elmnts = $(this).siblings('input:checked');
1778 elmnts.each(function(i){
1779 var dep = $(this).data("dependency");
1780 if (this.value!='100') {
1781 $(dep).each(function(j, val) {
1782 $("select[name^='extra_fields["+val.field+"]']:invalid, input[name^='extra_fields["+val.field+"]']:invalid").each(function() {
1783 this.setCustomValidity("");
1784 $(this).off("change.invalid");
1786 $("select[name^='extra_fields["+val.field+"]'] option").each(function(k,opt){
1787 if (this.value!='100') {
1788 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1789 $(this).prop('disabled', false).removeClass('option_disabled');
1791 $(this).prop('disabled', true);
1792 $(this).addClass('option_disabled');
1796 $("input[name^='extra_fields["+val.field+"]']").each(function(k,opt){
1797 if (this.value!='100') {
1798 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1799 $(this).prop('disabled', false).removeClass($(this).attr('type')+'_disabled');
1801 $(this).prop('disabled', true);
1802 $(this).addClass($(this).attr('type')+'_disabled');
1808 $(dep.fields).each(function(j, val) {
1809 $("select[name^='extra_fields["+val+"]']:invalid, input[name^='extra_fields["+val+"]']:invalid").each(function() {
1810 this.setCustomValidity("");
1812 $("select[name^='extra_fields["+val+"]'] option.option_disabled").each(function() {
1813 $(this).prop('disabled', false).removeClass('option_disabled');
1815 $("input.radio_disable[name^='extra_fields["+val+"]']").each(function() {
1816 $(this).prop('disabled', false).removeClass('radio_disabled');
1818 $("input.checkbox_disabled[name^='extra_fields["+val+"]']").each(function() {
1819 $(this).prop('disabled', false).removeClass('checkbox_disabled');
1824 $("select[name^='extra_fields'] option:selected:disabled").parent().each(function() {
1825 $(this).children('option:selected:disabled').prop('disabled', false);
1826 this.setCustomValidity(invalidSelectMsg);
1827 $(this).on("change.invalid", function() {
1828 $(this).children('option.option_disabled:not(:disabled):not(:selected)').prop('disabled', true);
1829 if (!$(this).children('option.option_disabled:selected').length) {
1830 this.setCustomValidity("");
1831 $(this).off("change.invalid");
1835 $("input[name^='extra_fields']:checked:disabled").each(function() {
1836 $(this).prop('disabled', false);
1837 this.setCustomValidity(invalidInputMsg);
1838 if ($(this).attr('type') == 'radio') {
1839 $(this).siblings('input[type="radio"]').on("change.invalid", function() {
1840 $(this).siblings('input[type="radio"]:invalid').prop('disabled', true).addClass('input_disabled').each(function() {
1841 this.setCustomValidity("");
1843 $(this).siblings('input[type="radio"]').off("change.invalid");
1844 $(this).off("change.invalid");
1847 $(this).on("change.invalid", function() {
1848 $(this).prop('disabled', true);
1849 this.setCustomValidity("");
1850 $(this).off("change.invalid");
1856 return html_e('script', array('type'=>'text/javascript'), '//<![CDATA['."\n".'$(function(){'.$jsvariable."\n".$jseffort."\n".$javascript.'});'."\n".'//]]>');
1863 // c-file-style: "bsd"