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-2012, Franck Villaume - TrivialDev
11 * This file is part of FusionForge. FusionForge is free software;
12 * you can redistribute it and/or modify it under the terms of the
13 * GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the Licence, or (at your option)
17 * FusionForge is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 * html_feedback_top() - Show the feedback output at the top of the page.
30 * @param string The feedback.
32 function html_feedback_top($feedback) {
34 echo $HTML->feedback($feedback);
38 * html_warning_top() - Show the warning output at the top of the page.
40 * @param string The warning message.
42 function html_warning_top($msg) {
44 echo $HTML->warning_msg($msg);
48 * html_error_top() - Show the error output at the top of the page.
50 * @param string The error message.
52 function html_error_top($msg) {
54 echo $HTML->error_msg($msg);
58 * make_user_link() - Make a username reference into a link to that users User page on SF.
60 * @param string The username of the user to link.
62 function make_user_link($username,$displayname='') {
63 if (empty($displayname))
64 $displayname = $username;
66 if (!strcasecmp($username,'Nobody') || !strcasecmp($username,'None')) {
69 return '<a href="/users/'.$username.'">'.$displayname.'</a>';
74 * html_feedback_bottom() - Show the feedback output at the bottom of the page.
76 * @param string The feedback.
78 function html_feedback_bottom($feedback) {
80 echo $HTML->feedback($feedback);
84 * html_blankimage() - Show the blank spacer image.
86 * @param int The height of the image
87 * @param int The width of the image
89 function html_blankimage($height,$width) {
90 return '<img src="/images/blank.png" width="' . $width . '" height="' . $height . '" alt="" />';
94 * html_abs_image() - Show an image given an absolute URL.
97 * @param int width of the image
98 * @param int height of the image
99 * @param array Any <img> tag parameters (i.e. 'border', 'alt', etc...)
101 function html_abs_image($url, $width, $height, $args) {
102 $return = ('<img src="' . $url . '"');
104 while(list($k,$v) = each($args)) {
105 $return .= ' '.$k.'="'.$v.'"';
108 if (!isset($args['alt'])) {
109 $return .= ' alt=""';
112 // Add image dimensions (if given)
113 $return .= $width ?" width=\"" . $width . "\"": '';
114 $return .= $height? " height=\"" . $height . "\"": '';
121 * html_image() - Build an image tag of an image contained in $src
123 * @param string The source location of the image
124 * @param int The width of the image
125 * @param int The height of the image
126 * @param array Any IMG tag parameters associated with this image (i.e. 'border', 'alt', etc...)
127 * @param bool DEPRECATED
129 function html_image($src, $width='', $height='', $args=array(), $display=1) {
132 if (method_exists($HTML, 'html_image')) {
133 $HTML->html_image($src, $width, $height, $args);
135 $s = ((session_issecure()) ? forge_get_config('images_secure_url') : forge_get_config('images_url') );
136 return html_abs_image($s.$HTML->imgroot.$src, $width, $height, $args);
140 * html_get_language_popup() - Pop up box of supported languages.
142 * @param string The title of the popup box.
143 * @param string Which element of the box is to be selected.
144 * @return string The html select box.
146 function html_get_language_popup($title='language_id', $selected='xzxz') {
147 $res = db_query_params('SELECT * FROM supported_languages ORDER BY name ASC',
149 return html_build_select_box($res, $title, $selected, false);
153 * html_get_theme_popup() - Pop up box of supported themes.
155 * @param string The title of the popup box.
156 * @param string Which element of the box is to be selected.
157 * @return string The html select box.
159 function html_get_theme_popup($title='theme_id', $selected='xzxz') {
160 $res=db_query_params('SELECT theme_id, fullname FROM themes WHERE enabled=true',
162 $nbTheme = db_numrows($res);
164 $thetheme = db_result($res, 0, 'fullname');
165 return util_html_secure($thetheme) . html_e('input', array(
168 'value' => db_result($res, 0, 'theme_id'),
170 } else if ($nbTheme < 1) {
173 return html_build_select_box($res,$title,$selected,false);
178 * html_get_ccode_popup() - Pop up box of supported country_codes.
180 * @param string The title of the popup box.
181 * @param string Which element of the box is to be selected.
182 * @return string The html select box.
184 function html_get_ccode_popup($title='ccode', $selected='xzxz') {
185 $res=db_query_params('SELECT ccode,country_name FROM country_code ORDER BY country_name',
187 return html_build_select_box($res, $title, $selected, false);
191 * html_get_timezone_popup() - Pop up box of supported Timezones.
192 * Assumes you have included Timezones array file.
194 * @param string The title of the popup box.
195 * @param string Which element of the box is to be selected.
196 * @return string The html select box.
198 function html_get_timezone_popup($title='timezone', $selected='xzxz') {
200 if ($selected == 'xzxzxzx') {
201 $r = file('/etc/timezone');
202 $selected = str_replace("\n", '', $r[0]);
204 return html_build_select_box_from_arrays($TZs, $TZs, $title, $selected, false);
209 * html_build_select_box_from_assoc() - Takes one assoc array and returns a pop-up box.
211 * @param array An array of items to use.
212 * @param string The name you want assigned to this form element.
213 * @param string The value of the item that should be checked.
214 * @param boolean Whether we should swap the keys / names.
215 * @param bool Whether or not to show the '100 row'.
216 * @param string What to call the '100 row' defaults to none.
218 function html_build_select_box_from_assoc ($arr,$select_name,$checked_val='xzxz',$swap=false,$show_100=false,$text_100='None') {
220 $keys=array_values($arr);
221 $vals=array_keys($arr);
223 $vals=array_values($arr);
224 $keys=array_keys($arr);
226 return html_build_select_box_from_arrays ($keys,$vals,$select_name,$checked_val,$show_100,$text_100);
230 * html_build_select_box_from_array() - Takes one array, with the first array being the "id"
231 * or value and the array being the text you want displayed.
233 * @param array An array of items to use.
234 * @param string The name you want assigned to this form element.
235 * @param string The value of the item that should be checked.
237 function html_build_select_box_from_array ($vals,$select_name,$checked_val='xzxz',$samevals = 0) {
239 <select name="'.$select_name.'">';
241 $rows = count($vals);
243 for ($i = 0; $i < $rows; $i++) {
245 $return .= "\n\t\t<option value=\"" . $vals[$i] . "\"";
246 if ($vals[$i] == $checked_val) {
247 $return .= ' selected="selected"';
250 $return .= "\n\t\t<option value=\"" . $i .'"';
251 if ($i == $checked_val) {
252 $return .= ' selected="selected"';
255 $return .= '>'.htmlspecialchars($vals[$i]).'</option>';
264 * html_build_radio_buttons_from_arrays() - Takes two arrays, with the first array being the "id" or value and the other
265 * array being the text you want displayed.
267 * The infamous '100 row' has to do with the SQL Table joins done throughout all this code.
268 * There must be a related row in users, categories, et , and by default that
269 * row is 100, so almost every pop-up box has 100 as the default
270 * Most tables in the database should therefore have a row with an id of 100 in it so that joins are successful
272 * @param array The ID or value
273 * @param array Text to be displayed
274 * @param string Name to assign to this form element
275 * @param string The item that should be checked
276 * @param bool Whether or not to show the '100 row'
277 * @param string What to call the '100 row' defaults to none
278 * @param bool Whether or not to show the 'Any row'
279 * @param string What to call the 'Any row' defaults to any
281 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') {
282 if ($text_100=='none'){
288 if (count($texts) != $rows) {
289 $return .= 'ERROR - uneven row counts';
292 //we don't always want the default Any row shown
295 <input type="radio" name="'.$select_name.'" value=""'.(($checked_val=='') ? ' checked="checked"' : '').' /> '. $text_any .'<br />';
297 //we don't always want the default 100 row shown
300 <input type="radio" name="'.$select_name.'" value="100"'.(($checked_val==100) ? ' checked="checked"' : '').' /> '. $text_100 .'<br />';
303 $checked_found=false;
305 for ($i=0; $i<$rows; $i++) {
306 // uggh - sorry - don't show the 100 row
307 // if it was shown above, otherwise do show it
308 if (($vals[$i] != '100') || ($vals[$i] == '100' && !$show_100)) {
310 <input type="radio" name="'.$select_name.'" value="'.$vals[$i].'"';
311 if ((string)$vals[$i] == (string)$checked_val) {
313 $return .= ' checked="checked"';
315 $return .= ' /> '.htmlspecialchars($texts[$i]).'<br />';
319 // If the passed in "checked value" was never "SELECTED"
320 // we want to preserve that value UNLESS that value was 'xzxz', the default value
322 if (!$checked_found && $checked_val != 'xzxz' && $checked_val && $checked_val != 100) {
324 <input type="radio" value="'.$checked_val.'" checked="checked" /> '._('No Change').'<br />';
331 * html_get_tooltip_description() - Get the tooltip description of the element
333 * @param string element name
336 function html_get_tooltip_description($element_name) {
337 global $use_tooltips;
339 switch( $element_name ) {
341 return( _('This drop-down box represents the person to which a tracker item is assigned.'));
343 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\'.'));
345 return( _('Tracker category'));
347 return( _('Tracker group'));
349 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.'));
350 case 'new_artifact_type_id':
351 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.'));
353 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.'));
355 return( _('Resolution'));
357 return( _('The summary text-box represents a short tracker item summary. Useful when browsing through several tracker items.'));
358 case 'canned_response':
359 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'));
361 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.'));
363 return( htmlentities(_('Enter the complete description.').'<br/><br/>'.
364 _("<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>"),
365 ENT_COMPAT, 'UTF-8'));
367 return( _('When you wish to attach a file to a tracker item you must check this checkbox before submitting changes.'));
369 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!'),
370 ENT_COMPAT, 'UTF-8'));
379 function html_use_jquery() {
380 use_javascript('/scripts/jquery/jquery-1.4.2.min.js');
383 function html_use_tooltips() {
385 use_javascript('/scripts/jquery-tipsy/src/javascripts/jquery.tipsy.js');
386 use_javascript('/js/jquery-common.js');
387 use_stylesheet('/scripts/jquery-tipsy/src/stylesheets/tipsy.css');
390 function html_use_storage() {
392 use_javascript('/scripts/jquery-storage/jquery.Storage.js');
395 function html_use_simplemenu() {
397 use_javascript('/scripts/jquery-simpletreemenu/js/jquery-simpleTreeMenu-1.1.0.js');
398 use_stylesheet('/scripts/jquery-simpletreemenu/css/jquery-simpleTreeMenu-1.1.0.css');
401 function html_use_coolfieldset() {
403 use_javascript('/scripts/coolfieldset/js/jquery.coolfieldset.js');
404 use_javascript('/js/jquery-common.js');
405 use_stylesheet('/scripts/coolfieldset/css/jquery.coolfieldset.css');
408 function html_use_jqueryui() {
410 use_javascript('/scripts/jquery-ui/js/jquery-ui-1.8.17.custom.min.js');
411 use_stylesheet('/scripts/jquery-ui/css/overcast/jquery-ui-1.8.17.custom.css');
415 * html_build_select_box_from_arrays() - Takes two arrays, with the first array being the "id" or value and the other
416 * array being the text you want displayed.
418 * The infamous '100 row' has to do with the SQL Table joins done throughout all this code.
419 * There must be a related row in users, categories, et , and by default that
420 * row is 100, so almost every pop-up box has 100 as the default
421 * Most tables in the database should therefore have a row with an id of 100 in it so that joins are successful
423 * @param array The ID or value
424 * @param array Text to be displayed
425 * @param string Name to assign to this form element
426 * @param string The item that should be checked
427 * @param bool Whether or not to show the '100 row'
428 * @param string What to call the '100 row' defaults to none
429 * @param bool Whether or not to show the 'Any row'
430 * @param string What to call the 'Any row' defaults to any
431 * @param array Array of all allowed values from the full list.
433 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) {
434 $have_a_subelement = false;
436 if ($text_100=='none'){
442 if (count($texts) != $rows) {
443 $return .= 'ERROR - uneven row counts';
446 $title = html_get_tooltip_description($select_name);
449 $id = ' id="tracker-'.$select_name.'"';
450 if (preg_match('/\[\]/', $id)) {
456 <select'.$id.' name="'.$select_name.'" title="'.util_html_secure($title).'">';
458 //we don't always want the default Any row shown
461 <option value=""'.(($checked_val=='') ? ' selected="selected"' : '').'>'. util_html_secure($text_any) .'</option>';
462 $have_a_subelement = true;
464 //we don't always want the default 100 row shown
467 <option value="100"'.(($checked_val==100) ? ' selected="selected"' : '').'>'. util_html_secure($text_100) .'</option>';
468 $have_a_subelement = true;
471 $checked_found=false;
473 for ($i=0; $i<$rows; $i++) {
474 // uggh - sorry - don't show the 100 row
475 // if it was shown above, otherwise do show it
476 if (($vals[$i] != '100') || ($vals[$i] == '100' && !$show_100)) {
478 <option value="'.util_html_secure($vals[$i]).'"';
479 if ((string)$vals[$i] == (string)$checked_val) {
481 $return .= ' selected="selected"';
483 if (is_array($allowed) && !in_array($vals[$i], $allowed)) {
484 $return .= ' disabled="disabled" class="option_disabled"';
486 $return .= '>'.util_html_secure($texts[$i]).'</option>';
487 $have_a_subelement = true;
491 // If the passed in "checked value" was never "SELECTED"
492 // we want to preserve that value UNLESS that value was 'xzxz', the default value
494 if (!$checked_found && $checked_val != 'xzxz' && $checked_val && $checked_val != 100) {
496 <option value="'.util_html_secure($checked_val).'" selected="selected">'._('No Change').'</option>';
497 $have_a_subelement = true;
500 if (!$have_a_subelement) {
501 /* <select></select> without <option/> in between is invalid */
502 return '<!-- select without options -->';
511 * html_build_select_box() - Takes a result set, with the first column being the "id" or value and
512 * the second column being the text you want displayed.
514 * @param int The result set
515 * @param string Text to be displayed
516 * @param string The item that should be checked
517 * @param bool Whether or not to show the '100 row'
518 * @param string What to call the '100 row'. Defaults to none.
520 function html_build_select_box ($result, $name, $checked_val="xzxz", $show_100=true, $text_100='none', $show_any=false, $text_any='Select One') {
521 if ($text_100=='none'){
524 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);
528 * html_build_select_box_sorted() - Takes a result set, with the first column being the "id" or value and
529 * the second column being the text you want displayed.
531 * @param int The result set
532 * @param string Text to be displayed
533 * @param string The item that should be checked
534 * @param bool Whether or not to show the '100 row'
535 * @param string What to call the '100 row'. Defaults to none.
537 function html_build_select_box_sorted ($result, $name, $checked_val="xzxz",$show_100=true,$text_100='none') {
538 if ($text_100=='none'){
541 $vals = util_result_column_to_array($result, 0);
542 $texts = util_result_column_to_array($result, 1);
543 array_multisort($texts, SORT_ASC, SORT_STRING,
545 return html_build_select_box_from_arrays ($vals, $texts, $name, $checked_val, $show_100, $text_100);
549 * html_build_multiple_select_box() - Takes a result set, with the first column being the "id" or value
550 * and the second column being the text you want displayed.
552 * @param int The result set
553 * @param string Text to be displayed
554 * @param string The item that should be checked
555 * @param int The size of this box
556 * @param bool Whether or not to show the '100 row'
558 function html_build_multiple_select_box ($result,$name,$checked_array,$size='8',$show_100=true) {
559 $checked_count=count($checked_array);
561 <select name="'.$name.'" multiple="multiple" size="'.$size.'">';
564 Put in the default NONE box
567 <option value="100"';
568 for ($j=0; $j<$checked_count; $j++) {
569 if ($checked_array[$j] == '100') {
570 $return .= ' selected="selected"';
573 $return .= '>'._('None').'</option>';
576 $rows=db_numrows($result);
577 for ($i=0; $i<$rows; $i++) {
578 if ((db_result($result,$i,0) != '100') || (db_result($result,$i,0) == '100' && !$show_100)) {
580 <option value="'.db_result($result,$i,0).'"';
582 Determine if it's checked
584 $val=db_result($result,$i,0);
585 for ($j=0; $j<$checked_count; $j++) {
586 if ($val == $checked_array[$j]) {
587 $return .= ' selected="selected"';
590 $return .= '>'. substr(db_result($result,$i,1),0,35). '</option>';
599 * html_build_multiple_select_box_from_arrays() - Takes two arrays and builds a multi-select box
601 * @param array id of the field
602 * @param array Text to be displayed
603 * @param string id of the items selected
604 * @param string The item that should be checked
605 * @param int The size of this box
606 * @param bool Whether or not to show the '100 row'
608 function html_build_multiple_select_box_from_arrays($ids,$texts,$name,$checked_array,$size='8',$show_100=true,$text_100='none') {
609 $checked_count=count($checked_array);
611 <select name="'.$name.'" multiple="multiple" size="'.$size.'">';
613 if ($text_100=='none') {
617 Put in the default NONE box
620 <option value="100"';
621 for ($j=0; $j<$checked_count; $j++) {
622 if ($checked_array[$j] == '100') {
623 $return .= ' selected="selected"';
626 $return .= '>'.$text_100.'</option>';
630 for ($i=0; $i<$rows; $i++) {
631 if (( $ids[$i] != '100') || ($ids[$i] == '100' && !$show_100)) {
633 <option value="'.$ids[$i].'"';
635 Determine if it's checked
638 for ($j=0; $j<$checked_count; $j++) {
639 if ($val == $checked_array[$j]) {
640 $return .= ' selected="selected"';
643 $return .= '>'.$texts[$i].' </option>';
652 * html_build_checkbox() - Render checkbox control
654 * @param name - name of control
655 * @param value - value of control
656 * @param checked - true if control should be checked
657 * @return html code for checkbox control
659 function html_build_checkbox($name, $value, $checked) {
660 return '<input type="checkbox" name="'.$name.'"'
661 .' value="'.$value.'"'
662 .($checked ? 'checked="checked"' : '').'>';
667 * build_priority_select_box() - Wrapper for html_build_priority_select_box()
669 * @see html_build_priority_select_box()
671 function build_priority_select_box ($name='priority', $checked_val='3', $nochange=false) {
672 echo html_build_priority_select_box ($name, $checked_val, $nochange);
676 * html_build_priority_select_box() - Return a select box of standard priorities.
677 * The name of this select box is optional and so is the default checked value.
679 * @param string Name of the select box
680 * @param string The value to be checked
681 * @param bool Whether to make 'No Change' selected.
683 function html_build_priority_select_box ($name='priority', $checked_val='3', $nochange=false) {
685 <select id="tracker-<?php echo $name ?>" name="<?php echo $name; ?>" title="<?php echo util_html_secure(html_get_tooltip_description($name)) ?>">
686 <?php if($nochange) { ?>
687 <option value="100"<?php if ($nochange) {echo " selected=\"selected\"";} ?>><?php echo _('No Change') ?></option>
689 <option value="1"<?php if ($checked_val=="1") {echo " selected=\"selected\"";} ?>>1 - <?php echo _('Lowest') ?></option>
690 <option value="2"<?php if ($checked_val=="2") {echo " selected=\"selected\"";} ?>>2</option>
691 <option value="3"<?php if ($checked_val=="3") {echo " selected=\"selected\"";} ?>>3</option>
692 <option value="4"<?php if ($checked_val=="4") {echo " selected=\"selected\"";} ?>>4</option>
693 <option value="5"<?php if ($checked_val=="5") {echo " selected=\"selected\"";} ?>>5 - <?php echo _('Highest') ?></option>
700 * html_buildcheckboxarray() - Build an HTML checkbox array.
702 * @param array Options array
703 * @param name Checkbox name
704 * @param array Array of boxes to be pre-checked
706 function html_buildcheckboxarray($options,$name,$checked_array) {
707 $option_count=count($options);
708 $checked_count=count($checked_array);
710 for ($i=1; $i<=$option_count; $i++) {
712 <br /><input type="checkbox" name="'.$name.'" value="'.$i.'"';
713 for ($j=0; $j<$checked_count; $j++) {
714 if ($i == $checked_array[$j]) {
715 echo ' checked="checked"';
718 echo ' /> '.$options[$i];
723 * site_header() - everything required to handle security and
724 * add navigation for user pages like /my/ and /account/
726 * @param array Must contain $user_id
728 function site_header($params) {
731 Check to see if active user
732 Check to see if logged in
734 $HTML->header($params);
738 * site_footer() - Show the HTML site footer.
740 * @param array Footer params array
742 function site_footer($params) {
744 $HTML->footer($params);
748 * site_project_header() - everything required to handle
749 * security and state checks for a project web page
751 * @param params array() must contain $toptab and $group
753 function site_project_header($params) {
757 Check to see if active
758 Check to see if project rather than foundry
759 Check to see if private (if private check if user_ismember)
762 $group_id=$params['group'];
764 //get the project object
765 $project = group_get_object($group_id);
767 if (!$project || !is_object($project)) {
769 } else if ($project->isError()) {
770 if ($project->isPermissionDeniedError()) {
771 if (!session_get_user()) {
772 $next = '/account/login.php?error_msg='.urlencode($project->getErrorMessage());
773 if (getStringFromServer('REQUEST_METHOD') != 'POST') {
774 $next .= '&return_to='.urlencode(getStringFromServer('REQUEST_URI'));
776 session_redirect($next);
779 exit_error(sprintf(_('Project access problem: %s'),$project->getErrorMessage()),'home');
781 exit_error(sprintf(_('Project Problem: %s'),$project->getErrorMessage()),'home');
784 // Check permissions in case of restricted access
785 session_require_perm ('project_read', $group_id);
787 //for dead projects must be member of admin project
788 if (!$project->isActive()) {
789 session_require_global_perm ('forge_admin');
792 if (isset($params['title'])){
793 $h1=$params['title'];
794 $params['title']=$project->getPublicName().': '.$params['title'];
796 $h1=$project->getPublicName();
797 $params['title']=$project->getPublicName();
799 if (!isset($params['h1'])){
803 if ($project->getDescription()) {
804 $params['meta-description'] = $project->getDescription();
807 if (forge_get_config('use_project_tags')) {
808 $res = db_query_params('SELECT name FROM project_tags WHERE group_id = $1', array($group_id));
809 if ($res && db_numrows($res) > 0) {
810 while ($row = db_fetch_array($res)) {
811 $array[] = $row['name'];
813 $params['meta-keywords'] = htmlspecialchars(join(', ', $array));
817 site_header($params);
821 * site_project_footer() - currently a simple shim
822 * that should be on every project page, rather than
823 * a direct call to site_footer() or theme_footer()
825 * @param params array() empty
827 function site_project_footer($params) {
828 site_footer($params);
832 * site_user_header() - everything required to handle security and
833 * add navigation for user pages like /my/ and /account/
835 * @param params array() must contain $user_id
837 function site_user_header($params) {
841 Check to see if active user
842 Check to see if logged in
844 site_header($params);
845 echo ($HTML->beginSubMenu());
850 $arr_t[] = _('My Personal Page');
852 $arr_attr[] = array('title' => _('View your personal page, a selection of widgets to follow the informations from projects.'), 'class' => 'tabtitle-nw');
855 $arr_t[] = _('Trackers dashboard');
856 $arr_l[] = '/my/dashboard.php';
857 $arr_attr[] = array('title' => _('View your tasks and artifacts.'), 'class' => 'tabtitle');
860 if (forge_get_config('use_diary')) {
861 $arr_t[] = _('Diary & Notes');
862 $arr_l[] = '/my/diary.php';
863 $arr_attr[] = array('title' => _('Manage your diary. Add, modify or delete your notes.'), 'class' => 'tabtitle');
866 $arr_t[] = _('Account Maintenance');
867 $arr_l[] = '/account/';
868 $arr_attr[] = array('title' => _('Manage your account. Change your password, select your preferences.'), 'class' => 'tabtitle');
870 if (!forge_get_config('project_registration_restricted')
871 || forge_check_global_perm('approve_projects', '')) {
872 $arr_t[] = _('Register Project');
873 $arr_l[] = '/register/';
874 $arr_attr[] = array('title' => _('Register a new project in forge, following the workflow.'), 'class' => 'tabtitle');
877 echo ($HTML->printSubMenu($arr_t, $arr_l, $arr_attr));
878 if ( plugin_hook_listeners("usermenu") > 0 ) {
879 echo $HTML->subMenuSeparator();
881 plugin_hook("usermenu", false);
882 echo ($HTML->endSubMenu());
886 * site_user_footer() - currently a simple shim that should be on every user page,
887 * rather than a direct call to site_footer() or theme_footer()
889 * @param params array() empty
891 function site_user_footer($params) {
892 site_footer($params);
896 * html_clean_hash_string() - Remove noise characters from hex hash string
898 * Thruout SourceForge, URLs with hexadecimal hash string parameters
899 * are being sent via email to request confirmation of user actions.
900 * It was found that some mail clients distort this hash, so we take
901 * special steps to encode it in the way which help to preserve its
902 * recognition. This routine
904 * @param hashstr required hash parameter as received from browser
905 * @return pure hex string
907 function html_clean_hash_string($hashstr) {
909 if (substr($hashstr,0,1)=="_") {
910 $hashstr = substr($hashstr, 1);
913 if (substr($hashstr, strlen($hashstr)-1, 1)==">") {
914 $hashstr = substr($hashstr, 0, strlen($hashstr)-1);
920 function relative_date ($date) {
921 $delta = time() - $date;
923 return sprintf(ngettext('%d second ago', '%d seconds ago', $delta), $delta);
925 $delta = round($delta/60);
927 return sprintf(ngettext('%d minute ago', '%d minutes ago', $delta), $delta);
929 $delta = round($delta/60);
931 return sprintf(ngettext('%d hour ago', '%d hours ago', $delta), $delta);
933 $delta = round($delta/24);
935 return sprintf(ngettext('%d day ago', '%d days ago', $delta), $delta);
937 $delta = round($delta/7);
939 return sprintf(ngettext('%d week ago', '%d weeks ago', $delta), $delta);
941 return date(_('Y-m-d H:i'), $date);
944 /* TODO: think about beautifying output */
947 * html_eo() - Return proper element XHTML start tag
949 * @param string $name
951 * @param array $attrs
952 * (optional) associative array of element attributes
953 * values: arrays are space-imploded;
954 * false values and empty arrays ignored
956 * XHTML string suitable for echo'ing
958 function html_eo($name, $attrs=array()) {
960 foreach ($attrs as $key => $value) {
961 if (is_array($value)) {
962 $value = count($value) ? implode(" ", $value) : false;
964 if ($value === false) {
967 $rv .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
974 * html_e() - Return proper element XHTML start/end sequence
976 * @param string $name
978 * @param array $attrs
979 * (optional) associative array of element attributes
980 * values: arrays are space-imploded;
981 * false values and empty arrays ignored
982 * @param string $content
983 * (optional) XHTML to be placed inside
984 * @param bool $shortform
985 * (optional) allow short open-close form
988 * XHTML string suitable for echo'ing
990 function html_e($name, $attrs=array(), $content="", $shortform=true) {
992 foreach ($attrs as $key => $value) {
993 if (is_array($value)) {
994 $value = count($value) ? implode(" ", $value) : false;
996 if ($value === false) {
999 $rv .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
1001 if ($content === "" && $shortform) {
1004 $rv .= '>' . $content . '</' . $name . '>';
1009 $html_autoclose_stack = array();
1010 $html_autoclose_pos = 0;
1013 * html_ap() - Return XHTML element autoclose stack position
1017 function html_ap() {
1018 global $html_autoclose_pos;
1020 return $html_autoclose_pos;
1024 * html_ao() - Return proper element XHTML start tag, with autoclose
1026 * @param string $name
1028 * @param array $attrs
1029 * (optional) associative array of element attributes
1030 * values: arrays are space-imploded;
1031 * false values and empty arrays ignored
1033 * XHTML string suitable for echo'ing
1035 function html_ao($name, $attrs=array()) {
1036 global $html_autoclose_pos, $html_autoclose_stack;
1038 $html_autoclose_stack[$html_autoclose_pos++] = array(
1042 return html_eo($name, $attrs);
1046 * html_aonce() - Return once proper element XHTML start tag, with autoclose
1049 initialise this to false; will be modified
1050 * @param string $name
1052 * @param array $attrs
1053 * (optional) associative array of element attributes
1054 * values: arrays are space-imploded;
1055 * false values and empty arrays ignored
1057 * XHTML string suitable for echo'ing
1059 function html_aonce(&$sptr, $name, $attrs=array()) {
1060 if ($sptr !== false) {
1065 return html_ao($name, $attrs);
1069 * html_ac() - Return proper element XHTML end tags, autoclosing
1071 * @param integer $spos
1072 * stack position to return to
1073 * (nothing is done if === false)
1075 * XHTML string suitable for echo'ing
1077 function html_ac($spos) {
1078 global $html_autoclose_pos, $html_autoclose_stack;
1080 if ($spos === false) {
1081 /* support for html_aonce() */
1085 if ($html_autoclose_pos < $spos) {
1086 $e = "html_autoclose stack underflow; closing down to " .
1087 $spos . " but we're down to " . $html_autoclose_pos .
1089 throw new Exception($e);
1093 while ($html_autoclose_pos > $spos) {
1094 --$html_autoclose_pos;
1095 $rv .= '</' . $html_autoclose_stack[$html_autoclose_pos]['name'] . '>';
1096 unset($html_autoclose_stack[$html_autoclose_pos]);
1102 * html_a_copy() - Return a copy of part of the autoclose stack
1104 * @param integer $spos
1105 * stack position caller will return to
1107 * argument suitable for html_a_apply()
1109 function html_a_copy($spos) {
1110 global $html_autoclose_pos, $html_autoclose_stack;
1112 if ($spos === false) {
1116 if ($spos > $html_autoclose_pos) {
1117 $e = "html_autoclose stack underflow; closing down to " .
1118 $spos . " but we're down to " . $html_autoclose_pos .
1120 throw new Exception($e);
1124 while ($spos < $html_autoclose_pos) {
1125 $rv[] = $html_autoclose_stack[$spos++];
1131 * html_a_apply() - Reopen tags based on an autoclose stack copy
1133 * @param opaque $scopy
1134 * return value from html_a_copy()
1136 * XHTML string suitable for echo'ing
1138 function html_a_apply($scopy) {
1139 /* array_reduce() would be useful here... IF IT WORKED, FFS! */
1141 foreach ($scopy as $value) {
1142 $rv .= html_ao($value['name'], $value['attr']);
1149 // c-file-style: "bsd"