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, Franck Villaume - TrivialDev
10 * Copyright 2016, 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.'include/utils_crossref.php';
34 require_once $gfcommon.'include/UserManager.class.php';
36 class ArtifactTypeHtml extends ArtifactType {
38 function header($params) {
40 if (!forge_get_config('use_tracker')) {
43 $group_id= $this->Group->getID();
45 //required by new site_project_header
46 $params['group']=$group_id;
47 $params['toptab']='tracker';
48 $params['tabtext']=$this->getName();
54 $labels[] = _("View Trackers");
55 $links[] = '/tracker/?group_id='.$group_id;
56 $attr[] = array('title' => _('Get the list of available trackers'));
57 $labels[] = $this->getName();
58 $links[] = '/tracker/?func=browse&group_id='.$group_id.'&atid='. $this->getID();
59 $attr[] = array('title' => _('Browse this tracker.'));
60 $labels[] = _('Export CSV');
61 $links[] = '/tracker/?func=csv&group_id='.$group_id.'&atid='. $this->getID();
62 $attr[] = array('title' => _('Download data from this tracker as csv file.'));
63 if (forge_check_perm ('tracker',$this->getID(),'submit')) {
64 $labels[] = _('Submit New');
65 $links[] = '/tracker/?func=add&group_id='.$group_id.'&atid='. $this->getID();
66 $attr[] = array('title' => _('Add a new issue.'));
69 if (session_loggedin()) {
70 $labels[] = _('Reporting');
71 $links[] = '/tracker/reporting/?group_id='.$group_id.'&atid='. $this->getID();
72 $attr[] = array('title' => _('Various graph about statistics.'));
73 if ($this->isMonitoring()) {
74 $labels[] = _('Stop Monitor');
75 $links[] = '/tracker/?group_id='.$group_id.'&atid='. $this->getID().'&func=monitor&stopmonitor=1';
76 $attr[] = array('title' => _('Remove this tracker from your monitoring.'));
78 $labels[] = _('Monitor');
79 $links[] = '/tracker/?group_id='.$group_id.'&atid='. $this->getID().'&func=monitor&startmonitor=1';
80 $attr[] = array('title' => _('Add this tracker from your monitoring.'));
83 if (forge_check_perm('tracker_admin', $group_id)) {
84 $labels[] = _('Administration');
85 $links[] = '/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID();
86 $attr[] = array('title' => _('Global administration for trackers. Create, clone, workflow, fields ...'));
90 $params['submenu'] = $HTML->subMenu($labels, $links, $attr);
91 site_project_header($params);
94 plugin_hook("blocks", "tracker_".$this->getName());
98 function footer($params = array()) {
99 site_project_footer($params);
102 function adminHeader($params) {
104 $this->header($params);
105 $group_id= $this->Group->getID();
107 $links_arr[]='/tracker/admin/?group_id='.$group_id;
108 $title_arr[]=_('New Tracker');
109 $attr_arr[] = array('title'=>_('Create a new tracker.'));
111 $links_arr[]='/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&update_type=1';
112 $title_arr[]=_('Update Settings');
113 $attr_arr[] = array('title'=>_('Set up preferences like expiration times, email addresses.'));
115 $links_arr[]='/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&add_extrafield=1';
116 $title_arr[]=_('Manage Custom Fields');
117 $attr_arr[] = 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.'));
119 $links_arr[]='/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&workflow=1';
120 $title_arr[]=_('Manage Workflow');
121 $attr_arr[] = array('title'=>_('Edit tracker workflow.'));
123 $links_arr[]='/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&customize_list=1';
124 $title_arr[]=_('Customize List');
125 $attr_arr[] = array('title'=>_('Customize display for the tracker.'));
127 $links_arr[]='/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&add_canned=1';
128 $title_arr[]=_('Manage Canned Responses');
129 $attr_arr[] = array('title'=>_('Create/change generic response messages for the tracker.'));
131 $links_arr[]='/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&clone_tracker=1';
132 $title_arr[]=_('Apply Template Tracker');
133 $attr_arr[] = array('title'=>_('Duplicate parameters and fields from a template trackers in this one.'));
135 $links_arr[]='/tracker/admin/?group_id='.$group_id.'&atid='.$this->getID().'&delete=1';
136 $title_arr[]=_('Delete');
137 $attr_arr[] = array('title'=>_('Permanently delete this tracker.'));
139 echo $HTML->printSubMenu($title_arr, $links_arr, $attr_arr);
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,
174 if ($mode == 'NEW') {
175 $efarr = $this->getExtraFields($types, false, false);
177 $efarr = $this->getExtraFields($types);
179 //each two columns, we'll reset this and start a new row
180 $template = $this->getRenderHTML($types, $mode);
182 if ($mode=='QUERY') {
183 $keys=array_keys($efarr);
184 for ($k=0; $k<count($keys); $k++) {
186 $type = $efarr[$i]['field_type'];
187 if ($type == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
188 $type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
189 $type == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
190 $type == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
191 $type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
192 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTISELECT;
193 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_USER ||
194 $type == ARTIFACT_EXTRAFIELDTYPE_MULTIUSER) {
195 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTIUSER;
196 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_RELEASE ||
197 $type == ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE) {
198 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE;
199 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_DATETIME ||
200 $type == ARTIFACT_EXTRAFIELDTYPE_DATE) {
201 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_DATERANGE;
203 $efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_TEXT;
208 // 'DISPLAY' mode is for rendering in 'read-only' mode (for detail view).
209 if ($mode === 'DISPLAY') {
210 $keys=array_keys($efarr);
211 for ($k=0; $k<count($keys); $k++) {
215 if (!isset($selected[$efarr[$i]['extra_field_id']]))
216 $selected[$efarr[$i]['extra_field_id']] = '';
218 $value = $selected[$efarr[$i]['extra_field_id']];
219 $type = $efarr[$i]['field_type'];
221 if ($type == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
222 $type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
223 $type == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
224 $type == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
225 $type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
227 $value = $efarr[$i]['show100label'];
229 $arr = $this->getExtraFieldElements($efarr[$i]['extra_field_id']);
231 // Convert the values (ids) to names in the ids order.
233 for ($j=0; $j<count($arr); $j++) {
234 if (is_array($value) && in_array($arr[$j]['element_id'],$value)) {
235 $new[]= $arr[$j]['element_name'];
236 } elseif ($arr[$j]['element_id'] === $value) {
237 $new[] = $arr[$j]['element_name'];
240 $value = join('<br />', $new);
242 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
243 $type == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
244 $value = preg_replace('/((http|https|ftp):\/\/\S+)/',
245 "<a href=\"\\1\" target=\"_blank\">\\1</a>", $value);
246 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
247 // Convert artifact id to links.
248 $value = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $value);
249 } elseif ($type == ARTIFACT_EXTRAFIELDTYPE_DATETIME && $value!='') {
250 $value = date('Y-m-d H:i', $value);
252 $template = str_replace('{$PostName:'.$efarr[$i]['field_name'].'}', $post_name, $template);
253 $template = str_replace('{$'.$efarr[$i]['field_name'].'}', $value, $template);
258 if ($mode == 'UPDATE' || $mode == 'NEW') {
259 if ($mode == 'NEW') {
260 $efInFormula = $this->getExtraFieldsInFormula($types, false, false);
261 $efWithFormula = $this->getExtraFieldsWithFormula($types, false, false);
263 $efInFormula = $this->getExtraFieldsInFormula($types);
264 $efWithFormula = $this->getExtraFieldsWithFormula($types);
268 $keys = array_keys($efarr);
269 for ($k = 0; $k < count($keys); $k++) {
274 if (!empty($efarr[$i]['description'])) {
275 $attrs['title'] = $efarr[$i]['description'];
277 if ($efarr[$i]['is_required'] == 1 && $mode != 'QUERY') {
278 $attrs['required'] = 'required';
281 if ($mode == 'UPDATE' || $mode == 'NEW') {
282 if (in_array($efarr[$i]['extra_field_id'],$efInFormula)) {
283 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'in-formula';
285 if (in_array($efarr[$i]['extra_field_id'],$efWithFormula)) {
286 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-formula readonly';
287 $attrs['readonly'] = 'readonly';
291 if (!isset($selected[$efarr[$i]['extra_field_id']]))
292 $selected[$efarr[$i]['extra_field_id']] = '';
294 if ($status_show_100) {
295 $efarr[$i]['show100'] = $status_show_100;
298 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
299 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
300 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
301 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
303 if (!is_null($efarr[$i]['parent']) && !empty($efarr[$i]['parent']) && $efarr[$i]['parent']!='100') {
304 $aefParentId = $efarr[$i]['parent'];
305 $selectedElmnts = (isset($selected[$aefParentId]) ? $selected[$aefParentId] : '');
306 $aef = new ArtifactExtraField($this,$efarr[$i]['extra_field_id']);
307 $allowed = $aef->getAllowedValues($selectedElmnts);
311 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT) {
313 $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);
315 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) {
317 $str = $this->renderCheckbox($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['show100'],$efarr[$i]['show100label'], $allowed, $attrs);
319 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO) {
321 $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);
323 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
324 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER) {
326 if (!empty($efarr[$i]['pattern'])) {
327 $attrs['pattern'] = $efarr[$i]['pattern'];
329 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT) {
330 $str = $this->renderTextField($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2'], $attrs);
332 $str = $this->renderIntegerField($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2'], $attrs);
334 if ($mode == 'QUERY') {
335 $post_name = ' <i>'._('(%% for wildcards)').'</i> ';
338 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
340 $str = $this->renderTextArea($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2'], $attrs);
341 if ($mode == 'QUERY') {
342 $post_name = ' <i>'._('(%% for wildcards)').'</i>';
345 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
347 $str = $this->renderMultiSelectBox($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['show100'],$efarr[$i]['show100label'], $allowed, $attrs);
349 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
351 // Get the allowed values from the workflow.
352 $atw = new ArtifactWorkflow($this, $efarr[$i]['extra_field_id']);
354 // Special treatment for the initial step (Submit).
355 // In this case, the initial value is the first value.
356 if (isset($selected[$efarr[$i]['extra_field_id']]) && $selected[$efarr[$i]['extra_field_id']]) {
357 $selected_node = $selected[$efarr[$i]['extra_field_id']];
359 $selected_node = 100;
362 $allowed = $atw->getNextNodes($selected_node);
363 $allowed[] = $selected_node;
364 $str = $this->renderSelect($efarr[$i]['extra_field_id'],$selected_node,$status_show_100,$text_100,$show_any,$text_any, $allowed, $attrs);
366 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
368 $str = $this->renderRelationField($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2'], $attrs);
369 if ($mode == 'UPDATE' || $mode == 'NEW') {
370 $post_name = html_image('ic/forum_edit.gif', 37, 15 ,array('title'=>"Click to edit", 'alt'=>"Click to edit", 'onclick'=>"switch2edit(this, 'show$i', 'edit$i')"));
372 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME) {
373 $str = $this->renderDatetime($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']], $attrs);
374 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER) {
375 $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);
376 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTIUSER) {
377 $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);
378 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE) {
379 $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);
380 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE) {
381 $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);
382 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATERANGE) {
383 if ($mode == 'QUERY') {
384 $post_name = ' <i>'._('(YYYY-MM-DD YYYY-MM-DD Format)').'</i>';
386 $str = $this->renderDateRange($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']], $attrs);
388 $template = str_replace('{$PostName:'.$efarr[$i]['field_name'].'}',$post_name,$template);
389 $template = str_replace('{$'.$efarr[$i]['field_name'].'}',$str,$template);
391 if($template != NULL){
392 if ($mode == 'UPDATE' || $mode == 'NEW') {
393 echo $this->javascript();
399 function renderRelatedTasks($group, $ah) {
401 if (!$group->usesPM()) {
407 $taskcount = db_numrows($ah->getRelatedTasks());
408 db_result_reset($ah->getRelatedTasks());
410 if (forge_check_perm('tracker_admin', $ah->ArtifactType->Group->getID())) {
416 $totalPercentage = 0;
417 if ($taskcount > 0) {
418 $title_arr = array();
419 $title_arr[] = _('Task Id and Summary');
420 $title_arr[] = _('Progress');
421 $title_arr[] = _('Start Date');
422 $title_arr[] = _('End Date');
423 $title_arr[] = _('Status');
424 (($is_admin) ? $title_arr[]=_('Remove Relation') : '');
425 $return .= $HTML->listTableTop($title_arr);
427 for ($i = 0; $i < $taskcount; $i++) {
428 $taskinfo = db_fetch_array($ah->relatedtasks, $i);
429 $totalPercentage += $taskinfo['percent_complete'];
430 $taskid = $taskinfo['project_task_id'];
431 $projectid = $taskinfo['group_project_id'];
432 $groupid = $taskinfo['group_id'];
433 if (forge_check_perm('pm', $projectid, 'read')) {
434 $summary = util_unconvert_htmlspecialchars($taskinfo['summary']);
435 $startdate = date(_('Y-m-d H:i'), $taskinfo['start_date']);
436 $enddate = date(_('Y-m-d H:i'), $taskinfo['end_date']);
437 $status = $taskinfo['status_name'];
439 <td>'.util_make_link('/pm/task.php?func=detailtask&project_task_id='.$taskid.'&group_id='.$groupid.'&group_project_id='.$projectid, '[T'.$taskid.'] '.$summary).'</td>
440 <td><div class="percentbar" style="width: 100px;">
441 <div style="width:'.round($taskinfo['percent_complete']).'px;"></div></div></td>
442 <td>'.$startdate.'</td>
443 <td>'.$enddate.'</td>
444 <td>'.$status.' ('.$taskinfo['percent_complete'].'%)</td>'.
445 (($is_admin) ? '<td><input type="checkbox" name="remlink[]" value="'.$taskid.'" /></td>' : '').
449 $return .= $HTML->listTableBottom();
451 $return .= "\n<hr /><p style=\"text-align:right;\">";
452 $return .= _('Average completion rate')._(': ').(int)($totalPercentage/$taskcount).'%';
455 $return .= $HTML->information(_('No related tasks'));
460 function renderFiles($group_id, $ah) {
462 $file_list =& $ah->getFiles();
463 $count=count($file_list);
467 $return .= '<strong>'._("Attachments")._(':').'</strong>'.'<br/>';
469 $title_arr[] = _('Size');
470 $title_arr[] = _('Name');
471 $title_arr[] = _('Date');
472 $title_arr[] = _('By');
473 $title_arr[] = _('Download');
474 $return .= $HTML->listTableTop($title_arr);
476 foreach ($file_list as $file) {
478 $return .= '<td>'.human_readable_bytes($file->getSize()).'</td>';
479 $return .= '<td>'.htmlspecialchars($file->getName()).'</td>';
480 $return .= '<td>'.date(_('Y-m-d H:i'), $file->getDate()).'</td>';
481 $return .= '<td>'.$file->getSubmittedUnixName().'</td>';
482 $return .= '<td>'.util_make_link('/tracker/download.php/'.$group_id.'/'. $this->getID().'/'. $ah->getID() .'/'.$file->getID().'/'.$file->getName(), htmlspecialchars($file->getName())).'</td>';
483 if (forge_check_perm ('tracker', $this->getID(), 'tech')) {
484 $return .= '<td><input type="checkbox" name="delete_file[]" value="'. $file->getID() .'">'._('Delete').'</td>';
489 $return .= $HTML->listTableBottom();
491 $return .= $HTML->information(_('No attached documents'));
499 * @param array $types
500 * @param string $mode
501 * @return string HTML template.
503 function getRenderHTML($types=array(), $mode='') {
504 // Use template only for the browse (not for query or mass update)
505 if (($mode === 'DISPLAY' || $mode === 'DETAIL' || $mode === 'UPDATE' || $mode == 'NEW')
506 && $this->data_array['custom_renderer']) {
507 return preg_replace('/<!--(\S+.*?)-->/','{$\\1}',$this->data_array['custom_renderer']);
509 return $this->generateRenderHTML($types, $mode);
516 * @param array $types
517 * @param string $mode Display mode (QUERY OR DISPLAY OR NEW)
518 * @return string HTML template.
520 function generateRenderHTML($types=array(), $mode) {
521 if ($mode == 'NEW') {
522 $efarr = $this->getExtraFields($types, false, false);
524 $efarr = $this->getExtraFields($types);
526 //each two columns, we'll reset this and start a new row
529 <!-- Start Extra Fields Rendering -->
533 $keys=array_keys($efarr);
535 if ($count == 0) return '';
537 for ($k=0; $k<$count; $k++) {
540 // Do not show the required star in query mode (creating/updating a query).
541 $is_required = ($mode == 'QUERY' || $mode == 'DISPLAY') ? 0 : $efarr[$i]['is_required'];
542 if ($mode == 'QUERY' && ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME || $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATE)) {
543 $name = sprintf(_('%s range'), $efarr[$i]['field_name']).($is_required ? utils_requiredField() : '')._(': ');
545 $name = $efarr[$i]['field_name'].($is_required ? utils_requiredField() : '')._(': ');
547 $name = '<strong>'.$name.'</strong>';
549 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
550 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
551 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
552 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT ||
553 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
554 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER ||
555 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE ) {
558 <td class="halfwidth top">'.$name.'<br />{$'.$efarr[$i]['field_name'].'}</td>';
560 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
561 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER ||
562 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION ||
563 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME) {
565 //text fields might be really wide, so need a row to themselves.
566 if (($col_count == 1) && ($efarr[$i]['attribute1'] > 30)) {
576 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
578 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
580 //text areas might be really wide, so need a row to themselves.
581 if (($col_count == 1) && ($efarr[$i]['attribute2'] > 30)) {
591 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
596 //we've done two columns - if there are more to do, start a new row
597 if (($col_count == 2) && ($k != $count-1)) {
604 if ($col_count == 1) {
610 <!-- End Extra Fields Rendering -->';
615 * renderSelect - this function builds pop up box with choices.
617 * @param int $extra_field_id The ID of this field.
618 * @param string $checked The item that should be checked
619 * @param bool|string $show_100 Whether to show the '100 row'
620 * @param string $text_100 What to call the '100 row'
621 * @param bool $show_any
622 * @param string $text_any
623 * @param bool $allowed
624 * @param array $attrs
625 * @return string HTML code for the box and choices
627 function renderSelect ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false, $attrs = array ()) {
628 if ($text_100 == 'none'){
631 $arr = $this->getExtraFieldElements($extra_field_id);
632 $aef = new ArtifactExtraField($this, $extra_field_id);
633 $aefChildren = $aef->getChildren();
634 if (!empty($aefChildren)) {
635 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
637 if ($aef->isAutoAssign()) {
638 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'autoassign';
642 $opt_attrs = array();
643 $attrs_100 = array();
645 for ($i=0; $i<count($arr); $i++) {
646 $vals[$i]=$arr[$i]['element_id'];
647 $texts[$i]=$arr[$i]['element_name'];
648 $opt_attrs[$i]=array();
649 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
650 if (!empty($aefChildren)) {
651 $cElmntArr = $aefe->getChildrenElements();
652 if (!empty($cElmntArr))
655 foreach ($cElmntArr as $key=>$cElmnt) {
656 $childField = new ArtifactExtraField($this, $key);
657 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
659 $dependency = '['.$dependency.']';
660 $opt_attrs[$i]= array( 'data-dependency'=>$dependency);
663 if ($aef->isAutoAssign()) {
664 $autoAssignTo = UserManager::instance()->getUserById($aefe->getAutoAssignto());
665 $opt_attrs[$i]=array_merge(isset($opt_attrs[$i]) ? $opt_attrs[$i] : array(), array( 'data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
669 if ($show_100 && !empty($aefChildren)) {
670 $attrs_100 = array( 'data-dependency'=>'{"fields": ['.implode(', ',$aefChildren).']}');
672 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);
676 * renderUserField - this function builds pop up box with users.
678 * @param int $extra_field_id The ID of this field.
679 * @param string $checked The item that should be checked
680 * @param bool|string $show_100 Whether to show the '100 row'
681 * @param string $text_100 What to call the '100 row'
682 * @param bool $show_any
683 * @param string $text_any
684 * @param bool $allowed
685 * @param array $attrs
686 * @return string HTML code for the box and choices
688 function renderUserField ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false, $attrs = array ()) {
689 if ($text_100 == 'none' || $text_100 == 'nobody'){
690 $text_100=_('Nobody');
692 $arr = $this->getExtraFieldElements($extra_field_id);
693 $selectedRolesId = array();
694 for ($i=0; $i<count($arr); $i++) {
695 $selectedRolesId[$i]=$arr[$i]['element_name'];
697 $roles = $this->getGroup()->getRoles();
698 $userArray = array();
699 foreach ($roles as $role) {
700 if (in_array($role->getID(), $selectedRolesId)) {
701 foreach ($role->getUsers() as $user) {
702 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
706 if (is_integer($checked) && !isset($userArray[$checked])) {
707 $checkedUser = user_get_object($checked);
708 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
710 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
711 $keys = array_keys($userArray);
712 $vals = array_values($userArray);
713 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);
717 * renderMultiUserField - this function builds pop up box with users.
719 * @param int $extra_field_id The ID of this field.
720 * @param string $checked The item that should be checked
721 * @param bool|string $show_100 Whether to show the '100 row'
722 * @param string $text_100 What to call the '100 row'
723 * @param bool $show_any
724 * @param string $text_any
725 * @param bool $allowed
726 * @param array $attrs
727 * @return string HTML code for the box and choices
729 function renderMultiUserField ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false, $attrs = array ()) {
730 if ($text_100 == 'none' || $text_100 == 'nobody'){
731 $text_100=_('Nobody');
737 if (!is_array($checked)) {
738 $checked = explode(',',$checked);
741 $arr = $this->getExtraFieldElements($extra_field_id);
742 $selectedRolesId = array();
743 for ($i=0; $i<count($arr); $i++) {
744 $selectedRolesId[$i]=$arr[$i]['element_name'];
746 $roles = $this->getGroup()->getRoles();
747 $userArray = array();
748 foreach ($roles as $role) {
749 if (in_array($role->getID(), $selectedRolesId)) {
750 foreach ($role->getUsers() as $user) {
751 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
755 if (is_integer($checked) && !isset($userArray[$checked])) {
756 $checkedUser = user_get_object($checked);
757 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
759 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
760 $size = min( count($userArray)+1, 15);
761 $keys = array_keys($userArray);
762 $vals = array_values($userArray);
764 return html_build_multiple_select_box_from_arrays ($keys,$vals,'extra_fields['.$extra_field_id.'][]',$checked,$size, $show_100,$text_100,$allowed, $attrs);
768 * renderReleaseField - this function builds 2 pop up boxes with packages & releases.
770 * @param int $extra_field_id The ID of this field.
771 * @param string $checked The item that should be checked
772 * @param bool|string $show_100 Whether to show the '100 row'
773 * @param string $text_100 What to call the '100 row'
774 * @param bool $show_any
775 * @param string $text_any
776 * @param bool $allowed
777 * @param array $attrs
778 * @return string HTML code for the box and choices
780 function renderReleaseField($extra_field_id, $checked = 'xzxz', $show_100 = false, $text_100 = 'none', $show_any = false, $text_any = 'Any', $allowed = false, $attrs = array ()) {
781 if ($text_100 == 'none'){
785 $releasesArray = array();
786 $releasesAttrs = array();
789 $arr = $this->getExtraFieldElements($extra_field_id);
790 $selectedPackagesId = array();
791 for ($i=0; $i<count($arr); $i++) {
792 $selectedPackagesId[$i]=$arr[$i]['element_name'];
795 $fpFactory = new FRSPackageFactory($this->getGroup());
796 $packages = $fpFactory->getFRSs(true);
797 uasort($packages, 'compareObjectName');
798 foreach ($packages as $package) {
799 $releases = $package->getReleases();
800 uasort($releases, 'compareObjectName');
801 foreach ($releases as $release) {
802 $optGroup[] = $package->getName();
803 $releasesArray[$release->getID()] = $release->getName();
807 $keys = array_keys($releasesArray);
808 $vals = array_values($releasesArray);
809 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);
813 * renderMultiReleaseField - this function builds 2 pop up boxes with packages & releases.
815 * @param int $extra_field_id The ID of this field.
816 * @param string $checked The item that should be checked
817 * @param bool|string $show_100 Whether to show the '100 row'
818 * @param string $text_100 What to call the '100 row'
819 * @param bool $show_any
820 * @param string $text_any
821 * @param bool $allowed
822 * @param array $attrs
823 * @return string HTML code for the box and choices
825 function renderMultiReleaseField ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false, $attrs = array ()) {
826 if ($text_100 == 'none'){
832 if (!is_array($checked)) {
833 $checked = explode(',',$checked);
836 $releasesArray = array();
837 $releasesAttrs = array();
840 $arr = $this->getExtraFieldElements($extra_field_id);
841 $selectedPackagesId = array();
842 for ($i=0; $i<count($arr); $i++) {
843 $selectedPackagesId[$i]=$arr[$i]['element_name'];
846 $packages = get_frs_packages($this->getGroup());
847 uasort($packages, 'compareObjectName');
848 foreach ($packages as $package) {
849 if (in_array($package->getID(), $selectedPackagesId)) {
850 $releases = $package->getReleases();
851 uasort($releases, 'compareObjectName');
852 foreach ($releases as $release) {
853 $optGroup[] = $package->getName();
854 $releasesArray[$release->getID()] = $release->getName();
858 $size = min( count($releasesArray) + count($optGroup) + 1, 15);
859 $keys = array_keys($releasesArray);
860 $vals = array_values($releasesArray);
861 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);
865 * renderRadio - this function builds radio buttons.
867 * @param int $extra_field_id The $int ID of this field.
868 * @param string $checked The $string item that should be checked
869 * @param bool $show_100 Whether $string to show the '100 row'
870 * @param string $text_100 What $string to call the '100 row'
871 * @param bool $show_any
872 * @param string $text_any
873 * @param array $attrs Array of other attributes
874 * @return string HTML code using radio buttons
876 function renderRadio ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed = false, $attrs = array()) {
878 $arr = $this->getExtraFieldElements($extra_field_id);
879 $aef = new ArtifactExtraField($this, $extra_field_id);
880 $aefChildren = $aef->getChildren();
881 if (!empty($aefChildren)) {
882 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
887 $radios_attrs = array();
888 $attrs_100 = array();
890 for ($i=0; $i<count($arr); $i++) {
891 $vals[$i]=$arr[$i]['element_id'];
892 $texts[$i]=$arr[$i]['element_name'];
893 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
895 if (!empty($aefChildren)) {
896 $cElmntArr = $aefe->getChildrenElements();
897 if (!empty($cElmntArr))
899 foreach ($cElmntArr as $key=>$cElmnt) {
900 $childField = new ArtifactExtraField($this, $key);
901 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
903 $dependency = '['.$dependency.']';
904 $radios_attrs[$i]['data-dependency']=$dependency;
907 if ($aef->isAutoAssign()) {
908 $autoAssignTo = UserManager::instance()->getUserById($aefe->getAutoAssignto());
909 $radios_attrs[$i]=array_merge(isset($radios_attrs[$i]) ? $radios_attrs[$i] : array(), array( 'data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
912 if ($show_100 && !empty($aefChildren)) {
913 $attrs_100 = array( 'data-dependency'=>'{"fields": ['.implode(', ',$aefChildren).']}');
915 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);
919 * renderCheckbox - this function builds checkboxes.
921 * @param int $extra_field_id The ID of this field.
922 * @param array $checked The items that should be checked
923 * @param bool|string $show_100 Whether to show the '100 row'
924 * @param string $text_100 What to call the '100 row'
925 * @param array $attrs Array of other attributes
926 * @return string radio buttons
928 function renderCheckbox ($extra_field_id,$checked=array(),$show_100=false,$text_100='none', $allowed=false, $attrs = array()) {
929 if ($text_100 == 'none'){
933 if (!$checked || !is_array($checked)) {
936 if (!empty($attrs['title'])) {
937 $attrs['title'] = util_html_secure($attrs['title']);
940 $arr = $this->getExtraFieldElements($extra_field_id);
941 $aef = new ArtifactExtraField($this, $extra_field_id);
942 $aefChildren = $aef->getChildren();
943 if (!empty($aefChildren)) {
944 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
949 $chk_attrs = array();
950 $attrs_100 = array();
952 for ($i=0; $i<count($arr); $i++) {
953 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
954 $vals[$i] = $arr[$i]['element_id'];
955 $texts[$i] = $arr[$i]['element_name'];
956 $chk_attrs[$i] = array();
958 if (!empty($aefChildren)) {
959 $cElmntArr = $aefe->getChildrenElements();
960 if (!empty($cElmntArr))
962 foreach ($cElmntArr as $key=>$cElmnt) {
963 $childField = new ArtifactExtraField($this, $key);
964 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
966 $dependency = '['.$dependency.']';
967 $chk_attrs[$i]['data-dependency']=$dependency;
971 if ($show_100 && !empty($aefChildren)) {
972 $attrs_100['data-dependency'] ='{"fields": ['.implode(', ',$aefChildren).']}';
974 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);
978 * renderMultiSelectBox - this function builds checkboxes.
980 * @param int $extra_field_id The ID of this field.
981 * @param array $checked The items that should be checked
982 * @param bool|string $show_100 Whether to show the '100 row'
983 * @param string $text_100 What to call the '100 row'
984 * @param array $attrs Array of other attributes
985 * @return string radio multiselectbox
987 function renderMultiSelectBox($extra_field_id,$checked=array(),$show_100=false,$text_100='none', $allowed=false, $attrs=array()) {
991 if (!is_array($checked)) {
992 $checked = explode(',',$checked);
994 $arr = $this->getExtraFieldElements($extra_field_id);
995 $aef = new ArtifactExtraField($this, $extra_field_id);
996 $aefChildren = $aef->getChildren();
997 if (!empty($aefChildren)) {
998 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
1002 $opt_attrs = array();
1003 $attrs_100 = array();
1005 for ($i=0; $i<count($arr); $i++) {
1006 $vals[$i]=$arr[$i]['element_id'];
1007 $texts[$i]=$arr[$i]['element_name'];
1008 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
1009 if (!empty($aefChildren)) {
1010 $cElmntArr = $aefe->getChildrenElements();
1011 if (!empty($cElmntArr))
1014 foreach ($cElmntArr as $key=>$cElmnt) {
1015 $childField = new ArtifactExtraField($this, $key);
1016 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1018 $dependency = '['.$dependency.']';
1019 $opt_attrs[$i]= array( 'data-dependency'=>$dependency);
1023 $size = min( count($arr)+1, 15);
1024 if ($show_100 && !empty($aefChildren)) {
1025 $attrs_100 = array( 'data-dependency'=>'{"fields": ['.implode(', ',$aefChildren).']}');
1027 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);
1031 * renderTextField - this function builds a text field.
1033 * @param int $extra_field_id The ID of this field.
1034 * @param string $contents The data for this field.
1035 * @param string $size
1036 * @param string $maxlength
1037 * @param array $attrs Array of other attributes
1038 * @return string HTML code of corresponding input tag.
1040 function renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1041 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength), $attrs));
1045 * renderIntegerField - this function builds a text field.
1047 * @param int $extra_field_id The ID of this field.
1048 * @param string $contents The data for this field.
1049 * @param string $size
1050 * @param string $maxlength
1051 * @param array $attrs Array of other attributes
1052 * @return string HTML code of corresponding input tag.
1054 function renderIntegerField ($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1055 $intAttrs = array('type'=>'number', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0);
1056 $newattrs = array_merge($intAttrs, $attrs);
1057 return html_e('input',$newattrs);
1061 * renderRelationField - this function builds a relation field.
1063 * @param int $extra_field_id The ID of this field.
1064 * @param string $contents The data for this field.
1065 * @param string $size
1066 * @param string $maxlength
1067 * @param array $attrs Array of other attributes
1068 * @return string text area and data.
1070 function renderRelationField ($extra_field_id,$contents,$size,$maxlength, $attrs = array()) {
1071 $arr = $this->getExtraFieldElements($extra_field_id);
1072 for ($i=0; $i<count($arr); $i++) {
1073 $keys[$i]=$arr[$i]['element_id'];
1074 $vals[$i]=$arr[$i]['element_name'];
1076 // Convert artifact id to links.
1077 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1078 $edit_contents = $this->renderTextField ($extra_field_id,$contents,$size,$maxlength);
1079 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)
1080 .html_e('div',array_merge(array( 'id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1084 * renderTextArea - this function builds a text area.
1086 * @param int $extra_field_id The ID of this field.
1087 * @param string $contents The data for this field.
1088 * @param string $rows
1089 * @param string $cols
1090 * @param array $attrs Array of other attributes
1091 * @return string text area and data.
1093 function renderTextArea ($extra_field_id,$contents,$rows,$cols, $attrs = array()) {
1094 return html_e('textarea', array_merge(array('name'=>'extra_fields['.$extra_field_id.']', 'rows'=>$rows, 'cols'=>$cols), $attrs), $contents, false);
1099 * renderDatetime - this function builds a Datetime field.
1101 * @param int $extra_field_id The ID of this field.
1102 * @param string $datetime datetime for this field.
1103 * @param array $attrs Array of other attributes
1104 * @return string datetime.
1106 function renderDatetime($extra_field_id, $datetime, $attrs = array()) {
1107 if (!$datetime=='') {
1108 $datetime_format = _('Y-m-d H:i');
1109 $datetime = date($datetime_format,$datetime);
1111 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'class'=>'datetimepicker', 'value'=>$datetime),$attrs));
1114 function renderDateRange($extra_field_id, $dateRange, $attrs = array()) {
1115 // http://html5pattern.com/Dates
1116 // Date with leapyear-check
1117 $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)))';
1118 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));
1121 function technicianBox($name = 'assigned_to[]', $checked = 'xzxz', $show_100 = true, $text_100 = 'none', $extra_id = '-1', $extra_name = '', $multiple = false) {
1122 if ($text_100=='none'){
1123 $text_100=_('Nobody');
1126 $engine = RBACEngine::getInstance();
1127 $techs = $engine->getUsersByAllowedAction('tracker', $this->getID(), 'tech') ;
1132 sortUserList($techs);
1134 foreach ($techs as $tech) {
1135 $ids[] = $tech->getID() ;
1136 $names[] = $tech->getRealName() ;
1139 if ($extra_id != '-1') {
1141 $names[]=$extra_name;
1145 if (!is_array($checked)) {
1146 $checked = explode(',',$checked);
1148 $size = min(count($ids)+1, 15);
1149 return html_build_multiple_select_box_from_arrays($ids, $names, $name, $checked, $size, $show_100, $text_100);
1151 return html_build_select_box_from_arrays($ids, $names, $name, $checked, $show_100, $text_100);
1155 function submitterBox ($name='submitted_by[]',$checked='xzxz',$show_100=true,$text_100='none',$extra_id='-1',$extra_name='',$multiple=false) {
1156 if ($text_100=='none'){
1157 $text_100=_('Nobody');
1159 $result = $this->getSubmitters();
1160 $ids =& util_result_column_to_array($result,0);
1161 $names =& util_result_column_to_array($result,1);
1162 if ($extra_id != '-1') {
1164 $names[]=$extra_name;
1168 if (!is_array($checked)) {
1169 $checked = explode(',',$checked);
1171 $size = min( count($ids)+1, 15);
1172 return html_build_multiple_select_box_from_arrays ($ids,$names,$name,$checked,$size,$show_100,$text_100);
1174 return html_build_select_box_from_arrays ($ids,$names,$name,$checked,$show_100,$text_100);
1178 function lastModifierBox ($name='last_modified_by[]',$checked='xzxz',$show_100=true,$text_100='none',$extra_id='-1',$extra_name='',$multiple=false) {
1179 if ($text_100=='none'){
1180 $text_100=_('Nobody');
1182 $result = $this->getLastModifiers();
1183 $ids =& util_result_column_to_array($result,0);
1184 $names =& util_result_column_to_array($result,1);
1185 if ($extra_id != '-1') {
1187 $names[]=$extra_name;
1191 if (!is_array($checked)) {
1192 $checked = explode(',',$checked);
1194 $size = min( count($ids)+1, 15);
1195 return html_build_multiple_select_box_from_arrays ($ids,$names,$name,$checked,$size,$show_100,$text_100);
1197 return html_build_select_box_from_arrays ($ids,$names,$name,$checked,$show_100,$text_100);
1201 function cannedResponseBox ($name='canned_response',$checked='xzxz') {
1202 return html_build_select_box ($this->getCannedResponses(),$name,$checked);
1206 * statusBox - show the statuses - automatically shows the "custom statuses" if they exist
1210 function statusBox ($name='status_id',$checked='xzxz',$show_100=false,$text_100='none') {
1211 if ($text_100=='none'){
1212 $text_100=_('None');
1214 return html_build_select_box($this->getStatuses(),$name,$checked,$show_100,$text_100);
1217 function javascript() {
1219 var invalidSelectMsg = '"._("One or more of the selected options is not allowed")."';
1220 var invalidInputMsg = '". _("This choice is not allowed")."';
1221 var groupId =".$this->Group->getID().";
1222 var atId = ".$this->getID().";";
1223 $javascript = <<<'EOS'
1224 $.expr[':'].invalid = function(elem, index, match) {
1225 for (let invalid of document.querySelectorAll(':invalid') ) {
1226 if (elem === invalid) { return true; }
1230 $("input[type='radio'].readonly, input[type='checkbox'].readonly").on('click', function(){
1232 }).on('keydown', function(event){
1233 if(event.keyCode !== 9) return false;
1235 $(".in-formula[name^='extra_fields']").on('change', function(){
1239 data: 'rtype=ajax&function=get_formulas_results&group_id='+groupId+'&atid='+atId+'&'+$("[name^='extra_fields']" ).serialize(),
1242 success: function(answer){
1243 if(answer['message']) {
1244 showMessage(answer['message'], 'error');
1246 fields = answer['fields'];
1247 $.each(fields, function (index, field) {
1248 fieldObj = $("[name^='extra_fields["+field.id+"]']");
1249 console.log(fieldObj);
1250 console.log(fieldObj.is("input[type='radio']"));
1251 if (fieldObj.is("input[type='checkbox']")){
1252 console.log("checkbox");
1253 fieldObj.each(function() {
1255 for (var key in field.value) {
1256 if (field.value[key] == $(this).val()) {
1261 if (in_array > -1) {
1262 $(this).prop("checked",true);
1264 $(this).prop("checked",false);
1267 } else if (fieldObj.is("input[type='radio']")){
1268 console.log("radio");
1269 fieldObj.each(function() {
1270 console.log($(this));
1272 for (var key in field.value) {
1273 if (field.value[key] == $(this).val()) {
1278 console.log(in_array);
1279 if (in_array > -1) {
1280 $(this).prop("checked",true);
1282 $(this).prop("checked",false);
1285 } else if (fieldObj.is("input")){
1286 fieldObj.val(field.value);
1287 } else if (fieldObj.is("select")){
1288 fieldObj.val(field.value);
1289 } else if (fieldObj.is("textarea")){
1290 fieldObj.val(field.value);
1297 $(".autoassign[name^='extra_fields']").on('change', function(){
1298 if ($(this).prop('tagName') == 'SELECT') {
1299 var elmnts = $(this).children('option:selected');
1301 var elmnts = $(this).siblings('input:checked');
1303 elmnts.each(function(i){
1304 var aat = $(this).data("autoassignto");
1305 $("select#tracker-assigned_to option[value="+aat.id+"]").prop('selected', true);
1306 $("span#tracker-assigned_to").text(aat.name);
1309 $(".with-depcy[name^='extra_fields']").on('change', function(){
1310 if ($(this).prop('tagName') == 'SELECT') {
1311 var elmnts = $(this).children('option:selected');
1313 var elmnts = $(this).siblings('input:checked');
1315 elmnts.each(function(i){
1316 var dep = $(this).data("dependency");
1317 if (this.value!='100') {
1318 $(dep).each(function(j, val) {
1319 $("select[name^='extra_fields["+val.field+"]']:invalid, input[name^='extra_fields["+val.field+"]']:invalid").each(function() {
1320 this.setCustomValidity("");
1321 $(this).off("change.invalid");
1323 $("select[name^='extra_fields["+val.field+"]'] option").each(function(k,opt){
1324 if (this.value!='100') {
1325 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1326 $(this).prop('disabled', false).removeClass('option_disabled');
1328 $(this).prop('disabled', true);
1329 $(this).addClass('option_disabled');
1333 $("input[name^='extra_fields["+val.field+"]']").each(function(k,opt){
1334 if (this.value!='100') {
1335 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1336 $(this).prop('disabled', false).removeClass($(this).attr('type')+'_disabled');
1338 $(this).prop('disabled', true);
1339 $(this).addClass($(this).attr('type')+'_disabled');
1345 $(dep.fields).each(function(j, val) {
1346 $("select[name^='extra_fields["+val+"]']:invalid, input[name^='extra_fields["+val+"]']:invalid").each(function() {
1347 this.setCustomValidity("");
1349 $("select[name^='extra_fields["+val+"]'] option.option_disabled").each(function() {
1350 $(this).prop('disabled', false).removeClass('option_disabled');
1352 $("input.radio_disable[name^='extra_fields["+val+"]']").each(function() {
1353 $(this).prop('disabled', false).removeClass('radio_disabled');
1355 $("input.checkbox_disabled[name^='extra_fields["+val+"]']").each(function() {
1356 $(this).prop('disabled', false).removeClass('checkbox_disabled');
1361 $("select[name^='extra_fields'] option:selected:disabled").parent().each(function() {
1362 $(this).children('option:selected:disabled').prop('disabled', false);
1363 this.setCustomValidity(invalidSelectMsg);
1364 $(this).on("change.invalid", function() {
1365 $(this).children('option.option_disabled:not(:disabled):not(:selected)').prop('disabled', true);
1366 if (!$(this).children('option.option_disabled:selected').length) {
1367 this.setCustomValidity("");
1368 $(this).off("change.invalid");
1372 $("input[name^='extra_fields']:checked:disabled").each(function() {
1373 $(this).prop('disabled', false);
1374 this.setCustomValidity(invalidInputMsg);
1375 if ($(this).attr('type') == 'radio') {
1376 $(this).siblings('input[type="radio"]').on("change.invalid", function() {
1377 $(this).siblings('input[type="radio"]:invalid').prop('disabled', true).addClass('input_disabled').each(function() {
1378 this.setCustomValidity("");
1380 $(this).siblings('input[type="radio"]').off("change.invalid");
1381 $(this).off("change.invalid");
1384 $(this).on("change.invalid", function() {
1385 $(this).prop('disabled', true);
1386 this.setCustomValidity("");
1387 $(this).off("change.invalid");
1393 return html_e('script', array( 'type'=>'text/javascript'), '//<![CDATA['."\n".'$(function(){'.$jsvariable."\n".$javascript.'});'."\n".'//]]>');
1400 // c-file-style: "bsd"