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.'widget/WidgetLayoutManager.class.php';
37 class ArtifactTypeHtml extends ArtifactType {
39 function header($params) {
41 if (!forge_get_config('use_tracker')) {
45 $group_id = $this->Group->getID();
47 //required by new site_project_header
48 $params['group'] = $group_id;
49 $params['toptab'] = 'tracker';
50 $params['tabtext'] = $this->getName();
56 $labels[] = _("View Trackers");
57 $links[] = '/tracker/?group_id='.$group_id;
58 $attr[] = array('title' => _('Get the list of available trackers'));
59 $labels[] = $this->getName();
60 $links[] = '/tracker/?func=browse&group_id='.$group_id.'&atid='. $this->getID();
61 $attr[] = array('title' => _('Browse this tracker.'));
62 $labels[] = _('Export CSV');
63 $links[] = '/tracker/?func=csv&group_id='.$group_id.'&atid='. $this->getID();
64 $attr[] = array('title' => _('Download data from this tracker as csv file.'));
65 if (forge_check_perm('tracker', $this->getID(),'submit')) {
66 $labels[] = _('Submit New');
67 $links[] = '/tracker/?func=add&group_id='.$group_id.'&atid='. $this->getID();
68 $attr[] = array('title' => _('Add a new issue.'));
71 if (session_loggedin()) {
72 $labels[] = _('Reporting');
73 $links[] = '/tracker/reporting/?group_id='.$group_id.'&atid='. $this->getID();
74 $attr[] = array('title' => _('Various graph about statistics.'));
75 if ($this->isMonitoring()) {
76 $labels[] = _('Stop Monitor');
77 $links[] = '/tracker/?group_id='.$group_id.'&atid='. $this->getID().'&func=monitor&stopmonitor=1';
78 $attr[] = array('title' => _('Remove this tracker from your monitoring.'));
80 $labels[] = _('Monitor');
81 $links[] = '/tracker/?group_id='.$group_id.'&atid='. $this->getID().'&func=monitor&startmonitor=1';
82 $attr[] = array('title' => _('Add this tracker from your monitoring.'));
85 if (forge_check_perm('tracker_admin', $group_id)) {
86 $labels[] = _('Administration');
87 $links[] = '/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID();
88 $attr[] = array('title' => _('Global administration for trackers. Create, clone, workflow, fields ...'));
90 if (forge_get_config('use_tracker_widget_display')) {
91 $sql = "SELECT l.* FROM layouts AS l INNER JOIN owner_layouts AS o ON(l.id = o.layout_id)
92 WHERE o.owner_type = $1
94 AND o.is_default = 1";
95 $res = db_query_params($sql,array('t', $this->getID()));
96 if($res && db_numrows($res) < 1) {
97 $lm = new WidgetLayoutManager();
98 $lm->createDefaultLayoutForTracker($this->getID());
99 $res = db_query_params($sql,array('t', $this->getID()));
101 $id = db_result($res, 0 , 'id');
102 $url = '/widgets/widgets.php?owner=t'.$this->getID().'&layout_id='.$id;
103 $labels[] = _('Add widgets');
104 $labels[] = _('Customize Layout');
105 $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.'));
106 $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.'));
108 $links[] = $url.'&update=layout';
113 $params['submenu'] = $HTML->subMenu($labels, $links, $attr);
114 site_project_header($params);
117 plugin_hook('blocks', 'tracker_'.$this->getName());
121 function footer($params = array()) {
122 site_project_footer($params);
125 function adminHeader($params) {
127 $this->header($params);
128 $group_id= $this->Group->getID();
129 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id, _('New Tracker'), array('title'=>_('Create a new tracker.'))));
130 $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.'))));
131 $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.'))));
132 $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.'))));
133 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&workflow=1', _('Manage Workflow'), array('title'=>_('Edit tracker workflow.'))));
134 $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.'))));
135 $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.'))));
136 $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.'))));
137 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&delete=1', _('Delete'), array('title'=>_('Permanently delete this tracker.'))));
138 $hook_params = array('group_id' => $group_id, 'atid' => $this->getID());
139 plugin_hook_by_reference("admin_tracker_add_actions", $hook_params);
140 if (isset($hook_params['result'])) {
141 foreach($hook_params['result'] as $action => $values) {
142 $elementLi[] = array('content' => util_make_link('/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID()."&$action=1", $values['text'], array('title'=>$values['description'])));
145 echo $HTML->html_list($elementLi);
148 function adminFooter($params) {
149 $this->footer($params);
152 function renderSubmitInstructions() {
153 $msg = $this->getSubmitInstructions();
154 return str_replace("\n","<br />", $msg);
157 function renderBrowseInstructions() {
158 $msg = $this->getBrowseInstructions();
159 return str_replace("\n","<br />", $msg);
163 * renderExtraFields - ???
165 * @param array $selected
166 * @param bool $show_100 Display the specific '100' value. Default is false.
167 * @param string $text_100 Label displayed for the '100' value. Default is 'none'
168 * @param bool $show_any
169 * @param string $text_any
170 * @param array $types
171 * @param bool $status_show_100 Force display of the '100' value if needed. Default is false.
172 * @param string $mode QUERY, DISPLAY, UPDATE, NEW
174 function renderExtraFields($selected = array(),
175 $show_100 = false, $text_100 = 'none',
176 $show_any = false, $text_any = 'Any',
178 $status_show_100 = false,
183 if ($mode == 'NEW') {
184 $efarr = $this->getExtraFields($types, false, false);
186 $efarr = $this->getExtraFields($types);
188 //each two columns, we'll reset this and start a new row
189 $template = $this->getRenderHTML($types, $mode);
191 if ($mode=='QUERY') {
192 $keys=array_keys($efarr);
193 for ($k=0; $k<count($keys); $k++) {
195 $type = $efarr[$i]['field_type'];
196 if ($type == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
197 $type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
198 $type == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
199 $type == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
200 $type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
201 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTISELECT;
202 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_USER ||
203 $type == ARTIFACT_EXTRAFIELDTYPE_MULTIUSER) {
204 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTIUSER;
205 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_RELEASE ||
206 $type == ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE) {
207 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE;
208 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_DATETIME ||
209 $type == ARTIFACT_EXTRAFIELDTYPE_DATE) {
210 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_DATERANGE;
211 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
212 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_EFFORTRANGE;
214 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_TEXT;
219 // 'DISPLAY' mode is for rendering in 'read-only' mode (for detail view).
220 if ($mode === 'DISPLAY') {
221 $keys=array_keys($efarr);
222 for ($k=0; $k<count($keys); $k++) {
226 if (!isset($selected[$efarr[$i]['extra_field_id']])) {
227 $selected[$efarr[$i]['extra_field_id']] = '';
229 $value = $selected[$efarr[$i]['extra_field_id']];
230 $type = $efarr[$i]['field_type'];
232 if ($type == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
233 $type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
234 $type == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
235 $type == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
236 $type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
238 $value = $efarr[$i]['show100label'];
240 $arr = $this->getExtraFieldElements($efarr[$i]['extra_field_id']);
242 // Convert the values (ids) to names in the ids order.
244 for ($j=0; $j<count($arr); $j++) {
245 if (is_array($value) && in_array($arr[$j]['element_id'], $value)) {
246 $new[]= $arr[$j]['element_name'];
247 } elseif ($arr[$j]['element_id'] === $value) {
248 $new[] = $arr[$j]['element_name'];
251 $value = join('<br />', $new);
253 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
254 $type == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
255 $value = preg_replace('/((http|https|ftp):\/\/\S+)/',
256 "<a href=\"\\1\" target=\"_blank\">\\1</a>", $value);
257 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_RELATION || $type == ARTIFACT_EXTRAFIELDTYPE_PARENT) {
258 // Convert artifact id to links.
259 $value = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $value);
260 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_DATETIME && $value!='') {
261 $value = date('Y-m-d H:i', $value);
262 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
263 if (!isset($effortUnitSet)) {
264 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
265 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
267 $value = $effortUnitFactory->encodedToString($value);
269 $template = str_replace('{$PostName:'.$efarr[$i]['field_name'].'}', $post_name, $template);
270 $template = str_replace('{$'.$efarr[$i]['field_name'].'}', $value, $template);
275 if ($mode == 'UPDATE' || $mode == 'NEW') {
276 if ($mode == 'NEW') {
277 $efInFormula = $this->getExtraFieldsInFormula($types, false, false);
278 $efWithFormula = $this->getExtraFieldsWithFormula($types, false, false);
280 $efInFormula = $this->getExtraFieldsInFormula($types);
281 $efWithFormula = $this->getExtraFieldsWithFormula($types);
285 $keys = array_keys($efarr);
286 for ($k = 0; $k < count($keys); $k++) {
291 if (!empty($efarr[$i]['description'])) {
292 $attrs['title'] = $efarr[$i]['description'];
294 if ($efarr[$i]['is_required'] == 1 && $mode != 'QUERY') {
295 $attrs['required'] = 'required';
298 if ($mode == 'UPDATE' || $mode == 'NEW') {
299 if (in_array($efarr[$i]['extra_field_id'], $efInFormula)) {
300 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'in-formula';
302 if (in_array($efarr[$i]['extra_field_id'], $efWithFormula)) {
303 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-formula readonly';
304 $attrs['readonly'] = 'readonly';
308 if (!isset($selected[$efarr[$i]['extra_field_id']])) {
309 $selected[$efarr[$i]['extra_field_id']] = '';
311 if ($status_show_100) {
312 $efarr[$i]['show100'] = $status_show_100;
317 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
318 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
319 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
320 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
321 if (!is_null($efarr[$i]['parent']) && !empty($efarr[$i]['parent']) && $efarr[$i]['parent']!='100') {
322 $aefParentId = $efarr[$i]['parent'];
323 $selectedElmnts = (isset($selected[$aefParentId]) ? $selected[$aefParentId] : '');
324 $aef = new ArtifactExtraField($this, $efarr[$i]['extra_field_id']);
325 $allowed = $aef->getAllowedValues($selectedElmnts);
329 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT) {
330 $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);
332 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) {
334 $str = $this->renderCheckbox($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $allowed, $attrs);
336 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO) {
338 $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);
340 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
341 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER) {
343 if (!empty($efarr[$i]['pattern'])) {
344 $attrs['pattern'] = $efarr[$i]['pattern'];
346 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT) {
347 $str = $this->renderTextField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
349 $str = $this->renderIntegerField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
351 if ($mode == 'QUERY') {
352 $post_name = ' <i>'._('(%% for wildcards)').'</i> ';
355 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
357 $str = $this->renderTextArea($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
358 if ($mode == 'QUERY') {
359 $post_name = ' <i>'._('(%% for wildcards)').'</i>';
362 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
364 $str = $this->renderMultiSelectBox($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['show100'], $efarr[$i]['show100label'], $allowed, $attrs);
366 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
367 // parent artifact can't be close if a child is still open
368 if ($mode == 'UPDATE' &&
369 $efarr[$i]['aggregation_rule'] == ARTIFACT_EXTRAFIELD_AGGREGATION_RULE_STATUS_CLOSE_RESTRICTED &&
370 $ah->hasChildren()) {
371 $children = $ah->getChildren();
373 foreach ($children as $child) {
374 if ($child['status_id'] == 1) {
380 $aef = new ArtifactExtraField($this, $efarr[$i]['extra_field_id']);
381 $statusArr = $aef->getAvailableValues();
382 $openStatus = array();
383 foreach ($statusArr as $status) {
384 if ($status['status_id']==1) {
385 $openStatus[] = $status['element_id'];
389 $allowed = array_intersect($allowed, $openStatus);
391 $allowed = $openStatus;
396 // Get the allowed values from the workflow.
397 $atw = new ArtifactWorkflow($this, $efarr[$i]['extra_field_id']);
399 // Special treatment for the initial step (Submit).
400 // In this case, the initial value is the first value.
401 if (isset($selected[$efarr[$i]['extra_field_id']]) && $selected[$efarr[$i]['extra_field_id']]) {
402 $selected_node = $selected[$efarr[$i]['extra_field_id']];
404 $selected_node = 100;
408 $allowedWF = $atw->getNextNodes($selected_node);
410 $allowed = array_intersect($allowed, $allowedWF);
412 $allowed = $allowedWF;
414 $allowed[] = $selected_node;
415 $str = $this->renderSelect($efarr[$i]['extra_field_id'], $selected_node, $status_show_100, $text_100, $show_any, $text_any, $allowed, $attrs);
417 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
418 $str = $this->renderRelationField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
419 if ($mode == 'UPDATE' || $mode == 'NEW') {
420 $post_name = $HTML->getEditFieldPic(_('Click to edit'), $alt = _('Click to edit'), array('onclick'=>"switch2edit(this, 'show$i', 'edit$i')"));
422 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_PARENT) {
423 $str = $this->renderParentField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
424 if ($mode == 'UPDATE' || $mode == 'NEW') {
425 $post_name = $HTML->getEditFieldPic(_('Click to edit'), $alt = _('Click to edit'), array('onclick'=>"switch2edit(this, 'show$i', 'edit$i')"));
427 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME) {
428 $str = $this->renderDatetime($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $attrs);
429 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER) {
430 $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);
431 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTIUSER) {
432 $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);
433 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE) {
434 $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);
435 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE) {
436 $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);
437 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATERANGE) {
438 if ($mode == 'QUERY') {
439 $post_name = ' <i>'._('(YYYY-MM-DD YYYY-MM-DD Format)').'</i>';
441 $str = $this->renderDateRange($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $attrs);
442 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
443 $str = $this->renderEffort($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
444 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_EFFORTRANGE) {
445 $str = $this->renderEffortRange($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $efarr[$i]['attribute1'], $efarr[$i]['attribute2'], $attrs);
446 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SLA) {
447 $str = $this->renderSLAField($efarr[$i]['extra_field_id'], $selected[$efarr[$i]['extra_field_id']], $attrs);
449 $template = str_replace('{$PostName:'.$efarr[$i]['field_name'].'}', $post_name, $template);
450 $template = str_replace('{$'.$efarr[$i]['field_name'].'}', $str, $template);
452 if($template != NULL){
453 if ($mode == 'UPDATE' || $mode == 'NEW' || $mode == 'QUERY') {
454 echo $this->javascript();
460 function renderRelatedTasks($group, $ah, $formid = null) {
462 if (!$group->usesPM()) {
468 $tasks = $ah->getRelatedTasks();
469 $taskcount = db_numrows($tasks);
471 if (forge_check_perm('tracker_admin', $ah->ArtifactType->Group->getID())) {
476 $totalPercentage = 0;
477 if ($taskcount > 0) {
478 $title_arr = array();
479 $title_arr[] = _('Task Id and Summary');
480 $title_arr[] = _('Progress');
481 $title_arr[] = _('Start Date');
482 $title_arr[] = _('End Date');
483 $title_arr[] = _('Status');
484 (($is_admin) ? $title_arr[]=_('Remove Relation') : '');
485 $return .= $HTML->listTableTop($title_arr);
487 while ($taskinfo = db_fetch_array($tasks)) {
488 $totalPercentage += $taskinfo['percent_complete'];
489 $taskid = $taskinfo['project_task_id'];
490 $projectid = $taskinfo['group_project_id'];
491 $groupid = $taskinfo['group_id'];
492 if (forge_check_perm('pm', $projectid, 'read')) {
493 $summary = util_unconvert_htmlspecialchars($taskinfo['summary']);
494 $startdate = date(_('Y-m-d H:i'), $taskinfo['start_date']);
495 $enddate = date(_('Y-m-d H:i'), $taskinfo['end_date']);
496 $status = $taskinfo['status_name'];
498 <td>'.util_make_link('/pm/t_follow.php/'.$taskid, '[T'.$taskid.'] '.$summary).'</td>
499 <td><div class="percentbar" style="width: 100px;">
500 <div style="width:'.round($taskinfo['percent_complete']).'px;"></div></div></td>
501 <td>'.$startdate.'</td>
502 <td>'.$enddate.'</td>
503 <td>'.$status.' ('.$taskinfo['percent_complete'].'%)</td>'.
504 (($is_admin) ? '<td><input type="checkbox" '.(($formid) ? 'form="'.$formid.'"' : '').' name="remlink[]" value="'.$taskid.'" /></td>' : '').
508 $return .= $HTML->listTableBottom();
510 $return .= "\n<hr /><p style=\"text-align:right;\">";
511 $return .= _('Average completion rate')._(': ').(int)($totalPercentage/$taskcount).'%';
514 $return .= $HTML->information(_('No related tasks'));
519 function renderFiles($group_id, $ah, $formid = null) {
521 $file_list =& $ah->getFiles();
522 $count=count($file_list);
526 $return .= '<strong>'._("Attachments")._(':').'</strong>'.'<br/>';
528 $title_arr[] = _('Size');
529 $title_arr[] = _('Name');
530 $title_arr[] = _('Date');
531 $title_arr[] = _('By');
532 $title_arr[] = _('Download');
533 if (forge_check_perm('tracker', $this->getID(), 'tech')) {
536 $return .= $HTML->listTableTop($title_arr);
538 foreach ($file_list as $file) {
540 $return .= '<td>'.human_readable_bytes($file->getSize()).'</td>';
541 $return .= '<td>'.htmlspecialchars($file->getName()).'</td>';
542 $return .= '<td>'.date(_('Y-m-d H:i'), $file->getDate()).'</td>';
543 $return .= '<td>'.util_display_user($file->getSubmittedUnixName(), $file->getSubmittedBy(), $file->getSubmittedRealName()).'</td>';
544 $return .= '<td>'.util_make_link('/tracker/download.php/'.$group_id.'/'. $this->getID().'/'. $ah->getID() .'/'.$file->getID().'/'.$file->getName(), htmlspecialchars($file->getName())).'</td>';
545 if (forge_check_perm('tracker', $this->getID(), 'tech')) {
546 $return .= '<td><input type="checkbox" name="delete_file[]" '.(($formid) ? 'form="'.$formid.'"' : '').' value="'. $file->getID() .'">'._('Delete').'</td>';
551 $return .= $HTML->listTableBottom();
553 $return .= $HTML->information(_('No attached documents'));
561 * @param array $types
562 * @param string $mode
563 * @return string HTML template.
565 function getRenderHTML($types=array(), $mode='') {
566 // Use template only for the browse (not for query or mass update)
567 if (($mode === 'DISPLAY' || $mode === 'DETAIL' || $mode === 'UPDATE' || $mode == 'NEW')
568 && $this->data_array['custom_renderer']) {
569 return preg_replace('/<!--(\S+.*?)-->/','{$\\1}', $this->data_array['custom_renderer']);
571 return $this->generateRenderHTML($types, $mode);
578 * @param array $types
579 * @param string $mode Display mode (QUERY OR DISPLAY OR NEW)
580 * @return string HTML template.
582 function generateRenderHTML($types, $mode) {
583 if ($mode == 'NEW') {
584 $efarr = $this->getExtraFields($types, false, false);
586 $efarr = $this->getExtraFields($types);
588 //each two columns, we'll reset this and start a new row
591 <!-- Start Extra Fields Rendering -->
595 $keys=array_keys($efarr);
600 for ($k=0; $k<$count; $k++) {
603 // Do not show the required star in query mode (creating/updating a query).
604 $is_required = ($mode == 'QUERY' || $mode == 'DISPLAY') ? 0 : $efarr[$i]['is_required'];
605 if ($mode == 'QUERY' && ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME || $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATE)) {
606 $name = sprintf(_('%s range'), $efarr[$i]['field_name']).($is_required ? utils_requiredField() : '')._(': ');
608 $name = $efarr[$i]['field_name'].($is_required ? utils_requiredField() : '')._(': ');
610 $name = '<strong>'.$name.'</strong>';
612 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
613 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
614 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
615 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT ||
616 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
617 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER ||
618 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE ) {
621 <td class="halfwidth top">'.$name.'<br />{$'.$efarr[$i]['field_name'].'}</td>';
623 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
624 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER ||
625 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION ||
626 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_PARENT ||
627 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME ||
628 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_EFFORT) {
630 //text fields might be really wide, so need a row to themselves.
631 if (($col_count == 1) && ($efarr[$i]['attribute1'] > 30)) {
641 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
643 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
645 //text areas might be really wide, so need a row to themselves.
646 if (($col_count == 1) && ($efarr[$i]['attribute2'] > 30)) {
656 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
661 //we've done two columns - if there are more to do, start a new row
662 if (($col_count == 2) && ($k != $count-1)) {
669 if ($col_count == 1) {
675 <!-- End Extra Fields Rendering -->';
680 * renderSelect - this function builds pop up box with choices.
682 * @param int $extra_field_id The ID of this field.
683 * @param string $checked The item that should be checked
684 * @param bool|string $show_100 Whether to show the '100 row'
685 * @param string $text_100 What to call the '100 row'
686 * @param bool $show_any
687 * @param string $text_any
688 * @param bool $allowed
689 * @param array $attrs
690 * @return string HTML code for the box and choices
692 function renderSelect($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
693 if ($text_100 == 'none'){
696 $arr = $this->getExtraFieldElements($extra_field_id);
697 $aef = new ArtifactExtraField($this, $extra_field_id);
698 $aefChildren = $aef->getChildren();
699 if (!empty($aefChildren)) {
700 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
702 if ($aef->isAutoAssign()) {
703 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'autoassign';
707 $opt_attrs = array();
708 $attrs_100 = array();
710 for ($i=0; $i<count($arr); $i++) {
711 $vals[$i]=$arr[$i]['element_id'];
712 $texts[$i]=$arr[$i]['element_name'];
713 $opt_attrs[$i]=array();
714 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
715 if (!empty($aefChildren)) {
716 $cElmntArr = $aefe->getChildrenElements();
717 if (!empty($cElmntArr))
720 foreach ($cElmntArr as $key=>$cElmnt) {
721 $childField = new ArtifactExtraField($this, $key);
722 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
724 $dependency = '['.$dependency.']';
725 $opt_attrs[$i]= array('data-dependency'=>$dependency);
728 if ($aef->isAutoAssign()) {
729 $autoAssignTo = user_get_object($aefe->getAutoAssignto());
730 $opt_attrs[$i]=array_merge(isset($opt_attrs[$i]) ? $opt_attrs[$i] : array(), array('data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
734 if ($show_100 && !empty($aefChildren)) {
735 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
737 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);
741 * renderUserField - this function builds pop up box with users.
743 * @param int $extra_field_id The ID of this field.
744 * @param string $checked The item that should be checked
745 * @param bool|string $show_100 Whether to show the '100 row'
746 * @param string $text_100 What to call the '100 row'
747 * @param bool $show_any
748 * @param string $text_any
749 * @param bool $allowed
750 * @param array $attrs
751 * @return string HTML code for the box and choices
753 function renderUserField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
754 if ($text_100 == 'none' || $text_100 == 'nobody'){
755 $text_100=_('Nobody');
757 $arr = $this->getExtraFieldElements($extra_field_id);
758 $selectedRolesId = array();
759 for ($i=0; $i<count($arr); $i++) {
760 $selectedRolesId[$i]=$arr[$i]['element_name'];
762 $roles = $this->getGroup()->getRoles();
763 $userArray = array();
764 foreach ($roles as $role) {
765 if (in_array($role->getID(), $selectedRolesId)) {
766 foreach ($role->getUsers() as $user) {
767 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
771 if (is_integer($checked) && !isset($userArray[$checked])) {
772 $checkedUser = user_get_object($checked);
773 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
775 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
776 $keys = array_keys($userArray);
777 $vals = array_values($userArray);
778 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);
782 * renderMultiUserField - this function builds pop up box with users.
784 * @param int $extra_field_id The ID of this field.
785 * @param string $checked The item that should be checked
786 * @param bool|string $show_100 Whether to show the '100 row'
787 * @param string $text_100 What to call the '100 row'
788 * @param bool $show_any
789 * @param string $text_any
790 * @param bool $allowed
791 * @param array $attrs
792 * @return string HTML code for the box and choices
794 function renderMultiUserField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs = array()) {
795 if ($text_100 == 'none' || $text_100 == 'nobody'){
796 $text_100=_('Nobody');
802 if (!is_array($checked)) {
803 $checked = explode(',', $checked);
806 $arr = $this->getExtraFieldElements($extra_field_id);
807 $selectedRolesId = array();
808 for ($i=0; $i<count($arr); $i++) {
809 $selectedRolesId[$i]=$arr[$i]['element_name'];
811 $roles = $this->getGroup()->getRoles();
812 $userArray = array();
813 foreach ($roles as $role) {
814 if (in_array($role->getID(), $selectedRolesId)) {
815 foreach ($role->getUsers() as $user) {
816 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
820 if (is_integer($checked) && !isset($userArray[$checked])) {
821 $checkedUser = user_get_object($checked);
822 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
824 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
825 $size = min(count($userArray)+1, 15);
826 $keys = array_keys($userArray);
827 $vals = array_values($userArray);
829 return html_build_multiple_select_box_from_arrays($keys, $vals,'extra_fields['.$extra_field_id.'][]', $checked, $size, $show_100, $text_100, $allowed, $attrs);
833 * renderReleaseField - this function builds 2 pop up boxes with packages & releases.
835 * @param int $extra_field_id The ID of this field.
836 * @param string $checked The item that should be checked
837 * @param bool|string $show_100 Whether to show the '100 row'
838 * @param string $text_100 What to call the '100 row'
839 * @param bool $show_any
840 * @param string $text_any
841 * @param bool $allowed
842 * @param array $attrs
843 * @return string HTML code for the box and choices
845 function renderReleaseField($extra_field_id, $checked = 'xzxz', $show_100 = false, $text_100 = 'none', $show_any = false, $text_any = 'Any', $allowed = false, $attrs = array()) {
846 if ($text_100 == 'none'){
850 $releasesArray = array();
851 $releasesAttrs = array();
854 $arr = $this->getExtraFieldElements($extra_field_id);
855 $selectedPackagesId = array();
856 for ($i=0; $i<count($arr); $i++) {
857 $selectedPackagesId[$i]=$arr[$i]['element_name'];
860 $fpFactory = new FRSPackageFactory($this->getGroup());
861 if ($fpFactory && is_object($fpFactory) && !$fpFactory->isError()) {
862 $packages = $fpFactory->getFRSs(true);
863 uasort($packages, 'compareObjectName');
864 foreach ($packages as $package) {
865 $releases = $package->getReleases(false);
866 uasort($releases, 'compareObjectName');
867 foreach ($releases as $release) {
868 $optGroup[] = $package->getName();
869 $releasesArray[$release->getID()] = $release->getName();
873 $keys = array_keys($releasesArray);
874 $vals = array_values($releasesArray);
875 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);
878 return $HTML->error_msg(_('Unable to get FRS Package Factory'));
883 * renderMultiReleaseField - this function builds 2 pop up boxes with packages & releases.
885 * @param int $extra_field_id The ID of this field.
886 * @param string $checked The item that should be checked
887 * @param bool|string $show_100 Whether to show the '100 row'
888 * @param string $text_100 What to call the '100 row'
889 * @param bool $show_any
890 * @param string $text_any
891 * @param bool $allowed
892 * @param array $attrs
893 * @return string HTML code for the box and choices
895 function renderMultiReleaseField($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed=false, $attrs=array()) {
896 if ($text_100 == 'none'){
902 if (!is_array($checked)) {
903 $checked = explode(',', $checked);
906 $releasesArray = array();
907 $releasesAttrs = array();
910 $arr = $this->getExtraFieldElements($extra_field_id);
911 $selectedPackagesId = array();
912 for ($i=0; $i<count($arr); $i++) {
913 $selectedPackagesId[$i]=$arr[$i]['element_name'];
916 $packages = get_frs_packages($this->getGroup());
917 uasort($packages, 'compareObjectName');
918 foreach ($packages as $package) {
919 if (in_array($package->getID(), $selectedPackagesId)) {
920 $releases = $package->getReleases();
921 uasort($releases, 'compareObjectName');
922 foreach ($releases as $release) {
923 $optGroup[] = $package->getName();
924 $releasesArray[$release->getID()] = $release->getName();
928 $size = min(count($releasesArray) + count($optGroup) + 1, 15);
929 $keys = array_keys($releasesArray);
930 $vals = array_values($releasesArray);
931 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);
935 * renderRadio - this function builds radio buttons.
937 * @param int $extra_field_id The $int ID of this field.
938 * @param string $checked The $string item that should be checked
939 * @param bool $show_100 Whether $string to show the '100 row'
940 * @param string $text_100 What $string to call the '100 row'
941 * @param bool $show_any
942 * @param string $text_any
943 * @param bool $allowed
944 * @param array $attrs Array of other attributes
945 * @return string HTML code using radio buttons
947 function renderRadio($extra_field_id, $checked='xzxz', $show_100=false, $text_100='none', $show_any=false, $text_any='Any', $allowed = false, $attrs = array()) {
949 $arr = $this->getExtraFieldElements($extra_field_id);
950 $aef = new ArtifactExtraField($this, $extra_field_id);
951 $aefChildren = $aef->getChildren();
952 if (!empty($aefChildren)) {
953 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
958 $radios_attrs = array();
959 $attrs_100 = array();
961 for ($i=0; $i<count($arr); $i++) {
962 $vals[$i]=$arr[$i]['element_id'];
963 $texts[$i]=$arr[$i]['element_name'];
964 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
966 if (!empty($aefChildren)) {
967 $cElmntArr = $aefe->getChildrenElements();
968 if (!empty($cElmntArr)) {
969 foreach ($cElmntArr as $key=>$cElmnt) {
970 $childField = new ArtifactExtraField($this, $key);
971 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
973 $dependency = '['.$dependency.']';
974 $radios_attrs[$i]['data-dependency']=$dependency;
977 if ($aef->isAutoAssign()) {
978 $autoAssignTo = user_get_object($aefe->getAutoAssignto());
979 $radios_attrs[$i]=array_merge(isset($radios_attrs[$i]) ? $radios_attrs[$i] : array(), array('data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
982 if ($show_100 && !empty($aefChildren)) {
983 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
985 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);
989 * renderCheckbox - this function builds checkboxes.
991 * @param int $extra_field_id The ID of this field.
992 * @param array $checked The items that should be checked
993 * @param bool|string $show_100 Whether to show the '100 row'
994 * @param string $text_100 What to call the '100 row'
995 * @param bool $allowed
996 * @param array $attrs Array of other attributes
997 * @return string radio buttons
999 function renderCheckbox($extra_field_id, $checked=array(), $show_100=false, $text_100='none', $allowed=false, $attrs = array()) {
1000 if ($text_100 == 'none'){
1001 $text_100=_('None');
1004 if (!$checked || !is_array($checked)) {
1007 if (!empty($attrs['title'])) {
1008 $attrs['title'] = util_html_secure($attrs['title']);
1011 $arr = $this->getExtraFieldElements($extra_field_id);
1012 $aef = new ArtifactExtraField($this, $extra_field_id);
1013 $aefChildren = $aef->getChildren();
1014 if (!empty($aefChildren)) {
1015 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
1020 $chk_attrs = array();
1021 $attrs_100 = array();
1023 for ($i=0; $i<count($arr); $i++) {
1024 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
1025 $vals[$i] = $arr[$i]['element_id'];
1026 $texts[$i] = $arr[$i]['element_name'];
1027 $chk_attrs[$i] = array();
1029 if (!empty($aefChildren)) {
1030 $cElmntArr = $aefe->getChildrenElements();
1031 if (!empty($cElmntArr))
1033 foreach ($cElmntArr as $key=>$cElmnt) {
1034 $childField = new ArtifactExtraField($this, $key);
1035 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1037 $dependency = '['.$dependency.']';
1038 $chk_attrs[$i]['data-dependency']=$dependency;
1042 if ($show_100 && !empty($aefChildren)) {
1043 $attrs_100['data-dependency'] ='{"fields": ['.implode(', ', $aefChildren).']}';
1045 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);
1049 * renderMultiSelectBox - this function builds checkboxes.
1051 * @param int $extra_field_id The ID of this field.
1052 * @param array $checked The items that should be checked
1053 * @param bool|string $show_100 Whether to show the '100 row'
1054 * @param string $text_100 What to call the '100 row'
1055 * @param bool $allowed
1056 * @param array $attrs Array of other attributes
1057 * @return string radio multiselectbox
1059 function renderMultiSelectBox($extra_field_id, $checked=array(), $show_100=false, $text_100='none', $allowed=false, $attrs=array()) {
1063 if (!is_array($checked)) {
1064 $checked = explode(',', $checked);
1066 $arr = $this->getExtraFieldElements($extra_field_id);
1067 $aef = new ArtifactExtraField($this, $extra_field_id);
1068 $aefChildren = $aef->getChildren();
1069 if (!empty($aefChildren)) {
1070 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
1074 $opt_attrs = array();
1075 $attrs_100 = array();
1077 for ($i=0; $i<count($arr); $i++) {
1078 $vals[$i]=$arr[$i]['element_id'];
1079 $texts[$i]=$arr[$i]['element_name'];
1080 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
1081 if (!empty($aefChildren)) {
1082 $cElmntArr = $aefe->getChildrenElements();
1083 if (!empty($cElmntArr))
1086 foreach ($cElmntArr as $key=>$cElmnt) {
1087 $childField = new ArtifactExtraField($this, $key);
1088 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1090 $dependency = '['.$dependency.']';
1091 $opt_attrs[$i]= array('data-dependency'=>$dependency);
1095 $size = min(count($arr)+1, 15);
1096 if ($show_100 && !empty($aefChildren)) {
1097 $attrs_100 = array('data-dependency'=>'{"fields": ['.implode(', ', $aefChildren).']}');
1099 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);
1103 * renderTextField - this function builds a text field.
1105 * @param int $extra_field_id The ID of this field.
1106 * @param string $contents The data for this field.
1107 * @param string $size
1108 * @param string $maxlength
1109 * @param array $attrs Array of other attributes
1110 * @return string HTML code of corresponding input tag.
1112 function renderTextField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1113 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength), $attrs));
1117 * renderIntegerField - this function builds a text field.
1119 * @param int $extra_field_id The ID of this field.
1120 * @param string $contents The data for this field.
1121 * @param string $size
1122 * @param string $maxlength
1123 * @param array $attrs Array of other attributes
1124 * @return string HTML code of corresponding input tag.
1126 function renderIntegerField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1127 $intAttrs = array('type'=>'number', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0);
1128 $newattrs = array_merge($intAttrs, $attrs);
1129 return html_e('input', $newattrs);
1133 * renderRelationField - this function builds a relation field.
1135 * @param int $extra_field_id The ID of this field.
1136 * @param string $contents The data for this field.
1137 * @param string $size
1138 * @param string $maxlength
1139 * @param array $attrs Array of other attributes
1140 * @return string text area and data.
1142 function renderRelationField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1143 $arr = $this->getExtraFieldElements($extra_field_id);
1144 for ($i=0; $i<count($arr); $i++) {
1145 $keys[$i]=$arr[$i]['element_id'];
1146 $vals[$i]=$arr[$i]['element_name'];
1148 // Convert artifact id to links.
1149 $attrsTxt = array();
1150 if (isset($attrs['form'])) {
1151 $attrsTxt['form'] = $attrs['form'];
1153 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1154 $edit_contents = $this->renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrsTxt);
1155 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)
1156 .html_e('div',array_merge(array('id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1160 * renderParentField - this function builds a parent field.
1162 * @param int $extra_field_id The ID of this field.
1163 * @param string $contents The data for this field.
1164 * @param string $size
1165 * @param string $maxlength
1166 * @param array $attrs Array of other attributes
1167 * @return string text area and data.
1169 function renderParentField($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1171 $arr = $this->getExtraFieldElements($extra_field_id);
1172 for ($i=0; $i<count($arr); $i++) {
1173 $keys[$i]=$arr[$i]['element_id'];
1174 $vals[$i]=$arr[$i]['element_name'];
1176 $attrsTxt = array();
1177 if (isset($attrs['form'])) {
1178 $attrsTxt['form'] = $attrs['form'];
1180 if (is_object($ah)) {
1181 $attrsTxt['pattern']='^(?!'.$ah->getID().'$)\d*$';
1183 $attrsTxt['pattern']='^\d*$';
1185 // Convert artifact id to links.
1186 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1187 $edit_contents = $this->renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrsTxt);
1188 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)
1189 .html_e('div',array_merge(array('id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1193 * renderTextArea - this function builds a text area.
1195 * @param int $extra_field_id The ID of this field.
1196 * @param string $contents The data for this field.
1197 * @param string $rows
1198 * @param string $cols
1199 * @param array $attrs Array of other attributes
1200 * @return string text area and data.
1202 function renderTextArea($extra_field_id, $contents, $rows, $cols, $attrs = array()) {
1203 return html_e('textarea', array_merge(array('name'=>'extra_fields['.$extra_field_id.']', 'rows'=>$rows, 'cols'=>$cols), $attrs), $contents, false);
1207 * renderDatetime - this function builds a Datetime field.
1209 * @param int $extra_field_id The ID of this field.
1210 * @param string $datetime datetime for this field.
1211 * @param array $attrs Array of other attributes
1212 * @return string datetime.
1214 function renderDatetime($extra_field_id, $datetime, $attrs = array()) {
1215 if (!$datetime=='') {
1216 $datetime_format = _('Y-m-d H:i');
1217 $datetime = date($datetime_format, $datetime);
1219 if (isset($attrs['class'])) {
1220 $attrs['class'] = $attrs['class'] . ' datetimepicker';
1222 $attrs['class'] = 'datetimepicker';
1224 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$datetime), $attrs));
1227 function renderDateRange($extra_field_id, $dateRange, $attrs = array()) {
1228 // http://html5pattern.com/Dates
1229 // Date with leapyear-check
1230 $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)))';
1231 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));
1235 * renderEffort - this function builds a Effort extra field.
1237 * @param int $extra_field_id The ID of this field.
1238 * @param string $contents The data for this field.
1239 * @param string $size
1240 * @param string $maxlength
1241 * @param array $attrs Array of other attributes
1242 * @return string text area and data.
1244 function renderEffort($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1245 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1246 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
1247 $units = $effortUnitFactory->getUnits();
1251 foreach ($units as $unit) {
1252 $vals [] = $unit->getID();
1253 $texts [] = $unit->getName();
1254 $opts_attrs []['data-factor'] = $unit->getConversionFactorForBaseUnit();
1256 $valueInUnitBase = $effortUnitFactory->encodedToValueInBaseUnit($contents);
1257 $value = $effortUnitFactory->encodedToValue($contents);
1258 $unitId = $effortUnitFactory->encodedToUnitId($contents);
1259 if (isset($attrs['class'])) {
1260 $attrs['class'] .= ' effort';
1262 $attrs['class'] = 'effort';
1264 $attrs['data-effortid'] = $extra_field_id;
1265 $hiddenAttrs = array();
1266 if (isset($attrs['form'])) {
1267 $hiddenAttrs['form'] = $attrs['form'];
1269 $return = html_e('input', array_merge(array('type'=>'hidden', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$valueInUnitBase.'U'.$unitId), $hiddenAttrs));
1270 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value['.$extra_field_id.']', 'value'=>$value, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrs));
1271 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit['.$extra_field_id.']', $unitId, false, '', false, '', false, $attrs, $opts_attrs);
1275 function renderEffortRange($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1276 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1277 $effortUnitFactory = New EffortUnitFactory($effortUnitSet);
1278 $units = $effortUnitFactory->getUnits();
1281 $opts_attrs = array();
1282 foreach ($units as $unit) {
1283 $vals [] = $unit->getID();
1284 $texts [] = $unit->getName();
1285 $opts_attrs []['data-factor'] = $unit->getConversionFactorForBaseUnit();
1287 $contentsFrom = '0U'.$vals[0];
1288 $contentsTo = '0U'.$vals[0];
1289 if (preg_match('/^(\d+U\d+) ?(\d+U\d+)?$/', $contents, $matches)) {
1290 $contentsFrom = $matches[1];
1291 $contentsTo = $matches[2];
1293 $valueInUnitBaseFrom = $effortUnitFactory->encodedToValueInBaseUnit($contentsFrom);
1294 $valueInUnitBaseTo = $effortUnitFactory->encodedToValueInBaseUnit($contentsTo);
1295 $valueFrom = $effortUnitFactory->encodedToValue($contentsFrom);
1296 $valueTo = $effortUnitFactory->encodedToValue($contentsTo);
1297 $unitIdFrom = $effortUnitFactory->encodedToUnitId($contentsFrom);
1298 $unitIdTo = $effortUnitFactory->encodedToUnitId($contentsTo);
1299 $attrs['data-effortid'] = $extra_field_id;
1300 if (isset($attrs['class'])) {
1301 $attrs['class'] .= ' effort-range';
1303 $attrs['class'] = 'effort-range';
1305 $attrsFrom = $attrs;
1307 $hiddenAttrs = array();
1308 if (isset($attrs['form'])) {
1309 $hiddenAttrs['form'] = $attrs['form'];
1311 $return = html_e('input', array_merge(array('type'=>'hidden', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$valueInUnitBaseFrom.'U'.$unitIdFrom.' '.$valueInUnitBaseTo.'U'.$unitIdTo), $hiddenAttrs));
1312 $return .= _('Between')._(':').html_e('br');
1313 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value_from['.$extra_field_id.']', 'value'=>$valueFrom, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrsFrom));
1314 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit_from['.$extra_field_id.']', $unitIdFrom, false, '', false, '', false, $attrsFrom, $opts_attrs);
1315 $return .= html_e('br');
1316 $return .= _('and')._(':').html_e('br');
1317 $return .= html_e('input', array_merge(array('type'=>'number', 'name'=>'value_to['.$extra_field_id.']', 'value'=>$valueTo, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrsTo));
1318 $return .= html_build_select_box_from_arrays($vals, $texts, 'unit_to['.$extra_field_id.']', $unitIdTo, false, '', false, '', false, $attrsTo, $opts_attrs);
1323 * renderFormulaField - this function builds a formula field (RO).
1325 * @param string $contents The data for this field.
1328 function renderFormulaField($contents) {
1333 * renderSLAField - this function builds a formula field (RO).
1335 * @param int $extra_field_id The ID of this field.
1336 * @param string $contents The data for this field.
1340 function renderSLAField($extra_field_id, $contents, $ef) {
1343 if (!isset($aid) ||!$aid) {
1347 $config = parse_ini_string($ef["attributes"]);
1348 $field_name = $config['field'];
1349 $rule = explode(':', $config['values']);
1351 if (!isset($config['start_id']) || $aid >= $config['start_id']) {
1352 // Get Max value from
1353 // @todo use global variable $aid !!!
1354 $artifact = artifact_get_object($aid);
1355 $value = $artifact->getFieldDataByKey('alias', $field_name);
1356 $field = $artifact->getFieldTypeByKey('alias', $field_name);
1357 $elements = $artifact->ArtifactType->getExtraFieldElements($field['extra_field_id']);
1358 for ($i = 0; $i < count($elements); $i++) {
1359 if ($elements[$i]['element_name'] == $value) {
1360 $max = 60 * $rule[$i];
1366 $value = '<span class="label label-warning">Not Set</span>';
1367 } elseif ($ef['alias'] == 'response_time') {
1368 // Implement Response SLA: Time from create to first comment.
1369 $messages = $artifact->getMessageObjects();
1370 if (!empty($messages)) {
1371 $message = $messages[count($messages) - 1];
1372 $current_time = $message->getAddDate();
1373 $timer_is_running = false;
1374 } elseif ($artifact->getStatusID() == 2) {
1375 $current_time = $artifact->getCloseDate();
1376 $timer_is_running = false;
1378 $current_time = time();
1379 $timer_is_running = true;
1381 $time = $current_time - $artifact->getOpenDate();
1382 $percent = round(100 * $time / $max);
1383 $remaining = $max - $time;
1384 if ($remaining >= 0) {
1385 $value = gmdate("G\h i", $remaining);
1387 $value = '<b>-' . gmdate("G\h i", -$remaining) . "</b>";
1390 if (!$timer_is_running) {
1391 if ($remaining >= 0) {
1392 $value = '<span class="label label-success">' . _('OK') . '</span>';
1394 $value = '<span class="label label-danger">' . _('Missed') . '</span>';
1397 if ($percent > 90) {
1398 $value = '<span class="sla-alert">' . $value . '</span>';
1399 } elseif ($percent > 80) {
1400 $value = '<span class="sla-warning">' . $value . '</span>';
1402 $value = '<span class="sla-normal">' . $value . '</span>';
1405 } elseif ($ef['alias'] == 'resolution_time') {
1406 // Implement Resolution SLA: Time from create to closed state.
1407 list($last_status_change, $timer, $timer_is_running) = json_decode($contents);
1410 if ($timer_is_running) {
1411 $time += time() - $last_status_change;
1414 $percent = round(100 * $time / $max);
1415 $remaining = $max - $time;
1416 if ($remaining >= 0) {
1417 $value = gmdate("G\h i", $remaining);
1419 $value = '<b>-' . gmdate("G\h i", -$remaining) . "</b>";
1421 if ($artifact->getStatusID() == 2) {
1422 if ($remaining >= 0) {
1423 $value = '<span class="label label-success">' . _('OK') . '</span>';
1425 $value = '<span class="label label-danger">' . _('Missed') . '</span>';
1428 if (!$timer_is_running) {
1429 $value = ' <span class="label label-info">' . _('Suspended') . '</span>';
1430 } elseif ($percent > 90) {
1431 $value = '<span class="sla-alert">' . $value . '</span>';
1432 } elseif ($percent > 80) {
1433 $value = '<span class="sla-warning">' . $value . '</span>';
1435 $value = '<span class="sla-normal">' . $value . '</span>';
1440 $value = '<span class="label label-default">' . _('Not Available') . '</span>';
1445 function technicianBox($name = 'assigned_to[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false, $attrs = array()) {
1446 if ($text_100=='none'){
1447 $text_100=_('Nobody');
1449 $engine = RBACEngine::getInstance();
1450 $techs = $engine->getUsersByAllowedAction('tracker', $this->getID(), 'tech') ;
1455 sortUserList($techs);
1457 foreach ($techs as $tech) {
1458 $ids[] = $tech->getID() ;
1459 $names[] = $tech->getRealName() ;
1462 if ($extra_id != '-1') {
1464 $names[]=$extra_name;
1468 if (!is_array($checked)) {
1469 $checked = explode(',', $checked);
1471 $size = min(count($ids)+1, 15);
1472 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100, false, $attrs);
1474 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100, false, false, false, $attrs);
1478 function submitterBox($name = 'submitted_by[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false, $attrs = array()) {
1479 if ($text_100=='none'){
1480 $text_100=_('Nobody');
1482 $result = $this->getSubmitters();
1483 $ids =& util_result_column_to_array($result,0);
1484 $names =& util_result_column_to_array($result,1);
1485 if ($extra_id != '-1') {
1487 $names[]=$extra_name;
1491 if (!is_array($checked)) {
1492 $checked = explode(',', $checked);
1494 $size = min(count($ids)+1, 15);
1495 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100, false, $attrs);
1497 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100, false, $attrs);
1501 function lastModifierBox($name = 'last_modified_by[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false) {
1502 if ($text_100=='none'){
1503 $text_100=_('Nobody');
1505 $result = $this->getLastModifiers();
1506 $ids =& util_result_column_to_array($result, 0);
1507 $names =& util_result_column_to_array($result, 1);
1508 if ($extra_id != '-1') {
1510 $names[] = $extra_name;
1514 if (!is_array($checked)) {
1515 $checked = explode(',', $checked);
1517 $size = min(count($ids)+1, 15);
1518 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100);
1520 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100);
1524 function cannedResponseBox($name = 'canned_response', $checked = 'xzxz', $attrs = array()) {
1525 if (!isset($attrs['id'])) {
1526 $attrs['id'] = $name;
1528 return html_build_select_box($this->getCannedResponses(), $name, $checked, true, 'none', false, '', false, $attrs);
1532 * statusBox - show the statuses - automatically shows the "custom statuses" if they exist
1534 * @param string $name
1535 * @param string $checked
1536 * @param bool $show_100
1537 * @param string $text_100
1538 * @param array $attrs
1539 * @return string HTML code
1541 function statusBox($name = 'status_id', $checked = 'xzxz', $show_100 = false, $text_100 = 'none', $attrs = array()) {
1542 if ($text_100=='none'){
1543 $text_100=_('None');
1545 return html_build_select_box($this->getStatuses(), $name, $checked, $show_100, $text_100, false, '', false, $attrs);
1549 * priorityBox - show the priorities
1551 * @param string $name
1552 * @param string $checked_val
1553 * @param bool $nochange
1554 * @param array $attrs
1555 * @param bool $show_any
1556 * @return string HTML code
1558 function priorityBox($name = 'priority', $checked_val = '3', $nochange = false, $attrs = array(), $show_any = false){
1559 return html_build_priority_select_box($name, $checked_val, $nochange, $attrs, $show_any);
1562 function javascript() {
1564 var invalidSelectMsg = '"._("One or more of the selected options is not allowed")."';
1565 var invalidInputMsg = '". _("This choice is not allowed")."';
1566 var groupId =".$this->Group->getID().";
1567 var atId = ".$this->getID().";";
1568 $effortUnitSet = New EffortUnitSet($this, $this->getEffortUnitSet());
1569 if ($effortUnitSet->isAutoconvert()) {
1570 $jseffort = <<<'EOS'
1571 $("select.effort").change(function(){
1572 var effortid = $(this).data("effortid");
1573 var value = parseInt(parseInt($("input[name='extra_fields["+effortid+"]']").val())/$("select[name='unit["+effortid+"]'] option:selected").data('factor'));
1574 $("input[name='value["+effortid+"]']").val(value);
1575 $("input[name='extra_fields["+effortid+"]']").val(value*$("select[name='unit["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit["+effortid+"]']").val());
1577 $("input.effort").change(function(){
1578 var effortid = $(this).data("effortid");
1579 var value = $("input[name='value["+effortid+"]']").val();
1580 $("input[name='extra_fields["+effortid+"]']").val(value*$("select[name='unit["+effortid+"]'] option:selected").data('factor')+'U'+$("select[name='unit["+effortid+"]']").val());
1582 $(".effort-range").change(function(){
1583 var effortid = $(this).data("effortid");
1584 $("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());
1588 $jseffort = <<<'EOS'
1589 $(".effort").change(function(){
1590 var effortid = $(this).data("effortid");
1591 $("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());
1593 $(".effort-range").change(function(){
1594 var effortid = $(this).data("effortid");
1595 $("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());
1599 $javascript = <<<'EOS'
1600 function showMessage( msg_text, msg_class) {
1601 $("#maindiv h1").append($("<p>", { "class": msg_class }).html( msg_text )).show();
1603 $.expr[':'].invalid = function(elem, index, match) {
1604 var invalids = document.querySelectorAll(':invalid'), result = false, len = invalids.length;
1606 for (var i=0; i<len; i++) {
1607 if (elem === invalids[i]) {
1616 $("input[type='radio'].readonly, input[type='checkbox'].readonly").on('click', function(){
1618 }).on('keydown', function(event){
1619 if(event.keyCode !== 9) return false;
1621 $(".in-formula").change(function(){
1625 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(),
1628 success: function(answer){
1629 if(answer['message']) {
1630 showMessage(answer['message'], 'error');
1632 fields = answer['fields'];
1633 $.each(fields, function (index, field) {
1634 if (field.error!=null){
1635 showMessage(field.error, 'error');
1637 fieldObj = $("[name^='extra_fields["+field.id+"]']");
1638 if (fieldObj.is("input[type='checkbox']")){
1639 fieldObj.each(function() {
1641 for (var key in field.value) {
1642 if (field.value[key] == $(this).val()) {
1647 if (in_array > -1) {
1648 $(this).prop("checked",true);
1650 $(this).prop("checked",false);
1653 } else if (fieldObj.is("input[type='radio']")){
1654 fieldObj.each(function() {
1656 for (var key in field.value) {
1657 if (field.value[key] == $(this).val()) {
1662 if (in_array > -1) {
1663 $(this).prop("checked",true);
1665 $(this).prop("checked",false);
1668 } else if (fieldObj.is("input")){
1669 fieldObj.val(field.value);
1670 } else if (fieldObj.is("select")){
1671 fieldObj.val(field.value);
1672 } else if (fieldObj.is("textarea")){
1673 fieldObj.val(field.value);
1681 $("img.addparent").click(function(){
1685 data: 'rtype=ajax&function=add_parent&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&parent_id='+$("input#parent_id").val(),
1688 success: function(answer){
1689 if(answer['message']) {
1690 showMessage(answer['message'], 'error');
1692 $("table.parent").replaceWith(answer['parent']);
1693 $("input[name='extra_fields["+answer['parent_efid']+"]']").val(answer['parent_id']);
1694 $("div#show"+answer['parent_efid']).html(answer['parent_link']);
1695 $("input#parent_id").val('');
1696 $("div.addparent").addClass('hide');
1697 $("img.removeparent").click(function(){
1698 removeParent($(this).data("id"));
1706 $("img.addchild").click(function(){
1710 data: 'rtype=ajax&function=add_child&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&child_id='+$("input#child_id").val(),
1713 success: function(answer){
1714 if(answer['message']) {
1715 showMessage(answer['message'], 'error');
1717 $("table.children").replaceWith(answer['children']);
1718 $("input#child_id").val('');
1719 $("img.removechild").click(function(){
1720 removeChild($(this).data("id"));
1729 $("img.removeparent").click(function(){
1730 removeParent($(this).data("id"));
1733 function removeParent(id) {
1737 data: 'rtype=ajax&function=remove_parent&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&parent_id='+id,
1740 success: function(answer){
1741 if(answer['message']) {
1742 showMessage(answer['message'], 'error');
1744 $("table#parent"+id).remove();
1745 $("div.addparent").removeClass('hide');
1752 $("img.removechild").click(function(){
1753 removeChild($(this).data("id"));
1756 function removeChild(id) {
1760 data: 'rtype=ajax&function=remove_child&group_id='+groupId+'&atid='+atId+'&aid='+$("input#aid").val()+'&child_id='+id,
1763 success: function(answer){
1764 if(answer['message']) {
1765 showMessage(answer['message'], 'error');
1767 $("span#child"+id).remove();
1773 $(".autoassign[name^='extra_fields']").change(function(){
1774 if ($(this).prop('tagName') == 'SELECT') {
1775 var elmnts = $(this).children('option:selected');
1777 var elmnts = $(this).siblings('input:checked');
1779 elmnts.each(function(i){
1780 var aat = $(this).data("autoassignto");
1781 $("select#tracker-assigned_to option[value="+aat.id+"]").prop('selected', true);
1782 $("span#tracker-assigned_to").text(aat.name);
1785 $(".with-depcy[name^='extra_fields']").change(function(){
1786 if ($(this).prop('tagName') == 'SELECT') {
1787 var elmnts = $(this).children('option:selected');
1789 var elmnts = $(this).siblings('input:checked');
1791 elmnts.each(function(i){
1792 var dep = $(this).data("dependency");
1793 if (this.value!='100') {
1794 $(dep).each(function(j, val) {
1795 $("select[name^='extra_fields["+val.field+"]']:invalid, input[name^='extra_fields["+val.field+"]']:invalid").each(function() {
1796 this.setCustomValidity("");
1797 $(this).off("change.invalid");
1799 $("select[name^='extra_fields["+val.field+"]'] option").each(function(k,opt){
1800 if (this.value!='100') {
1801 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1802 $(this).prop('disabled', false).removeClass('option_disabled');
1804 $(this).prop('disabled', true);
1805 $(this).addClass('option_disabled');
1809 $("input[name^='extra_fields["+val.field+"]']").each(function(k,opt){
1810 if (this.value!='100') {
1811 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1812 $(this).prop('disabled', false).removeClass($(this).attr('type')+'_disabled');
1814 $(this).prop('disabled', true);
1815 $(this).addClass($(this).attr('type')+'_disabled');
1821 $(dep.fields).each(function(j, val) {
1822 $("select[name^='extra_fields["+val+"]']:invalid, input[name^='extra_fields["+val+"]']:invalid").each(function() {
1823 this.setCustomValidity("");
1825 $("select[name^='extra_fields["+val+"]'] option.option_disabled").each(function() {
1826 $(this).prop('disabled', false).removeClass('option_disabled');
1828 $("input.radio_disable[name^='extra_fields["+val+"]']").each(function() {
1829 $(this).prop('disabled', false).removeClass('radio_disabled');
1831 $("input.checkbox_disabled[name^='extra_fields["+val+"]']").each(function() {
1832 $(this).prop('disabled', false).removeClass('checkbox_disabled');
1837 $("select[name^='extra_fields'] option:selected:disabled").parent().each(function() {
1838 $(this).children('option:selected:disabled').prop('disabled', false);
1839 this.setCustomValidity(invalidSelectMsg);
1840 $(this).on("change.invalid", function() {
1841 $(this).children('option.option_disabled:not(:disabled):not(:selected)').prop('disabled', true);
1842 if (!$(this).children('option.option_disabled:selected').length) {
1843 this.setCustomValidity("");
1844 $(this).off("change.invalid");
1848 $("input[name^='extra_fields']:checked:disabled").each(function() {
1849 $(this).prop('disabled', false);
1850 this.setCustomValidity(invalidInputMsg);
1851 if ($(this).attr('type') == 'radio') {
1852 $(this).siblings('input[type="radio"]').on("change.invalid", function() {
1853 $(this).siblings('input[type="radio"]:invalid').prop('disabled', true).addClass('input_disabled').each(function() {
1854 this.setCustomValidity("");
1856 $(this).siblings('input[type="radio"]').off("change.invalid");
1857 $(this).off("change.invalid");
1860 $(this).on("change.invalid", function() {
1861 $(this).prop('disabled', true);
1862 this.setCustomValidity("");
1863 $(this).off("change.invalid");
1869 return html_e('script', array('type'=>'text/javascript'), '//<![CDATA['."\n".'$(function(){'.$jsvariable."\n".$jseffort."\n".$javascript.'});'."\n".'//]]>');
1876 // c-file-style: "bsd"