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', $this->getID(), 'manager')) {
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);
259 $keys = array_keys($efarr);
260 for ($k = 0; $k < count($keys); $k++) {
265 if (!empty($efarr[$i]['description'])) {
266 $attrs['title'] = $efarr[$i]['description'];
268 if ($efarr[$i]['is_required'] == 1 && $mode != 'QUERY') {
269 $attrs['required'] = 'required';
272 if (!isset($selected[$efarr[$i]['extra_field_id']]))
273 $selected[$efarr[$i]['extra_field_id']] = '';
275 if ($status_show_100) {
276 $efarr[$i]['show100'] = $status_show_100;
279 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
280 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
281 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
282 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
284 if (!is_null($efarr[$i]['parent']) && !empty($efarr[$i]['parent']) && $efarr[$i]['parent']!='100') {
285 $aefParentId = $efarr[$i]['parent'];
286 $selectedElmnts = (isset($selected[$aefParentId]) ? $selected[$aefParentId] : '');
287 $aef = new ArtifactExtraField($this,$efarr[$i]['extra_field_id']);
288 $allowed = $aef->getAllowedValues($selectedElmnts);
292 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT) {
294 $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);
296 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) {
298 $str = $this->renderCheckbox($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['show100'],$efarr[$i]['show100label'], $allowed, $attrs);
300 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO) {
302 $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);
304 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
305 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER) {
307 if (!empty($efarr[$i]['pattern'])) {
308 $attrs['pattern'] = $efarr[$i]['pattern'];
310 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT) {
311 $str = $this->renderTextField($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2'], $attrs);
313 $str = $this->renderIntegerField($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2'], $attrs);
315 if ($mode == 'QUERY') {
316 $post_name = ' <i>'._('(%% for wildcards)').'</i> ';
319 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
321 $str = $this->renderTextArea($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2'], $attrs);
322 if ($mode == 'QUERY') {
323 $post_name = ' <i>'._('(%% for wildcards)').'</i>';
326 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
328 $str = $this->renderMultiSelectBox($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['show100'],$efarr[$i]['show100label'], $allowed, $attrs);
330 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
332 // Get the allowed values from the workflow.
333 $atw = new ArtifactWorkflow($this, $efarr[$i]['extra_field_id']);
335 // Special treatment for the initial step (Submit).
336 // In this case, the initial value is the first value.
337 if (isset($selected[$efarr[$i]['extra_field_id']]) && $selected[$efarr[$i]['extra_field_id']]) {
338 $selected_node = $selected[$efarr[$i]['extra_field_id']];
340 $selected_node = 100;
343 $allowed = $atw->getNextNodes($selected_node);
344 $allowed[] = $selected_node;
345 $str = $this->renderSelect($efarr[$i]['extra_field_id'],$selected_node,$status_show_100,$text_100,$show_any,$text_any, $allowed, $attrs);
347 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
349 $str = $this->renderRelationField($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2'], $attrs);
350 if ($mode == 'UPDATE' || $mode == 'NEW') {
351 $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')"));
353 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME) {
354 $str = $this->renderDatetime($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']], $attrs);
355 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER) {
356 $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);
357 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTIUSER) {
358 $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);
359 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE) {
360 $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);
361 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTIRELEASE) {
362 $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);
363 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATERANGE) {
364 if ($mode == 'QUERY') {
365 $post_name = ' <i>'._('(YYYY-MM-DD YYYY-MM-DD Format)').'</i>';
367 $str = $this->renderDateRange($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']], $attrs);
369 $template = str_replace('{$PostName:'.$efarr[$i]['field_name'].'}',$post_name,$template);
370 $template = str_replace('{$'.$efarr[$i]['field_name'].'}',$str,$template);
372 if($template != NULL){
373 if ($mode == 'UPDATE' || $mode == 'NEW') {
374 echo $this->javascript();
380 function renderRelatedTasks($group, $ah) {
382 if (!$group->usesPM()) {
388 $taskcount = db_numrows($ah->getRelatedTasks());
389 db_result_reset($ah->getRelatedTasks());
391 if (forge_check_perm('tracker_admin', $ah->ArtifactType->Group->getID())) {
397 $totalPercentage = 0;
398 if ($taskcount > 0) {
399 $title_arr = array();
400 $title_arr[] = _('Task Id and Summary');
401 $title_arr[] = _('Progress');
402 $title_arr[] = _('Start Date');
403 $title_arr[] = _('End Date');
404 $title_arr[] = _('Status');
405 (($is_admin) ? $title_arr[]=_('Remove Relation') : '');
406 $return .= $HTML->listTableTop($title_arr);
408 for ($i = 0; $i < $taskcount; $i++) {
409 $taskinfo = db_fetch_array($ah->relatedtasks, $i);
410 $totalPercentage += $taskinfo['percent_complete'];
411 $taskid = $taskinfo['project_task_id'];
412 $projectid = $taskinfo['group_project_id'];
413 $groupid = $taskinfo['group_id'];
414 if (forge_check_perm('pm', $projectid, 'read')) {
415 $summary = util_unconvert_htmlspecialchars($taskinfo['summary']);
416 $startdate = date(_('Y-m-d H:i'), $taskinfo['start_date']);
417 $enddate = date(_('Y-m-d H:i'), $taskinfo['end_date']);
418 $status = $taskinfo['status_name'];
420 <td>'.util_make_link('/pm/task.php?func=detailtask&project_task_id='.$taskid.'&group_id='.$groupid.'&group_project_id='.$projectid, '[T'.$taskid.'] '.$summary).'</td>
421 <td><div class="percentbar" style="width: 100px;">
422 <div style="width:'.round($taskinfo['percent_complete']).'px;"></div></div></td>
423 <td>'.$startdate.'</td>
424 <td>'.$enddate.'</td>
425 <td>'.$status.' ('.$taskinfo['percent_complete'].'%)</td>'.
426 (($is_admin) ? '<td><input type="checkbox" name="remlink[]" value="'.$taskid.'" /></td>' : '').
430 $return .= $HTML->listTableBottom();
432 $return .= "\n<hr /><p style=\"text-align:right;\">";
433 $return .= _('Average completion rate')._(': ').(int)($totalPercentage/$taskcount).'%';
436 $return .= $HTML->information(_('No related tasks'));
441 function renderFiles($group_id, $ah) {
443 $file_list =& $ah->getFiles();
444 $count=count($file_list);
448 $return .= '<strong>'._("Attachments")._(':').'</strong>'.'<br/>';
450 $title_arr[] = _('Size');
451 $title_arr[] = _('Name');
452 $title_arr[] = _('Date');
453 $title_arr[] = _('By');
454 $title_arr[] = _('Download');
455 $return .= $HTML->listTableTop($title_arr);
457 foreach ($file_list as $file) {
459 $return .= '<td>'.human_readable_bytes($file->getSize()).'</td>';
460 $return .= '<td>'.htmlspecialchars($file->getName()).'</td>';
461 $return .= '<td>'.date(_('Y-m-d H:i'), $file->getDate()).'</td>';
462 $return .= '<td>'.$file->getSubmittedUnixName().'</td>';
463 $return .= '<td>'.util_make_link('/tracker/download.php/'.$group_id.'/'. $this->getID().'/'. $ah->getID() .'/'.$file->getID().'/'.$file->getName(), htmlspecialchars($file->getName())).'</td>';
464 if (forge_check_perm ('tracker', $this->getID(), 'tech')) {
465 $return .= '<td><input type="checkbox" name="delete_file[]" value="'. $file->getID() .'">'._('Delete').'</td>';
470 $return .= $HTML->listTableBottom();
472 $return .= $HTML->information(_('No attached documents'));
480 * @param array $types
481 * @param string $mode
482 * @return string HTML template.
484 function getRenderHTML($types=array(), $mode='') {
485 // Use template only for the browse (not for query or mass update)
486 if (($mode === 'DISPLAY' || $mode === 'DETAIL' || $mode === 'UPDATE' || $mode == 'NEW')
487 && $this->data_array['custom_renderer']) {
488 return preg_replace('/<!--(\S+.*?)-->/','{$\\1}',$this->data_array['custom_renderer']);
490 return $this->generateRenderHTML($types, $mode);
497 * @param array $types
498 * @param string $mode Display mode (QUERY OR DISPLAY OR NEW)
499 * @return string HTML template.
501 function generateRenderHTML($types=array(), $mode) {
502 if ($mode == 'NEW') {
503 $efarr = $this->getExtraFields($types, false, false);
505 $efarr = $this->getExtraFields($types);
507 //each two columns, we'll reset this and start a new row
510 <!-- Start Extra Fields Rendering -->
514 $keys=array_keys($efarr);
516 if ($count == 0) return '';
518 for ($k=0; $k<$count; $k++) {
521 // Do not show the required star in query mode (creating/updating a query).
522 $is_required = ($mode == 'QUERY' || $mode == 'DISPLAY') ? 0 : $efarr[$i]['is_required'];
523 if ($mode == 'QUERY' && ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME || $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATE)) {
524 $name = sprintf(_('%s range'), $efarr[$i]['field_name']).($is_required ? utils_requiredField() : '')._(': ');
526 $name = $efarr[$i]['field_name'].($is_required ? utils_requiredField() : '')._(': ');
528 $name = '<strong>'.$name.'</strong>';
530 if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
531 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
532 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
533 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT ||
534 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
535 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_USER ||
536 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELEASE ) {
539 <td class="halfwidth top">'.$name.'<br />{$'.$efarr[$i]['field_name'].'}</td>';
541 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
542 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_INTEGER ||
543 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION ||
544 $efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_DATETIME) {
546 //text fields might be really wide, so need a row to themselves.
547 if (($col_count == 1) && ($efarr[$i]['attribute1'] > 30)) {
557 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
559 } elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
561 //text areas might be really wide, so need a row to themselves.
562 if (($col_count == 1) && ($efarr[$i]['attribute2'] > 30)) {
572 <td style="width:'.(50*$colspan).'%" colspan="'.$colspan.'" class="top">'.$name.'{$PostName:'.$efarr[$i]['field_name'].'}<br />{$'.$efarr[$i]['field_name'].'}</td>';
577 //we've done two columns - if there are more to do, start a new row
578 if (($col_count == 2) && ($k != $count-1)) {
585 if ($col_count == 1) {
591 <!-- End Extra Fields Rendering -->';
596 * renderSelect - this function builds pop up box with choices.
598 * @param int $extra_field_id The ID of this field.
599 * @param string $checked The item that should be checked
600 * @param bool|string $show_100 Whether to show the '100 row'
601 * @param string $text_100 What to call the '100 row'
602 * @param bool $show_any
603 * @param string $text_any
604 * @param bool $allowed
605 * @param array $attrs
606 * @return string HTML code for the box and choices
608 function renderSelect ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false, $attrs = array ()) {
609 if ($text_100 == 'none'){
612 $arr = $this->getExtraFieldElements($extra_field_id);
613 $aef = new ArtifactExtraField($this, $extra_field_id);
614 $aefChildren = $aef->getChildren();
615 if (!empty($aefChildren)) {
616 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
618 if ($aef->isAutoAssign()) {
619 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'autoassign';
623 $opt_attrs = array();
624 $attrs_100 = array();
626 for ($i=0; $i<count($arr); $i++) {
627 $vals[$i]=$arr[$i]['element_id'];
628 $texts[$i]=$arr[$i]['element_name'];
629 $opt_attrs[$i]=array();
630 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
631 if (!empty($aefChildren)) {
632 $cElmntArr = $aefe->getChildrenElements();
633 if (!empty($cElmntArr))
636 foreach ($cElmntArr as $key=>$cElmnt) {
637 $childField = new ArtifactExtraField($this, $key);
638 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
640 $dependency = '['.$dependency.']';
641 $opt_attrs[$i]= array( 'data-dependency'=>$dependency);
644 if ($aef->isAutoAssign()) {
645 $autoAssignTo = UserManager::instance()->getUserById($aefe->getAutoAssignto());
646 $opt_attrs[$i]=array_merge(isset($opt_attrs[$i]) ? $opt_attrs[$i] : array(), array( 'data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
650 if ($show_100 && !empty($aefChildren)) {
651 $attrs_100 = array( 'data-dependency'=>'{"fields": ['.implode(', ',$aefChildren).']}');
653 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);
657 * renderUserField - this function builds pop up box with users.
659 * @param int $extra_field_id The ID of this field.
660 * @param string $checked The item that should be checked
661 * @param bool|string $show_100 Whether to show the '100 row'
662 * @param string $text_100 What to call the '100 row'
663 * @param bool $show_any
664 * @param string $text_any
665 * @param bool $allowed
666 * @param array $attrs
667 * @return string HTML code for the box and choices
669 function renderUserField ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false, $attrs = array ()) {
670 if ($text_100 == 'none' || $text_100 == 'nobody'){
671 $text_100=_('Nobody');
673 $arr = $this->getExtraFieldElements($extra_field_id);
674 $selectedRolesId = array();
675 for ($i=0; $i<count($arr); $i++) {
676 $selectedRolesId[$i]=$arr[$i]['element_name'];
678 $roles = $this->getGroup()->getRoles();
679 $userArray = array();
680 foreach ($roles as $role) {
681 if (in_array($role->getID(), $selectedRolesId)) {
682 foreach ($role->getUsers() as $user) {
683 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
687 if (is_integer($checked) && !isset($userArray[$checked])) {
688 $checkedUser = user_get_object($checked);
689 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
691 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
692 $keys = array_keys($userArray);
693 $vals = array_values($userArray);
694 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);
698 * renderMultiUserField - this function builds pop up box with users.
700 * @param int $extra_field_id The ID of this field.
701 * @param string $checked The item that should be checked
702 * @param bool|string $show_100 Whether to show the '100 row'
703 * @param string $text_100 What to call the '100 row'
704 * @param bool $show_any
705 * @param string $text_any
706 * @param bool $allowed
707 * @param array $attrs
708 * @return string HTML code for the box and choices
710 function renderMultiUserField ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false, $attrs = array ()) {
711 if ($text_100 == 'none' || $text_100 == 'nobody'){
712 $text_100=_('Nobody');
718 if (!is_array($checked)) {
719 $checked = explode(',',$checked);
722 $arr = $this->getExtraFieldElements($extra_field_id);
723 $selectedRolesId = array();
724 for ($i=0; $i<count($arr); $i++) {
725 $selectedRolesId[$i]=$arr[$i]['element_name'];
727 $roles = $this->getGroup()->getRoles();
728 $userArray = array();
729 foreach ($roles as $role) {
730 if (in_array($role->getID(), $selectedRolesId)) {
731 foreach ($role->getUsers() as $user) {
732 $userArray[$user->getID()] = $user->getRealName().(($user->getStatus()=='S') ? ' '._('[SUSPENDED]') : '');
736 if (is_integer($checked) && !isset($userArray[$checked])) {
737 $checkedUser = user_get_object($checked);
738 $userArray[$checkedUser->getID()] = $checkedUser->getRealName().' '._('[DELETED]');
740 asort($userArray,SORT_FLAG_CASE | SORT_STRING);
741 $size = min( count($userArray)+1, 15);
742 $keys = array_keys($userArray);
743 $vals = array_values($userArray);
745 return html_build_multiple_select_box_from_arrays ($keys,$vals,'extra_fields['.$extra_field_id.'][]',$checked,$size, $show_100,$text_100,$allowed, $attrs);
749 * renderReleaseField - this function builds 2 pop up boxes with packages & releases.
751 * @param int $extra_field_id The ID of this field.
752 * @param string $checked The item that should be checked
753 * @param bool|string $show_100 Whether to show the '100 row'
754 * @param string $text_100 What to call the '100 row'
755 * @param bool $show_any
756 * @param string $text_any
757 * @param bool $allowed
758 * @param array $attrs
759 * @return string HTML code for the box and choices
761 function renderReleaseField ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false, $attrs = array ()) {
762 if ($text_100 == 'none'){
766 $releasesArray = array();
767 $releasesAttrs = array();
770 $arr = $this->getExtraFieldElements($extra_field_id);
771 $selectedPackagesId = array();
772 for ($i=0; $i<count($arr); $i++) {
773 $selectedPackagesId[$i]=$arr[$i]['element_name'];
776 $packages = get_frs_packages($this->getGroup());
777 uasort($packages, 'compareObjectName');
778 foreach ($packages as $package) {
779 if (in_array($package->getID(), $selectedPackagesId)) {
780 $releases = $package->getReleases();
781 uasort($releases, 'compareObjectName');
782 foreach ($releases as $release) {
783 $optGroup[] = $package->getName();
784 $releasesArray[$release->getID()] = $release->getName();
789 $keys = array_keys($releasesArray);
790 $vals = array_values($releasesArray);
791 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);
795 * renderMultiReleaseField - this function builds 2 pop up boxes with packages & releases.
797 * @param int $extra_field_id The ID of this field.
798 * @param string $checked The item that should be checked
799 * @param bool|string $show_100 Whether to show the '100 row'
800 * @param string $text_100 What to call the '100 row'
801 * @param bool $show_any
802 * @param string $text_any
803 * @param bool $allowed
804 * @param array $attrs
805 * @return string HTML code for the box and choices
807 function renderMultiReleaseField ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false, $attrs = array ()) {
808 if ($text_100 == 'none'){
814 if (!is_array($checked)) {
815 $checked = explode(',',$checked);
818 $releasesArray = array();
819 $releasesAttrs = array();
822 $arr = $this->getExtraFieldElements($extra_field_id);
823 $selectedPackagesId = array();
824 for ($i=0; $i<count($arr); $i++) {
825 $selectedPackagesId[$i]=$arr[$i]['element_name'];
828 $packages = get_frs_packages($this->getGroup());
829 uasort($packages, 'compareObjectName');
830 foreach ($packages as $package) {
831 if (in_array($package->getID(), $selectedPackagesId)) {
832 $releases = $package->getReleases();
833 uasort($releases, 'compareObjectName');
834 foreach ($releases as $release) {
835 $optGroup[] = $package->getName();
836 $releasesArray[$release->getID()] = $release->getName();
840 $size = min( count($releasesArray) + count($optGroup) + 1, 15);
841 $keys = array_keys($releasesArray);
842 $vals = array_values($releasesArray);
843 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);
847 * renderRadio - this function builds radio buttons.
849 * @param int $extra_field_id The $int ID of this field.
850 * @param string $checked The $string item that should be checked
851 * @param bool $show_100 Whether $string to show the '100 row'
852 * @param string $text_100 What $string to call the '100 row'
853 * @param bool $show_any
854 * @param string $text_any
855 * @param array $attrs Array of other attributes
856 * @return string HTML code using radio buttons
858 function renderRadio ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed = false, $attrs = array()) {
860 $arr = $this->getExtraFieldElements($extra_field_id);
861 $aef = new ArtifactExtraField($this, $extra_field_id);
862 $aefChildren = $aef->getChildren();
863 if (!empty($aefChildren)) {
864 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
869 $radios_attrs = array();
870 $attrs_100 = array();
872 for ($i=0; $i<count($arr); $i++) {
873 $vals[$i]=$arr[$i]['element_id'];
874 $texts[$i]=$arr[$i]['element_name'];
875 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
877 if (!empty($aefChildren)) {
878 $cElmntArr = $aefe->getChildrenElements();
879 if (!empty($cElmntArr))
881 foreach ($cElmntArr as $key=>$cElmnt) {
882 $childField = new ArtifactExtraField($this, $key);
883 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
885 $dependency = '['.$dependency.']';
886 $radios_attrs[$i]['data-dependency']=$dependency;
889 if ($aef->isAutoAssign()) {
890 $autoAssignTo = UserManager::instance()->getUserById($aefe->getAutoAssignto());
891 $radios_attrs[$i]=array_merge(isset($radios_attrs[$i]) ? $radios_attrs[$i] : array(), array( 'data-autoassignto'=>'{"id":'.$aefe->getAutoAssignto().', "name":"'.$autoAssignTo->getRealName().' ('.$autoAssignTo->getUnixName().')"}'));
894 if ($show_100 && !empty($aefChildren)) {
895 $attrs_100 = array( 'data-dependency'=>'{"fields": ['.implode(', ',$aefChildren).']}');
897 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);
901 * renderCheckbox - this function builds checkboxes.
903 * @param int $extra_field_id The ID of this field.
904 * @param array $checked The items that should be checked
905 * @param bool|string $show_100 Whether to show the '100 row'
906 * @param string $text_100 What to call the '100 row'
907 * @param array $attrs Array of other attributes
908 * @return string radio buttons
910 function renderCheckbox ($extra_field_id,$checked=array(),$show_100=false,$text_100='none', $allowed=false, $attrs = array()) {
911 if ($text_100 == 'none'){
915 if (!$checked || !is_array($checked)) {
918 if (!empty($attrs['title'])) {
919 $attrs['title'] = util_html_secure($attrs['title']);
922 $arr = $this->getExtraFieldElements($extra_field_id);
923 $aef = new ArtifactExtraField($this, $extra_field_id);
924 $aefChildren = $aef->getChildren();
925 if (!empty($aefChildren)) {
926 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
931 $chk_attrs = array();
932 $attrs_100 = array();
934 for ($i=0; $i<count($arr); $i++) {
935 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
936 $vals[$i] = $arr[$i]['element_id'];
937 $texts[$i] = $arr[$i]['element_name'];
938 $chk_attrs[$i] = array();
940 if (!empty($aefChildren)) {
941 $cElmntArr = $aefe->getChildrenElements();
942 if (!empty($cElmntArr))
944 foreach ($cElmntArr as $key=>$cElmnt) {
945 $childField = new ArtifactExtraField($this, $key);
946 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
948 $dependency = '['.$dependency.']';
949 $chk_attrs[$i]['data-dependency']=$dependency;
953 if ($show_100 && !empty($aefChildren)) {
954 $attrs_100['data-dependency'] ='{"fields": ['.implode(', ',$aefChildren).']}';
956 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);
960 * renderMultiSelectBox - this function builds checkboxes.
962 * @param int $extra_field_id The ID of this field.
963 * @param array $checked The items that should be checked
964 * @param bool|string $show_100 Whether to show the '100 row'
965 * @param string $text_100 What to call the '100 row'
966 * @param array $attrs Array of other attributes
967 * @return string radio multiselectbox
969 function renderMultiSelectBox($extra_field_id,$checked=array(),$show_100=false,$text_100='none', $allowed=false, $attrs=array()) {
973 if (!is_array($checked)) {
974 $checked = explode(',',$checked);
976 $arr = $this->getExtraFieldElements($extra_field_id);
977 $aef = new ArtifactExtraField($this, $extra_field_id);
978 $aefChildren = $aef->getChildren();
979 if (!empty($aefChildren)) {
980 $attrs['class'] = (empty($attrs['class']) ? '':$attrs['class'].' ').'with-depcy';
984 $opt_attrs = array();
985 $attrs_100 = array();
987 for ($i=0; $i<count($arr); $i++) {
988 $vals[$i]=$arr[$i]['element_id'];
989 $texts[$i]=$arr[$i]['element_name'];
990 $aefe = new ArtifactExtraFieldElement($aef, $arr[$i]['element_id']);
991 if (!empty($aefChildren)) {
992 $cElmntArr = $aefe->getChildrenElements();
993 if (!empty($cElmntArr))
996 foreach ($cElmntArr as $key=>$cElmnt) {
997 $childField = new ArtifactExtraField($this, $key);
998 $dependency .= (empty($dependency) ? '':', ').'{"field":'.$key.', "elmnt": ['.implode(', ', $cElmnt).']}';
1000 $dependency = '['.$dependency.']';
1001 $opt_attrs[$i]= array( 'data-dependency'=>$dependency);
1005 $size = min( count($arr)+1, 15);
1006 if ($show_100 && !empty($aefChildren)) {
1007 $attrs_100 = array( 'data-dependency'=>'{"fields": ['.implode(', ',$aefChildren).']}');
1009 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);
1013 * renderTextField - this function builds a text field.
1015 * @param int $extra_field_id The ID of this field.
1016 * @param string $contents The data for this field.
1017 * @param string $size
1018 * @param string $maxlength
1019 * @param array $attrs Array of other attributes
1020 * @return string HTML code of corresponding input tag.
1022 function renderTextField ($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1023 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength), $attrs));
1027 * renderIntegerField - this function builds a text field.
1029 * @param int $extra_field_id The ID of this field.
1030 * @param string $contents The data for this field.
1031 * @param string $size
1032 * @param string $maxlength
1033 * @param array $attrs Array of other attributes
1034 * @return string HTML code of corresponding input tag.
1036 function renderIntegerField ($extra_field_id, $contents, $size, $maxlength, $attrs = array()) {
1037 return html_e('input', array_merge(array('type'=>'number', 'name'=>'extra_fields['.$extra_field_id.']', 'value'=>$contents, 'size'=>$size, 'maxlength'=>$maxlength, 'min'=>0), $attrs));
1041 * renderRelationField - this function builds a relation field.
1043 * @param int $extra_field_id The ID of this field.
1044 * @param string $contents The data for this field.
1045 * @param string $size
1046 * @param string $maxlength
1047 * @param array $attrs Array of other attributes
1048 * @return string text area and data.
1050 function renderRelationField ($extra_field_id,$contents,$size,$maxlength, $attrs = array()) {
1051 $arr = $this->getExtraFieldElements($extra_field_id);
1052 for ($i=0; $i<count($arr); $i++) {
1053 $keys[$i]=$arr[$i]['element_id'];
1054 $vals[$i]=$arr[$i]['element_name'];
1056 // Convert artifact id to links.
1057 $html_contents = preg_replace_callback('/\b(\d+)\b/', create_function('$matches', 'return _artifactid2url($matches[1], \'title\');'), $contents);
1058 $edit_contents = $this->renderTextField ($extra_field_id,$contents,$size,$maxlength);
1059 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)
1060 .html_e('div',array_merge(array( 'id'=>'show'.$extra_field_id, 'style'=>'display: block'), $attrs), $html_contents);
1064 * renderTextArea - this function builds a text area.
1066 * @param int $extra_field_id The ID of this field.
1067 * @param string $contents The data for this field.
1068 * @param string $rows
1069 * @param string $cols
1070 * @param array $attrs Array of other attributes
1071 * @return string text area and data.
1073 function renderTextArea ($extra_field_id,$contents,$rows,$cols, $attrs = array()) {
1074 return html_e('textarea', array_merge(array('name'=>'extra_fields['.$extra_field_id.']', 'rows'=>$rows, 'cols'=>$cols), $attrs), $contents, false);
1079 * renderDatetime - this function builds a Datetime field.
1081 * @param int $extra_field_id The ID of this field.
1082 * @param string $datetime datetime for this field.
1083 * @param array $attrs Array of other attributes
1084 * @return string datetime.
1086 function renderDatetime($extra_field_id, $datetime, $attrs = array()) {
1087 if (!$datetime=='') {
1088 $datetime = date('Y-m-d H:i',$datetime);
1090 return html_e('input', array_merge(array('type'=>'text', 'name'=>'extra_fields['.$extra_field_id.']', 'class'=>'datetimepicker', 'value'=>$datetime),$attrs));
1093 function renderDateRange($extra_field_id, $dateRange, $attrs = array()) {
1094 // http://html5pattern.com/Dates
1095 // Date with leapyear-check
1096 $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)))';
1097 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));
1099 function technicianBox ($name='assigned_to[]',$checked='xzxz',$show_100=true,$text_100='none',$extra_id='-1',$extra_name='',$multiple=false) {
1100 if ($text_100=='none'){
1101 $text_100=_('Nobody');
1104 $engine = RBACEngine::getInstance () ;
1105 $techs = $engine->getUsersByAllowedAction ('tracker', $this->getID(), 'tech') ;
1110 sortUserList($techs);
1112 foreach ($techs as $tech) {
1113 $ids[] = $tech->getID() ;
1114 $names[] = $tech->getRealName() ;
1117 if ($extra_id != '-1') {
1119 $names[]=$extra_name;
1123 if (!is_array($checked)) {
1124 $checked = explode(',',$checked);
1126 $size = min( count($ids)+1, 15);
1127 return html_build_multiple_select_box_from_arrays ($ids,$names,$name,$checked,$size,$show_100,$text_100);
1129 return html_build_select_box_from_arrays ($ids,$names,$name,$checked,$show_100,$text_100);
1133 function submitterBox ($name='submitted_by[]',$checked='xzxz',$show_100=true,$text_100='none',$extra_id='-1',$extra_name='',$multiple=false) {
1134 if ($text_100=='none'){
1135 $text_100=_('Nobody');
1137 $result = $this->getSubmitters();
1138 $ids =& util_result_column_to_array($result,0);
1139 $names =& util_result_column_to_array($result,1);
1140 if ($extra_id != '-1') {
1142 $names[]=$extra_name;
1146 if (!is_array($checked)) {
1147 $checked = explode(',',$checked);
1149 $size = min( count($ids)+1, 15);
1150 return html_build_multiple_select_box_from_arrays ($ids,$names,$name,$checked,$size,$show_100,$text_100);
1152 return html_build_select_box_from_arrays ($ids,$names,$name,$checked,$show_100,$text_100);
1156 function lastModifierBox ($name='last_modified_by[]',$checked='xzxz',$show_100=true,$text_100='none',$extra_id='-1',$extra_name='',$multiple=false) {
1157 if ($text_100=='none'){
1158 $text_100=_('Nobody');
1160 $result = $this->getLastModifiers();
1161 $ids =& util_result_column_to_array($result,0);
1162 $names =& util_result_column_to_array($result,1);
1163 if ($extra_id != '-1') {
1165 $names[]=$extra_name;
1169 if (!is_array($checked)) {
1170 $checked = explode(',',$checked);
1172 $size = min( count($ids)+1, 15);
1173 return html_build_multiple_select_box_from_arrays ($ids,$names,$name,$checked,$size,$show_100,$text_100);
1175 return html_build_select_box_from_arrays ($ids,$names,$name,$checked,$show_100,$text_100);
1179 function cannedResponseBox ($name='canned_response',$checked='xzxz') {
1180 return html_build_select_box ($this->getCannedResponses(),$name,$checked);
1184 * statusBox - show the statuses - automatically shows the "custom statuses" if they exist
1188 function statusBox ($name='status_id',$checked='xzxz',$show_100=false,$text_100='none') {
1189 if ($text_100=='none'){
1190 $text_100=_('None');
1192 return html_build_select_box($this->getStatuses(),$name,$checked,$show_100,$text_100);
1195 function javascript() {
1197 var invalidSelectMsg = '"._("One or more of the selected options is not allowed")."';
1198 var invalidInputMsg = '". _("This choice is not allowed")."';";
1199 $javascript = <<<'EOS'
1200 $.expr[':'].invalid = function(elem, index, match) {
1201 for (let invalid of document.querySelectorAll(':invalid') ) {
1202 if (elem === invalid) { return true; }
1206 $(".package").on('change', function(){
1207 $(this).children('option:selected').each(function(i){
1208 var releases = $(this).data("releases");
1209 $("select[name^='extra_fields["+releases.field+"]']")[0].setCustomValidity("");
1210 $("select[name^='extra_fields["+releases.field+"]'] option").not( "[value='100']" ).each(function(j,opt){
1211 if (this.value!='100') {
1212 if ($.inArray(parseInt(this.value),releases.elmnt)>-1) {
1213 $(this).prop('disabled', false).removeClass('option_disabled');
1215 $(this).prop('disabled', true);
1216 $(this).addClass('option_disabled');
1220 $("select[name^='extra_fields["+releases.field+"]'] option:selected:disabled").parent().each(function() {
1221 $(this).children('option:selected:disabled').prop('disabled', false);
1222 this.setCustomValidity(invalidSelectMsg);
1223 $(this).on("change.invalid", function() {
1224 $(this).children('option.option_disabled:not(:disabled):not(:selected)').prop('disabled', true);
1225 if (!$(this).children('option.option_disabled:selected').length) {
1226 this.setCustomValidity("");
1227 $(this).off("change.invalid");
1233 $(".autoassign[name^='extra_fields']").on('change', function(){
1234 if ($(this).prop('tagName') == 'SELECT') {
1235 var elmnts = $(this).children('option:selected');
1237 var elmnts = $(this).siblings('input:checked');
1239 elmnts.each(function(i){
1240 var aat = $(this).data("autoassignto");
1241 $("select#tracker-assigned_to option[value="+aat.id+"]").prop('selected', true);
1242 $("span#tracker-assigned_to").text(aat.name);
1245 $(".with-depcy[name^='extra_fields']").on('change', function(){
1246 if ($(this).prop('tagName') == 'SELECT') {
1247 var elmnts = $(this).children('option:selected');
1249 var elmnts = $(this).siblings('input:checked');
1251 elmnts.each(function(i){
1252 var dep = $(this).data("dependency");
1253 if (this.value!='100') {
1254 $(dep).each(function(j, val) {
1255 $("select[name^='extra_fields["+val.field+"]']:invalid, input[name^='extra_fields["+val.field+"]']:invalid").each(function() {
1256 this.setCustomValidity("");
1257 $(this).off("change.invalid");
1259 $("select[name^='extra_fields["+val.field+"]'] option").each(function(k,opt){
1260 if (this.value!='100') {
1261 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1262 $(this).prop('disabled', false).removeClass('option_disabled');
1264 $(this).prop('disabled', true);
1265 $(this).addClass('option_disabled');
1269 $("input[name^='extra_fields["+val.field+"]']").each(function(k,opt){
1270 if (this.value!='100') {
1271 if ($.inArray(parseInt(this.value),val.elmnt)>-1) {
1272 $(this).prop('disabled', false).removeClass($(this).attr('type')+'_disabled');
1274 $(this).prop('disabled', true);
1275 $(this).addClass($(this).attr('type')+'_disabled');
1281 $(dep.fields).each(function(j, val) {
1282 $("select[name^='extra_fields["+val+"]']:invalid, input[name^='extra_fields["+val+"]']:invalid").each(function() {
1283 this.setCustomValidity("");
1285 $("select[name^='extra_fields["+val+"]'] option.option_disabled").each(function() {
1286 $(this).prop('disabled', false).removeClass('option_disabled');
1288 $("input.radio_disable[name^='extra_fields["+val+"]']").each(function() {
1289 $(this).prop('disabled', false).removeClass('radio_disabled');
1291 $("input.checkbox_disabled[name^='extra_fields["+val+"]']").each(function() {
1292 $(this).prop('disabled', false).removeClass('checkbox_disabled');
1297 $("select[name^='extra_fields'] option:selected:disabled").parent().each(function() {
1298 $(this).children('option:selected:disabled').prop('disabled', false);
1299 this.setCustomValidity(invalidSelectMsg);
1300 $(this).on("change.invalid", function() {
1301 $(this).children('option.option_disabled:not(:disabled):not(:selected)').prop('disabled', true);
1302 if (!$(this).children('option.option_disabled:selected').length) {
1303 this.setCustomValidity("");
1304 $(this).off("change.invalid");
1308 $("input[name^='extra_fields']:checked:disabled").each(function() {
1309 $(this).prop('disabled', false);
1310 this.setCustomValidity(invalidInputMsg);
1311 if ($(this).attr('type') == 'radio') {
1312 $(this).siblings('input[type="radio"]').on("change.invalid", function() {
1313 $(this).siblings('input[type="radio"]:invalid').prop('disabled', true).addClass('input_disabled').each(function() {
1314 this.setCustomValidity("");
1316 $(this).siblings('input[type="radio"]').off("change.invalid");
1317 $(this).off("change.invalid");
1320 $(this).on("change.invalid", function() {
1321 $(this).prop('disabled', true);
1322 this.setCustomValidity("");
1323 $(this).off("change.invalid");
1329 return html_e('script', array( 'type'=>'text/javascript'), '//<![CDATA['."\n".'$(function(){'.$jsvariable."\n".$javascript.'});'."\n".'//]]>');
1336 // c-file-style: "bsd"