5 * Copyright 1999-2001 (c) VA Linux Systems
6 * Copyright 2010 (c) FusionForge Team
7 * Copyright (C) 2010-2012 Alain Peyrat - Alcatel-Lucent
8 * Copyright 2011, Franck Villaume - Capgemini
9 * Copyright 2011-2013, Franck Villaume - TrivialDev
10 * Copyright © 2011, 2012
11 * Thorsten “mirabilos” Glaser <t.glaser@tarent.de>
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.'include/minijson.php';
32 * html_generic_fileheader() - Output <html><head> and <meta/> inside.
34 * @param $title string
35 * Mandatory content of <title> attribute, will be HTML-secured
38 function html_generic_fileheader($title) {
39 global $HTML, $sysDTDs, $sysXMLNSs;
42 throw new Exception('A title is mandatory in XHTML!');
45 $HTML->headerHTMLDeclaration();
47 echo '<meta http-equiv="Content-Type" ' .
48 'content="text/html; charset=utf-8" />' . "\n";
49 echo '<script type="text/javascript">//<![CDATA[' .
50 "\n\tvar sys_url_base = " . minijson_encode(util_make_url("/"),
53 $HTML->headerForgepluckerMeta();
54 echo html_e('title', array(), util_html_secure($title)) . "\n";
58 * html_feedback_top() - Show the feedback output at the top of the page.
60 * @param string The feedback.
62 function html_feedback_top($feedback) {
64 echo $HTML->feedback($feedback);
68 * html_warning_top() - Show the warning output at the top of the page.
70 * @param string The warning message.
72 function html_warning_top($msg) {
74 echo $HTML->warning_msg($msg);
78 * html_error_top() - Show the error output at the top of the page.
80 * @param string The error message.
82 function html_error_top($msg) {
84 echo $HTML->error_msg($msg);
88 * make_user_link() - Make a username reference into a link to that users User page on SF.
90 * @param string The username of the user to link.
93 function make_user_link($username, $displayname = '') {
94 if (empty($displayname))
95 $displayname = $username;
97 if (!strcasecmp($username, 'Nobody') || !strcasecmp($username, 'None')) {
100 return '<a href="/users/'.$username.'">'.$displayname.'</a>';
105 * html_feedback_bottom() - Show the feedback output at the bottom of the page.
107 * @param string The feedback.
109 function html_feedback_bottom($feedback) {
111 echo $HTML->feedback($feedback);
115 * html_blankimage() - Show the blank spacer image.
117 * @param int The height of the image
118 * @param int The width of the image
121 function html_blankimage($height, $width) {
122 return '<img src="/images/blank.png" width="'.$width.'" height="'.$height.'" alt="" />';
126 * html_abs_image() - Show an image given an absolute URL.
129 * @param int width of the image
130 * @param int height of the image
131 * @param array Any <img> tag parameters (i.e. 'border', 'alt', etc...)
134 function html_abs_image($url, $width, $height, $args) {
135 $return = ('<img src="'.$url.'"');
137 while (list($k, $v) = each($args)) {
138 $return .= ' '.$k.'="'.$v.'"';
141 if (!isset($args['alt'])) {
142 $return .= ' alt=""';
145 // Add image dimensions (if given)
146 $return .= $width ? " width=\"".$width."\"" : '';
147 $return .= $height ? " height=\"".$height."\"" : '';
154 * html_image() - Build an image tag of an image contained in $src
156 * @param string The source location of the image
157 * @param int The width of the image
158 * @param int The height of the image
159 * @param array Any IMG tag parameters associated with this image (i.e. 'border', 'alt', etc...)
160 * @param bool DEPRECATED
163 function html_image($src, $width = '', $height = '', $args = array(), $display = 1) {
166 if (method_exists($HTML, 'html_image')) {
167 $HTML->html_image($src, $width, $height, $args);
169 $s = ((session_issecure()) ? forge_get_config('images_secure_url') : forge_get_config('images_url') );
170 return html_abs_image($s.$HTML->imgroot.$src, $width, $height, $args);
174 * html_get_language_popup() - Pop up box of supported languages.
176 * @param string The title of the popup box.
177 * @param string Which element of the box is to be selected.
178 * @return string The html select box.
180 function html_get_language_popup($title = 'language_id', $selected = 'xzxz') {
181 $res = db_query_params('SELECT * FROM supported_languages ORDER BY name ASC',
183 return html_build_select_box($res, $title, $selected, false);
187 * html_get_theme_popup() - Pop up box of supported themes.
189 * @param string The title of the popup box.
190 * @param string Which element of the box is to be selected.
191 * @return string The html select box.
193 function html_get_theme_popup($title = 'theme_id', $selected = 'xzxz') {
194 $res = db_query_params('SELECT theme_id, fullname FROM themes WHERE enabled=true',
196 $nbTheme = db_numrows($res);
198 $thetheme = db_result($res, 0, 'fullname');
199 return util_html_secure($thetheme) . html_e('input', array(
202 'value' => db_result($res, 0, 'theme_id'),
204 } elseif ($nbTheme < 1) {
207 return html_build_select_box($res, $title, $selected, false);
212 * html_get_ccode_popup() - Pop up box of supported country_codes.
214 * @param string The title of the popup box.
215 * @param string Which element of the box is to be selected.
216 * @return string The html select box.
218 function html_get_ccode_popup($title = 'ccode', $selected = 'xzxz') {
219 $res = db_query_params('SELECT ccode,country_name FROM country_code ORDER BY country_name',
221 return html_build_select_box($res, $title, $selected, false);
225 * html_get_timezone_popup() - Pop up box of supported Timezones.
226 * Assumes you have included Timezones array file.
228 * @param string The title of the popup box.
229 * @param string Which element of the box is to be selected.
230 * @return string The html select box.
232 function html_get_timezone_popup($title = 'timezone', $selected = 'xzxz') {
234 if ($selected == 'xzxzxzx') {
235 $r = file('/etc/timezone');
236 $selected = str_replace("\n", '', $r[0]);
238 return html_build_select_box_from_arrays($TZs, $TZs, $title, $selected, false);
243 * html_build_select_box_from_assoc() - Takes one assoc array and returns a pop-up box.
245 * @param array An array of items to use.
246 * @param string The name you want assigned to this form element.
247 * @param string The value of the item that should be checked.
248 * @param boolean Whether we should swap the keys / names.
249 * @param bool Whether or not to show the '100 row'.
250 * @param string What to call the '100 row' defaults to none.
253 function html_build_select_box_from_assoc($arr, $select_name, $checked_val = 'xzxz', $swap = false, $show_100 = false, $text_100 = 'None') {
255 $keys = array_values($arr);
256 $vals = array_keys($arr);
258 $vals = array_values($arr);
259 $keys = array_keys($arr);
261 return html_build_select_box_from_arrays($keys, $vals, $select_name, $checked_val, $show_100, $text_100);
265 * html_build_select_box_from_array() - Takes one array, with the first array being the "id"
266 * or value and the array being the text you want displayed.
268 * @param array An array of items to use.
269 * @param string The name you want assigned to this form element.
270 * @param string The value of the item that should be checked.
273 function html_build_select_box_from_array($vals, $select_name, $checked_val = 'xzxz', $samevals = 0) {
275 <select name="'.$select_name.'">';
277 $rows = count($vals);
279 for ($i = 0; $i < $rows; $i++) {
281 $return .= "\n\t\t<option value=\"".$vals[$i]."\"";
282 if ($vals[$i] == $checked_val) {
283 $return .= ' selected="selected"';
286 $return .= "\n\t\t<option value=\"".$i.'"';
287 if ($i == $checked_val) {
288 $return .= ' selected="selected"';
291 $return .= '>'.htmlspecialchars($vals[$i]).'</option>';
300 * html_build_radio_buttons_from_arrays() - Takes two arrays, with the first array being the "id" or value and the other
301 * array being the text you want displayed.
303 * The infamous '100 row' has to do with the SQL Table joins done throughout all this code.
304 * There must be a related row in users, categories, et , and by default that
305 * row is 100, so almost every pop-up box has 100 as the default
306 * Most tables in the database should therefore have a row with an id of 100 in it so that joins are successful
308 * @param array The ID or value
309 * @param array Text to be displayed
310 * @param string Name to assign to this form element
311 * @param string The item that should be checked
312 * @param bool Whether or not to show the '100 row'
313 * @param string What to call the '100 row' defaults to none
314 * @param bool Whether or not to show the 'Any row'
315 * @param string What to call the 'Any row' defaults to any
318 function html_build_radio_buttons_from_arrays($vals, $texts, $select_name, $checked_val = 'xzxz', $show_100 = true, $text_100 = 'none', $show_any = false, $text_any = 'any') {
319 if ($text_100 == 'none') {
320 $text_100 = _('None');
324 $rows = count($vals);
325 if (count($texts) != $rows) {
326 $return .= 'Error: uneven row counts';
329 //we don't always want the default Any row shown
332 <input type="radio" name="'.$select_name.'" value=""'.(($checked_val == '')? ' checked="checked"' : '').' /> '.$text_any.'<br />';
334 //we don't always want the default 100 row shown
337 <input type="radio" name="'.$select_name.'" value="100"'.(($checked_val == 100)? ' checked="checked"' : '').' /> '.$text_100.'<br />';
340 $checked_found = false;
342 for ($i = 0; $i < $rows; $i++) {
343 // uggh - sorry - don't show the 100 row
344 // if it was shown above, otherwise do show it
345 if (($vals[$i] != '100') || ($vals[$i] == '100' && !$show_100)) {
347 <input type="radio" id="'.$select_name.'_'.$vals[$i].'" name="'.$select_name.'" value="'.$vals[$i].'"';
348 if ((string)$vals[$i] == (string)$checked_val) {
349 $checked_found = true;
350 $return .= ' checked="checked"';
352 $return .= ' /> '.htmlspecialchars($texts[$i]).'<br />';
356 // If the passed in "checked value" was never "SELECTED"
357 // we want to preserve that value UNLESS that value was 'xzxz', the default value
359 if (!$checked_found && $checked_val != 'xzxz' && $checked_val && $checked_val != 100) {
361 <input type="radio" value="'.$checked_val.'" checked="checked" /> '._('No Change').'<br />';
368 * html_get_tooltip_description() - Get the tooltip description of the element
370 * @param string element name
374 function html_get_tooltip_description($element_name) {
375 global $use_tooltips;
377 switch( $element_name ) {
379 return( _('This drop-down box represents the person to which a tracker item is assigned.'));
381 return( _('This drop-down box represents the current status of a tracker item.<br /><br />You can set the status to \'Pending\' if you are waiting for a response from the tracker item author. When the author responds the status is automatically reset to that of \'Open\'. Otherwise, if the author doesn\'t respond with an admin-defined amount of time (default is 14 days) then the item is given a status of \'Deleted\'.'));
383 return( _('Tracker category'));
385 return( _('Tracker group'));
387 return( _('The Sort By option allows you to determine how the browse results are sorted.<br /><br /> You can sort by ID, Priority, Summary, Open Date, Close Date, Submitter, or Assignee. You can also have the results sorted in Ascending or Descending order.'));
388 case 'new_artifact_type_id':
389 return( _('The Data Type option determines the type of tracker item this is. Since the tracker rolls into one the bug, patch, support, etc... managers you need to be able to determine which one of these an item should belong.<br /><br />This has the added benefit of enabling an admin to turn a support request into a bug.'));
391 return( _('The priority option allows a user to define a tracker item priority (ranging from 1-Lowest to 5-Highest).<br /><br />This is especially helpful for bugs and support requests where a user might find a critical problem with a project.'));
393 return( _('Resolution'));
395 return( _('The summary text-box represents a short tracker item summary. Useful when browsing through several tracker items.'));
396 case 'canned_response':
397 return( _('The canned response drop-down represents a list of project admin-defined canned responses to common support or bug submission.<br /><br /> If you are a project admin you can click the \'(admin)\' link to define your own canned responses'));
399 return( _('Anyone can add here comments to give additional information, answers and solutions. Please, be as precise as possible to avoid misunderstanding. If relevant, screenshots or documents can be added as attached files.'));
401 return( htmlentities(_('Enter the complete description.').'<br/><br/>'.
402 _("<div align=\"left\"><b>Editing tips:</b><br/><strong>http,https or ftp</strong>: Hyperlinks.<br/><strong>[#NNN]</strong>: Tracker id NNN.<br/><strong>[TNNN]</strong>: Task id NNN.<br/><strong>[wiki:<pagename>]</strong>: Wiki page.<br/><strong>[forum:<msg_id>]</strong>: Forum post.</div>"),
403 ENT_COMPAT, 'UTF-8'));
405 return( _('When you wish to attach a file to a tracker item you must check this checkbox before submitting changes.'));
407 return( htmlentities(_('You can monitor or un-monitor this item by clicking the "Monitor" button. <br /><br /><strong>Note!</strong> this will send you additional email. If you add comments to this item, or submitted, or are assigned this item, you will also get emails for those reasons as well!'),
408 ENT_COMPAT, 'UTF-8'));
417 function html_use_jquery() {
418 use_javascript('/scripts/jquery/jquery-1.8.3.js');
421 function html_use_tooltips() {
423 use_javascript('/scripts/jquery-tipsy/src/javascripts/jquery.tipsy.js');
424 use_javascript('/js/jquery-common.js');
425 use_stylesheet('/scripts/jquery-tipsy/src/stylesheets/tipsy.css');
428 function html_use_storage() {
430 use_javascript('/scripts/jquery-storage/jquery.Storage.js');
433 function html_use_simplemenu() {
435 use_javascript('/scripts/jquery-simpletreemenu/js/jquery-simpleTreeMenu-1.5.0.js');
436 use_stylesheet('/scripts/jquery-simpletreemenu/css/jquery-simpleTreeMenu-1.5.0.css');
439 function html_use_coolfieldset() {
441 use_javascript('/scripts/coolfieldset/js/jquery.coolfieldset.js');
442 use_javascript('/js/jquery-common.js');
443 use_stylesheet('/scripts/coolfieldset/css/jquery.coolfieldset.css');
446 function html_use_jqueryui() {
448 use_javascript('/scripts/jquery-ui/js/jquery-ui-1.9.2.custom.js');
449 use_stylesheet('/scripts/jquery-ui/css/overcast/jquery-ui-1.9.2.custom.css');
452 function html_use_jqueryjqplot() {
454 use_javascript('/scripts/jquery-jqplot/jquery.jqplot.js');
455 use_stylesheet('/scripts/jquery-jqplot/jquery.jqplot.css');
458 function html_use_jqueryjqplotpluginCanvas() {
459 html_use_jqueryjqplot();
460 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.canvasTextRenderer.js');
461 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.canvasAxisLabelRenderer.js');
462 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.canvasAxisTickRenderer.js');
463 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.categoryAxisRenderer.js');
466 function html_use_jqueryjqplotpluginBar() {
467 html_use_jqueryjqplot();
468 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.barRenderer.js');
469 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.pointLabels.js');
470 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.categoryAxisRenderer.js');
473 function html_use_jqueryjqplotpluginPie() {
474 html_use_jqueryjqplot();
475 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.pieRenderer.js');
478 function html_use_jqueryjqplotpluginhighlighter() {
479 html_use_jqueryjqplot();
480 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.highlighter.js');
483 function html_use_jqueryjqplotplugindateAxisRenderer() {
484 html_use_jqueryjqplot();
485 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.dateAxisRenderer.js');
488 function html_use_jqueryteamworkgantt() {
490 use_javascript('/scripts/jquery-teamwork-gantt/libs/jquery.livequery.min.js');
491 use_javascript('/scripts/jquery-teamwork-gantt/libs/jquery.timers.js');
492 use_javascript('/scripts/jquery-teamwork-gantt/libs/platform.js');
493 use_javascript('/scripts/jquery-teamwork-gantt/libs/date.js');
494 use_javascript('/scripts/jquery-teamwork-gantt/libs/date.js');
495 use_javascript('/scripts/jquery-teamwork-gantt/libs/i18nJs.js');
496 use_javascript('/scripts/jquery-teamwork-gantt/libs/dateField/jquery.dateField.js');
497 use_javascript('/scripts/jquery-teamwork-gantt/libs/JST/jquery.JST.js');
498 use_javascript('/scripts/jquery-teamwork-gantt/ganttUtilities.js');
499 use_javascript('/scripts/jquery-teamwork-gantt/ganttTask.js');
500 use_javascript('/scripts/jquery-teamwork-gantt/ganttDrawer.js');
501 use_javascript('/scripts/jquery-teamwork-gantt/ganttGridEditor.js');
502 use_javascript('/scripts/jquery-teamwork-gantt/ganttMaster.js');
503 use_stylesheet('/scripts/jquery-teamwork-gantt/platform.css');
504 use_stylesheet('/scripts/jquery-teamwork-gantt/libs/dateField/jquery.dateField.css');
505 use_stylesheet('/scripts/jquery-teamwork-gantt/gantt.css');
509 * html_build_select_box_from_arrays() - Takes two arrays, with the first array being the "id" or value and the other
510 * array being the text you want displayed.
512 * The infamous '100 row' has to do with the SQL Table joins done throughout all this code.
513 * There must be a related row in users, categories, et , and by default that
514 * row is 100, so almost every pop-up box has 100 as the default
515 * Most tables in the database should therefore have a row with an id of 100 in it so that joins are successful
517 * @param array The ID or value
518 * @param array Text to be displayed
519 * @param string Name to assign to this form element
520 * @param string The item that should be checked
521 * @param bool Whether or not to show the '100 row'
522 * @param string What to call the '100 row' defaults to none
523 * @param bool Whether or not to show the 'Any row'
524 * @param string What to call the 'Any row' defaults to any
525 * @param array Array of all allowed values from the full list.
528 function html_build_select_box_from_arrays($vals, $texts, $select_name, $checked_val = 'xzxz', $show_100 = true, $text_100 = 'none', $show_any = false, $text_any = 'any', $allowed = false) {
529 $have_a_subelement = false;
531 if ($text_100 == 'none') {
532 $text_100 = _('None');
536 $rows = count($vals);
537 if (count($texts) != $rows) {
538 $return .= _('Error: uneven row counts');
541 //TODO: remove this ugly ack to get something more generic...
542 $title = html_get_tooltip_description($select_name);
545 $id = ' id="tracker-'.$select_name.'"';
546 if (preg_match('/\[\]/', $id)) {
552 <select'.$id.' name="'.$select_name.'" title="'.util_html_secure($title).'">';
554 //we don't always want the default Any row shown
557 <option value=""'.(($checked_val == '') ? ' selected="selected"' : '').'>'. util_html_secure($text_any) .'</option>';
558 $have_a_subelement = true;
560 //we don't always want the default 100 row shown
563 <option value="100"'.(($checked_val == 100) ? ' selected="selected"' : '').'>'. util_html_secure($text_100) .'</option>';
564 $have_a_subelement = true;
567 $checked_found = false;
569 for ($i = 0; $i < $rows; $i++) {
570 // uggh - sorry - don't show the 100 row
571 // if it was shown above, otherwise do show it
572 if (($vals[$i] != '100') || ($vals[$i] == '100' && !$show_100)) {
574 <option value="'.util_html_secure($vals[$i]).'"';
575 if ((string)$vals[$i] == (string)$checked_val) {
576 $checked_found = true;
577 $return .= ' selected="selected"';
579 if (is_array($allowed) && !in_array($vals[$i], $allowed)) {
580 $return .= ' disabled="disabled" class="option_disabled"';
582 $return .= '>'.util_html_secure($texts[$i]).'</option>';
583 $have_a_subelement = true;
587 // If the passed in "checked value" was never "SELECTED"
588 // we want to preserve that value UNLESS that value was 'xzxz', the default value
590 if (!$checked_found && $checked_val != 'xzxz' && $checked_val && $checked_val != 100) {
592 <option value="'.util_html_secure($checked_val).'" selected="selected">'._('No Change').'</option>';
593 $have_a_subelement = true;
596 if (!$have_a_subelement) {
597 /* <select></select> without <option/> in between is invalid */
598 return '<!-- select without options -->';
607 * html_build_select_box() - Takes a result set, with the first column being the "id" or value and
608 * the second column being the text you want displayed.
610 * @param int The result set
611 * @param string Text to be displayed
612 * @param string The item that should be checked
613 * @param bool Whether or not to show the '100 row'
614 * @param string What to call the '100 row'. Defaults to none.
616 function html_build_select_box($result, $name, $checked_val = "xzxz", $show_100 = true, $text_100 = 'none', $show_any = false, $text_any = 'Select One', $allowed = false) {
617 if ($text_100 == 'none') {
618 $text_100 = _('None');
620 if ($text_any == 'Select One') {
621 $text_any = _('Select One');
623 return html_build_select_box_from_arrays(util_result_column_to_array($result, 0), util_result_column_to_array($result, 1), $name, $checked_val, $show_100, $text_100, $show_any, $text_any);
627 * html_build_select_box_sorted() - Takes a result set, with the first column being the "id" or value and
628 * the second column being the text you want displayed.
630 * @param int The result set
631 * @param string Text to be displayed
632 * @param string The item that should be checked
633 * @param bool Whether or not to show the '100 row'
634 * @param string What to call the '100 row'. Defaults to none.
637 function html_build_select_box_sorted($result, $name, $checked_val = "xzxz", $show_100 = true, $text_100 = 'none') {
638 if ($text_100 == 'none') {
639 $text_100 = _('None');
641 $vals = util_result_column_to_array($result, 0);
642 $texts = util_result_column_to_array($result, 1);
643 array_multisort($texts, SORT_ASC, SORT_STRING,
645 return html_build_select_box_from_arrays ($vals, $texts, $name, $checked_val, $show_100, $text_100);
649 * html_build_multiple_select_box() - Takes a result set, with the first column being the "id" or value
650 * and the second column being the text you want displayed.
652 * @param int The result set
653 * @param string Text to be displayed
654 * @param string The item that should be checked
655 * @param int The size of this box
656 * @param bool Whether or not to show the '100 row'
659 function html_build_multiple_select_box($result, $name, $checked_array, $size = '8', $show_100 = true) {
660 $checked_count = count($checked_array);
662 <select name="'.$name.'" multiple="multiple" size="'.$size.'">';
665 Put in the default NONE box
668 <option value="100"';
669 for ($j = 0; $j < $checked_count; $j++) {
670 if ($checked_array[$j] == '100') {
671 $return .= ' selected="selected"';
674 $return .= '>'._('None').'</option>';
677 $rows = db_numrows($result);
678 for ($i = 0; $i < $rows; $i++) {
679 if ((db_result($result, $i, 0) != '100') || (db_result($result, $i, 0) == '100' && !$show_100)) {
681 <option value="'.db_result($result, $i, 0).'"';
683 Determine if it's checked
685 $val = db_result($result, $i, 0);
686 for ($j = 0; $j < $checked_count; $j++) {
687 if ($val == $checked_array[$j]) {
688 $return .= ' selected="selected"';
691 $return .= '>'.substr(db_result($result, $i, 1), 0, 35).'</option>';
700 * html_build_multiple_select_box_from_arrays() - Takes two arrays and builds a multi-select box
702 * @param array id of the field
703 * @param array Text to be displayed
704 * @param string id of the items selected
705 * @param string The item that should be checked
706 * @param int The size of this box
707 * @param bool Whether or not to show the '100 row'
710 function html_build_multiple_select_box_from_arrays($ids, $texts, $name, $checked_array, $size = '8', $show_100 = true, $text_100 = 'none') {
711 $checked_count = count($checked_array);
713 <select name="'.$name.'" multiple="multiple" size="'.$size.'">';
715 if ($text_100 == 'none') {
716 $text_100 = _('None');
719 Put in the default NONE box
722 <option value="100"';
723 for ($j = 0; $j < $checked_count; $j++) {
724 if ($checked_array[$j] == '100') {
725 $return .= ' selected="selected"';
728 $return .= '>'.$text_100.'</option>';
732 for ($i = 0; $i < $rows; $i++) {
733 if (($ids[$i] != '100') || ($ids[$i] == '100' && !$show_100)) {
735 <option value="'.$ids[$i].'"';
737 Determine if it's checked
740 for ($j = 0; $j < $checked_count; $j++) {
741 if ($val == $checked_array[$j]) {
742 $return .= ' selected="selected"';
745 $return .= '>'.$texts[$i].' </option>';
754 * html_build_checkbox() - Render checkbox control
756 * @param name - name of control
757 * @param value - value of control
758 * @param checked - true if control should be checked
759 * @return html code for checkbox control
761 function html_build_checkbox($name, $value, $checked) {
762 return '<input type="checkbox" name="'.$name.'"'
763 .' value="'.$value.'"'
764 .($checked ? 'checked="checked"' : '').'>';
768 * build_priority_select_box() - Wrapper for html_build_priority_select_box()
770 * @see html_build_priority_select_box()
772 function build_priority_select_box($name = 'priority', $checked_val = '3', $nochange = false) {
773 html_build_priority_select_box($name, $checked_val, $nochange);
777 * html_build_priority_select_box() - Return a select box of standard priorities.
778 * The name of this select box is optional and so is the default checked value.
780 * @param string $name Name of the select box
781 * @param string $checked_val The value to be checked
782 * @param bool $nochange Whether to make 'No Change' selected.
784 function html_build_priority_select_box($name = 'priority', $checked_val = '3', $nochange = false) {
786 <select id="tracker-<?php echo $name ?>" name="<?php echo $name; ?>" title="<?php echo util_html_secure(html_get_tooltip_description($name)) ?>">
787 <?php if ($nochange) { ?>
788 <option value="100"<?php if ($nochange) {echo " selected=\"selected\"";} ?>><?php echo _('No Change') ?></option>
790 <option value="1"<?php if ($checked_val=="1") {echo " selected=\"selected\"";} ?>>1 - <?php echo _('Lowest') ?></option>
791 <option value="2"<?php if ($checked_val=="2") {echo " selected=\"selected\"";} ?>>2</option>
792 <option value="3"<?php if ($checked_val=="3") {echo " selected=\"selected\"";} ?>>3</option>
793 <option value="4"<?php if ($checked_val=="4") {echo " selected=\"selected\"";} ?>>4</option>
794 <option value="5"<?php if ($checked_val=="5") {echo " selected=\"selected\"";} ?>>5 - <?php echo _('Highest') ?></option>
801 * html_buildcheckboxarray() - Build an HTML checkbox array.
803 * @param array Options array
804 * @param name Checkbox name
805 * @param array Array of boxes to be pre-checked
807 function html_buildcheckboxarray($options, $name, $checked_array) {
808 $option_count = count($options);
809 $checked_count = count($checked_array);
811 for ($i = 1; $i <= $option_count; $i++) {
813 <br /><input type="checkbox" name="'.$name.'" value="'.$i.'"';
814 for ($j = 0; $j < $checked_count; $j++) {
815 if ($i == $checked_array[$j]) {
816 echo ' checked="checked"';
819 echo ' /> '.$options[$i];
824 * site_header() - everything required to handle security and
825 * add navigation for user pages like /my/ and /account/
827 * @param array Must contain $user_id
829 function site_header($params) {
832 Check to see if active user
833 Check to see if logged in
835 $HTML->header($params);
839 * site_footer() - Show the HTML site footer.
841 * @param array Footer params array
843 function site_footer($params) {
845 $HTML->footer($params);
849 * site_project_header() - everything required to handle
850 * security and state checks for a project web page
852 * @param params array() must contain $toptab and $group
854 function site_project_header($params) {
857 Check to see if active
858 Check to see if project rather than foundry
859 Check to see if private (if private check if user_ismember)
862 $group_id = $params['group'];
864 //get the project object
865 $project = group_get_object($group_id);
867 if (!$project || !is_object($project)) {
869 } elseif ($project->isError()) {
870 if ($project->isPermissionDeniedError()) {
871 if (!session_get_user()) {
872 $next = '/account/login.php?error_msg='.urlencode($project->getErrorMessage());
873 if (getStringFromServer('REQUEST_METHOD') != 'POST') {
874 $next .= '&return_to='.urlencode(getStringFromServer('REQUEST_URI'));
876 session_redirect($next);
878 exit_error(sprintf(_('Project access problem: %s'), $project->getErrorMessage()), 'home');
880 exit_error(sprintf(_('Project Problem: %s'), $project->getErrorMessage()), 'home');
883 // Check permissions in case of restricted access
884 session_require_perm('project_read', $group_id);
886 //for dead projects must be member of admin project
887 if (!$project->isActive()) {
888 session_require_global_perm('forge_admin');
891 if (isset($params['title'])) {
892 $h1 = $params['title'];
893 $params['title'] = $project->getPublicName().': '.$params['title'];
895 $h1 = $project->getPublicName();
896 $params['title'] = $project->getPublicName();
898 if (!isset($params['h1'])) {
902 if ($project->getDescription()) {
903 $params['meta-description'] = $project->getDescription();
906 if (forge_get_config('use_project_tags')) {
907 $res = db_query_params('SELECT name FROM project_tags WHERE group_id = $1', array($group_id));
908 if ($res && db_numrows($res) > 0) {
909 while ($row = db_fetch_array($res)) {
910 $array[] = $row['name'];
912 $params['meta-keywords'] = htmlspecialchars(join(', ', $array));
916 site_header($params);
920 * site_project_footer() - currently a simple shim
921 * that should be on every project page, rather than
922 * a direct call to site_footer() or theme_footer()
924 * @param params array() empty
926 function site_project_footer($params) {
927 site_footer($params);
931 * site_user_header() - everything required to handle security and
932 * add navigation for user pages like /my/ and /account/
934 * @param params array() must contain $user_id
936 function site_user_header($params) {
940 Check to see if active user
941 Check to see if logged in
943 site_header($params);
944 echo $HTML->beginSubMenu();
949 $arr_t[] = _('My Personal Page');
951 $arr_attr[] = array('title' => _('View your personal page, a selection of widgets to follow the informations from projects.'), 'class' => 'tabtitle-nw');
953 if (forge_get_config('use_tracker')) {
954 $arr_t[] = _('My Trackers Dashboard');
955 $arr_l[] = '/my/dashboard.php';
956 $arr_attr[] = array('title' => _('View your tasks and artifacts.'), 'class' => 'tabtitle');
960 if (forge_get_config('use_diary')) {
961 $arr_t[] = _('My Diary and Notes');
962 $arr_l[] = '/my/diary.php';
963 $arr_attr[] = array('title' => _('Manage your diary. Add, modify or delete your notes.'), 'class' => 'tabtitle');
966 $arr_t[] = _('My Account');
967 $arr_l[] = '/account/';
968 $arr_attr[] = array('title' => _('Manage your account. Change your password, select your preferences.'), 'class' => 'tabtitle');
970 if (!forge_get_config('project_registration_restricted')
971 || forge_check_global_perm('approve_projects', '')) {
972 $arr_t[] = _('Register Project');
973 $arr_l[] = '/register/';
974 $arr_attr[] = array('title' => _('Register a new project in forge, following the workflow.'), 'class' => 'tabtitle');
977 echo ($HTML->printSubMenu($arr_t, $arr_l, $arr_attr));
978 if (plugin_hook_listeners("usermenu") > 0) {
979 echo $HTML->subMenuSeparator();
981 plugin_hook("usermenu", false);
982 echo $HTML->endSubMenu();
986 * site_user_footer() - currently a simple shim that should be on every user page,
987 * rather than a direct call to site_footer() or theme_footer()
989 * @param params array() empty
991 function site_user_footer($params) {
992 site_footer($params);
996 * html_clean_hash_string() - Remove noise characters from hex hash string
998 * Thruout SourceForge, URLs with hexadecimal hash string parameters
999 * are being sent via email to request confirmation of user actions.
1000 * It was found that some mail clients distort this hash, so we take
1001 * special steps to encode it in the way which help to preserve its
1002 * recognition. This routine
1004 * @param hashstr required hash parameter as received from browser
1005 * @return pure hex string
1007 function html_clean_hash_string($hashstr) {
1009 if (substr($hashstr, 0, 1) == "_") {
1010 $hashstr = substr($hashstr, 1);
1013 if (substr($hashstr, strlen($hashstr) - 1, 1) == ">") {
1014 $hashstr = substr($hashstr, 0, strlen($hashstr) - 1);
1020 function relative_date($date) {
1021 $delta = max(time() - $date, 0);
1023 return sprintf(ngettext('%d second ago', '%d seconds ago', $delta), $delta);
1025 $delta = round($delta / 60);
1027 return sprintf(ngettext('%d minute ago', '%d minutes ago', $delta), $delta);
1029 $delta = round($delta / 60);
1031 return sprintf(ngettext('%d hour ago', '%d hours ago', $delta), $delta);
1033 $delta = round($delta / 24);
1035 return sprintf(ngettext('%d day ago', '%d days ago', $delta), $delta);
1037 $delta = round($delta / 7);
1039 return sprintf(ngettext('%d week ago', '%d weeks ago', $delta), $delta);
1041 return date(_('Y-m-d H:i'), $date);
1044 /* TODO: think about beautifying output */
1047 * html_eo() - Return proper element XHTML start tag
1049 * @param string $name
1051 * @param array $attrs
1052 * (optional) associative array of element attributes
1053 * values: arrays are space-imploded;
1054 * false values and empty arrays ignored
1056 * XHTML string suitable for echo'ing
1058 function html_eo($name, $attrs = array()) {
1060 foreach ($attrs as $key => $value) {
1061 if (is_array($value)) {
1062 $value = count($value) ? implode(" ", $value) : false;
1064 if ($value === false) {
1067 $rv .= ' '.$key.'="'.htmlspecialchars($value).'"';
1074 * html_e() - Return proper element XHTML start/end sequence
1076 * @param string $name
1078 * @param array $attrs
1079 * (optional) associative array of element attributes
1080 * values: arrays are space-imploded;
1081 * false values and empty arrays ignored
1082 * @param string $content
1083 * (optional) XHTML to be placed inside
1084 * @param bool $shortform
1085 * (optional) allow short open-close form
1088 * XHTML string suitable for echo'ing
1090 function html_e($name, $attrs = array(), $content = "", $shortform = true) {
1092 foreach ($attrs as $key => $value) {
1093 if (is_array($value)) {
1094 $value = count($value) ? implode(" ", $value) : false;
1096 if ($value === false) {
1099 $rv .= ' '.$key.'="'.htmlspecialchars($value).'"';
1101 if ($content === "" && $shortform) {
1104 $rv .= '>'.$content.'</'.$name.'>';
1109 $html_autoclose_stack = array();
1110 $html_autoclose_pos = 0;
1113 * html_ap() - Return XHTML element autoclose stack position
1117 function html_ap() {
1118 global $html_autoclose_pos;
1120 return $html_autoclose_pos;
1124 * html_ao() - Return proper element XHTML start tag, with autoclose
1126 * @param string $name
1128 * @param array $attrs
1129 * (optional) associative array of element attributes
1130 * values: arrays are space-imploded;
1131 * false values and empty arrays ignored
1133 * XHTML string suitable for echo'ing
1135 function html_ao($name, $attrs = array()) {
1136 global $html_autoclose_pos, $html_autoclose_stack;
1138 $html_autoclose_stack[$html_autoclose_pos++] = array(
1142 return html_eo($name, $attrs);
1146 * html_aonce() - Return once proper element XHTML start tag, with autoclose
1149 initialise this to false; will be modified
1150 * @param string $name
1152 * @param array $attrs
1153 * (optional) associative array of element attributes
1154 * values: arrays are space-imploded;
1155 * false values and empty arrays ignored
1157 * XHTML string suitable for echo'ing
1159 function html_aonce(&$sptr, $name, $attrs = array()) {
1160 if ($sptr !== false) {
1165 return html_ao($name, $attrs);
1169 * html_ac() - Return proper element XHTML end tags, autoclosing
1171 * @param $spos integer
1172 * stack position to return to (nothing is done if === false)
1174 * @return string XHTML string suitable for echo'ing
1176 function html_ac($spos) {
1177 global $html_autoclose_pos, $html_autoclose_stack;
1179 if ($spos === false) {
1180 /* support for html_aonce() */
1184 if ($html_autoclose_pos < $spos) {
1185 $e = "html_autoclose stack underflow; closing down to ".
1186 $spos." but we're down to ".$html_autoclose_pos.
1188 throw new Exception($e);
1192 while ($html_autoclose_pos > $spos) {
1193 --$html_autoclose_pos;
1194 $rv .= '</'.$html_autoclose_stack[$html_autoclose_pos]['name'].'>';
1195 unset($html_autoclose_stack[$html_autoclose_pos]);
1201 * html_a_copy() - Return a copy of part of the autoclose stack
1203 * @param $spos integer
1204 * stack position caller will return to
1207 * argument suitable for html_a_apply()
1209 function html_a_copy($spos) {
1210 global $html_autoclose_pos, $html_autoclose_stack;
1212 if ($spos === false) {
1216 if ($spos > $html_autoclose_pos) {
1217 $e = "html_autoclose stack underflow; closing down to ".
1218 $spos." but we're down to ".$html_autoclose_pos.
1220 throw new Exception($e);
1224 while ($spos < $html_autoclose_pos) {
1225 $rv[] = $html_autoclose_stack[$spos++];
1231 * html_a_apply() - Reopen tags based on an autoclose stack copy
1233 * @param opaque $scopy
1234 * return value from html_a_copy()
1236 * XHTML string suitable for echo'ing
1238 function html_a_apply($scopy) {
1239 /* array_reduce() would be useful here... IF IT WORKED, FFS! */
1241 foreach ($scopy as $value) {
1242 $rv .= html_ao($value['name'], $value['attr']);
1249 // c-file-style: "bsd"