5 * Copyright 1999-2001 (c) VA Linux Systems
6 * Copyright 2010 - Alain Peyrat
7 * Copyright 2010-2011, Franck Villaume - Capgemini
8 * Copyright 2010-2012, Alain Peyrat - Alcatel-Lucent
9 * Copyright © 2011 Thorsten Glaser – tarent GmbH
10 * Copyright 2011 - Marc-Etienne Vargenau, Alcatel-Lucent
11 * Copyright 2012-2013, Franck Villaume - TrivialDev
12 * Copyright (C) 2012 Alain Peyrat - Alcatel-Lucent
13 * http://fusionforge.org
15 * This file is part of FusionForge. FusionForge is free software;
16 * you can redistribute it and/or modify it under the terms of the
17 * GNU General Public License as published by the Free Software
18 * Foundation; either version 2 of the Licence, or (at your option)
21 * FusionForge is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
28 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33 * Extends the basic Error class to add HTML functions
34 * for displaying all site dependent HTML, while allowing
35 * extendibility/overriding by themes via the Theme class.
37 * Make sure browser.php is included _before_ you create an instance
42 require_once $gfcommon.'include/constants.php';
43 require_once $gfcommon.'include/FusionForge.class.php';
44 require_once $gfcommon.'include/Navigation.class.php';
46 class Layout extends Error {
49 * Which doctype to use. Can be configured in the
50 * constructor. If set to 'strict', headerHTMLDeclaration will
51 * create a doctype definition that uses the strict doctype,
52 * otherwise it will use the transitional doctype.
53 * @var string $doctype
55 var $doctype = 'transitional';
58 * The default main page content
59 * @var string $rootindex
61 var $rootindex = 'index_std.php';
64 * The base directory of the theme in the servers file system
65 * @var string $themedir
70 * The base url of the theme
71 * @var string $themeurl
76 * The base directory of the image files in the servers file system
82 * The base url of the image files
83 * @var string $imgbaseurl
88 * The base directory of the js files in the servers file system
94 * The base url of the js files
95 * @var string $jsbaseurl
100 * The navigation object that provides the basic links. Should
106 * The color bars in pm reporting
108 var $COLOR_LTBACK1 = '#C0C0C0';
111 var $js_min = array();
112 var $javascripts = array();
114 var $css_min = array();
115 var $stylesheets = array();
118 * Layout() - Constructor
121 // parent constructor
124 $this->navigation = new Navigation();
126 // determine rootindex
127 if ( file_exists(forge_get_config('custom_path') . '/index_std.php') ) {
128 $this->rootindex = forge_get_config('custom_path') . '/index_std.php';
130 $this->rootindex = $GLOBALS['gfwww'].'index_std.php';
133 // determine theme{dir,url}
134 $this->themedir = forge_get_config('themes_root') . '/' . forge_get_config('default_theme') . '/';
135 if (!file_exists ($this->themedir)) {
136 html_error_top(_("Cannot find theme directory!"));
139 $this->themeurl = util_make_url('themes/' . forge_get_config('default_theme') . '/');
141 // determine {css,img,js}{url,dir}
142 if (file_exists ($this->themedir . 'images/')) {
143 $this->imgdir = $this->themedir . 'images/';
144 $this->imgbaseurl = $this->themeurl . 'images/';
146 $this->imgdir = $this->themedir;
147 $this->imgbaseurl = $this->themeurl;
150 if (file_exists ($this->themedir . 'js/')) {
151 $this->jsdir = $this->themedir . 'js/';
152 $this->jsbaseurl = $this->themeurl . 'js/';
154 $this->jsdir = $this->themedir;
155 $this->jsbaseurl = $this->themeurl;
158 $this->addStylesheet('/themes/css/fusionforge.css');
163 * Build the list of required Javascript files.
165 * If js file is found, then a timestamp is automatically added to ensure
166 * that file is cached only if not changed.
168 * @param string $js path to the JS file
170 function addJavascript($js) {
171 // If a minified version of the javascript is available, then use it.
172 if (isset($this->js_min[$js])) {
173 $js = $this->js_min[$js];
175 if ($js && !isset($this->js[$js])) {
176 $this->js[$js] = true;
177 $filename = $GLOBALS['fusionforge_basedir'].'/www'.$js;
178 if (file_exists($filename)) {
179 $js .= '?'.date ("U", filemtime($filename));
181 $filename = str_replace('/scripts/', $GLOBALS['fusionforge_basedir'].'/vendor/', $js);
182 if (file_exists($filename)) {
183 $js .= '?'.date ("U", filemtime($filename));
186 $this->javascripts[] = $js;
190 function addStylesheet($css, $media='') {
191 if (isset($this->css_min[$css])) {
192 $css = $this->css_min[$css];
194 if (!isset($this->css[$css])) {
195 $this->css[$css] = true;
196 $filename = $GLOBALS['fusionforge_basedir'].'/www'.$css;
197 if (file_exists($filename)) {
198 $css .= '?'.date ("U", filemtime($filename));
200 $filename = str_replace('/scripts/', $GLOBALS['fusionforge_basedir'].'/vendor/', $css);
201 if (file_exists($filename)) {
202 $css .= '?'.date ("U", filemtime($filename));
205 $this->stylesheets[] = array('css' => $css, 'media' => $media);
210 * getJavascripts - include javascript in html page. check to load only once the file
212 function getJavascripts() {
214 foreach ($this->javascripts as $js) {
216 $code .= '<script type="text/javascript" src="'.$js.'"></script>'."\n";
218 $this->javascripts = array();
223 * getStylesheets - include stylesheet in html page. check to load only once the file
225 function getStylesheets() {
227 foreach ($this->stylesheets as $c) {
230 $code .= '<link rel="stylesheet" type="text/css" href="'.$c['css'].'" media="'.$c['media'].'" />'."\n";
232 $code .= '<link rel="stylesheet" type="text/css" href="'.$c['css'].'"/>'."\n";
235 $this->stylesheets = array();
240 * header() - generates the complete header of page by calling
241 * headerStart() and bodyHeader().
243 function header($params) {
244 $this->headerStart($params);
246 $this->bodyHeader($params);
250 * headerStart() - generates the header code for all themes up to the
252 * Override any of the methods headerHTMLDeclaration(), headerTitle(),
253 * headerFavIcon(), headerRSS(), headerSearch(), headerCSS(), or
254 * headerJS() to adapt your theme.
256 * @param array $params Header parameters array
258 function headerStart($params) {
259 $this->headerHTMLDeclaration();
262 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
263 <?php if (isset($params['meta-description'])) { ?>
264 <meta name="description" content="<?php echo $params['meta-description'] ?>" />
266 <?php if (isset($params['meta-keywords'])) { ?>
267 <meta name="keywords" content="<?php echo $params['meta-keywords'] ?>" />
270 plugin_hook('htmlhead', array());
271 $this->headerTitle($params);
272 $this->headerFavIcon();
274 $this->headerSearch();
277 $this->headerForgepluckerMeta();
278 $this->headerLinkedDataAutodiscovery();
285 * headerHTMLDeclaration() - generates the HTML declaration, i.e. the
286 * XML declaration, the doctype definition, and the opening <html>.
289 function headerHTMLDeclaration() {
290 global $sysDTDs, $sysXMLNSs;
292 print '<' . '?xml version="1.0" encoding="utf-8"?>'."\n";
293 if (!util_ifsetor($this->doctype) || !util_ifsetor($sysDTDs[$this->doctype])) {
294 $this->doctype = 'transitional';
296 echo $sysDTDs[$this->doctype]['doctype'] . "\n";
297 echo '<html xml:lang="' . _('en') . '" lang="' . _('en') .
298 '" ' . $sysXMLNSs . ">\n";
302 * headerTitle() - creates the <title> header
304 * @param array $params Header parameters array
306 function headerTitle($params) {
307 echo $this->navigation->getTitle($params);
311 * headerFavIcon() - creates the favicon <link> headers.
314 function headerFavIcon() {
315 echo $this->navigation->getFavIcon();
319 * headerRSS() - creates the RSS <link> headers.
322 function headerRSS() {
323 echo $this->navigation->getRSS();
327 * headerSearch() - creates the search <link> header.
330 function headerSearch() {
331 echo '<link rel="search" title="'
332 . forge_get_config ('forge_name').'" href="'
333 . util_make_url ('/export/search_plugin.php')
334 . '" type="application/opensearchdescription+xml"/>'."\n";
338 * Create the CSS headers for all cssfiles in $cssfiles and
339 * calls the plugin cssfile hook.
341 function headerCSS() {
342 plugin_hook ('cssfile',$this);
343 echo $this->getStylesheets();
347 * headerJS() - creates the JS headers and calls the plugin javascript hook
348 * @todo generalize this
350 function headerJS() {
352 <script type="text/javascript" src="'. util_make_uri('/js/common.js') .'"></script>
353 <script type="text/javascript">/* <![CDATA[ */';
354 plugin_hook ("javascript",false);
357 plugin_hook ("javascript_file",false);
358 echo $this->getJavascripts();
360 // invoke the 'javascript' hook for custom javascript addition
361 $params = array('return' => false);
362 plugin_hook("javascript",$params);
363 $javascript = $params['return'];
365 echo '<script type="text/javascript">';
373 * headerLinkedDataAutodiscovery() - creates the link+alternate links to alternate
374 * representations for Linked Data autodiscovery
376 function headerLinkedDataAutodiscovery() {
378 // retrieve the script's prefix
379 $script_name = getStringFromServer('SCRIPT_NAME');
380 $end = strpos($script_name,'/',1);
382 $script_name = substr($script_name,0,$end);
385 // Only activated for /projects, /users or /softwaremap for the moment
386 if ($script_name == '/projects' || $script_name == '/users' || $script_name == '/softwaremap') {
388 $php_self = getStringFromServer('PHP_SELF');
390 // invoke the 'alt_representations' hook to add potential 'alternate' links (useful for Linked Data)
391 // cf. http://www.w3.org/TR/cooluris/#linking
392 $params = array('script_name' => $script_name,
393 'php_self' => $php_self,
394 'return' => array());
396 plugin_hook_by_reference('alt_representations', $params);
398 foreach($params['return'] as $link) {
404 function headerForgepluckerMeta() {
406 * Forge-Identification Meta Header, Version 1.0
407 * cf. http://home.gna.org/forgeplucker/forge-identification.html
410 $ff = new FusionForge();
411 printf('<meta name="Forge-Identification" content="%s:%s" />',
412 $ff->software_type, $ff->software_version);
416 function bodyHeader($params){
419 <table border="0" width="100%" cellspacing="0" cellpadding="0" id="headertable">
421 <td><a href="<?php echo util_make_url ('/'); ?>"><?php echo html_image('logo.png',198,52,array('border'=>'0')); ?></a></td>
422 <td><?php $this->searchBox(); ?></td>
423 <td align="right"><?php
424 $items = $this->navigation->getUserLinks();
425 for ($j = 0; $j < count($items['titles']); $j++) {
426 echo util_make_link($items['urls'][$j], $items['titles'][$j], array('class'=>'lnkutility'), true);
429 $params['template'] = ' {menu}';
430 plugin_hook('headermenu', $params);
440 <table class="fullwidth">
446 <?php $this->outerTabs($params); ?>
453 <td class="align-left toptab" width="9"><img src="<?php echo $this->imgbaseurl; ?>tabs/topleft.png" height="9" width="9" alt="" /></td>
454 <td class="toptab" width="30"><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="30" height="1" alt="" /></td>
455 <td class="toptab"><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="1" height="1" alt="" /></td>
456 <td class="toptab" width="30"><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="30" height="1" alt="" /></td>
457 <td class="align-right toptab" width="9"><img src="<?php echo $this->imgbaseurl; ?>tabs/topright.png" height="9" width="9" alt="" /></td>
462 <!-- Outer body row -->
464 <td class="toptab"><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="10" height="1" alt="" /></td>
465 <td class="top toptab" width="99%" colspan="3">
467 <!-- Inner Tabs / Shell -->
469 <table class="fullwidth">
473 if (isset($params['group']) && $params['group']) {
479 <?php $this->projectTabs($params['toptab'],$params['group']); ?>
489 <td class="align-left projecttab" width="9"><img src="<?php echo $this->imgbaseurl; ?>tabs/topleft-inner.png" height="9" width="9" alt="" /></td>
490 <td class="projecttab" ><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="1" height="1" alt="" /></td>
491 <td class="align-right projecttab" width="9"><img src="<?php echo $this->imgbaseurl; ?>tabs/topright-inner.png" height="9" width="9" alt="" /></td>
495 <td class="projecttab" ><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="10" height="1" alt="" /></td>
496 <td style="width:99%" class="top projecttab">
502 function footer($params) {
506 <!-- end main body row -->
509 <td width="10" class="footer3" ><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="2" height="1" alt="" /></td>
512 <td class="align-left footer1" width="9"><img src="<?php echo $this->imgbaseurl; ?>tabs/bottomleft-inner.png" height="11" width="11" alt="" /></td>
513 <td class="footer3"><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="1" height="1" alt="" /></td>
514 <td class="align-right footer1" width="9"><img src="<?php echo $this->imgbaseurl; ?>tabs/bottomright-inner.png" height="11" width="11" alt="" /></td>
518 <!-- end inner body row -->
521 <td width="10" class="footer2"><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="2" height="1" alt="" /></td>
524 <td class="align-left footer2" width="9"><img src="<?php echo $this->imgbaseurl; ?>tabs/bottomleft.png" height="9" width="9" alt="" /></td>
525 <td class="footer2" colspan="3"><img src="<?php echo $this->imgbaseurl; ?>clear.png" width="1" height="1" alt="" /></td>
526 <td class="align-right footer2" width="9"><img src="<?php echo $this->imgbaseurl; ?>tabs/bottomright.png" height="9" width="9" alt="" /></td>
530 $this->footerEnd($params);
533 function footerEnd($params) { ?>
535 <!-- PLEASE LEAVE "Powered By FusionForge" on your site -->
537 <?php echo $this->navigation->getPoweredBy(); ?>
540 <?php echo $this->navigation->getShowSource();
542 plugin_hook('webanalytics_url', array());
548 function getRootIndex() {
549 return $this->rootindex;
553 * boxTop() - Top HTML box.
555 * @param string $title Box title
556 * @return string the html code
558 function boxTop($title) {
560 <!-- Box Top Start -->
562 <table class="fullwidth" style="background:url('.$this->imgroot.'vert-grad.png)">
563 <tr class="align-center">
564 <td class="top align-right" width="10" style="background:url('.$this->imgbaseurl.'box-topleft.png)"><img src="'.$this->imgbaseurl.'clear.png" width="10" height="20" alt="" /></td>
565 <td class="fullwidth" style="background:url('.$this->imgbaseurl.'box-grad.png)"><span class="titlebar">'.$title.'</span></td>
566 <td class="top" width="10" style="background:url('.$this->imgbaseurl.'box-topright.png)"><img src="'.$this->imgbaseurl.'clear.png" width="10" height="20" alt="" /></td>
570 <table cellspacing="2" cellpadding="2" class="fullwidth">
571 <tr class="align-left">
574 <!-- Box Top End -->';
578 * boxMiddle() - Middle HTML box.
580 * @param string $title Box title
581 * @return string The html code
583 function boxMiddle($title) {
585 <!-- Box Middle Start -->
588 <tr class="align-center">
589 <td colspan="2" style="background:url('.$this->imgbaseurl.'box-grad.png)"><span class="titlebar">'.$title.'</span></td>
591 <tr class="align-left">
593 <!-- Box Middle End -->';
597 * boxBottom() - Bottom HTML box.
599 * @return string the html code
601 function boxBottom() {
603 <!-- Box Bottom Start -->
610 <!-- Box Bottom End -->';
614 * boxGetAltRowStyle() - Get an alternating row style for tables.
616 * @param int $i Row number
617 * @return string the class code
619 function boxGetAltRowStyle($i) {
621 return ' class="altRowStyleEven"';
623 return ' class="altRowStyleOdd"';
628 * listTableTop() - Takes an array of titles and builds the first row of a new table.
630 * @param array $titleArray The array of titles
631 * @param array $linksArray The array of title links
632 * @param string $class The css classes to add (optional)
633 * @param string $id The id of the table (needed by sortable for example)
634 * @param array $thClassArray specific class for th column
635 * @return string the html code
637 function listTableTop($titleArray, $linksArray=array(), $class='', $id='', $thClassArray=array()) {
640 $args .= ' class="listing '.$class.'"';
642 $args .= ' class="listing full"';
645 $args .= ' id="'.$id.'"';
650 if (count($titleArray)) {
651 $return .= '<thead><tr>';
653 $count=count($titleArray);
654 for ($i=0; $i<$count; $i++) {
656 if ($thClassArray && $thClassArray[$i]) {
657 $th .= ' class="'.$thClassArray[$i].'"';
659 $cell = $titleArray[$i];
661 $cell = util_make_link($linksArray[$i],$titleArray[$i]);
663 $return .= "\n".' <th'.$th.'>'.$cell.'</th>';
665 $return .= "\n".'</tr></thead>'."\n";
667 $return .= '<tbody>';
671 function listTableBottom() {
672 return '</tbody>'."\n".'</table>';
675 function outerTabs($params) {
676 $menu = $this->navigation->getSiteMenu();
677 echo $this->tabGenerator($menu['urls'], $menu['titles'], $menu['tooltips'], false, $menu['selected'], '');
681 * Prints out the quicknav menu, contained here in case we
682 * want to allow it to be overridden.
684 function quickNav() {
685 if (!session_loggedin()) {
688 // get all projects that the user belongs to
689 $groups = session_get_user()->getGroups();
691 if (count($groups) < 1) {
694 sortProjectList($groups);
697 <form id="quicknavform" name="quicknavform" action=""><div>
698 <select name="quicknav" id="quicknav" onchange="location.href=document.quicknavform.quicknav.value">
699 <option value="">'._('Quick Jump To...').'</option>';
701 foreach ($groups as $g) {
702 $group_id = $g->getID();
703 $menu = $this->navigation->getProjectMenu($group_id);
706 <option value="' . $menu['starturl'] . '">'
707 . $menu['name'] .'</option>';
709 for ($j = 0; $j < count($menu['urls']); $j++) {
711 <option value="' . $menu['urls'][$j] .'"> '
712 . $menu['titles'][$j] . '</option>';
713 if (@$menu['adminurls'][$j]) {
715 <option value="' . $menu['adminurls'][$j]
716 . '"> '
717 . _('Admin') . '</option>';
729 * projectTabs() - Prints out the project tabs, contained here in case
730 * we want to allow it to be overriden.
732 * @param string $toptab Is the tab currently selected
733 * @param string $group_id Is the group we should look up get title info
735 function projectTabs($toptab, $group_id) {
736 // get group info using the common result set
737 $menu = $this->navigation->getProjectMenu($group_id, $toptab);
738 echo $this->tabGenerator($menu['urls'], $menu['titles'], $menu['tooltips'], true, $menu['selected'], 'white');
741 function tabGenerator($TABS_DIRS, $TABS_TITLES, $TABS_TOOLTIPS, $nested=false, $selected=false, $sel_tab_bgcolor='white', $total_width='100%') {
743 $count=count($TABS_DIRS);
744 $width=intval((100/$count));
749 <table class="tabGenerator" ';
751 if ($total_width != '100%') {
752 $return .= 'style="width:' . $total_width . ';"';
762 for ($i=0; $i<$count; $i++) {
765 // this is the first tab, choose an image with end-name
768 $issel=($selected==$i);
769 $bgimg=(($issel)?'theme-'.$inner.'-selected-bg.png':'theme-'.$inner.'-notselected-bg.png');
770 // $rowspan=(($issel)?'rowspan="2" ' : '');
773 <td '.$rowspan.'class="top" width="10" style="background:url('.$this->imgbaseurl . 'theme-'.$inner.'-end-'.(($issel) ? '' : 'not').'selected.png)">'.
774 '<img src="'.$this->imgbaseurl . 'clear.png" height="25" width="10" alt="" /></td>'.
775 '<td '.$rowspan.'style="background:url('.$this->imgbaseurl . $bgimg.')" width="'.$width.'%" align="center">'.util_make_link ($TABS_DIRS[$i],$TABS_TITLES[$i],array('class'=>(($issel)?'tabsellink':'tablink')),true).'</td>';
776 } elseif ($i==$count-1) {
778 // this is the last tab, choose an image with name-end
780 $wassel=($selected==$i-1);
781 $issel=($selected==$i);
782 $bgimg=(($issel)?'theme-'.$inner.'-selected-bg.png':'theme-'.$inner.'-notselected-bg.png');
783 // $rowspan=(($issel)?'rowspan="2" ' : '');
785 // Build image between current and prior tab
788 <td '.$rowspan.'colspan="2" class="top" width="20" style="background:url('.$this->imgbaseurl . 'theme-'.$inner.'-'.(($wassel) ? '' : 'not').'selected-'.(($issel) ? '' : 'not').'selected.png)">'.
789 '<img src="'.$this->imgbaseurl . 'clear.png" height="2" width="20" alt="" /></td>'.
790 '<td '.$rowspan.'style="background:url('.$this->imgbaseurl . $bgimg.')" width="'.$width.'%" align="center">'.util_make_link($TABS_DIRS[$i], $TABS_TITLES[$i], array('class'=>(($issel)?'tabsellink':'tablink')), true).'</td>';
792 // Last graphic on right-side
795 <td '.$rowspan.'class="top" width="10" style="background:url('.$this->imgbaseurl . 'theme-'.$inner.'-'.(($issel) ? '' : 'not').'selected-end.png)">'.
796 '<img src="'.$this->imgbaseurl . 'clear.png" height="2" width="10" alt="" /></td>';
802 $wassel=($selected==$i-1);
803 $issel=($selected==$i);
804 $bgimg=(($issel)?'theme-'.$inner.'-selected-bg.png':'theme-'.$inner.'-notselected-bg.png');
805 // $rowspan=(($issel)?'rowspan="2" ' : '');
807 // Build image between current and prior tab
810 <td '.$rowspan.'colspan="2" class="top" width="20" style="background:url('.$this->imgbaseurl . 'theme-'.$inner.'-'.(($wassel) ? '' : 'not').'selected-'.(($issel) ? '' : 'not').'selected.png)">'.
811 '<img src="'.$this->imgbaseurl . 'clear.png" height="2" width="20" alt="" /></td>'.
812 '<td '.$rowspan.'style="background:url('.$this->imgbaseurl . $bgimg.')" width="'.$width.'%" align="center">'.util_make_link($TABS_DIRS[$i], $TABS_TITLES[$i], array('class'=>(($issel)?'tabsellink':'tablink')), true).'</td>';
819 // Building a bottom row in this table, which will be darker
821 if ($selected == 0) {
823 $end_cols=((count($TABS_DIRS)*3)-3);
824 } elseif ($selected == (count($TABS_DIRS)-1)) {
825 $beg_cols=((count($TABS_DIRS)*3)-3);
828 $beg_cols=($selected*3);
829 $end_cols=(((count($TABS_DIRS)*3)-3)-$beg_cols);
833 $return .= '<td colspan="'.$beg_cols.'" height="1" class="notSelTab"><img src="'.$this->imgbaseurl.'clear.png" height="1" width="10" alt="" /></td>';
835 $return .= '<td colspan="3" height="1" class="selTab"><img src="'.$this->imgbaseurl.'clear.png" height="1" width="10" alt="" /></td>';
837 $return .= '<td colspan="'.$end_cols.'" height="1" class="notSelTab"><img src="'.$this->imgbaseurl.'clear.png" height="1" width="10" alt="" /></td>';
848 function searchBox() {
849 echo $this->navigation->getSearchBox();
853 * beginSubMenu() - Opening a submenu.
855 * @return string Html to start a submenu.
857 function beginSubMenu() {
864 * endSubMenu() - Closing a submenu.
866 * @return string Html to end a submenu.
868 function endSubMenu() {
869 $return = '</strong></p>';
874 * printSubMenu() - Takes two array of titles and links and builds the contents of a menu.
876 * @param array $title_arr The array of titles.
877 * @param array $links_arr The array of title links.
878 * @param array $attr_arr The array of string for title attributes.
879 * @return string Html to build a submenu.
881 function printSubMenu($title_arr, $links_arr, $attr_arr) {
882 $count=count($title_arr);
886 for ($i=0; $i<$count; $i++) {
887 $return .= util_make_link($links_arr[$i],$title_arr[$i],$attr_arr[$i]). $this->subMenuSeparator();
889 $return .= util_make_link($links_arr[$i],$title_arr[$i],$attr_arr[$i]);
894 * subMenuSeparator() - returns the separator used between submenus
896 * @return string Html to build a submenu separator.
898 function subMenuSeparator() {
903 * subMenu() - Takes two array of titles and links and build a menu.
905 * @param array $title_arr The array of titles.
906 * @param array $links_arr The array of title links.
907 * @param array $attr_arr The array of string for title attributes.
908 * @return string Html to build a submenu.
910 function subMenu($title_arr, $links_arr, $attr_arr = array()) {
911 $return = $this->beginSubMenu();
912 $return .= $this->printSubMenu($title_arr, $links_arr, $attr_arr);
913 $return .= $this->endSubMenu();
918 * multiTableRow() - create a multilevel row in a table
920 * @param string $row_attr the row attributes
921 * @param array $cell_data the array of cell data, each element is an array,
922 * the first item being the text,
923 * the subsequent items are attributes (dont include
924 * the bgcolor for the title here, that will be
925 * handled by $istitle
926 * @param bool $istitle is this row part of the title ?
927 * @return string the html code
929 function multiTableRow($row_attr, $cell_data, $istitle) {
933 $return .=' class="align-center multiTableRowTitle"';
936 for ( $c = 0; $c < count($cell_data); $c++ ) {
938 for ( $a=1; $a < count($cell_data[$c]); $a++) {
939 $return .= $cell_data[$c][$a].' ';
943 $return .='<span class="multiTableRowTitle">';
945 $return .= $cell_data[$c][0];
959 * feedback() - returns the htmlized feedback string when an action is performed.
961 * @param string $feedback feedback string
962 * @return string htmlized feedback
964 function feedback($feedback) {
969 <p class="feedback">'.strip_tags($feedback, '<br>').'</p>';
973 * warning_msg() - returns the htmlized warning string when an action is performed.
975 * @param string $msg msg string
976 * @return string htmlized warning
978 function warning_msg($msg) {
983 <p class="warning_msg">'.strip_tags($msg, '<br>').'</p>';
988 * error_msg() - returns the htmlized error string when an action is performed.
990 * @param string $msg msg string
991 * @return string htmlized error
993 function error_msg($msg) {
998 <p class="error">'.strip_tags($msg, '<br>')."</p>\n";
1003 * information() - returns the htmlized information string.
1005 * @param string $msg msg string
1006 * @return string htmlized information
1008 function information($msg) {
1013 <p class="information">'.strip_tags($msg, '<br>').'</p>';
1018 * getThemeIdFromName()
1020 * @param string $dirname the dirname of the theme
1021 * @return int the theme id
1023 function getThemeIdFromName($dirname) {
1024 $res = db_query_params ('SELECT theme_id FROM themes WHERE dirname=$1',
1026 return db_result($res,0,'theme_id');
1029 function confirmBox($msg, $params, $buttons, $image='*none*') {
1030 if ($image == '*none*') {
1031 $image = html_image('stop.png','48','48',array());
1034 foreach ($params as $b => $v) {
1035 $prms[] = '<input type="hidden" name="'.$b.'" value="'.$v.'" />'."\n";
1037 $prm = join(' ', $prms);
1039 foreach ($buttons as $b => $v) {
1040 $btns[] = '<input type="submit" name="'.$b.'" value="'.$v.'" />'."\n";
1042 $btn = join(' '."\n ", $btns);
1045 <div id="infobox" style="margin-top: 15%; margin-left: 15%; margin-right: 15%; text-align: center;">
1046 <table align="center">
1049 <td>'.$msg.'<br/></td>
1052 <td colspan="2" align="center">
1054 <form action="' . getStringFromServer('PHP_SELF') . '" method="get" >
1065 function html_input($name, $id = '', $label = '', $type = 'text', $value = '', $extra_params = '') {
1069 $return = '<div class="field-holder">
1072 $return .= '<label for="' . $id . '">' . $label . '</label>
1075 $return .= '<input id="' . $id . '" type="' . $type . '"';
1076 //if input is a submit then name is not present
1078 $return .= ' name="' . $name . '"';
1081 $return .= ' value="' . $value . '"';
1083 if (is_array($extra_params)) {
1084 foreach ($extra_params as $key => $extra_params_value) {
1085 $return .= $key . '="' . $extra_params_value . '" ';
1093 function html_checkbox($name, $value, $id = '', $label = '', $checked = '', $extra_params = '') {
1097 $return = '<div class="field-holder">
1099 $return .= '<input name="' . $name . '" id="' . $id . '" type="checkbox" value="' . $value . '" ';
1101 $return .= 'checked="checked" ';
1103 if (is_array($extra_params)) {
1104 foreach ($extra_params as $key => $extra_params_value) {
1105 $return .= $key . '="' . $extra_params_value . '" ';
1110 $return .= '<label for="' . $id . '">' . $label . '</label>
1113 $return .= '</div>';
1117 function html_text_input_img_submit($name, $img_src, $id = '', $label = '', $value = '', $img_title = '', $img_alt = '', $extra_params = '', $img_extra_params = '') {
1125 $img_alt = $img_title;
1127 $return = '<div class="field-holder">
1130 $return .= '<label for="' . $id . '">' . $label . '</label>
1133 $return .= '<input id="' . $id . '" type="text" name="' . $name . '"';
1135 $return .= ' value="' . $value . '"';
1137 if (is_array($extra_params)) {
1138 foreach ($extra_params as $key => $extra_params_value) {
1139 $return .= $key . '="' . $extra_params_value . '" ';
1143 <input type="image" id="' . $id . '_submit" src="' . $this->imgbaseurl . $img_src . '" alt="' . util_html_secure($img_alt) . '" title="' . util_html_secure($img_title) . '"';
1144 if (is_array($img_extra_params)) {
1145 foreach ($img_extra_params as $key => $img_extra_params_value) {
1146 $return .= $key . '="' . $img_extra_params_value . '" ';
1154 function html_select($vals, $name, $label = '', $id = '', $checked_val = '', $text_is_value = false, $extra_params = '') {
1158 $return = '<div class="field-holder">
1161 $return .= '<label for="' . $id . '">' . $label . '</label>
1164 $return .= '<select name="' . $name . '" id="' . $id . '" ';
1165 if (is_array($extra_params)) {
1166 foreach ($extra_params as $key => $extra_params_value) {
1167 $return .= $key . '="' . $extra_params_value . '" ';
1171 $rows = count($vals);
1172 for ($i = 0; $i < $rows; $i++) {
1173 if ( $text_is_value ) {
1175 <option value="' . $vals[$i] . '"';
1176 if ($vals[$i] == $checked_val) {
1177 $return .= ' selected="selected"';
1181 <option value="' . $i . '"';
1182 if ($i == $checked_val) {
1183 $return .= ' selected="selected"';
1186 $return .= '>' . htmlspecialchars($vals[$i]) . '</option>';
1194 function html_textarea($name, $id = '', $label = '', $value = '', $extra_params = '') {
1198 $return = '<div class="field-holder">
1201 $return .= '<label for="' . $id . '">' . $label . '</label>
1204 $return .= '<textarea id="' . $id . '" name="' . $name . '" ';
1205 if (is_array($extra_params)) {
1206 foreach ($extra_params as $key => $extra_params_value) {
1207 $return .= $key . '="' . $extra_params_value . '" ';
1214 $return .= '</textarea>
1220 * @todo use listTableTop and make this function deprecated ?
1222 function html_table_top($cols, $summary = '', $class = '', $extra_params = '') {
1223 $return = '<table summary="' . $summary . '" ';
1225 $return .= 'class="' . $class . '" ';
1227 if (is_array($extra_params)) {
1228 foreach ($extra_params as $key => $extra_params_value) {
1229 $return .= $key . '="' . $extra_params_value . '" ';
1233 $return .= '<thead><tr>';
1234 $nbCols = count($cols);
1235 for ($i = 0; $i < $nbCols; $i++) {
1236 $return .= '<th scope="col">' . $cols[$i] . '</th>';
1238 $return .= '</tr></thead>';
1242 function getMonitorPic($title = '', $alt = '') {
1243 return $this->getPicto('ic/mail16w.png', $title, $alt);
1246 function getReleaseNotesPic($title = '', $alt = '') {
1247 return $this->getPicto('ic/manual16c.png', $title, $alt);
1250 /* no picto for download */
1251 function getDownloadPic($title = '', $alt = '') {
1252 return $this->getPicto('ic/save.png', $title, $alt);
1255 function getHomePic($title = '', $alt = '') {
1256 return $this->getPicto('ic/home16b.png', $title, $alt);
1259 function getFollowPic($title = '', $alt = '') {
1260 return $this->getPicto('ic/tracker20g.png', $title, $alt);
1263 function getForumPic($title = '', $alt = '') {
1264 return $this->getPicto('ic/forum20g.png', $title, $alt);
1267 function getDocmanPic($title = '', $alt = '') {
1268 return $this->getPicto('ic/docman16b.png', $title, $alt);
1271 function getMailPic($title = '', $alt = '') {
1272 return $this->getPicto('ic/mail16b.png', $title, $alt);
1275 function getPmPic($title = '', $alt = '') {
1276 return $this->getPicto('ic/taskman20g.png', $title, $alt);
1279 function getSurveyPic($title = '', $alt = '') {
1280 return $this->getPicto('ic/survey16b.png', $title, $alt);
1283 function getScmPic($title = '', $alt = '') {
1284 return $this->getPicto('ic/cvs16b.png', $title, $alt);
1287 function getFtpPic($title = '', $alt = '') {
1288 return $this->getPicto('ic/ftp16b.png', $title, $alt);
1291 function getPicto($url, $title, $alt, $width = '20', $height = '20') {
1295 return html_image($url, $width, $height, array('title'=>$title, 'alt'=>$alt));
1299 * toSlug() - protect a string to be used as a link or an anchor
1301 * @param string $string the string used as a link or an anchor
1302 * @param string $space the character used as a replacement for a space
1303 * @return string a protected string with only alphanumeric characters
1305 function toSlug($string, $space = "-") {
1306 if (function_exists('iconv')) {
1307 $string = @iconv('UTF-8', 'ASCII//TRANSLIT', $string);
1309 $string = preg_replace("/[^a-zA-Z0-9_:. -]/", "-", $string);
1310 $string = strtolower($string);
1311 $string = str_replace(" ", $space, $string);
1312 if (!preg_match("/^[a-zA-Z:_]/", $string)) {
1313 /* some chars aren't allowed at the begin */
1314 $string = "_" . $string;
1319 function widget(&$widget, $layout_id, $readonly, $column_id, $is_minimized, $display_preferences, $owner_id, $owner_type) {
1320 $element_id = 'widget_'. $widget->id .'-'. $widget->getInstanceId();
1321 echo '<div class="widget" id="'. $element_id . "\">\n";
1322 echo '<div class="widget_titlebar '. ($readonly?'':'widget_titlebar_handle') . "\">\n";
1323 echo '<div class="widget_titlebar_title">'. $widget->getTitle() . "</div>\n";
1325 echo '<div class="widget_titlebar_close"><a href="/widgets/updatelayout.php?owner='. $owner_type.$owner_id .'&action=widget&name['. $widget->id .'][remove]='. $widget->getInstanceId() .'&column_id='. $column_id .'&layout_id='. $layout_id .'">'. $this->getPicto('ic/close.png', _('Close'), _('Close')) . "</a></div>\n";
1326 if ($is_minimized) {
1327 echo '<div class="widget_titlebar_maximize"><a href="/widgets/updatelayout.php?owner='. $owner_type.$owner_id .'&action=maximize&name['. $widget->id .']='. $widget->getInstanceId() .'&column_id='. $column_id .'&layout_id='. $layout_id .'">'. $this->getPicto($this->_getTogglePlusForWidgets(), _('Maximize'), _('Maximize')) . "</a></div>\n";
1329 echo '<div class="widget_titlebar_minimize"><a href="/widgets/updatelayout.php?owner='. $owner_type.$owner_id .'&action=minimize&name['. $widget->id .']='. $widget->getInstanceId() .'&column_id='. $column_id .'&layout_id='. $layout_id .'">'. $this->getPicto($this->_getToggleMinusForWidgets(), _('Minimize'), _('Minimize')) . "</a></div>\n";
1331 if (strlen($widget->hasPreferences())) {
1332 echo '<div class="widget_titlebar_prefs"><a href="/widgets/updatelayout.php?owner='. $owner_type.$owner_id .'&action=preferences&name['. $widget->id .']='. $widget->getInstanceId() .'&layout_id='. $layout_id .'">'. _('Preferences') . "</a></div>\n";
1335 if ($widget->hasRss()) {
1336 echo '<div class="widget_titlebar_rss"><a href="'.$widget->getRssUrl($owner_id, $owner_type) . "\">rss</a></div>\n";
1340 if ($is_minimized) {
1341 $style = 'display:none;';
1343 echo '<div class="widget_content" style="'. $style . "\">\n";
1344 if (!$readonly && $display_preferences) {
1345 echo '<div class="widget_preferences">'. $widget->getPreferencesForm($layout_id, $owner_id, $owner_type) . "</div>\n";
1347 if ($widget->isAjax()) {
1348 echo '<div id="'. $element_id .'-ajax">';
1349 echo '<noscript><iframe width="99%" frameborder="0" src="'. $widget->getIframeUrl($owner_id, $owner_type) .'"></iframe></noscript>';
1352 echo $widget->getContent();
1355 if ($widget->isAjax()) {
1356 echo '<script type="text/javascript">/* <![CDATA[ */'."
1357 jQuery(document).ready(function() {
1358 jQuery('#$element_id-ajax').html('<div style=\"text-align:center\">". $this->getPicto('ic/spinner.gif',_('Spinner'), _('Spinner'), 10, 10) ."</div>');
1359 jQuery.ajax({url:'". $widget->getAjaxUrl($owner_id, $owner_type) ."',
1360 success: function(result){jQuery('#$element_id-ajax').html(result)},
1363 /* ]]> */</script>";
1368 function _getTogglePlusForWidgets() {
1369 return 'ic/toggle_plus.png';
1372 function _getToggleMinusForWidgets() {
1373 return 'ic/toggle_minus.png';
1376 /* Get the navigation links for the software map pages (trove,
1377 * tag cloud, full project list) according to what's enabled
1379 function printSoftwareMapLinks() {
1380 $subMenuTitle = array();
1381 $subMenuUrl = array();
1382 $subMenuAttr = array();
1384 if (forge_get_config('use_project_tags')) {
1385 $subMenuTitle[] = _('Tag Cloud');
1386 $subMenuUrl[] = '/softwaremap/tag_cloud.php';
1387 $subMenuAttr[] = array('title' => _('Browse per tags defined by the projects.'), 'class' => 'tabtitle-nw');
1390 if (forge_get_config('use_trove')) {
1391 $subMenuTitle[] = _('Project Tree');
1392 $subMenuUrl[] = '/softwaremap/trove_list.php';
1393 $subMenuAttr[] = array('title' => _('Browse by Category'), 'class' => 'tabtitle');
1396 if (forge_get_config('use_project_full_list')) {
1397 $subMenuTitle[] = _('Project List');
1398 $subMenuUrl[] = '/softwaremap/full_list.php';
1399 $subMenuAttr[] = array('title' => _('Complete listing of available projects.'), 'class' => 'tabtitle');
1402 // Allow plugins to add more softwaremap submenu entries
1403 $hookParams = array();
1404 $hookParams['TITLES'] = & $subMenuTitle;
1405 $hookParams['URLS'] = & $subMenuUrl;
1406 $hookParams['ATTRS'] = & $subMenuAttr;
1407 plugin_hook("softwaremap_links", $hookParams);
1409 echo $this->subMenu($subMenuTitle, $subMenuUrl, $subMenuAttr);
1412 function displayStylesheetElements() {
1413 /* Codendi/Tuleap compatibility */
1419 // c-file-style: "bsd"