User Tools

Site Tools


mantisbt:issue:8253

Improved Mantis/DokuWiki integration

FIXME I think, the description is a little bit clumsy. Please correct. — Silbaer 2007/11/08 06:44

The following description works with mantis version 1.1.0rc2 and dokuwiki version 2007-06-26b. It based on this description, but extend the integration.

You can get all these files from issue 8253 too. But save your original files:!:

Dokuwiki

Grab the latest (2007-06-26b) version and install it. Make sure it is working properly.

Mantis Syntax Plug-in

If you only want links back to mantis, there is no need for a systax plugin. You can use interwiki shortcuts to create links back to mantis (see here). But I used a configuration setting from the syntax plugin in the mantis authentication class and perhaps there are other improvements in the future, so here is it.

The syntax plugin based an the plugin from Victor Boctor, but I changed the syntax. Now it uses { like almost all other plugins instead of ~. For the syntax-plugin with support for the dokuwiki admin panel, you have to create several files:

\lib\plugins\mantis\syntax.php

<?php
/**
 * Mantis Plugin: Hyperlinks references to Mantis Issues
 * 
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Timo Falk
 */
 
 
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
 
/**
 * A plug-in that hyper links references to Mantis issues.  References
 * to Mantis issues should use the following format: {{Mantis:123}}.
 *
 * All DokuWiki plugins to extend the parser/rendering mechanism
 * need to inherit from this class
 */
class syntax_plugin_mantis extends DokuWiki_Syntax_Plugin {
 
    /**
     * return some info
     */
    function getInfo(){
        return array(
            'author' => 'Timo Falk',
            'email'  => 'Timo . Falk at gmx . de',
            'date'   => '2007-11-01',
            'name'   => 'Mantis enhanced Issues Plugin',
            'desc'   => 'Support References to Mantis Issues. Based on Mantis Issues Plugin by Victor Boctor',
        );
    }
 
    /**
     * What kind of syntax are we?
     */
    function getType(){
        return 'substition';  # typo is intentional
    }
 
    /**
     * What about paragraphs?
     */
    function getPType(){
        return 'normal';
    }
 
    /**
     * Where to sort in?
     */ 
    function getSort(){
        return 156;
    }
 
 
    /**
     * Connect pattern to lexer
     */
    function connectTo($mode) {
        $this->Lexer->addSpecialPattern('\{\{Mantis:[0-9]+\}\}', $mode, 'plugin_mantis');
    }
 
 
    /**
     * Handle the match
     */
    function handle($match, $state, $pos, &$handler){
        $match = substr( $match, 9, -2 ); // strip "{{Mantis:" from start and "}}" from end
        return array( strtolower( $match ) );
    }
 
    /**
     * Create output
     */
    function render($format, &$renderer, $data) {
        if ( $format == 'xhtml' ) {
 
//			$renderer->externallink( $this->getConf('mantis_url') . 'view.php?id=' . $data[0], $data[0] );
 
        $link['target'] = $conf['target']['wiki'];
        $link['style']  = '';
        $link['pre']    = '';
        $link['suf']    = '';
        $link['more']   = '';
        $link['class']  = 'mantislink';
        $link['url']    = $this->getConf('mantis_url') . 'view.php?id=' . $data[0];
        $link['name']   = $this->getConf('LinkPrefix') . $data[0];
        $link['title']  = $renderer->_xmlEntities($url);
 
        //output formatted
        $renderer->doc .= $renderer->_formatLink($link);
 
 
 
            return true;
        }
        return false;
    }
}
?>

dokuwiki\lib\plugins\mantis\style.css

div.dokuwiki a.mantislink {
  background: transparent url(images/bug.gif) 0px 1px no-repeat;
  padding: 1px 0px 1px 16px;
}

dokuwiki\lib\plugins\mantis\conf\default.php

<?php
$conf['mantis_root']      = '';
$conf['mantis_url']     = '';  
$conf['LinkPrefix']     = 'issue ';  
?>

dokuwiki\lib\plugins\mantis\conf\metadata.php

<?php
$meta['mantis_root']         = array('string');
$meta['mantis_url']        = array('string');
$meta['LinkPrefix']        = array('string');
?>

dokuwiki\lib\plugins\mantis\images\bug.gif

A Picture with a little bug, placed in front of the bug links. The file is grabed from this dokuwiki page.

dokuwiki\lib\plugins\mantis\lang\en\settings.php

<?php
/*USE : UTF8*/
 
/*
 * English language file
 */
$lang['mantis_root']         = "file path to Mantis, must terminate with /";
$lang['mantis_url'] 		 = "url to Mantis, must terminate with /";
?>

Mantis Template

Go to DokuWiki settings and configure mantis URL at the bottom of the setup page. Copy the default template (dokuwiki/lib/tpl/default/) into a new folder called mantis.

Replace the main.php with this one:

<?php
/**
 * DokuWiki mantis Template
 *
 * This is the template you need to change for the overall look
 * of DokuWiki.
 *
 */
 
// must be run from within DokuWiki
if (!defined('DOKU_INC')) die();
 
  global $g_path ;
 
	require_once( $conf['plugin']['mantis']['mantis_root'] . 'core.php' );
 
	$g_path = $conf['plugin']['mantis']['mantis_url'];
 
	$t_core_path = config_get( 'core_path' );
 
 
		html_begin();
		html_head_begin();
		html_css();
		html_content_type();
		include( config_get( 'meta_include_file' ) );
		html_rss_link();
		echo '<link rel="shortcut icon" href="' . $g_path . 'images/favicon.ico" type="image/x-icon" />';
 
   tpl_metaheaders();
 
		html_title( $p_page_title );
		html_head_javascript();
 
    html_page_top2() 
 
//    $t_mantis_dir = dirname( __FILE__ ) . DIRECTORY_SEPARATOR;
?>
<div class="dokuwiki">
  <?php html_msgarea()?>
  <div class="stylehead">
 
    <div class="bar" id="bar__top">
      <div class="bar-left" id="bar__topleft">
        <?php tpl_button('edit')?>
        <?php tpl_button('history')?>
      </div>
 
      <div class="bar-right" id="bar__topright">
        <?php tpl_button('recent')?>
        <?php tpl_searchform()?>&nbsp;
      </div>
 
      <div class="clearer"></div>
    </div>
 
    <?php if($conf['breadcrumbs']){?>
    <div class="breadcrumbs">
      <?php tpl_breadcrumbs()?>
      <?php //tpl_youarehere() //(some people prefer this)?>
    </div>
    <?php }?>
 
    <?php if($conf['youarehere']){?>
    <div class="breadcrumbs">
      <?php tpl_youarehere() ?>
    </div>
    <?php }?>
 
  </div>
  <?php flush()?>
  <div class="page">
    <!-- wikipage start -->
    <?php tpl_content()?>
    <!-- wikipage stop -->
  </div>
 
  <div class="clearer">&nbsp;</div>
 
  <?php flush()?>
 
  <div class="stylefoot">
 
    <div class="meta">
      <div class="user">
        <?php tpl_userinfo()?>
      </div>
      <div class="doc">
        <?php tpl_pageinfo()?>
      </div>
    </div>
    <div class="bar" id="bar__bottom">
      <div class="bar-left" id="bar__bottomleft">
        <?php tpl_button('edit')?>
        <?php tpl_button('history')?>
      </div>
      <div class="bar-right" id="bar__bottomright">
        <?php tpl_button('subscription')?>
        <?php tpl_button('admin')?>
        <?php tpl_button('profile')?>
        <?php tpl_button('login')?>
        <?php tpl_button('index')?>
        <?php tpl_button('top')?>&nbsp;
      </div>
      <div class="clearer"></div>
    </div>
  </div>
</div>
 
<div class="no"><?php /* provide DokuWiki housekeeping, required in all templates */ tpl_indexerWebBug()?></div>
 
<?php
	html_page_bottom1( __FILE__ );
?>

Authentication (Single Sign-On)

Create dokuwiki\inc\auth\mantis.class.php with the code below.

<?php
/**
 * Mantis auth backend
 *
 * Uses external Trust mechanism to check against Mantis'
 * user cookie.
 *
 * @author    Victor Boctor (http://www.futureware.biz)
 */
 
 
require_once( $conf['plugin']['mantis']['mantis_root'] . 'core.php' );
 
#dbg($GLOBALS);
 
class auth_mantis extends auth_basic {
  /**
   * Constructor.
   *
   * Sets additional capabilities and config strings
   */
  function auth_mantis(){
    $this->cando['external'] = true;
  }
 
  /**
   * Authenticates the user using Mantis APIs.
   */
  function trustExternal($user,$pass,$sticky=false){
    global $USERINFO;
    global $conf;
 
    if ( auth_is_user_authenticated() ) {
      // okay we're logged in - set the globals
      $USERINFO['pass'] = current_user_get_field( 'password' );
      $USERINFO['name'] = current_user_get_field( 'username' );
      $USERINFO['mail'] = current_user_get_field( 'email' );
 
      $t_project_name = getNS( getID() );
      $t_project_id = project_get_id_by_name( $t_project_name );
      $t_access_level = access_get_project_level( $t_project_id );
      $t_access_level_string = strtoupper( get_enum_to_string( config_get( 'access_levels_enum_string' ),  $t_access_level ) );
 
      $USERINFO['grps'] = array( $t_access_level_string );
 
      $_SERVER['REMOTE_USER'] = $USERINFO['name'];
      $_SESSION[$conf['title']]['auth']['user'] = $USERINFO['name'];
      $_SESSION[$conf['title']]['auth']['info'] = $USERINFO;
 
      return true;
    }
 
    // to be sure
    auth_logoff();
 
    return false;
  }
 
  /**
   * Logout from Mantis
   */
  function logOff(){
	auth_logout();
  }
}
?>

Authorisation Configuration

Create dokuwiki\conf\acl.auth.php with the code below.

# acl.auth.php
# <?php exit()?>
# Don't modify the lines above
#
# Access Control Lists
#
# Editing this file by hand shouldn't be necessary. Use the ACL
# Manager interface instead.
#
# If your auth backend allows special char like spaces in groups
# or user names you need to urlencode them (only chars <128, leave
# UTF-8 multibyte chars as is)
#
# none   0
# read   1
# edit   2
# create 4
# upload 8
# delete 16
 
*	@VIEWER		1
*	@REPORTER	2
*	@DEVELOPER	8
*	@MANAGER	8
*	@ADMINISTRATOR	8
*	@ALL		0

Redirect dokuwiki login/logout

If you are using url rewrite, append the following lines in your .htaccess file. See also issue 8277.

RewriteCond %{QUERY_STRING} do=login
RewriteCond %{QUERY_STRING} (id=.*)
RewriteRule .* /mantis/login_page.php?return=/mantiswiki/doku.php?%1 [R,L]

RewriteCond %{QUERY_STRING} do=logout
RewriteRule .* /mantis/logout_page.php [R,L]

Mantis

In mantis there are changes in two file necessary.

code/html_api.php

<?php
# Mantis - a php based bugtracking system

# Copyright (C) 2000 - 2002  Kenzaburo Ito - kenito@300baud.org
# Copyright (C) 2002 - 2007  Mantis Team   - mantisbt-dev@lists.sourceforge.net

# Mantis is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Mantis is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Mantis.  If not, see <http://www.gnu.org/licenses/>.

	# --------------------------------------------------------
	# $Id: html_api.php,v 1.218.2.3 2007/10/22 07:08:33 vboctor Exp $
	# --------------------------------------------------------

	###########################################################################
	# HTML API
	#
	# These functions control the display of each page
	#
	# This is the call order of these functions, should you need to figure out
	#  which to modify or which to leave out.
	#
	#   html_page_top1
	#     html_begin
	#     html_head_begin
	#     html_css
	#     html_content_type
	#     html_rss_link
	#  (html_meta_redirect)
	#     html_title
	#   html_page_top2
	#     html_page_top2a
	#       html_head_end
	#       html_body_begin
	#       html_header
	#       html_top_banner
	#     html_login_info
	#    (print_project_menu_bar)
	#     print_menu
	#
	#  ...Page content here...
	#
	#   html_page_bottom1
	#    (print_menu)
	#     html_page_bottom1a
	#       html_bottom_banner
	#  	 html_footer
	#  	 html_body_end
	#  	 html_end
	#
	###########################################################################

	$t_core_dir = dirname( __FILE__ ).DIRECTORY_SEPARATOR;
 
	require_once( $t_core_dir . 'current_user_api.php' );
	require_once( $t_core_dir . 'string_api.php' );
	require_once( $t_core_dir . 'bug_api.php' );
	require_once( $t_core_dir . 'project_api.php' );
	require_once( $t_core_dir . 'helper_api.php' );
	require_once( $t_core_dir . 'authentication_api.php' );
	require_once( $t_core_dir . 'user_api.php' );
	require_once( $t_core_dir . 'rss_api.php' );
	require_once( $t_core_dir . 'wiki_api.php' );
 
	$g_rss_feed_url = null;
 
	# flag for error handler to skip header menus
	$g_error_send_page_header = true;
 
	# Projax library disabled by default.  It will be enabled if projax_api.php
	# is included.  But it must be included after html_api.php
	$g_enable_projax = false;
 
	# --------------------
	# Sets the url for the rss link associated with the current page.
	# null: means no feed (default).
	function html_set_rss_link( $p_rss_feed_url )
	{
		if ( OFF != config_get( 'rss_enabled' ) ) {
			global $g_rss_feed_url;
			$g_rss_feed_url = $p_rss_feed_url;
		}
	}
 
	# --------------------
	# Prints the link that allows auto-detection of the associated feed.
	function html_rss_link()
	{
		global $g_rss_feed_url;
 
		if ( $g_rss_feed_url !== null ) {
			echo "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"$g_rss_feed_url\" />";
		}
	}
 
	# --------------------
	# Print the part of the page that comes before meta redirect tags should
	#  be inserted
	function html_page_top1( $p_page_title = null ) {
	global $g_path;
		html_begin();
		html_head_begin();
		html_css();
		html_content_type();
		include( config_get( 'meta_include_file' ) );
		html_rss_link();
		echo '<link rel="shortcut icon" href="' . $g_path . 'images/favicon.ico" type="image/x-icon" />';
		html_title( $p_page_title );
		html_head_javascript();
	}
 
	# --------------------
	# Print the part of the page that comes after meta tags, but before the
	#  actual page content
	function html_page_top2() {
		html_page_top2a();
 
		if ( !db_is_connected() ) {
			return;
		}
 
		if ( auth_is_user_authenticated() ) {
			html_login_info();
 
			if( ON == config_get( 'show_project_menu_bar' ) ) {
				print_project_menu_bar();
				PRINT '<br />';
			}
		}
		print_menu();
	}
 
	# --------------------
	# Print the part of the page that comes after meta tags and before the
	#  actual page content, but without login info or menus.  This is used
	#  directly during the login process and other times when the user may
	#  not be authenticated
	function html_page_top2a() {
		global $g_error_send_page_header;
 
		html_head_end();
		html_body_begin();
		$g_error_send_page_header = false;
		html_header();
		html_top_banner();
	}
 
	# --------------------
	# Print the part of the page that comes below the page content
	# $p_file should always be the __FILE__ variable. This is passed to show source
	function html_page_bottom1( $p_file = null ) {
		if ( !db_is_connected() ) {
			return;
		}
 
		if ( config_get( 'show_footer_menu' ) ) {
			PRINT '<br />';
			print_menu();
		}
 
		html_page_bottom1a( $p_file );
	}
 
	# --------------------
	# Print the part of the page that comes below the page content but leave off
	#  the menu.  This is used during the login process and other times when the
	#  user may not be authenticated.
	function html_page_bottom1a( $p_file = null ) {
		if ( null === $p_file ) {
			$p_file = basename( $_SERVER['PHP_SELF'] );
		}
 
		html_bottom_banner();
		html_footer( $p_file );
		html_body_end();
		html_end();
	}
 
	# --------------------
	# (1) Print the document type and the opening <html> tag
	function html_begin() {
		# @@@ NOTE make this a configurable global.
		#echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">', "\n";
		#echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/transitional.dtd">', "\n";

		echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">', "\n";
		echo '<html>', "\n";
	}
 
	# --------------------
	# (2) Begin the <head> section
	function html_head_begin() {
		echo '<head>', "\n";
	}
 
	# --------------------
	# (3) Print the content-type
	function html_content_type() {
		echo "\t", '<meta http-equiv="Content-type" content="text/html;charset=', lang_get( 'charset' ), '" />', "\n";
	}
 
	# --------------------
	# (4) Print the window title
	function html_title( $p_page_title = null ) {
		$t_title = config_get( 'window_title' );
		echo "\t", '<title>';
		if ( 0 == strlen( $p_page_title ) ) {
			echo string_display( $t_title );
		} else {
			if ( 0 == strlen( $t_title ) ) {
				echo $p_page_title;
			} else {
				echo $p_page_title . ' - ' . string_display( $t_title );
			}
		}
		echo '</title>', "\n";
	}
 
	# --------------------
	# (5) Print the link to include the css file
	function html_css() {
		$t_css_url = config_get( 'css_include_file' );
		echo "\t", '<link rel="stylesheet" type="text/css" href="', $t_css_url, '" />', "\n";
 
		# fix for NS 4.x css
		echo "\t", '<script type="text/javascript" language="JavaScript"><!--', "\n";
		echo "\t\t", 'if(document.layers) {document.write("<style>td{padding:0px;}<\/style>")}', "\n";
		echo "\t", '// --></script>', "\n";
	}
 
	# --------------------
	# (6) Print an HTML meta tag to redirect to another page
	# This function is optional and may be called by pages that need a redirect.
	# $p_time is the number of seconds to wait before redirecting.
	# If we have handled any errors on this page and the 'stop_on_errors' config
	#  option is turned on, return false and don't redirect.
	function html_meta_redirect( $p_url, $p_time = null, $p_sanitize = false ) {
		if ( ON == config_get( 'stop_on_errors' ) && error_handled() ) {
			return false;
		}
 
		if ( null === $p_time ) {
			$p_time = current_user_get_pref( 'redirect_delay' );
		}
 
		if ( $p_sanitize ) {
			$t_url = string_sanitize_url( $p_url );
		} else {
			$t_url = $p_url;
		}
 
		echo "\t<meta http-equiv=\"Refresh\" content=\"$p_time;URL=$t_url\" />\n";
 
		return true;
	}
 
	# ---------------------
	# (6a) Javascript...
	function html_head_javascript() {
	global $g_path;
		if ( ON == config_get( 'use_javascript' ) ) {
			echo "\t" . '<script type="text/javascript" language="JavaScript" src="' . $g_path . 'javascript/common.js">';
			echo '</script>' . "\n";
			echo "\t" . '<script type="text/JavaScript" src="' . $g_path . 'javascript/ajax.js">';
			echo '</script>' . "\n";
 
			global $g_enable_projax;
 
			if ( $g_enable_projax ) {
				echo '<script type="text/javascript" src="' . $g_path . 'javascript/projax/prototype.js"></script>';
				echo '<script type="text/javascript" src="' . $g_path . 'javascript/projax/scriptaculous.js"></script>';
			}
		}
	}
 
	# --------------------
	# (7) End the <head> section
	function html_head_end() {
		echo '</head>', "\n";
	}
 
	# --------------------
	# (8) Begin the <body> section
	function html_body_begin() {
		echo '<body>', "\n";
	}
 
	# --------------------
	# (9) Print the title displayed at the top of the page
	function html_header() {
		$t_title = config_get( 'page_title' );
		echo '<div class="center"><span class="pagetitle">', string_display( $t_title ), '</span></div>', "\n";
	}
 
	# --------------------
	# (10) Print a user-defined banner at the top of the page if there is one.
	function html_top_banner() {
		global $g_path;
		$t_page = config_get( 'top_include_page' );
 
		if ( !is_blank( $t_page ) && file_exists( $t_page ) && !is_dir( $t_page ) ) {
			include( $t_page );
		} else {
			if ( is_page_name( 'login_page' ) ) {
				$t_align = 'center';
			} else {
				$t_align = 'left';
			}
 
			echo '<div align="', $t_align, '">';
			echo '<a href="http://www.mantisbt.org" title="Free Web Based Bug Tracker"><img border="0" width="242" height="102" alt="Mantis Bugtracker" src="' . $g_path . 'images/mantis_logo.gif" /></a>';
			echo '</div>';
		}
	}
 
	# --------------------
	# (11) Print the user's account information
	# Also print the select box where users can switch projects
	function html_login_info() {
	global $g_path;
		$t_username		= current_user_get_field( 'username' );
		$t_access_level	= get_enum_element( 'access_levels', current_user_get_access_level() );
		$t_now			= date( config_get( 'complete_date_format' ) );
		$t_realname = current_user_get_field( 'realname' );
 
		PRINT '<table class="hide">';
		PRINT '<tr>';
			PRINT '<td class="login-info-left">';
				if ( current_user_is_anonymous() ) {
					$t_return_page = $_SERVER['PHP_SELF'];
					if ( isset( $_SERVER['QUERY_STRING'] ) ) {
						$t_return_page .=  '?' . $_SERVER['QUERY_STRING'];
					}
 
					$t_return_page = string_url(  $t_return_page );
					PRINT lang_get( 'anonymous' ) . ' | <a href="' . $g_path . 'login_page.php?return=' . $t_return_page . '">' . lang_get( 'login_link' ) . '</a>';
					if ( config_get( 'allow_signup' ) == ON ) {
						PRINT ' | <a href="' . $g_path . 'signup_page.php">' . lang_get( 'signup_link' ) . '</a>';
					}
				} else {
					echo lang_get( 'logged_in_as' ), ": <span class=\"italic\">", string_display( $t_username ), "</span> <span class=\"small\">";
					echo is_blank( $t_realname ) ? "($t_access_level)" : "(" . string_display( $t_realname ) . " - $t_access_level)";
					echo "</span>";
				}
			PRINT '</td>';
			PRINT '<td class="login-info-middle">';
				PRINT "<span class=\"italic\">$t_now</span>";
			PRINT '</td>';
			PRINT '<td class="login-info-right">';
				PRINT '<form method="post" name="form_set_project" action="' . $g_path . 'set_project.php">';
 
				echo lang_get( 'email_project' ), ': ';
				if ( ON == config_get( 'show_extended_project_browser' ) ) {
					print_extended_project_browser( helper_get_current_project_trace() );
				} else {
					if ( ON == config_get( 'use_javascript' ) ) {
						PRINT '<select name="project_id" class="small" onchange="document.forms.form_set_project.submit();">';
					} else {
						PRINT '<select name="project_id" class="small">';
					}
					print_project_option_list( join( ';', helper_get_current_project_trace() ), true, null, true );
					PRINT '</select> ';
				}
				PRINT '<input type="submit" class="button-small" value="' . lang_get( 'switch' ) . '" />';
 
				if ( OFF != config_get( 'rss_enabled' ) ) {
					# Link to RSS issues feed for the selected project, including authentication details.
					PRINT '<a href="' . rss_get_issues_feed_url() . '">';
					PRINT '<img src="' . $g_path . 'images/rss.gif" alt="' . lang_get( 'rss' ) . '" style="border-style: none; margin: 5px; vertical-align: middle;" />';
					PRINT '</a>';
				}
 
				PRINT '</form>';
			PRINT '</td>';
		PRINT '</tr>';
		PRINT '</table>';
	}
 
	# --------------------
	# (12) Print a user-defined banner at the bottom of the page if there is one.
	function html_bottom_banner() {
		$t_page = config_get( 'bottom_include_page' );
 
		if ( !is_blank( $t_page ) && file_exists( $t_page ) && !is_dir( $t_page ) ) {
			include( $t_page );
		}
	}
 
	# --------------------
	# (13) Print the page footer information
	function html_footer( $p_file ) {
		global $g_timer, $g_queries_array, $g_request_time;
 
		# If a user is logged in, update their last visit time.
		# We do this at the end of the page so that:
		#  1) we can display the user's last visit time on a page before updating it
		#  2) we don't invalidate the user cache immediately after fetching it
		#  3) don't do this on the password verification or update page, as it causes the
		#    verification comparison to fail
		if ( auth_is_user_authenticated() && !( is_page_name( 'verify.php' ) || is_page_name( 'account_update.php' ) ) ) {
			$t_user_id = auth_get_current_user_id();
			user_update_last_visit( $t_user_id );
		}
 
		echo "\t", '<br />', "\n";
		echo "\t", '<hr size="1" />', "\n";
 
		echo '<table border="0" width="100%" cellspacing="0" cellpadding="0"><tr valign="top"><td>';
		if ( ON == config_get( 'show_version' ) ) {
			echo "\t", '<span class="timer"><a href="http://www.mantisbt.org/" title="Free Web Based Bug Tracker">Mantis ', config_get( 'mantis_version' ), '</a>',
					'[<a href="http://www.mantisbt.org/"  title="Free Web Based Bug Tracker" target="_blank">^</a>]</span>', "\n";
		}
		echo "\t", '<address>Copyright &copy; 2000 - 2007 Mantis Group</address>', "\n";
 
		# only display webmaster email is current user is not the anonymous user
		if ( ! is_page_name( 'login_page.php' ) && !current_user_is_anonymous() ) {
			echo "\t", '<address><a href="mailto:', config_get( 'webmaster_email' ), '">', config_get( 'webmaster_email' ), '</a></address>', "\n";
		}
 
		# print timings
		if ( ON == config_get( 'show_timer' ) ) {
			$g_timer->print_times();
		}
 
		# print db queries that were run
		if ( helper_show_queries() ) {
			$t_count = count( $g_queries_array );
			echo "\t",  $t_count, ' total queries executed.<br />', "\n";
			$t_unique_queries = 0;
			$t_shown_queries = array();
			for ( $i = 0; $i < $t_count; $i++ ) {
				if ( ! in_array( $g_queries_array[$i][0], $t_shown_queries ) ) {
					$t_unique_queries++;
					$g_queries_array[$i][3] = false;
					array_push( $t_shown_queries, $g_queries_array[$i][0] );
				} else {
					$g_queries_array[$i][3] = true;
				}
			}
			echo "\t",  $t_unique_queries . ' unique queries executed.<br />', "\n";
			if ( ON == config_get( 'show_queries_list' ) ) {
				echo "\t",  '<table>', "\n";
				$t_total = 0;
				for ( $i = 0; $i < $t_count; $i++ ) {
					$t_time = $g_queries_array[$i][1];
					$t_caller = $g_queries_array[$i][2];
					$t_total += $t_time;
					$t_style_tag = '';
					if ( true == $g_queries_array[$i][3] ) {
						$t_style_tag = ' style="color: red;"';
					}
					echo "\t",  '<tr valign="top"><td', $t_style_tag, '>', ($i+1), '</td>';
					echo '<td', $t_style_tag, '>', $t_time , '</td>';
					echo '<td', $t_style_tag, '><span style="color: gray;">', $t_caller, '</span><br />', string_html_specialchars($g_queries_array[$i][0]), '</td></tr>', "\n";
				}
 
				# @@@ Note sure if we should localize them given that they are debug info.  Will add if requested by users.
				echo "\t", '<tr><td></td><td>', $t_total, '</td><td>SQL Queries Total Time</td></tr>', "\n";
				echo "\t", '<tr><td></td><td>', round( microtime_float() - $g_request_time, 4 ), '</td><td>Page Request Total Time</td></tr>', "\n";
				echo "\t",  '</table>', "\n";
			}
		}
 
		echo '</td><td><div align="right">';
		echo '<a href="http://www.mantisbt.org" title="Free Web Based Bug Tracker"><img src="' . $g_path . 'images/mantis_logo_button.gif" width="88" height="35" alt="Powered by Mantis Bugtracker" border="0" /></a>';
		echo '</div></td></tr></table>';
	}
 
	# --------------------
	# (14) End the <body> section
	function html_body_end() {
		echo '</body>', "\n";
	}
 
	# --------------------
	# (15) Print the closing <html> tag
	function html_end() {
		echo '</html>', "\n";
	}
 
 
	###########################################################################
	# HTML Menu API
	###########################################################################

	function prepare_custom_menu_options( $p_config ) {
		$t_custom_menu_options = config_get( $p_config );
		$t_options = array();
 
		foreach( $t_custom_menu_options as $t_custom_option ) {
			$t_access_level = $t_custom_option[1];
			if ( access_has_project_level( $t_access_level ) ) {
				$t_caption = lang_get_defaulted( $t_custom_option[0] );
				$t_link = $t_custom_option[2];
				$t_options[] = "<a href=\"$t_link\">$t_caption</a>";
			}
		}
 
		return $t_options;
	}
 
	# --------------------
	# Print the main menu
	function print_menu() {
 
	global $g_path;
 
		if ( auth_is_user_authenticated() ) {
			$t_protected = current_user_get_field( 'protected' );
			$t_current_project = helper_get_current_project();
 
			PRINT '<table class="width100" cellspacing="0">';
			PRINT '<tr>';
			PRINT '<td class="menu">';
				$t_menu_options = array();
 
				# Main Page
				$t_menu_options[] = '<a href="' . $g_path . 'main_page.php">' . lang_get( 'main_link' ) . '</a>';
 
				# My View
				$t_menu_options[] = '<a href="' . $g_path . 'my_view_page.php">' . lang_get( 'my_view_link' ) . '</a>';
 
				# View Bugs
				$t_menu_options[] = '<a href="' . $g_path . 'view_all_bug_page.php">' . lang_get( 'view_bugs_link' ) . '</a>';
 
				# Report Bugs
				if ( access_has_project_level( config_get( 'report_bug_threshold' ) ) ) {
					$t_menu_options[] = string_get_bug_report_link();
				}
 
				# Changelog Page
				if ( access_has_project_level( config_get( 'view_changelog_threshold' ) ) ) {
					$t_menu_options[] = '<a href="' . $g_path . 'changelog_page.php">' . lang_get( 'changelog_link' ) . '</a>';
				}
 
				# Roadmap Page
				if ( access_has_project_level( config_get( 'roadmap_view_threshold' ) ) ) {
					$t_menu_options[] = '<a href="' . $g_path . 'roadmap_page.php">' . lang_get( 'roadmap_link' ) . '</a>';
				}
 
				# Summary Page
				if ( access_has_project_level( config_get( 'view_summary_threshold' ) ) ) {
					$t_menu_options[] = '<a href="' . $g_path . 'summary_page.php">' . lang_get( 'summary_link' ) . '</a>';
				}
 
				# Project Documentation Page
				if( ON == config_get( 'enable_project_documentation' ) ) {
					$t_menu_options[] = '<a href="' . $g_path . 'proj_doc_page.php">' . lang_get( 'docs_link' ) . '</a>';
				}
 
				# Project Wiki
				if ( wiki_is_enabled() ) {
					$t_menu_options[] = '<a href="' . $g_path . 'wiki.php?type=project&amp;id=' . $t_current_project . '">' . lang_get( 'wiki' ) . '</a>';
				}
 
				# Manage Users (admins) or Manage Project (managers) or Manage Custom Fields
				$t_show_access = min( config_get( 'manage_user_threshold' ), config_get( 'manage_project_threshold' ), config_get( 'manage_custom_fields_threshold' ) );
				if ( access_has_global_level( $t_show_access) || access_has_any_project( $t_show_access ) )  {
					$t_current_project = helper_get_current_project();
					if ( access_has_global_level( config_get( 'manage_user_threshold' ) ) ) {
						$t_link = '' . $g_path . 'manage_user_page.php';
					} else {
						if ( access_has_project_level( config_get( 'manage_project_threshold' ), $t_current_project )
								&& ( $t_current_project <> ALL_PROJECTS ) ) {
							$t_link = '' . $g_path . 'manage_proj_edit_page.php?project_id=' . $t_current_project;
						} else {
							$t_link = '' . $g_path . 'manage_proj_page.php';
						}
					}
					$t_menu_options[] = "<a href=\"$t_link\">" . lang_get( 'manage_link' ) . '</a>';
				}
 
				# News Page
				if ( access_has_project_level( config_get( 'manage_news_threshold' ) ) ) {
					# Admin can edit news for All Projects (site-wide)
					if ( ( ALL_PROJECTS != helper_get_current_project() ) || ( access_has_project_level( ADMINISTRATOR ) ) ) {
						$t_menu_options[] = '<a href="' . $g_path . 'news_menu_page.php">' . lang_get( 'edit_news_link' ) . '</a>';
					} else {
						$t_menu_options[] = '<a href="' . $g_path . 'login_select_proj_page.php">' . lang_get( 'edit_news_link' ) . '</a>';
					}
				}
 
				# Account Page (only show accounts that are NOT protected)
				if ( OFF == $t_protected ) {
					$t_menu_options[] = '<a href="' . $g_path . 'account_page.php">' . lang_get( 'account_link' ) . '</a>';
				}
 
				# Add custom options
				$t_custom_options = prepare_custom_menu_options( 'main_menu_custom_options' );
				$t_menu_options = array_merge( $t_menu_options, $t_custom_options );
				if ( config_get('time_tracking_enabled') && config_get('time_tracking_with_billing') )
					$t_menu_options[] = '<a href="' . $g_path . 'billing_page.php">' . lang_get( 'time_tracking_billing_link' ) . '</a>';
 
				# Logout (no if anonymously logged in)
				if ( !current_user_is_anonymous() ) {
					$t_menu_options[] = '<a href="' . $g_path . 'logout_page.php">' . lang_get( 'logout_link' ) . '</a>';
				}
				PRINT implode( $t_menu_options, ' | ' );
			PRINT '</td>';
			PRINT '<td class="menu right nowrap">';
				PRINT '<form method="post" action="' . $g_path . 'jump_to_bug.php">';
 
				if ( ON == config_get( 'use_javascript' ) ) {
					$t_bug_label = lang_get( 'issue_id' );
					PRINT "<input type=\"text\" name=\"bug_id\" size=\"10\" class=\"small\" value=\"$t_bug_label\" onfocus=\"if (this.value == '$t_bug_label') this.value = ''\" onblur=\"if (this.value == '') this.value = '$t_bug_label'\" />&nbsp;";
				} else {
					PRINT "<input type=\"text\" name=\"bug_id\" size=\"10\" class=\"small\" />&nbsp;";
				}
 
				PRINT '<input type="submit" class="button-small" value="' . lang_get( 'jump' ) . '" />&nbsp;';
				PRINT '</form>';
			PRINT '</td>';
			PRINT '</tr>';
			PRINT '</table>';
		}
	}
 
	# --------------------
	# Print the menu bar with a list of projects to which the user has access
	function print_project_menu_bar() {
	global $g_path;
		$t_project_ids = current_user_get_accessible_projects();
 
		PRINT '<table class="width100" cellspacing="0">';
		PRINT '<tr>';
			PRINT '<td class="menu">';
			PRINT '<a href="' . $g_path . 'set_project.php?project_id=' . ALL_PROJECTS . '">' . lang_get( 'all_projects' ) . '</a>';
 
			foreach ( $t_project_ids as $t_id ) {
				PRINT " | <a href=\"" . $g_path . "set_project.php?project_id=$t_id\">" . string_display( project_get_field( $t_id, 'name' ) ) . '</a>';
				print_subproject_menu_bar( $t_id, $t_id . ';' );
			}
 
			PRINT '</td>';
		PRINT '</tr>';
		PRINT '</table>';
	}
 
	# --------------------
	# Print the menu bar with a list of projects to which the user has access
	function print_subproject_menu_bar( $p_project_id, $p_parents = '' ) {
	global $g_path;
		$t_subprojects = current_user_get_accessible_subprojects( $p_project_id );
		$t_char = ':';
		foreach ( $t_subprojects as $t_subproject ) {
			PRINT "$t_char <a href=\"" . $g_path . "set_project.php?project_id=$p_parents$t_subproject\">" . string_display( project_get_field( $t_subproject, 'name' ) ) . '</a>';
			print_subproject_menu_bar( $t_subproject, $p_parents . $t_subproject . ';' );
			$t_char = ',';
		}
	}
 
	# --------------------
	# Print the menu for the graph summary section
	function print_menu_graph() {
	global $g_path;
		if ( config_get( 'use_jpgraph' ) ) {
			$t_icon_path = config_get( 'icon_path' );
 
			PRINT '<br />';
			PRINT '<a href="' . $g_path . 'summary_page.php"><img src="' . $t_icon_path.'synthese.gif" border="0" align="center" />' . lang_get( 'synthesis_link' ) . '</a> | ';
			PRINT '<a href="' . $g_path . 'summary_graph_imp_status.php"><img src="' . $t_icon_path.'synthgraph.gif" border="0" align="center" />' . lang_get( 'status_link' ) . '</a> | ';
			PRINT '<a href="' . $g_path . 'summary_graph_imp_priority.php"><img src="' . $t_icon_path.'synthgraph.gif" border="0" align="center" />' . lang_get( 'priority_link' ) . '</a> | ';
			PRINT '<a href="' . $g_path . 'summary_graph_imp_severity.php"><img src="' . $t_icon_path.'synthgraph.gif" border="0" align="center" />' . lang_get( 'severity_link' ) . '</a> | ';
			PRINT '<a href="' . $g_path . 'summary_graph_imp_category.php"><img src="' . $t_icon_path.'synthgraph.gif" border="0" align="center" />' . lang_get( 'category_link' ) . '</a> | ';
			PRINT '<a href="' . $g_path . 'summary_graph_imp_resolution.php"><img src="' . $t_icon_path.'synthgraph.gif" border="0" align="center" />' . lang_get( 'resolution_link' ) . '</a>';
		}
	}
 
	# --------------------
	# Print the menu for the manage section
	# $p_page specifies the current page name so it's link can be disabled
	function print_manage_menu( $p_page = '' ) {
	global $g_path;
		$t_manage_user_page 		= '' . $g_path . 'manage_user_page.php';
		$t_manage_project_menu_page = '' . $g_path . 'manage_proj_page.php';
		$t_manage_custom_field_page = '' . $g_path . 'manage_custom_field_page.php';
		$t_manage_config_page = '' . $g_path . 'adm_config_report.php';
		$t_manage_prof_menu_page    = '' . $g_path . 'manage_prof_menu_page.php';
		# $t_documentation_page 		= '' . $g_path . 'documentation_page.php';

		switch ( $p_page ) {
			case $t_manage_user_page:
				$t_manage_user_page = '';
				break;
			case $t_manage_project_menu_page:
				$t_manage_project_menu_page = '';
				break;
			case $t_manage_custom_field_page:
				$t_manage_custom_field_page = '';
				break;
			case $t_manage_config_page:
				$t_manage_config_page = '';
				break;
			case $t_manage_prof_menu_page:
				$t_manage_prof_menu_page = '';
				break;
#			case $t_documentation_page:
#				$t_documentation_page = '';
#				break;
		}
 
		PRINT '<br /><div align="center">';
		if ( access_has_global_level( config_get( 'manage_user_threshold' ) ) ) {
			print_bracket_link( $t_manage_user_page, lang_get( 'manage_users_link' ) );
		}
		if ( access_has_project_level( config_get( 'manage_project_threshold' ) ) ) {
			print_bracket_link( $t_manage_project_menu_page, lang_get( 'manage_projects_link' ) );
		}
		if ( access_has_global_level( config_get( 'manage_custom_fields_threshold' ) ) ) {
			print_bracket_link( $t_manage_custom_field_page, lang_get( 'manage_custom_field_link' ) );
		}
		if ( access_has_global_level( config_get( 'manage_global_profile_threshold' ) ) ) {
			print_bracket_link( $t_manage_prof_menu_page, lang_get( 'manage_global_profiles_link' ) );
		}
		if ( access_has_project_level( config_get( 'view_configuration_threshold' ) ) ) {
			print_bracket_link( $t_manage_config_page, lang_get( 'manage_config_link' ) );
		}
			# print_bracket_link( $t_documentation_page, lang_get( 'documentation_link' ) );
		PRINT '</div>';
	}
 
	# --------------------
	# Print the menu for the manage configuration section
	# $p_page specifies the current page name so it's link can be disabled
	function print_manage_config_menu( $p_page = '' ) {
	global $g_path;
		$t_configuration_report = '' . $g_path . 'adm_config_report.php';
		$t_permissions_summary_report = '' . $g_path . 'adm_permissions_report.php';
		$t_manage_work_threshold     = '' . $g_path . 'manage_config_work_threshold_page.php';
		$t_manage_email 		= '' . $g_path . 'manage_config_email_page.php';
		$t_manage_workflow 		= '' . $g_path . 'manage_config_workflow_page.php';
 
		switch ( $p_page ) {
			case $t_configuration_report:
				$t_configuration_report = '';
				break;
			case $t_permissions_summary_report:
				$t_permissions_summary_report = '';
				break;
			case $t_manage_work_threshold:
				$t_manage_work_threshold = '';
				break;
			case $t_manage_email:
				$t_manage_email = '';
				break;
			case $t_manage_workflow:
				$t_manage_workflow = '';
				break;
		}
 
		PRINT '<br /><div align="center">';
		if ( access_has_project_level( config_get( 'view_configuration_threshold' ) ) ) {
			print_bracket_link( $t_configuration_report, lang_get_defaulted( 'configuration_report' ) );
			print_bracket_link( $t_permissions_summary_report, lang_get( 'permissions_summary_report' ) );
			print_bracket_link( $t_manage_work_threshold, lang_get( 'manage_threshold_config' ) );
			print_bracket_link( $t_manage_workflow, lang_get( 'manage_workflow_config' ) );
			print_bracket_link( $t_manage_email, lang_get( 'manage_email_config' ) );
		}
		PRINT '</div>';
	}
 
	# --------------------
	# Print the menu for the account section
	# $p_page specifies the current page name so it's link can be disabled
	function print_account_menu( $p_page='' ) {
	global $g_path;
		$t_account_page 				= '' . $g_path . 'account_page.php';
		$t_account_prefs_page 			= '' . $g_path . 'account_prefs_page.php';
		$t_account_profile_menu_page 	= '' . $g_path . 'account_prof_menu_page.php';
		$t_account_sponsor_page			= '' . $g_path . 'account_sponsor_page.php';
 
		switch ( $p_page ) {
			case $t_account_page				: $t_account_page 				= ''; break;
			case $t_account_prefs_page			: $t_account_prefs_page 		= ''; break;
			case $t_account_profile_menu_page	: $t_account_profile_menu_page 	= ''; break;
			case $t_account_sponsor_page		: $t_account_sponsor_page		= ''; break;
		}
 
		print_bracket_link( $t_account_page, lang_get( 'account_link' ) );
		print_bracket_link( $t_account_prefs_page, lang_get( 'change_preferences_link' ) );
		if ( access_has_project_level( config_get( 'add_profile_threshold' ) ) ) {
			print_bracket_link( $t_account_profile_menu_page, lang_get( 'manage_profiles_link' ) );
		}
		if ( ( config_get( 'enable_sponsorship' ) == ON ) &&
			 ( access_has_project_level( config_get( 'view_sponsorship_total_threshold' ) ) ) &&
			 !current_user_is_anonymous() ) {
			print_bracket_link( $t_account_sponsor_page, lang_get( 'my_sponsorship' ) );
		}
	}
 
	# --------------------
	# Print the menu for the docs section
	# $p_page specifies the current page name so it's link can be disabled
	function print_doc_menu( $p_page='' ) {
	global $g_path;
		$t_documentation_html 	= config_get( 'manual_url' );
		$t_proj_doc_page 		= '' . $g_path . 'proj_doc_page.php';
		$t_proj_doc_add_page 	= '' . $g_path . 'proj_doc_add_page.php';
 
		switch ( $p_page ) {
			case $t_documentation_html	: $t_documentation_html	= ''; break;
			case $t_proj_doc_page		: $t_proj_doc_page		= ''; break;
			case $t_proj_doc_add_page	: $t_proj_doc_add_page	= ''; break;
		}
 
		print_bracket_link( $t_documentation_html, lang_get( 'user_documentation' ) );
		print_bracket_link( $t_proj_doc_page, lang_get( 'project_documentation' ) );
		if ( file_allow_project_upload() ) {
			print_bracket_link( $t_proj_doc_add_page, lang_get( 'add_file' ) );
		}
	}
 
	# --------------------
	# Print the menu for the summary section
	# $p_page specifies the current page name so it's link can be disabled
	function print_summary_menu( $p_page='' ) {
	global $g_path;
		PRINT '<div align="center">';
		print_bracket_link( '' . $g_path . 'print_all_bug_page.php', lang_get( 'print_all_bug_page_link' ) );
 
		if ( config_get( 'use_jpgraph' ) != 0 ) {
			$t_summary_page 		= '' . $g_path . 'summary_page.php';
			$t_summary_jpgraph_page = '' . $g_path . 'summary_jpgraph_page.php';
 
			switch ( $p_page ) {
				case $t_summary_page		: $t_summary_page			= ''; break;
				case $t_summary_jpgraph_page: $t_summary_jpgraph_page	= ''; break;
			}
 
			print_bracket_link( $t_summary_page, lang_get( 'summary_link' ) );
			print_bracket_link( $t_summary_jpgraph_page, lang_get( 'summary_jpgraph_link' ) );
		}
		PRINT '</div>';
	}
 
 
	#=========================
	# Candidates for moving to print_api
	#=========================

	# --------------------
	# Print the color legend for the status colors
	function html_status_legend() {
		PRINT '<br />';
		PRINT '<table class="width100" cellspacing="1">';
		PRINT '<tr>';
 
		$t_arr		= explode_enum_string( config_get( 'status_enum_string' ) );
		$enum_count	= count( $t_arr );
		$width		= (int)(100 / $enum_count);
		for ( $i=0; $i < $enum_count; $i++) {
			$t_s = explode_enum_arr( $t_arr[$i] );
			$t_val = get_enum_element( 'status', $t_s[0] );
			$t_color = get_status_color( $t_s[0] );
 
			PRINT "<td class=\"small-caption\" width=\"$width%\" bgcolor=\"$t_color\">$t_val</td>";
		}
 
		PRINT '</tr>';
		PRINT '</table>';
		if ( ON == config_get( 'status_percentage_legend' ) ) {
			html_status_percentage_legend();
		}
	}
 
 	# --------------------
	# Print the legend for the status percentage
	function html_status_percentage_legend() {
 
		$t_mantis_bug_table = config_get( 'mantis_bug_table' );
		$t_project_id = helper_get_current_project();
		$t_user_id = auth_get_current_user_id();
 
		#checking if it's a per project statistic or all projects
		$t_specific_where = helper_project_specific_where( $t_project_id, $t_user_id );
 
		$query = "SELECT status, COUNT(*) AS number
				FROM $t_mantis_bug_table
				WHERE $t_specific_where
				GROUP BY status";
		$result = db_query( $query );
 
		$t_bug_count = 0;
		$t_status_count_array = array();
 
		while ( $row = db_fetch_array( $result ) ) {
 
			$t_status_count_array[ $row['status'] ] = $row['number'];
			$t_bug_count += $row['number'];
		}
 
		$t_arr		= explode_enum_string( config_get( 'status_enum_string' ) );
		$enum_count	= count( $t_arr );
 
		if ( $t_bug_count > 0 ) {
			echo '<br />';
			echo '<table class="width100" cellspacing="1">';
			echo '<tr>';
			echo '<td class="form-title" colspan="'.$enum_count.'">'.lang_get( 'issue_status_percentage' ).'</td>';
			echo '</tr>';
			echo '<tr>';
 
			for ( $i=0; $i < $enum_count; $i++) {
				$t_s = explode_enum_arr( $t_arr[$i] );
				$t_color = get_status_color( $t_s[0] );
				$t_status = $t_s[0];
 
				if ( !isset( $t_status_count_array[ $t_status ] ) ) {
					$t_status_count_array[ $t_status ] = 0;
				}
 
				$width = round( ( $t_status_count_array[ $t_status ] / $t_bug_count ) * 100 );
 
				if ($width > 0) {
					echo "<td class=\"small-caption-center\" width=\"$width%\" bgcolor=\"$t_color\">$width%</td>";
				}
			}
 
			echo '</tr>';
			echo '</table>';
		}
	}
 
	# --------------------
	# Print an html button inside a form
	function html_button ( $p_action, $p_button_text, $p_fields = null, $p_method = 'post' ) {
		$p_action		= urlencode( $p_action );
		$p_button_text	= string_attribute( $p_button_text );
		if ( null === $p_fields ) {
			$p_fields = array();
		}
 
		if ( strtolower( $p_method ) == 'get' ) {
			$t_method = 'get';
		} else {
			$t_method = 'post';
		}
 
		PRINT "<form method=\"$t_method\" action=\"$p_action\">\n";
 
		foreach ( $p_fields as $key => $val ) {
			$key = string_attribute( $key );
			$val = string_attribute( $val );
 
			PRINT "	<input type=\"hidden\" name=\"$key\" value=\"$val\" />\n";
		}
 
		PRINT "	<input type=\"submit\" class=\"button\" value=\"$p_button_text\" />\n";
		PRINT "</form>\n";
	}
 
	# --------------------
	# Print a button to update the given bug
	function html_button_bug_update( $p_bug_id ) {
		if ( access_has_bug_level( config_get( 'update_bug_threshold' ), $p_bug_id ) ) {
			html_button( string_get_bug_update_page(),
						 lang_get( 'update_bug_button' ),
						 array( 'bug_id' => $p_bug_id ) );
		}
	}
 
	# --------------------
	# Print Change Status to: button
	#  This code is similar to print_status_option_list except
	#   there is no masking, except for the current state
	function html_button_bug_change_status( $p_bug_id ) {
	global $g_path;
		$t_bug_project_id = bug_get_field( $p_bug_id, 'project_id' );
		$t_bug_current_state = bug_get_field( $p_bug_id, 'status' );
		$t_current_access = access_get_project_level( $t_bug_project_id );
 
		$t_enum_list = get_status_option_list( $t_current_access, $t_bug_current_state, false,
				( bug_get_field( $p_bug_id, 'reporter_id' ) == auth_get_current_user_id() && ( ON == config_get( 'allow_reporter_close' ) ) ) );
 
		if ( count( $t_enum_list ) > 0 ) {
			# resort the list into ascending order after noting the key from the first element (the default)
			$t_default_arr = each( $t_enum_list );
			$t_default = $t_default_arr['key'];
			ksort( $t_enum_list );
			reset( $t_enum_list );
 
			echo "<form method=\"post\" action=\"" . $g_path . "bug_change_status_page.php\">";
 
			$t_button_text = lang_get( 'bug_status_to_button' );
			echo "<input type=\"submit\" class=\"button\" value=\"$t_button_text\" />";
 
			echo " <select name=\"new_status\">"; # space at beginning of line is important
			foreach ( $t_enum_list as $key => $val ) {
				echo "<option value=\"$key\" ";
				check_selected( $key, $t_default );
				echo ">$val</option>";
			}
			echo '</select>';
 
			$t_bug_id = string_attribute( $p_bug_id );
			echo "<input type=\"hidden\" name=\"bug_id\" value=\"$t_bug_id\" />\n";
 
			echo "</form>\n";
		}
	}
 
	# --------------------
	# Print Assign To: combo box of possible handlers
	function html_button_bug_assign_to( $p_bug_id ) {
	global $g_path;
		# make sure status is allowed of assign would cause auto-set-status
		$t_status = bug_get_field( $p_bug_id, 'status' );     # workflow implementation

		if ( ON == config_get( 'auto_set_status_to_assigned' ) &&
			!bug_check_workflow( $t_status, config_get( 'bug_assigned_status' ) ) ) {  # workflow
			return;
		}
 
		# make sure current user has access to modify bugs.
		if ( !access_has_bug_level( config_get( 'update_bug_assign_threshold', config_get( 'update_bug_threshold' ) ), $p_bug_id ) ) {
			return;
		}
 
		$t_reporter_id = bug_get_field( $p_bug_id, 'reporter_id' );
		$t_handler_id = bug_get_field( $p_bug_id, 'handler_id' );
		$t_current_user_id = auth_get_current_user_id();
		$t_new_status = ( ON == config_get( 'auto_set_status_to_assigned' ) ) ? config_get( 'bug_assigned_status' ) : $t_status;
 
		$t_options = array();
		$t_default_assign_to = null;
 
		if ( ( $t_handler_id != $t_current_user_id ) &&
			( access_has_bug_level( config_get( 'handle_bug_threshold' ), $p_bug_id, $t_current_user_id ) ) ) {
		    $t_options[] = array( $t_current_user_id, '[' . lang_get( 'myself' ) . ']' );
			$t_default_assign_to = $t_current_user_id;
		}
 
		if ( ( $t_handler_id != $t_reporter_id ) && user_exists( $t_reporter_id ) &&
			( access_has_bug_level( config_get( 'handle_bug_threshold' ), $p_bug_id, $t_reporter_id ) ) ) {
		    $t_options[] = array( $t_reporter_id, '[' . lang_get( 'reporter' ) . ']' );
 
			if ( $t_default_assign_to === null ) {
				$t_default_assign_to = $t_reporter_id;
			}
		}
 
		PRINT "<form method=\"post\" action=\"" . $g_path . "bug_assign.php\">";
 
		$t_button_text = lang_get( 'bug_assign_to_button' );
		PRINT "<input type=\"submit\" class=\"button\" value=\"$t_button_text\" />";
 
		PRINT " <select name=\"handler_id\">"; # space at beginning of line is important

		$t_already_selected = false;
 
		foreach ( $t_options as $t_entry ) {
			$t_id = string_attribute( $t_entry[0] );
			$t_caption = string_attribute( $t_entry[1] );
 
			# if current user and reporter can't be selected, then select the first
			# user in the list.
			if ( $t_default_assign_to === null ) {
			    $t_default_assign_to = $t_id;
			}
 
		    PRINT "<option value=\"$t_id\" ";
 
			if ( ( $t_id == $t_default_assign_to ) && !$t_already_selected ) {
				check_selected( $t_id, $t_default_assign_to );
			    $t_already_selected = true;
			}
 
			PRINT ">$t_caption</option>";
		}
 
		# allow un-assigning if already assigned.
		if ( $t_handler_id != 0 ) {
			PRINT "<option value=\"0\"></option>";
		}
 
		$t_project_id = bug_get_field( $p_bug_id, 'project_id' );
		# 0 means currently selected
		print_assign_to_option_list( 0, $t_project_id );
		PRINT "</select>";
 
		$t_bug_id = string_attribute( $p_bug_id );
		PRINT "<input type=\"hidden\" name=\"bug_id\" value=\"$t_bug_id\" />\n";
 
		PRINT "</form>\n";
	}
 
	# --------------------
	# Print a button to move the given bug to a different project
	function html_button_bug_move( $p_bug_id ) {
	global $g_path;
		$t_status = bug_get_field( $p_bug_id, 'status' );
 
		if ( access_has_bug_level( config_get( 'move_bug_threshold' ), $p_bug_id ) ) {
			html_button( '' . $g_path . 'bug_actiongroup_page.php',
						 lang_get( 'move_bug_button' ),
						 array( 'bug_arr[]' => $p_bug_id, 'action' => 'MOVE' ) );
		}
	}
 
	# --------------------
	# Print a button to move the given bug to a different project
	function html_button_bug_create_child( $p_bug_id ) {
		if ( ON == config_get( 'enable_relationship' ) ) {
			if ( access_has_bug_level( config_get( 'update_bug_threshold' ), $p_bug_id ) ) {
				html_button( string_get_bug_report_url(),
							 lang_get( 'create_child_bug_button' ),
							 array( 'm_id' => $p_bug_id ) );
			}
		}
	}
 
	# --------------------
	# Print a button to reopen the given bug
	function html_button_bug_reopen( $p_bug_id ) {
	global $g_path;
		$t_status = bug_get_field( $p_bug_id, 'status' );
		$t_reopen_status = config_get( 'bug_reopen_status' );
		$t_project = bug_get_field( $p_bug_id, 'project_id' );
 
		if ( access_has_bug_level( config_get( 'reopen_bug_threshold' ), $p_bug_id ) ||
				( ( bug_get_field( $p_bug_id, 'reporter_id' ) == auth_get_current_user_id() ) &&
	 		  	( ON == config_get( 'allow_reporter_reopen' ) )
				)
			 ) {
			html_button( '' . $g_path . 'bug_change_status_page.php',
						 lang_get( 'reopen_bug_button' ),
						 array( 'bug_id' => $p_bug_id ,
						 				'new_status' => $t_reopen_status,
						 				'reopen_flag' => ON ) );
		}
	}
 
	# --------------------
	# Print a button to monitor the given bug
	function html_button_bug_monitor( $p_bug_id ) {
	global $g_path;
		if ( access_has_bug_level( config_get( 'monitor_bug_threshold' ), $p_bug_id ) ) {
			html_button( '' . $g_path . 'bug_monitor.php',
						 lang_get( 'monitor_bug_button' ),
						 array( 'bug_id' => $p_bug_id, 'action' => 'add' ) );
		}
	}
 
	# --------------------
	# Print a button to unmonitor the given bug
	#  no reason to ever disallow someone from unmonitoring a bug
	function html_button_bug_unmonitor( $p_bug_id ) {
	global $g_path;
		html_button( '' . $g_path . 'bug_monitor.php',
					 lang_get( 'unmonitor_bug_button' ),
					 array( 'bug_id' => $p_bug_id, 'action' => 'delete' ) );
	}
 
	# --------------------
	# Print a button to delete the given bug
	function html_button_bug_delete( $p_bug_id ) {
	global $g_path;
		if ( access_has_bug_level( config_get( 'delete_bug_threshold' ), $p_bug_id ) ) {
			html_button( '' . $g_path . 'bug_actiongroup_page.php',
						 lang_get( 'delete_bug_button' ),
						 array( 'bug_arr[]' => $p_bug_id, 'action' => 'DELETE' ) );
		}
	}
 
	# --------------------
	# Print a button to create a wiki page
	function html_button_wiki( $p_bug_id ) {
	global $g_path;
		if ( ON == config_get( 'wiki_enable' ) ) {
			if ( access_has_bug_level( config_get( 'update_bug_threshold' ), $p_bug_id ) ) {
				html_button( '' . $g_path . 'wiki.php',
							 lang_get_defaulted( 'Wiki' ),
							 array( 'id' => $p_bug_id, 'type' => 'issue' ),
							 'get' );
			}
		}
	}
 
	# --------------------
	# Print all buttons for view bug pages
	function html_buttons_view_bug_page( $p_bug_id ) {
		$t_resolved = config_get( 'bug_resolved_status_threshold' );
		$t_status = bug_get_field( $p_bug_id, 'status' );
		$t_readonly = bug_is_readonly( $p_bug_id );
 
		PRINT '<table><tr class="vcenter">';
		if ( !$t_readonly ) {
			# UPDATE button
			echo '<td class="center">';
			html_button_bug_update( $p_bug_id );
			echo '</td>';
 
			# ASSIGN button
			echo '<td class="center">';
			html_button_bug_assign_to( $p_bug_id );
			echo '</td>';
		}
 
		# Change State button
		echo '<td class="center">';
		html_button_bug_change_status( $p_bug_id );
		echo '</td>';
 
		# MONITOR/UNMONITOR button
		echo '<td class="center">';
		if ( !current_user_is_anonymous() ) {
			if ( user_is_monitoring_bug( auth_get_current_user_id(), $p_bug_id ) ) {
				html_button_bug_unmonitor( $p_bug_id );
			} else {
				html_button_bug_monitor( $p_bug_id );
			}
		}
		echo '</td>';
 
		if ( !$t_readonly ) {
			# CREATE CHILD button
			echo '<td class="center">';
			html_button_bug_create_child( $p_bug_id );
			echo '</td>';
		}
 
		if ( $t_resolved <= $t_status ) { # resolved is not the same as readonly
			PRINT '<td class="center">';
			# REOPEN button
			html_button_bug_reopen( $p_bug_id );
			PRINT '</td>';
		}
 
		if ( !$t_readonly ) {
			# MOVE button
			echo '<td class="center">';
			html_button_bug_move( $p_bug_id );
			echo '</td>';
		}
 
		# DELETE button
		echo '<td class="center">';
		html_button_bug_delete( $p_bug_id );
		echo '</td>';
 
		helper_call_custom_function( 'print_bug_view_page_custom_buttons', array( $p_bug_id ) );
 
		echo '</tr></table>';
	}
 
	/**
	 * Print the Update Tag button
	 * @param integer Tag ID
	 */
	function html_button_tag_update( $p_tag_id ) {
	global $g_path;
		if ( access_has_global_level( config_get( 'tag_edit_threshold' ) ) 
			|| ( auth_get_current_user_id() == tag_get_field( $p_tag_id, 'user_id' )
				&& access_has_global_level( config_get( 'tag_edit_own_threshold' ) ) ) )
		{
			html_button( '' . $g_path . 'tag_update_page.php', lang_get( 'tag_update_button' ), array( 'tag_id' => $p_tag_id ) );
		}
	}
 
	/**
	 * Print the Delete Tag button
	 * @param integer Tag ID
	 */
	function html_button_tag_delete( $p_tag_id ) {
	global $g_path;
		if ( access_has_global_level( config_get( 'tag_edit_threshold' ) ) ) {
			html_button( '' . $g_path . 'tag_delete.php', lang_get( 'tag_delete_button' ), array( 'tag_id' => $p_tag_id ) );
		}
	}
 
	/**
	 * Print all buttons for the Tag View page
	 * @param integer Tag ID
	 */
	function html_buttons_tag_view_page( $p_tag_id ) {
		html_button_tag_update( $p_tag_id );
		html_button_tag_delete( $p_tag_id );
	}
?>

core/string_api.php

In this file only two functions are changed, so it is overkill to place the whole file here. Replace the functions with the following

	# --------------------
	# return an href anchor that links to a bug UPDATE page for the given bug
	#  account for the user preference and site override
	function string_get_bug_update_link( $p_bug_id, $p_user_id = null ) {
	global $g_path;
		$t_summary = string_attribute( bug_get_field( $p_bug_id, 'summary' ) );
		return '<a href="' . $g_path . '' . string_get_bug_update_url( $p_bug_id, $p_user_id ) . '" title="' . $t_summary . '">' . bug_format_id( $p_bug_id ) . '</a>';
	}
	# --------------------
	# return an href anchor that links to a bug REPORT page for the given bug
	#  account for the user preference and site override
	function string_get_bug_report_link( $p_user_id = null ) {
	global $g_path;
		return '<a href="' . $g_path . '' . string_get_bug_report_url( $p_user_id ) . '">' . lang_get( 'report_bug_link' ) . '</a>';
	}

Configuration

Dokuwiki

Log in as administrator, go to the admin panel and configure the mantis syntax plugin and the authentication backend. :!: You have to config the filepath and URL to your mantis in the syntax plugin before you activate the mantis authentication backend! :!:

Mantis

Include this in your mantis configuration. Adjust the paths so it match your install.

#####################
# Wiki Integration
#####################

# Wiki Integration Enabled?
$g_wiki_enable = ON;
 
# Wiki Engine
$g_wiki_engine = 'dokuwiki';
 
# Wiki namespace to be used as root for all pages relating to this mantis installation.
$g_wiki_root_namespace = 'mantis';
 
# URL under which the wiki engine is hosted.  Must be on the same server.
$g_wiki_engine_url = $t_protocol . '://' . $t_host . '/dokuwiki/';
 
# CSS file
$g_css_include_file = '/mantis/css/default.css';
mantisbt/issue/8253.txt · Last modified: 2010/05/04 09:08 by rattkin