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-2014, 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) {
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));
58 * html_feedback_top() - Show the feedback output at the top of the page.
60 * @param string $feedback 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 $msg 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 $msg 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 $username The username of the user to link.
91 * @param string $displayname The name to display.
94 function make_user_link($username, $displayname = '') {
95 if (empty($displayname))
96 $displayname = $username;
98 if (!strcasecmp($username, 'Nobody') || !strcasecmp($username, 'None')) {
101 return util_make_link('/users/'.$username, $displayname);
106 * html_feedback_bottom() - Show the feedback output at the bottom of the page.
108 * @param string $feedback The feedback.
110 function html_feedback_bottom($feedback) {
112 echo $HTML->feedback($feedback);
116 * html_blankimage() - Show the blank spacer image.
118 * @param int $height The height of the image
119 * @param int $width The width of the image
122 function html_blankimage($height, $width) {
123 return html_abs_image('/images/blank.png', $width, $height);
127 * html_abs_image() - Show an image given an absolute URL.
129 * @param string $url URL
130 * @param int $width width of the image
131 * @param int $height height of the image
132 * @param array $args Any <img> tag parameters (i.e. 'border', 'alt', etc...)
135 function html_abs_image($url, $width, $height, $args) {
136 global $use_tooltips;
138 if (!$use_tooltips && isset($args['title'])) {
141 if (!isset($args['alt'])) {
145 // Add image dimensions (if given)
146 $width ? $args['width'] = $width : '';
147 $height ? $args['height'] = $height : '';
149 return html_e('img', $args);
153 * html_image() - Build an image tag of an image contained in $src
155 * @param string $src The source location of the image
156 * @param int $width The width of the image
157 * @param int $height The height of the image
158 * @param array $args Any IMG tag parameters associated with this image (i.e. 'border', 'alt', etc...)
159 * @param bool $display DEPRECATED
162 function html_image($src, $width = 0, $height = 0, $args = array(), $display = true) {
165 if (method_exists($HTML, 'html_image')) {
166 $HTML->html_image($src, $width, $height, $args);
168 $s = ((session_issecure()) ? forge_get_config('images_secure_url') : forge_get_config('images_url') );
169 return html_abs_image($s.$HTML->imgroot.$src, $width, $height, $args);
173 * html_get_language_popup() - Pop up box of supported languages.
175 * @param string $title The title of the popup box.
176 * @param string $selected Which element of the box is to be selected.
177 * @return string The html select box.
179 function html_get_language_popup($title = 'language_id', $selected = 'xzxz') {
180 $res = db_query_params('SELECT * FROM supported_languages ORDER BY name ASC',
182 return html_build_select_box($res, $title, $selected, false);
186 * html_get_theme_popup() - Pop up box of supported themes.
188 * @param string $title The title of the popup box.
189 * @param string $selected Which element of the box is to be selected.
190 * @return string The html select box.
192 function html_get_theme_popup($title = 'theme_id', $selected = 'xzxz') {
193 $res = db_query_params('SELECT theme_id, fullname FROM themes WHERE enabled=true',
195 $nbTheme = db_numrows($res);
197 $thetheme = db_result($res, 0, 'fullname');
198 return util_html_secure($thetheme) . html_e('input', array(
201 'value' => db_result($res, 0, 'theme_id'),
203 } elseif ($nbTheme < 1) {
206 return html_build_select_box($res, $title, $selected, false);
211 * html_get_ccode_popup() - Pop up box of supported country_codes.
213 * @param string $title The title of the popup box.
214 * @param string $selected Which element of the box is to be selected.
215 * @return string The html select box.
217 function html_get_ccode_popup($title = 'ccode', $selected = 'xzxz') {
218 $res = db_query_params('SELECT ccode,country_name FROM country_code ORDER BY country_name',
220 return html_build_select_box($res, $title, $selected, false);
224 * html_get_timezone_popup() - Pop up box of supported Timezones.
225 * Assumes you have included Timezones array file.
227 * @param string $title The title of the popup box.
228 * @param string $selected Which element of the box is to be selected.
229 * @return string The html select box.
231 function html_get_timezone_popup($title = 'timezone', $selected = 'xzxz') {
233 if ($selected == 'xzxzxzx') {
234 $r = file('/etc/timezone');
235 $selected = str_replace("\n", '', $r[0]);
237 return html_build_select_box_from_arrays($TZs, $TZs, $title, $selected, false);
241 * html_build_select_box_from_assoc() - Takes one assoc array and returns a pop-up box.
243 * @param array $arr An array of items to use.
244 * @param string $select_name The name you want assigned to this form element.
245 * @param string $checked_val The value of the item that should be checked.
246 * @param bool $swap Whether we should swap the keys / names.
247 * @param bool $show_100 Whether or not to show the '100 row'.
248 * @param string $text_100 What to call the '100 row' defaults to none.
251 function html_build_select_box_from_assoc($arr, $select_name, $checked_val = 'xzxz', $swap = false, $show_100 = false, $text_100 = 'None') {
253 $keys = array_values($arr);
254 $vals = array_keys($arr);
256 $vals = array_values($arr);
257 $keys = array_keys($arr);
259 return html_build_select_box_from_arrays($keys, $vals, $select_name, $checked_val, $show_100, $text_100);
263 * html_build_select_box_from_array() - Takes one array, with the first array being the "id"
264 * or value and the array being the text you want displayed.
266 * @param array $vals An array of items to use.
267 * @param string $select_name The name you want assigned to this form element.
268 * @param string $checked_val The value of the item that should be checked.
269 * @param int $samevals
272 function html_build_select_box_from_array($vals, $select_name, $checked_val = 'xzxz', $samevals = 0) {
274 <select name="'.$select_name.'">';
276 $rows = count($vals);
278 for ($i = 0; $i < $rows; $i++) {
280 $return .= "\n\t\t<option value=\"".$vals[$i]."\"";
281 if ($vals[$i] == $checked_val) {
282 $return .= ' selected="selected"';
285 $return .= "\n\t\t<option value=\"".$i.'"';
286 if ($i == $checked_val) {
287 $return .= ' selected="selected"';
290 $return .= '>'.htmlspecialchars($vals[$i]).'</option>';
299 * html_build_radio_buttons_from_arrays() - Takes two arrays, with the first array being the "id" or value and the other
300 * array being the text you want displayed.
302 * The infamous '100 row' has to do with the SQL Table joins done throughout all this code.
303 * There must be a related row in users, categories, et , and by default that
304 * row is 100, so almost every pop-up box has 100 as the default
305 * Most tables in the database should therefore have a row with an id of 100 in it so that joins are successful
307 * @param array $vals The ID or value
308 * @param array $texts Text to be displayed
309 * @param string $select_name Name to assign to this form element
310 * @param string $checked_val The item that should be checked
311 * @param bool $show_100 Whether or not to show the '100 row'
312 * @param string $text_100 What to call the '100 row' defaults to none
313 * @param bool $show_any Whether or not to show the 'Any row'
314 * @param string $text_any What to call the 'Any row' defaults to any
317 function html_build_radio_buttons_from_arrays($vals, $texts, $select_name, $checked_val = 'xzxz',
318 $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 element name
374 function html_get_tooltip_description($element_name) {
375 global $use_tooltips;
376 if (!$use_tooltips) {
379 switch ($element_name) {
381 return _('This drop-down box represents the person to which a tracker item is assigned.');
383 return _('This drop-down box represents the current status of a tracker item.')
385 ._('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 does not respond with an admin-defined amount of time (default is 14 days) then the item is given a status of “Deleted”.');
387 return _('Tracker category');
389 return _('Tracker group');
391 return _('The Sort By option allows you to determine how the browse results are sorted.')
393 ._('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.');
394 case 'new_artifact_type_id':
395 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.')
397 ._('This has the added benefit of enabling an admin to turn a support request into a bug.');
399 return _('The priority option allows a user to define a tracker item priority (ranging from 1-Lowest to 5-Highest).')
401 ._('This is especially helpful for bugs and support requests where a user might find a critical problem with a project.');
403 return _('Resolution');
405 return _('The summary text-box represents a short tracker item summary. Useful when browsing through several tracker items.');
406 case 'canned_response':
407 return _('The canned response drop-down represents a list of project admin-defined canned responses to common support or bug submission.')
409 ._('If you are a project admin you can click the “Manage Canned Responses” link to define your own canned responses');
411 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.');
413 return _('Enter the complete description.')
415 ."<div align='left' >"
416 ._("<strong>Editing tips:</strong><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.")
419 return _('When you wish to attach a file to a tracker item you must check this checkbox before submitting changes.');
421 return _('You can monitor or un-monitor this item by clicking the “Monitor” button.')
423 ._('<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!');
425 return _('You can cast your vote for a Tracker Item to aid Project Management to decide which features to prioritise, and retract votes at any time. Please use this functionality sparingly, as it loses its meaning if you vote on *every* item.');
427 return _('This metric displays the number of people who can *currently* vote for features in this tracker, and how many of them did so. (This means historic votes of people no longer allowed to vote, while not lost, do not play into the numbers displayed.)');
433 function html_use_jquery() {
434 use_javascript('/scripts/jquery/jquery-1.10.2.js');
437 function html_use_storage() {
439 use_javascript('/scripts/jquery-storage/jquery.Storage.js');
442 function html_use_simplemenu() {
444 use_javascript('/scripts/jquery-simpletreemenu/js/jquery-simpleTreeMenu-1.5.0.js');
445 use_stylesheet('/scripts/jquery-simpletreemenu/css/jquery-simpleTreeMenu-1.5.0.css');
448 function html_use_coolfieldset() {
450 use_javascript('/scripts/coolfieldset/js/jquery.coolfieldset.js');
451 use_javascript('/js/jquery-common.js');
452 use_stylesheet('/scripts/coolfieldset/css/jquery.coolfieldset.css');
455 function html_use_jqueryui() {
457 use_javascript('/scripts/jquery-ui/js/jquery-ui-1.10.4.js');
460 function html_use_jqueryjqplot() {
462 use_javascript('/scripts/jquery-jqplot/jquery.jqplot.js');
463 use_stylesheet('/scripts/jquery-jqplot/jquery.jqplot.css');
466 function html_use_jqueryjqplotpluginCanvas() {
467 html_use_jqueryjqplot();
468 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.canvasTextRenderer.js');
469 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.canvasAxisLabelRenderer.js');
470 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.canvasAxisTickRenderer.js');
471 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.categoryAxisRenderer.js');
474 function html_use_jqueryjqplotpluginBar() {
475 html_use_jqueryjqplot();
476 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.barRenderer.js');
477 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.pointLabels.js');
478 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.categoryAxisRenderer.js');
481 function html_use_jqueryjqplotpluginPie() {
482 html_use_jqueryjqplot();
483 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.pieRenderer.js');
486 function html_use_jqueryjqplotpluginhighlighter() {
487 html_use_jqueryjqplot();
488 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.highlighter.js');
491 function html_use_jqueryjqplotplugindateAxisRenderer() {
492 html_use_jqueryjqplot();
493 use_javascript('/scripts/jquery-jqplot/plugins/jqplot.dateAxisRenderer.js');
496 function html_use_jqueryteamworkgantt() {
497 html_use_jquerybrowser();
498 use_javascript('/scripts/jquery-livequery/jquery.livequery.js');
499 use_javascript('/scripts/jquery-livequery/jquery.timers.js');
500 use_javascript('/scripts/jquery-teamwork-gantt/libs/platform.js');
501 use_javascript('/scripts/jquery-teamwork-gantt/libs/date.js');
502 use_javascript('/scripts/jquery-teamwork-gantt/libs/date.js');
503 use_javascript('/scripts/jquery-teamwork-gantt/libs/i18nJs.js');
504 use_javascript('/scripts/jquery-teamwork-gantt/libs/dateField/jquery.dateField.js');
505 use_javascript('/scripts/jquery-teamwork-gantt/libs/JST/jquery.JST.js');
506 use_javascript('/scripts/jquery-teamwork-gantt/ganttUtilities.js');
507 use_javascript('/scripts/jquery-teamwork-gantt/ganttTask.js');
508 use_javascript('/scripts/jquery-teamwork-gantt/ganttDrawer.js');
509 use_javascript('/scripts/jquery-teamwork-gantt/ganttGridEditor.js');
510 use_javascript('/scripts/jquery-teamwork-gantt/ganttMaster.js');
511 use_stylesheet('/scripts/jquery-teamwork-gantt/platform.css');
512 use_stylesheet('/scripts/jquery-teamwork-gantt/libs/dateField/jquery.dateField.css');
513 use_stylesheet('/scripts/jquery-teamwork-gantt/gantt.css');
516 function html_use_jquerysplitter() {
518 use_javascript('/scripts/jquery-splitter/js/jquery.splitter-0.8.0.js');
519 use_stylesheet('/scripts/jquery-splitter/css/jquery.splitter.css');
522 function html_use_jqueryautoheight() {
523 html_use_jquerybrowser();
524 use_javascript('/scripts/jquery-auto-height/jquery.iframe-auto-height.plugin.1.9.5.js');
527 function html_use_jquerybrowser() {
529 use_javascript('/scripts/jquery-auto-height/jquery.browser.js');
533 * html_build_select_box_from_arrays() - Takes two arrays, with the first array being the "id" or value and the other
534 * array being the text you want displayed.
536 * The infamous '100 row' has to do with the SQL Table joins done throughout all this code.
537 * There must be a related row in users, categories, et , and by default that
538 * row is 100, so almost every pop-up box has 100 as the default
539 * Most tables in the database should therefore have a row with an id of 100 in it so that joins are successful
541 * @param array $vals The ID or value
542 * @param array $texts Text to be displayed
543 * @param string $select_name Name to assign to this form element
544 * @param string $checked_val The item that should be checked
545 * @param bool $show_100 Whether or not to show the '100 row'
546 * @param string $text_100 What to call the '100 row' defaults to none
547 * @param bool $show_any Whether or not to show the 'Any row'
548 * @param string $text_any What to call the 'Any row' defaults to any
549 * @param bool|array $allowed Array of all allowed values from the full list.
552 function html_build_select_box_from_arrays($vals, $texts, $select_name, $checked_val = 'xzxz',
553 $show_100 = true, $text_100 = 'none',
554 $show_any = false, $text_any = 'any', $allowed = false) {
555 $have_a_subelement = false;
558 $rows = count($vals);
559 if (count($texts) != $rows) {
560 $return .= _('Error: uneven row counts');
563 //TODO: remove this ugly ack to get something more generic...
564 $title = html_get_tooltip_description($select_name);
567 $id = 'tracker-'.$select_name.'"';
568 if (preg_match('/\[\]/', $id)) {
573 $return .= html_ao('select', array('id' => $id, 'name' => $select_name, 'title' => util_html_secure($title)));
575 //we don't always want the default Any row shown
577 $attrs = array('value' => '');
579 $attrs['selected'] = 'selected';
580 $return .= html_e('option', $attrs, util_html_secure($text_any), false);
581 $have_a_subelement = true;
583 //we don't always want the default 100 row shown
585 if ($text_100 == 'none') {
586 $text_100 = _('None');
588 $attrs = array('value' => 100);
590 $attrs['selected'] = 'selected';
591 $return .= html_e('option', $attrs, util_html_secure($text_100), false);
592 $have_a_subelement = true;
595 $checked_found = false;
597 for ($i = 0; $i < $rows; $i++) {
598 // uggh - sorry - don't show the 100 row
599 // if it was shown above, otherwise do show it
600 if (($vals[$i] != '100') || ($vals[$i] == '100' && !$show_100)) {
602 $attrs['value'] = util_html_secure($vals[$i]);
603 if ((string)$vals[$i] == (string)$checked_val) {
604 $checked_found = true;
605 $attrs['selected'] = 'selected';
607 if (is_array($allowed) && !in_array($vals[$i], $allowed)) {
608 $attrs['disabled'] = 'disabled';
609 $attrs['class'] = 'option_disabled';
611 $return .= html_e('option', $attrs, util_html_secure($texts[$i]));
612 $have_a_subelement = true;
616 // If the passed in "checked value" was never "SELECTED"
617 // we want to preserve that value UNLESS that value was 'xzxz', the default value
619 if (!$checked_found && $checked_val != 'xzxz' && $checked_val && $checked_val != 100) {
620 $return .= html_e('option', array('value' => util_html_secure($checked_val), 'selected' => 'selected'), _('No Change'), false);
621 $have_a_subelement = true;
624 if (!$have_a_subelement) {
625 /* <select></select> without <option/> in between is invalid */
626 return '<!-- select without options -->'."\n";
629 $return .= html_ac(html_ap() -1);
634 * html_build_select_box() - Takes a result set, with the first column being the "id" or value and
635 * the second column being the text you want displayed.
637 * @param resource $result The result set
638 * @param string $name Text to be displayed
639 * @param string $checked_val The item that should be checked
640 * @param bool $show_100 Whether or not to show the '100 row'
641 * @param string $text_100 What to call the '100 row'. Defaults to none.
642 * @param bool $show_any Whether or not to show the 'Any row'
643 * @param string $text_any What to call the 'Any row' defaults to any
644 * @param bool $allowed Unused
647 function html_build_select_box($result, $name, $checked_val = "xzxz", $show_100 = true, $text_100 = 'none',
648 $show_any = false, $text_any = 'Select One', $allowed = false) {
649 if ($text_100 == 'none') {
650 $text_100 = _('None');
652 if ($text_any == 'Select One') {
653 $text_any = _('Select One');
655 return html_build_select_box_from_arrays(util_result_column_to_array($result, 0),
656 util_result_column_to_array($result, 1),
657 $name, $checked_val, $show_100, $text_100, $show_any, $text_any);
661 * html_build_select_box_sorted() - Takes a result set, with the first column being the "id" or value and
662 * the second column being the text you want displayed.
664 * @param int $result The result set
665 * @param string $name Text to be displayed
666 * @param string $checked_val The item that should be checked
667 * @param bool $show_100 Whether or not to show the '100 row'
668 * @param string $text_100 What to call the '100 row'. Defaults to none.
671 function html_build_select_box_sorted($result, $name, $checked_val = "xzxz", $show_100 = true, $text_100 = 'none') {
672 if ($text_100 == 'none') {
673 $text_100 = _('None');
675 $vals = util_result_column_to_array($result, 0);
676 $texts = util_result_column_to_array($result, 1);
677 array_multisort($texts, SORT_ASC, SORT_STRING, $vals);
678 return html_build_select_box_from_arrays ($vals, $texts, $name, $checked_val, $show_100, $text_100);
682 * html_build_multiple_select_box() - Takes a result set, with the first column being the "id" or value
683 * and the second column being the text you want displayed.
685 * @param resource $result The result set
686 * @param string $name Text to be displayed
687 * @param string $checked_array The item that should be checked
688 * @param int $size The size of this box
689 * @param bool $show_100 Whether or not to show the '100 row'
690 * @param string $text_100 The displayed text of the '100 row'
693 function html_build_multiple_select_box($result, $name, $checked_array, $size = 8, $show_100 = true, $text_100 = 'none') {
694 $checked_count = count($checked_array);
695 $return = html_ao('select', array('name' => $name, 'multiple' => 'multiple', 'size' => $size));
697 if ($text_100 == 'none') {
698 $text_100 = _('None');
701 Put in the default NONE box
703 $attrs = array('value' => 100);
704 for ($j = 0; $j < $checked_count; $j++) {
705 if ($checked_array[$j] == '100') {
706 $attrs['selected'] = 'selected';
709 $return .= html_e('option', $attrs, $text_100, false);
712 $rows = db_numrows($result);
713 for ($i = 0; $i < $rows; $i++) {
714 if ((db_result($result, $i, 0) != '100') || (db_result($result, $i, 0) == '100' && !$show_100)) {
716 $attrs = array('value' => db_result($result, $i, 0));
718 Determine if it's checked
720 $val = db_result($result, $i, 0);
721 for ($j = 0; $j < $checked_count; $j++) {
722 if ($val == $checked_array[$j]) {
723 $attrs['selected'] = 'selected';
726 $return .= html_e('option', $attrs, substr(db_result($result, $i, 1), 0, 35), false);
729 $return .= html_ac(html_ap() -1);
734 * html_build_multiple_select_box_from_arrays() - Takes two arrays and builds a multi-select box
736 * @param array $ids id of the field
737 * @param array $texts Text to be displayed
738 * @param string $name id of the items selected
739 * @param string $checked_array The item that should be checked
740 * @param int $size The size of this box
741 * @param bool $show_100 Whether or not to show the '100 row'
742 * @param string $text_100 What to call the '100 row' defaults to none.
745 function html_build_multiple_select_box_from_arrays($ids, $texts, $name, $checked_array, $size = 8, $show_100 = true, $text_100 = 'none') {
746 $checked_count = count($checked_array);
747 $return = html_ao('select', array('name' => $name, 'multiple' => 'multiple', 'size' => $size));
749 if ($text_100 == 'none') {
750 $text_100 = _('None');
753 Put in the default NONE box
755 $attrs = array('value' => 100);
756 for ($j = 0; $j < $checked_count; $j++) {
757 if ($checked_array[$j] == '100') {
758 $attrs['selected'] = 'selected';
761 $return .= html_e('option', $attrs, $text_100, false);
765 for ($i = 0; $i < $rows; $i++) {
766 if (($ids[$i] != '100') || ($ids[$i] == '100' && !$show_100)) {
768 $attrs = array('value' => $ids[$i]);
770 Determine if it's checked
773 for ($j = 0; $j < $checked_count; $j++) {
774 if ($val == $checked_array[$j]) {
775 $attrs['selected'] = 'selected';
778 $return .= html_e('option', $attrs, $texts[$i], false);
781 $return .= html_ac(html_ap() -1);
786 * html_build_checkbox() - Render checkbox control
788 * @param string $name name of control
789 * @param string $value value of control
790 * @param bool $checked true if control should be checked
791 * @return html code for checkbox control
793 function html_build_checkbox($name, $value, $checked) {
794 $attrs = array('id' => $name, 'name' => $name, 'value' => $value, 'type' => 'checkbox');
796 $attrs['checked'] = 'checked';
798 return html_e('input', $attrs);
802 * build_priority_select_box() - Wrapper for html_build_priority_select_box()
804 * @see html_build_priority_select_box()
806 function build_priority_select_box($name = 'priority', $checked_val = '3', $nochange = false) {
807 echo html_build_priority_select_box($name, $checked_val, $nochange);
811 * html_build_priority_select_box() - Return a select box of standard priorities.
812 * The name of this select box is optional and so is the default checked value.
814 * @param string $name Name of the select box
815 * @param string $checked_val The value to be checked
816 * @param bool $nochange Whether to make 'No Change' selected.
819 function html_build_priority_select_box($name = 'priority', $checked_val = '3', $nochange = false) {
820 $html = '<select id="tracker-'.$name.'" name="'.$name.'" title="'.util_html_secure(html_get_tooltip_description($name)).'">';
822 $html .= '<option value="100" selected="selected" >'._('No Change').'</option>';
824 $labelOption = array('1 - '._('Lowest'), '2', '3', '4', '5 - '._('Highest'));
825 for ($i = 1; $i <= 5; $i++) {
826 $html .= '<option value="'.$i.'" ';
827 if ($checked_val == $i) {
828 $html .= 'selected="selected" ';
830 $html .= '>'.$labelOption[$i -1].'</option>';
832 $html .= '</select>';
837 * html_buildcheckboxarray() - Build an HTML checkbox array.
839 * @param array $options Options array
840 * @param string $name Checkbox name
841 * @param array $checked_array Array of boxes to be pre-checked
843 function html_buildcheckboxarray($options, $name, $checked_array) {
844 $option_count = count($options);
845 $checked_count = count($checked_array);
847 for ($i = 1; $i <= $option_count; $i++) {
850 for ($j = 0; $j < $checked_count; $j++) {
851 if ($i == $checked_array[$j]) {
855 echo html_e('br').html_build_checkbox($name, $value, $checked).$options[$i];
860 * site_header() - everything required to handle security and
861 * add navigation for user pages like /my/ and /account/
863 * @param array $params Must contain $user_id
865 function site_header($params) {
868 Check to see if active user
869 Check to see if logged in
871 $HTML->header($params);
875 * site_footer() - Show the HTML site footer.
877 * @param array $params Footer params array
879 function site_footer($params = array()) {
881 $HTML->footer($params);
885 * site_project_header() - everything required to handle
886 * security and state checks for a project web page
888 * @param params array() must contain $toptab and $group
890 function site_project_header($params) {
893 Check to see if active
894 Check to see if project rather than foundry
895 Check to see if private (if private check if user_ismember)
898 $group_id = $params['group'];
900 //get the project object
901 $project = group_get_object($group_id);
903 if (!$project || !is_object($project)) {
905 } elseif ($project->isError()) {
906 if ($project->isPermissionDeniedError()) {
907 if (!session_get_user()) {
908 $next = '/account/login.php?error_msg='.urlencode($project->getErrorMessage());
909 if (getStringFromServer('REQUEST_METHOD') != 'POST') {
910 $next .= '&return_to='.urlencode(getStringFromServer('REQUEST_URI'));
912 session_redirect($next);
914 exit_error(sprintf(_('Project access problem: %s'), $project->getErrorMessage()), 'home');
916 exit_error(sprintf(_('Project Problem: %s'), $project->getErrorMessage()), 'home');
919 // Check permissions in case of restricted access
920 session_require_perm('project_read', $group_id);
922 //for dead projects must be member of admin project
923 if (!$project->isActive()) {
924 session_require_global_perm('forge_admin');
927 if (isset($params['title'])) {
928 $h1 = $params['title'];
929 $params['title'] = $project->getPublicName()._(': ').$params['title'];
931 $h1 = $project->getPublicName();
932 $params['title'] = $project->getPublicName();
934 if (!isset($params['h1'])) {
938 if ($project->getDescription()) {
939 $params['meta-description'] = $project->getDescription();
942 if (forge_get_config('use_project_tags')) {
943 $res = db_query_params('SELECT name FROM project_tags WHERE group_id = $1', array($group_id));
944 if ($res && db_numrows($res) > 0) {
945 while ($row = db_fetch_array($res)) {
946 $array[] = $row['name'];
948 $params['meta-keywords'] = htmlspecialchars(join(', ', $array));
952 site_header($params);
956 * site_project_footer() - currently a simple shim
957 * that should be on every project page, rather than
958 * a direct call to site_footer() or theme_footer()
960 * @param array $params array() empty
962 function site_project_footer($params = array()) {
963 site_footer($params);
967 * site_user_header() - everything required to handle security and
968 * add navigation for user pages like /my/ and /account/
970 * @param array $params array() must contain $user_id
972 function site_user_header($params) {
976 Check to see if active user
977 Check to see if logged in
979 site_header($params);
980 echo $HTML->beginSubMenu();
985 $arr_t[] = _('My Personal Page');
987 $arr_attr[] = array('title' => _('View your personal page, a selection of widgets to follow the informations from projects.'));
989 if (forge_get_config('use_tracker')) {
990 $arr_t[] = _('My Trackers Dashboard');
991 $arr_l[] = '/my/dashboard.php';
992 $arr_attr[] = array('title' => _('View your tasks and artifacts.'));
995 if (forge_get_config('use_diary')) {
996 $arr_t[] = _('My Diary and Notes');
997 $arr_l[] = '/my/diary.php';
998 $arr_attr[] = array('title' => _('Manage your diary. Add, modify or delete your notes.'));
1001 $arr_t[] = _('My Account');
1002 $arr_l[] = '/account/';
1003 $arr_attr[] = array('title' => _('Manage your account. Change your password, select your preferences.'));
1005 $arr_t[] = _('System actions queue');
1006 $arr_l[] = '/my/sysactionsq.php';
1007 $arr_attr[] = array('title' => _('In-progress and past system replication'));
1009 if (!forge_get_config('project_registration_restricted')
1010 || forge_check_global_perm('approve_projects', '')) {
1011 $arr_t[] = _('Register Project');
1012 $arr_l[] = '/register/';
1013 $arr_attr[] = array('title' => _('Register a new project in forge, following the workflow.'));
1016 echo ($HTML->printSubMenu($arr_t, $arr_l, $arr_attr));
1017 if (plugin_hook_listeners("usermenu") > 0) {
1018 echo $HTML->subMenuSeparator();
1020 plugin_hook("usermenu");
1021 echo $HTML->endSubMenu();
1025 * site_user_footer() - currently a simple shim that should be on every user page,
1026 * rather than a direct call to site_footer() or theme_footer()
1028 * @param array $params array() empty
1030 function site_user_footer($params = array()) {
1031 site_footer($params);
1035 * html_clean_hash_string() - Remove noise characters from hex hash string
1037 * Thruout SourceForge, URLs with hexadecimal hash string parameters
1038 * are being sent via email to request confirmation of user actions.
1039 * It was found that some mail clients distort this hash, so we take
1040 * special steps to encode it in the way which help to preserve its
1041 * recognition. This routine
1043 * @param string $hashstr required hash parameter as received from browser
1044 * @return string pure hex string
1046 function html_clean_hash_string($hashstr) {
1048 if (substr($hashstr, 0, 1) == "_") {
1049 $hashstr = substr($hashstr, 1);
1052 if (substr($hashstr, strlen($hashstr) - 1, 1) == ">") {
1053 $hashstr = substr($hashstr, 0, strlen($hashstr) - 1);
1059 function relative_date($date) {
1060 $delta = max(time() - $date, 0);
1062 return sprintf(ngettext('%d second ago', '%d seconds ago', $delta), $delta);
1064 $delta = round($delta / 60);
1066 return sprintf(ngettext('%d minute ago', '%d minutes ago', $delta), $delta);
1068 $delta = round($delta / 60);
1070 return sprintf(ngettext('%d hour ago', '%d hours ago', $delta), $delta);
1072 $delta = round($delta / 24);
1074 return sprintf(ngettext('%d day ago', '%d days ago', $delta), $delta);
1076 $delta = round($delta / 7);
1078 return sprintf(ngettext('%d week ago', '%d weeks ago', $delta), $delta);
1080 return date(_('Y-m-d H:i'), $date);
1083 /* TODO: think about beautifying output */
1086 * html_eo() - Return proper element XHTML start tag
1088 * @param string $name
1090 * @param array $attrs
1091 * (optional) associative array of element attributes
1092 * values: arrays are space-imploded;
1093 * false values and empty arrays ignored
1095 * XHTML string suitable for echo'ing
1097 function html_eo($name, $attrs = array()) {
1098 global $use_tooltips, $html_autoclose_pos;
1099 if (!$use_tooltips && isset($attrs['title'])) {
1100 $attrs['title'] = '';
1103 for ($i = 0; $i < $html_autoclose_pos; $i++) {
1107 foreach ($attrs as $key => $value) {
1108 if (is_array($value)) {
1109 $value = count($value) ? implode(" ", $value) : false;
1111 if ($value === false) {
1114 $rv .= ' '.$key.'="'.util_html_secure($value).'"';
1121 * html_e() - Return proper element XHTML start/end sequence
1123 * @param string $name
1125 * @param array $attrs
1126 * (optional) associative array of element attributes
1127 * values: arrays are space-imploded;
1128 * false values and empty arrays ignored
1129 * @param string $content
1130 * (optional) XHTML to be placed inside
1131 * @param bool $shortform
1132 * (optional) allow short open-close form
1135 * XHTML string suitable for echo'ing
1137 function html_e($name, $attrs = array(), $content = "", $shortform = true) {
1138 global $use_tooltips, $html_autoclose_pos;
1139 if (!$use_tooltips && isset($attrs['title'])) {
1140 $attrs['title'] = '';
1144 for ($i = 0; $i < $html_autoclose_pos +1; $i++) {
1149 foreach ($attrs as $key => $value) {
1150 if (is_array($value)) {
1151 $value = count($value) ? implode(" ", $value) : false;
1153 if ($value === false) {
1156 $rv .= ' '.$key.'="'.util_html_secure($value).'"';
1158 if ($content === "" && $shortform) {
1162 if (preg_match('/([\<])([^\>]{1,})*([\>])/i', $content)) {
1166 if (preg_match('/([\<])([^\>]{1,})*([\>])/i', $content)) {
1169 $rv .= '</'.$name.'>'."\n";
1174 $html_autoclose_stack = array();
1175 $html_autoclose_pos = 0;
1178 * html_ap() - Return XHTML element autoclose stack position
1182 function html_ap() {
1183 global $html_autoclose_pos;
1185 return $html_autoclose_pos;
1189 * html_ao() - Return proper element XHTML start tag, with autoclose
1191 * @param string $name
1193 * @param array $attrs
1194 * (optional) associative array of element attributes
1195 * values: arrays are space-imploded;
1196 * false values and empty arrays ignored
1198 * XHTML string suitable for echo'ing
1200 function html_ao($name, $attrs = array()) {
1201 global $html_autoclose_pos, $html_autoclose_stack;
1203 $html_autoclose_stack[$html_autoclose_pos++] = array(
1207 return html_eo($name, $attrs);
1211 * html_aonce() - Return once proper element XHTML start tag, with autoclose
1214 initialise this to false; will be modified
1215 * @param string $name
1217 * @param array $attrs
1218 * (optional) associative array of element attributes
1219 * values: arrays are space-imploded;
1220 * false values and empty arrays ignored
1222 * XHTML string suitable for echo'ing
1224 function html_aonce(&$sptr, $name, $attrs = array()) {
1225 if ($sptr !== false) {
1230 return html_ao($name, $attrs);
1234 * html_ac() - Return proper element XHTML end tags, autoclosing
1236 * @param $spos integer
1237 * stack position to return to (nothing is done if === false)
1239 * @return string XHTML string suitable for echo'ing
1241 function html_ac($spos) {
1242 global $html_autoclose_pos, $html_autoclose_stack;
1244 if ($spos === false) {
1245 /* support for html_aonce() */
1249 if ($html_autoclose_pos < $spos) {
1250 $e = "html_autoclose stack underflow; closing down to ".
1251 $spos." but we're down to ".$html_autoclose_pos.
1253 throw new Exception($e);
1257 while ($html_autoclose_pos > $spos) {
1258 for ($i = 0; $i < $html_autoclose_pos; $i++) {
1261 --$html_autoclose_pos;
1262 $rv .= '</'.$html_autoclose_stack[$html_autoclose_pos]['name'].'>'."\n";
1263 unset($html_autoclose_stack[$html_autoclose_pos]);
1269 * html_a_copy() - Return a copy of part of the autoclose stack
1272 * stack position caller will return to
1275 * argument suitable for html_a_apply()
1277 function html_a_copy($spos) {
1278 global $html_autoclose_pos, $html_autoclose_stack;
1280 if ($spos === false) {
1284 if ($spos > $html_autoclose_pos) {
1285 $e = "html_autoclose stack underflow; closing down to ".
1286 $spos." but we're down to ".$html_autoclose_pos.
1288 throw new Exception($e);
1292 while ($spos < $html_autoclose_pos) {
1293 $rv[] = $html_autoclose_stack[$spos++];
1299 * html_a_apply() - Reopen tags based on an autoclose stack copy
1301 * @param array $scopy
1302 * return value from html_a_copy()
1304 * XHTML string suitable for echo'ing
1306 function html_a_apply($scopy) {
1307 /* array_reduce() would be useful here... IF IT WORKED, FFS! */
1309 foreach ($scopy as $value) {
1310 $rv .= html_ao($value['name'], $value['attr']);
1316 * html_trove_limit_navigation_box() - displays the navigation links for paging browsing
1318 * @param string $php_self URL of the very same script
1319 * @param int $querytotalcount total number of results
1320 * @param int $trove_browselimit the maximum number displayed on a single page
1321 * @param int $page current page number (starting at 1)
1324 function html_trove_limit_navigation_box($php_self, $querytotalcount, $trove_browselimit, $page) {
1326 $html_limit = sprintf(_(' Displaying %1$s per page. Projects sorted by alphabetical order.'), $trove_browselimit).'<br/>';
1328 // display all the numbers
1329 for ($i=1;$i<=ceil($querytotalcount/$trove_browselimit);$i++) {
1332 $html_limit .= '<a href="'.$php_self;
1333 $html_limit .= '?page='.$i;
1334 $html_limit .= '">';
1335 } else $html_limit .= '<strong>';
1336 $html_limit .= '<'.$i.'>';
1338 $html_limit .= '</a>';
1339 } else $html_limit .= '</strong>';
1347 // c-file-style: "bsd"