<?php
/**
 * This file implements the Admin UI class.
 * Admin skins should derive from this class and override {@link get_template()}
 * for example.
 *
 * This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}.
 * See also {@link https://github.com/b2evolution/b2evolution}.
 *
 * @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
 *
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}.
 * Parts of this file are copyright (c)2005 by Daniel HAHLER - {@link http://thequod.de/contact}.
 *
 * @package admin
 *
 * @todo dh> Refactor to allow easier contributions!
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );


load_class( '_core/ui/_menu.class.php', 'Menu' );


/**
 * The general Admin UI class. It provides functions to handle the UI part of the
 * Backoffice.
 *
 * Admin skins should derive from this class and override {@link get_template()}
 * for example.
 *
 * @package admin
 * @todo CODE DOCUMENTATION!!!
 */
class AdminUI_general extends Menu
{
	/**
	 * Visual path seperator (used in html title, ..)
	 *
	 * @var string
	 */
	var $pathSeparator = ' &rsaquo; ';

	/*-------------------------------------------------------------------*/
	/*- The members below should not get overridden in a derived class. -*/

	/**
	 * The path of the current selected menu entry.
	 * Array of strings.
	 * The top level entry is at position 0. Selected submenu entries follow.
	 *
	 * Use {@link get_path()} or {@link get_path_range()} to access it.
	 * Use {@link set_path()}, {@link append_path_level()} or {@link set_path_level()} to set it.
	 *
	 * @access protected
	 * @var array
	 */
	var $path = array();

	/**
	 * The properties of the path entries.
	 * Numbered Array of arrays.
	 * The top level entry is at position 0. Selected submenu entries follow.
	 *
	 * Use {@link get_prop_for_path()} or {@link get_properties_for_path()} to access it
	 * Use {@link set_path()}, {@link append_path_level()} or {@link set_path_level()} to set it.
	 *
	 * @access protected
	 * @var array
	 */
	var $pathProps = array();

	/**
	 * The explicit title for the page.
	 * @var string
	 */
	var $title;

	/**
	 * The explicit title for the titlearea (<h1>).
	 * @var string
	 */
	var $title_titlearea;
	var $title_titlearea_appendix = '';

	/**
	 * Collection List buttons: title for 'all' button
	 * @var string
	 */
	var $coll_list_all_title = NULL;
	/**
	 * Collection List buttons: url for 'all' button
	 * @var string
	 */
	var $coll_list_all_url = '';
	/**
	 * Collection List buttons: permission name to test on to decide wether or not to display buttons
	 * @var string
	 */
	var $coll_list_permname = NULL;
	/**
	 * Collection List buttons: minimum level required to display button
	 * @var mixed
	 */
	var $coll_list_permlevel = 1;
	/**
	 * Collection List buttons: params of the url used for the buttons
	 * @var array
	 */
	var $coll_list_url_params = array();
	/**
	 * Collection List buttons: javascript to execute on click of a button
	 * @var string
	 */
	var $coll_list_onclick = NULL;
	/**
	 * Collection List buttons: display a link to add new collection
	 * @var boolean
	 */
	var $coll_list_disp_add = true;
	/**
	 * Collection List buttons: display a list of sections
	 * @var boolean
	 */
	var $coll_list_disp_sections = false;


	/**
	 * Bread crumb path
	 *
	 * Note: These are not real breadcrumbs. It's just "so to speak" for a hierarchical path.
	 */
	var $breadcrumbpath = array();


	/**
	 * Page bread crumb path
	 */
	var $display_breadcrumbpath = array();


	/**
	 * Titles of bread crumb paths
	 *
	 * Used to build a html <title> tag
	 */
	var $breadcrumb_titles = array();

	/**
	 * Titles of bread crumb paths
	 *
	 * Used to build a html <title> tag
	 */
	var $display_breadcrumb_titles = array();

	/**
	 * Manual link for entire pages, used to get a big scope describing functionalities.
	 *
	 */
	var $page_manual_link = '';

	/**
	 * Constructor.
	 */
	function __construct()
	{
		global $mode; // TODO: make it a real property
		global $baseurl;

		$this->mode = $mode;

		$this->init_templates();
	}


	/**
	 * This function should init the templates - like adding Javascript through the {@link add_headline()} method.
	 */
	function init_templates()
	{
		global $Hit, $check_browser_version;

		require_js_defer( '#jquery#', 'rsc_url' );
		require_js_defer( 'customized:jquery/raty/jquery.raty.min.js', 'rsc_url' );

		// Load general JS file:
		require_js_defer( 'build/evo_backoffice.bmin.js', 'rsc_url' );

		if( $check_browser_version && $Hit->get_browser_version() > 0 && $Hit->is_IE( 9, '<' ) )
		{	// Display info message if browser IE < 9 version and it is allowed by config var:
			global $Messages, $debug;
			$Messages->add( T_('Your web browser is too old. For this site to work correctly, we recommend you use a more recent browser.'), 'note' );
			if( $debug )
			{
				$Messages->add( 'User Agent: '.$Hit->get_user_agent(), 'note' );
			}
		}
	}

	/**
	* Note: These are not real breadcrumbs. It's just "so to speak" for a hierarchical path.
	*
	* @param boolean Add blog path
	* @param array Additional path: @see breadcrumbpath_add()
	* 		'text'
	* 		'url'
	* 		'help'  = NULL
	* 		'attrs' = ''
	*/
	function breadcrumbpath_init( $add_blog = true, $additional_path = array() )
	{
		global $Collection, $Blog, $Settings, $admin_url;

		// Path to site root
		$site_style = $Settings->get( 'site_color' ) != '' ? 'style="color:'.$Settings->get( 'site_color' ).'"' : '';
		$this->breadcrumbpath_add( $Settings->get( 'site_code' ), $admin_url.'?ctrl=dashboard', NULL, $site_style );

		if( !empty( $additional_path ) )
		{ // Additional path
			$path = array_merge( array(
					'text'  => '',
					'url'   => '',
					'help'  => NULL,
					'attrs' => '',
				), $additional_path );
			$this->breadcrumbpath_add( $path['text'], $path['url'], $path['help'], $path['attrs'] );
			$blog_url = $path['url'];
		}

		if( $add_blog && isset( $Blog ) )
		{ // Add path to Blog
			$this->breadcrumbpath_add( $Blog->dget('shortname'), !empty( $blog_url ) ? $blog_url : $admin_url.'?ctrl=coll_settings&amp;tab=dashboard&amp;blog=$blog$' );
		}

		// Initialize the default manual link, this is always visible when explicit manual link is not set for a page
		$this->page_manual_link = get_manual_link( '', NULL, T_('View manual'), 5 );
	}

	function display_breadcrumbpath_init()
	{
		$this->display_breadcrumbpath = array();
	}

	/**
	* Note: These are not real breadcrumbs. It's just "so to speak" for a hierarchical path.
	*
	* @param string Text
	* @param string Url
	* @param string Title for help
	* @param string Additional attributes for link tag
	*/
	function breadcrumbpath_add( $text, $url, $help = NULL, $attrs = '' )
	{
		global $Collection, $Blog;

		$blog_ID = isset($Blog) ? $Blog->ID : 0;
		$url = str_replace( '$blog$', $blog_ID, $url );

		$html = $text;
		if( check_user_perm( 'admin', 'normal' ) )
		{
			$html = '<a href="'.$url.'"'.( !empty( $attrs ) ? ' '.$attrs : '' ).'>'.$text.'</a>';
		}

		if( !empty($help) )
		{
			$html .= ' <abbr title="'.$help.'">?</abbr>';
		}

		$this->breadcrumbpath[] = $html;
		$this->breadcrumb_titles[] = strip_tags( $text );
	}

	/**
	* Note: These are not real breadcrumbs. It's just "so to speak" for a hierarchical path.
	*
	* @param string Text
	* @param string Url
	* @param string Title for help
	* @param string Additional attributes for link tag
	*/
	function display_breadcrumbpath_add( $text, $url = NULL, $help = NULL, $attrs = '' )
	{
		global $Collection, $Blog;

		$blog_ID = isset($Blog) ? $Blog->ID : 0;
		if( ! empty( $url ) )
		{
			$url = str_replace( '$blog$', $blog_ID, $url );

			$html = $text;
			if( check_user_perm( 'admin', 'normal' ) )
			{
				$html = '<a href="'.$url.'"'.( !empty( $attrs ) ? ' '.$attrs : '' ).'>'.$text.'</a>';
			}
		}
		else
		{
			$html = $text;
		}

		if( ! empty( $help ) )
		{
			$html .= ' <abbr title="'.$help.'">?</abbr>';
		}

		$this->display_breadcrumbpath[] = $html;
		$this->display_breadcrumb_titles[] = strip_tags( $text );
	}

	/**
	 * Adds a manual link to the entire page right after the breadcrumb
	 *
	 * @param string Topic to the manual page
	 */
	function set_page_manual_link( $topic )
	{
		$this->page_manual_slug = $topic;
		$this->page_manual_link = get_manual_link( $topic, NULL, T_('Manual page'), 5 );
	}

	/**
	 * Get breadcrumb path in html format
	 *
	 * @param array Params
	 * @return string Breadcrumb path links
	 */
	function breadcrumbpath_get_html( $params = array() )
	{
		$params = array_merge( array(
				'before'     => '<div class="breadcrumbpath floatleft">',
				'after'      => '</div><div class="breadcrumbpath floatright">'.$this->page_manual_link.'</div><div class="clear"></div>'."\n",
				'beforeText' => '&bull; <strong>'.T_('You are here').':</strong> ',
				'beforeEach' => '',
				'afterEach'  => '',
				'beforeSel'  => '<strong>',
				'afterSel'   => '</strong>',
				'separator'  => ' &gt; ',
			), $params );

		$r = '';

		if( $count = count( $this->breadcrumbpath ) )
		{
			$r = $params['before'].$params['beforeText'];

			for( $i=0; $i<$count-1; $i++ )
			{
				$r .= $params['beforeEach']
						.$this->breadcrumbpath[$i]
						.$params['separator']
					.$params['afterEach'];
			}

			$r .= $params['beforeSel'].$this->breadcrumbpath[$i].$params['afterSel'];

			$r .= $params['after'];
		}

		return $r;
	}

	/**
	 * Get breadcrumb path in html format
	 *
	 * @param array Params
	 * @return string Breadcrumb path links
	 */
	function display_breadcrumbpath_get_html( $params = array() )
	{
		$params = array_merge( array(
				'before'     => '<div class="col-md-12"><nav aria-label="breadcrumb"><ol class="breadcrumb">',
				'after'      => '</ol></nav></div>',
				'beforeText' => '',
				'beforeEach' => '<li class="breadcrumb-item">',
				'afterEach'  => '</li>',
				'beforeSel'  => '<li class="breadcrumb-item active">',
				'afterSel'   => '</li>',
				'separator'  => '',
			), $params );

		$r = '';

		if( $count = count( $this->display_breadcrumbpath ) )
		{
			$r = $params['before'].$params['beforeText'];

			for( $i=0; $i<$count-1; $i++ )
			{
				$r .= $params['beforeEach']
						.$this->display_breadcrumbpath[$i]
						.$params['separator']
					.$params['afterEach'];
			}

			$r .= $params['beforeSel'].$this->display_breadcrumbpath[$i].$params['afterSel'];

			$r .= $params['after'];
		}

		return $r;
	}

	/**
	 * Add menu entries to the beginning of the list for given path.
	 *
	 * @param NULL|string|array The path to add the entries to.
	 * @param array Menu entries to add (key (string) => entry (array)).
	 * @uses add_menu_entries()
	 */
	function unshift_menu_entries( $path, $entries )
	{
		// Get a reference to the node in the menu list.
		$node = & $this->get_node_by_path( $path, true );

		$node['entries'] = array_reverse( $node['entries'] );

		$this->add_menu_entries( $path, $entries );

		$node['entries'] = array_reverse( $node['entries'] );
	}


	/**
	 * Get the <title> of the page.
	 *
	 * This is either {@link $title} or will be constructed from title/text properties
	 * of the path entries.
	 *
	 * @param boolean If true, the fallback will be in reversed order
	 * @return string
	 */
	function get_title( $reversedDefault = false )
	{
		if( isset( $this->title ) )
		{	// Explicit title has been set:
			return $this->title;
		}
		else
		{	// Fallback: implode title/text properties of the path
			/*$titles = $this->get_properties_for_path( $this->path, array( 'title', 'text' ) );
			if( $reversedDefault )
			{ // We have asked for reverse order of the path elements:
				$titles = array_reverse($titles);
			}*/
			$titles = $this->breadcrumb_titles;
			if( count( $titles ) > 1 )
			{	// Remove 'Dashboard' text from the title
				array_shift( $titles );
			}
			return implode( ' &gt; ', $titles );
		}
	}


	/**
	 * Get the title for the titlearea (<h1>).
	 *
	 * This is the current path in the site structure
	 *
	 * @return string
	 */
	function get_title_for_titlearea()
	{
		if( ! isset( $this->title_titlearea ) )
		{ // Construct path:
			$titles = array();
			foreach( $this->path as $i => $lPath )
			{
				if( false !== ($title_text = $this->get_prop_for_path( $i, array( 'title', 'text' ) )) )
				{
					$titles[] = '<a href="'.$this->get_prop_for_path( $i, array( 'href' ) ).'">'.$title_text.'</a>';
				}
			}

			$this->title_titlearea = implode( $this->pathSeparator, $titles );
		}

		return $this->title_titlearea.$this->title_titlearea_appendix;
	}


	/**
	 * Append a string at the end of the existing titlearea.
	 *
	 * We actually keep the appended stuff separate from the main title, because the main title
	 * might in some occasions not be known immediately.
	 *
	 * @param string What to append to the titlearea
	 */
	function append_to_titlearea( $string )
	{
		$this->title_titlearea_appendix .= $this->pathSeparator.$string;
	}


	/**
	 * Get the title for HTML <title> tag.
	 *
	 * If no explicit title has been specified, auto construct one from path.
	 *
	 * @return string
	 */
	function get_html_title()
	{
		global $app_shortname;

		if( ! empty( $this->htmltitle ) )
		{	// Get html title which is specified for current page:
			$r = $this->htmltitle;
		}
		elseif( $htmltitle = $this->get_prop_for_node( $this->path, array( 'htmltitle' ) ) )
		{	// Explicit htmltitle set:
			$r = $htmltitle;
		}
		else
		{	// No explicit title set, construct Title from path
			$r = $this->get_title();
		}
		$r .= ' &ndash; '.$app_shortname;

		return $r;
	}


	/**
	 * Get a list of properties for a given path for a set of property names to check.
	 * The result is a list of properties for each node down the path.
	 *
	 * The property names must be given in $prop_by_ref, ordered by preference.
	 *
	 * @param string|array The path. See {@link get_node_by_path()}.
	 * @param array Alternative names of the property to receive (ordered by priority).
	 * @return array List of the properties.
	 */
	function get_properties_for_path( $path, $prop_by_pref )
	{
		if( !is_array($path) )
		{
			$path = array( $path );
		}
		$r = array();

		$prevPath = array();
		foreach( $path as $i => $lPath )
		{
			if( false !== ($prop = $this->get_prop_for_path( $i, $prop_by_pref )) )
			{
				$r[] = $prop;
			}

			$prevPath[] = $lPath;
		}

		return $r;
	}


	/**
	 * Get a property of a node, given by path.
	 *
	 * @param string|array The path. See {@link get_node_by_path()}.
	 * @param array Alternative names of the property to receive (ordered by priority).
	 * @return mixed|false False if property is not set for the node, otherwise its value.
	 */
	function get_prop_for_node( $path, $prop_by_pref )
	{
		$node = & $this->get_node_by_path( $path );

		foreach( $prop_by_pref as $lProp )
		{
			if( isset($node[$lProp]) )
			{
				return $node[$lProp];
			}
		}

		return false;
	}


	/**
	 * Get a property for a specific path entry.
	 *
	 * @param int The numeric index of the path entry to query (0 is first).
	 * @param array A list of properties to check, ordered by priority.
	 * @return mixed|false The first found property or false if it does not exist
	 */
	function get_prop_for_path( $depth, $prop_by_pref )
	{
		if( $pathWithProps = $this->get_path( $depth, true ) )
		{
			foreach( $prop_by_pref as $lProp )
			{
				if( isset($pathWithProps['props'][$lProp]) )
				{
					// echo "<br>path depth $depth property $lProp = ".$pathWithProps['props'][$lProp];
					return $pathWithProps['props'][$lProp];
				}
			}
		}

		return false;
	}


	/**
	 * Display doctype + <head>...</head> section
	 */
	function disp_html_head()
	{
		if( is_ajax_content() )
		{ // Don't display this content on AJAX request
			return;
		}

		global $adminskins_path;

		if( isset( $this->skin_name ) && file_exists( $adminskins_path.$this->skin_name.'/_html_header.inc.php' ) )
		{ // Get header of the skin
			require $adminskins_path.$this->skin_name.'/_html_header.inc.php';
		}
		else
		{ // Get general header
			require $adminskins_path.'_html_header.inc.php';
		}
	}


	/**
	 * Dsiplay the top of the HTML <body>...
	 *
	 * Typically includes title, menu, messages, etc.
	 *
	 * @param boolean Whether or not to display messages.
	 */
	function disp_body_top( $display_messages = true )
	{
		if( is_ajax_content() )
		{	// Don't display this content on AJAX request
			return;
		}

		load_funcs('skins/_skin.funcs.php');

		global $mode;

		/**
		 * @var Hit
		 */
		global $Hit;

		// #body_win and .body_firefox (for example) can be used to customize CSS per plaform/browser
		echo '<body id="body_'.$Hit->get_agent_platform().'" class="body_'.$Hit->get_agent_name().'">'."\n";

		if( ! empty( $mode ) )
		{
			global $Messages;

			$mode = preg_replace( '~[^a-z]~', '', $mode );	// sanitize
			echo '<div id="'.$mode.'_wrapper">';

			if( $display_messages )
			{ // Display info & error messages
				$Messages->display();
				// Clear the messages to avoid double displaying
				$Messages->clear();
			}
			return;
		}

		$skin_wrapper_class = 'skin_wrapper';
		if( show_toolbar() )
		{ // user is logged in
			if( $this->get_show_evobar() )
			{ // show evobar options is enabled for this admin skin
				require skin_fallback_path( '_toolbar.inc.php' );
				$skin_wrapper_class = $skin_wrapper_class.'_loggedin';
			}
		}
		else
		{ // user is not logged in
			require skin_fallback_path( '_toolbar.inc.php' );
			$skin_wrapper_class = $skin_wrapper_class.'_anonymous';
		}

		echo "\n";
		echo '<div id="skin_wrapper" class="'.$skin_wrapper_class.'">';

		echo "\n<!-- Start of skin_wrapper -->\n";

		echo $this->get_body_top();
	}


	/**
	 * Display body bottom, debug info and close </html>
	 */
	function disp_global_footer()
	{
		if( is_ajax_content() )
		{	// Don't display this content on AJAX request
			return;
		}

		global $adminskins_path, $mode;

		if( isset( $this->skin_name ) && file_exists( $adminskins_path.$this->skin_name.'/_html_footer.inc.php' ) )
		{ // Get footer of the skin
			require $adminskins_path.$this->skin_name.'/_html_footer.inc.php';
		}
		else
		{ // Get general footer
			require $adminskins_path.'_html_footer.inc.php';
		}
	}


	/**
	 * Display the start of a payload block
	 *
	 * Note: it is possible to display several payload blocks on a single page.
	 *       The first block uses the "sub" template, the others "block".
	 *
	 * @see disp_payload_end()
	 */
	function disp_payload_begin( $params = array() )
	{
		if( is_ajax_content() )
		{	// Don't display this content on AJAX request
			return;
		}

		global $mode;
		if( $mode == 'customizer' )
		{	// Don't display this content on skin customizer mode:
			return;
		}

		$params = array_merge( array(
				'display_menu2' => true,
				'display_menu3' => true,
				'display_breadcrumb' => true
			), $params );

		global $Plugins;

		if( empty($this->displayed_sub_begin) )
		{	// We haven't displayed sub menus yet (tabs):
			$Plugins->trigger_event( 'AdminBeginPayload' );

			// Display submenu (this also opens a div class="panelblock" or class="panelblocktabs")

			//echo ' disp_submenu-BEGIN ';
			$path0 = $this->get_path(0);
			$r = $this->get_html_menu( $path0, 'sub', 0, ! $params['display_menu2'] );

			//echo ' disp_submenu-END ';

			// Show breadcrumbs
			if( $params['display_breadcrumb'] )
			{
				echo $this->display_breadcrumbpath_get_html();
			}

			// Show 3rd level menu for settings tab
			$path1 = $this->get_path(1);
			$r .= $this->get_html_menu( array($path0, $path1), 'menu3', 0, ! $params['display_menu3'] );

			echo $this->replace_vars( $r );

			$this->displayed_sub_begin = 1;
		}
		else
		{
			$template = $this->get_template( 'block' );

			echo $template['begin'];
		}
	}


	/**
	 * Display the end of a payload block
	 *
	 * Note: it is possible to display several payload blocks on a single page.
	 *       The first block uses the "sub" template, the others "block".
	 * @see disp_payload_begin()
	 */
	function disp_payload_end()
	{
		if( is_ajax_content() )
		{	// Don't display this content on AJAX request
			return;
		}

		if( empty($this->displayed_sub_end) )
		{
			$name = 'sub';
			$this->displayed_sub_end = 1;
		}
		else
		{
			$name = 'block';
		}

		$template = $this->get_template( $name );

		echo $template['end'];
	}


	/**
	 * Display a view
	 *
	 * Note: doing the require inside of a function has the side effect of forcing the view
	 * to declare any global object it wants to use. This can be a little tedious but on the
	 * other hand it has the advantage of clearly showing what objects are used and makes it
	 * easier to audit the views in order to determine if they include more business logic
	 * than they ought to.
	 *
	 * @param string
	 * @param array params to be used in the view (optional)
	 */
	function disp_view( $view_name, $params = array() )
	{
		global $inc_path;

		// THESE ARE THE GLOBALS WE WANT TO MAKE AVAILABLE TO ALL VIEWS:
		global $action;
		global $ctrl;

		global $DB;	// Note: not sure it's agood idea to let the views hit on the db...

		global $current_User;


		require $inc_path.$view_name;
	}


	/**
	 * Set params for blog list.
	 *
	 * @param string name of required permission needed to display the blog in the list
	 * @param string level of required permission needed to display the blog in the list
	 * @param string Url format string for elements, with %d for blog number.
	 * @param string Title for "all" button
	 * @param string URL for "all" button
	 * @param string onclick attribute format string, with %s for blog number. (NOTE: %s so that we can use this.value when selected through list)
	 * @param boolean TRUE to display a link to add new collection
	 * @param boolean TRUE to display a list of sections
	 */
	function set_coll_list_params( $permname = 'blog_ismember', $permlevel = 1, $url_params = array(),
							$all_title = NULL, $all_url = '', $onclick = NULL, $display_add_link = true, $display_sections = false )
	{
		$this->coll_list_all_title = $all_title;
		$this->coll_list_all_url = $all_url;
		$this->coll_list_permname = $permname;
		$this->coll_list_permlevel = $permlevel;
		$this->coll_list_url_params = $url_params;
		$this->coll_list_onclick = $onclick;
		$this->coll_list_disp_add = $display_add_link;
		$this->coll_list_disp_sections = $display_sections;
	}


	/**
	 * Returns list of buttons for available Collections (aka Blogs) to work on.
	 *
	 * @return string HTML
	 */
	function get_bloglist_buttons( $title = '' )
	{
		global $blog, $pagenow;

		$max_buttons = 7;

		if( empty( $this->coll_list_permname ) )
		{	// We have not requested a list of blogs to be displayed
			return;
		}

		// Prepare url params:
		$url_params = '?';
		$form_params = '';
		foreach( $this->coll_list_url_params as $name => $value )
		{
			$url_params .= $name.'='.$value.'&amp;';
			$form_params .= '<input type="hidden" name="'.format_to_output( $name, 'htmlattr' ).'" value="'.format_to_output( $value, 'formvalue' ).'" />';
		}

		$template = $this->get_template( 'CollectionList' );

		$BlogCache = & get_BlogCache();

		$blog_array = $BlogCache->load_user_blogs( $this->coll_list_permname, $this->coll_list_permlevel );


		$buttons = '';
		$select_options = '';
		$not_favorite_blogs = false;
		foreach( $blog_array as $l_blog_ID )
		{ // Loop through all blogs that match the requested permission:

			$l_Blog = & $BlogCache->get_by_ID( $l_blog_ID );

			if( $l_Blog->favorite() || $l_blog_ID == $blog )
			{ // If blog is favorute OR current blog, Add blog as a button:
				$buttons .= $template[ $l_blog_ID == $blog ? 'beforeEachSel' : 'beforeEach' ];

				$buttons .= '<a href="'.format_to_output( $url_params.'blog='.$l_blog_ID, 'htmlattr' )
							.'" class="'.( $l_blog_ID == $blog ? 'CurrentBlog' : 'OtherBlog' ).'"';

				if( !is_null($this->coll_list_onclick) )
				{	// We want to include an onclick attribute:
					$buttons .= ' onclick="'.sprintf( $this->coll_list_onclick, $l_blog_ID ).'"';
				}

				$buttons .= '>'.$l_Blog->dget( 'shortname', 'htmlbody' ).'</a> ';

				if( $l_blog_ID == $blog )
				{
					$buttons .= $template['afterEachSel'];
				}
				else
				{
					$buttons .= $template['afterEach'];
				}
			}

			if( !$l_Blog->favorite() )
			{ // If blog is not favorute, Add it into the select list:
				$not_favorite_blogs = true;
				$select_options .= '<option value="'.$l_blog_ID.'"';
				if( $l_blog_ID == $blog )
				{
					$select_options .= ' selected="selected"';
				}
				$select_options .= '>'.$l_Blog->dget( 'shortname', 'htmlbody' ).'</option>';
			}
		}

		$r = $template['before'];

		$r .= $title;

		if( !empty( $this->coll_list_all_title ) )
		{ // We want to add an "all" button
			$r .= $template[ $blog == 0 ? 'beforeEachSel' : 'beforeEach' ];
			$r .= '<a href="'.format_to_output( $this->coll_list_all_url, 'htmlattr' )
						.'" class="'.( $blog == 0 ? 'CurrentBlog' : 'OtherBlog' ).'">'
						.format_to_output( $this->coll_list_all_title, 'htmlbody' ).'</a> ';
			$r .= $template[ $blog == 0 ? 'afterEachSel' : 'afterEach' ];
		}

		$r .= $template['buttons_start'];
		$r .= $buttons;
		$r .= $template['buttons_end'];


		$r .= $template['select_start'];
		if( $not_favorite_blogs )
		{ // Display select list with not favorite blogs
			$r .= '<form action="'.$pagenow.'" method="get">';
			$r .= $form_params;
			$r .= '<select name="blog" onchange="';
			if( empty( $this->coll_list_onclick ) )
			{ // Just submit...
				$r .= 'if(this.value>0) this.form.submit();';
			}
			else
			{
				$r .= sprintf( $this->coll_list_onclick, 'this.value' );
			}
			$r .= '">'
				.'<option value="0">'.T_('Select blog').'</option>'
				.$select_options.'</select>';
			$r .= '<noscript><input type="submit" value="Go" /></noscript></form>';
		}
		$r .= $template['select_end'];


		$r .= $template['after'];

		return $r;
	}


	/**
	 * Get a template by name.
	 *
	 * This is a method (and not a member array) to allow dynamic generation and T_()
	 *
	 * @param string Name of the template ('main', 'sub')
	 * @param integer Nesting level (start at 0)
	 * @param boolean TRUE to die on unknown template name
	 * @return array Associative array which defines layout and optionally properties.
	 */
	function get_template( $name, $level = 0, $die_on_unknown = false )
	{
		switch( $name )
		{
			case 'main':
				return array(
					'before' => '<div id="mainmenu"><ul>',
					'after' => '</ul></div>',
					'beforeEach' => '<li>',
					'afterEach' => '</li>',
					'beforeEachSel' => '<li class="current">',
					'afterEachSel' => '</li>',
					'beforeEachSelWithSub' => '<li class="parent">',
					'afterEachSelWithSub' => '</li>',
					'_props' => array(
						/**
						 * 'recurse'       => 'yes', // To display the submenus recursively
						 * 'recurse_level' => 2,     // Limit recursion
						 * @todo Move to new skin (recurse for subentries if an entry is selected)
						'recurseSelected' => true,
						*/
					),
				);
				break;


			case 'sub':
				// a payload block with embedded submenu
				return array(
						'before' => '<div class="pt">'
							."\n".'<ul class="hack">'
							."\n<li><!-- Yes, this empty UL is needed! It's a DOUBLE hack for correct CSS display --></li>"
							// TODO: this hack MAY NOT be needed when not using pixels instead of decimal ems or exs in the CSS
							."\n</ul>"
							."\n".'<div class="panelblocktabs">$top_block$'
							."\n".'<ul class="tabs">',
						'after' => "</ul>\n"
							.'<span style="float:right">$global_icons$</span>'
							."</div>\n</div>"
							."\n".'<div class="tabbedpanelblock">',
						'empty' => '<div class="panelblock"><span style="float:right">$global_icons$</span>',
						'beforeEach' => '<li>',
						'afterEach'  => '</li>',
						'beforeEachSel' => '<li class="current">',
						'afterEachSel' => '</li>',
						'end' => '</div>', // used to end payload block that opened submenu
					);


			case 'menu3':
				// level 3 submenu:
				return array(
							'before' => '<div class="menu3">',
							'after' => '</div>',
							'empty' => '',
							'beforeEach' => '<span class="option3">',
							'afterEach'  => '</span>',
							'beforeEachSel' => '<span class="current">',
							'afterEachSel' => '</span>',
						);


			case 'block':
				// an additional payload block, anywhere after the one with the submenu. Used by disp_payload_begin()/disp_payload_end()
				return array(
						'begin' => '<div class="panelblock">',
						'end' => "\n</div>",
					);


			case 'CollectionList':
				// Template for a list of Collections (Blogs)
				return array(
						'before' => '<div id="coll_list"><ul>',
						'after' => '</ul></div>',
						'buttons_start' => '',
						'buttons_end' => '',
						'beforeEach' => '<li>',
						'afterEach' => '</li>',
						'beforeEachSel' => '<li class="current">',
						'afterEachSel' => '</li>',
						'select_start' => '<li class="collection_select">',
						'select_end' => '</li>',
					);


			case 'Results':
				// Results list:
				return array(
					'page_url' => '', // All generated links will refer to the current page
					'before' => '<div class="results">',
					'content_start' => '<div id="$prefix$ajax_content">',
					'header_start' => '<div class="results_nav">',
						'header_text' => '<strong>'.T_('Pages').'</strong>: $prev$ $first$ $list_prev$ $list$ $list_next$ $last$ $next$',
						'header_text_single' => '',
					'header_end' => '</div>',
					'head_title' => '<div class="table_title"><span style="float:right">$global_icons$</span>$title$</div>'."\n",
					'filters_start' => '<div class="filters">',
					'filters_end' => '</div>',
					'messages_start' => '<div class="messages">',
					'messages_end' => '</div>',
					'messages_separator' => '<br />',
					'list_start' => '<div class="table_scroll">'."\n"
					               .'<table class="grouped $list_class$" cellspacing="0" $list_attrib$>'."\n",
						'head_start' => "<thead>\n",
							'line_start_head' => '<tr>',  // TODO: fusionner avec colhead_start_first; mettre a jour admin_UI_general; utiliser colspan="$headspan$"
							'colhead_start' => '<th $class_attrib$>',
							'colhead_start_first' => '<th class="firstcol $class$">',
							'colhead_start_last' => '<th class="lastcol $class$">',
							'colhead_end' => "</th>\n",
							'sort_asc_off' => get_icon( 'sort_asc_off' ),
							'sort_asc_on' => get_icon( 'sort_asc_on' ),
							'sort_desc_off' => get_icon( 'sort_desc_off' ),
							'sort_desc_on' => get_icon( 'sort_desc_on' ),
							'basic_sort_off' => '',
							'basic_sort_asc' => get_icon( 'ascending' ),
							'basic_sort_desc' => get_icon( 'descending' ),
						'head_end' => "</thead>\n\n",
						'tfoot_start' => "<tfoot>\n",
						'tfoot_end' => "</tfoot>\n\n",
						'body_start' => "<tbody>\n",
							'line_start' => '<tr class="even">'."\n",
							'line_start_odd' => '<tr class="odd">'."\n",
							'line_start_last' => '<tr class="even lastline">'."\n",
							'line_start_odd_last' => '<tr class="odd lastline">'."\n",
								'col_start' => '<td $class_attrib$ $colspan_attrib$>',
								'col_start_first' => '<td class="firstcol $class$" $colspan_attrib$>',
								'col_start_last' => '<td class="lastcol $class$" $colspan_attrib$>',
								'col_end' => "</td>\n",
							'line_end' => "</tr>\n\n",
							'grp_line_start' => '<tr class="group">'."\n",
							'grp_line_start_odd' => '<tr class="odd">'."\n",
							'grp_line_start_last' => '<tr class="lastline">'."\n",
							'grp_line_start_odd_last' => '<tr class="odd lastline">'."\n",
										'grp_col_start' => '<td $class_attrib$ $colspan_attrib$>',
										'grp_col_start_first' => '<td class="firstcol $class$" $colspan_attrib$>',
										'grp_col_start_last' => '<td class="lastcol $class$" $colspan_attrib$>',
								'grp_col_end' => "</td>\n",
							'grp_line_end' => "</tr>\n\n",
						'body_end' => "</tbody>\n\n",
						'total_line_start' => '<tr class="total">'."\n",
							'total_col_start' => '<td $class_attrib$>',
							'total_col_start_first' => '<td class="firstcol $class$">',
							'total_col_start_last' => '<td class="lastcol $class$">',
							'total_col_end' => "</td>\n",
						'total_line_end' => "</tr>\n\n",
					'list_end' => "</table></div>\n\n",
					'footer_start' => '<div class="results_nav nav_footer">',
					'footer_text' => '<strong>'.T_('Pages').'</strong>: $prev$ $first$ $list_prev$ $list$ $list_next$ $last$ $next$<br />$page_size$'
					                  /* T_('Page $scroll_list$ out of $total_pages$   $prev$ | $next$<br />'. */
					                  /* '<strong>$total_pages$ Pages</strong> : $prev$ $list$ $next$' */
					                  /* .' <br />$first$  $list_prev$  $list$  $list_next$  $last$ :: $prev$ | $next$') */,
					'footer_text_single' => '$page_size$',
					'footer_text_no_limit' => '', // Text if theres no LIMIT and therefor only one page anyway
						'prev_text' => T_('Previous'),
						'next_text' => T_('Next'),
						'no_prev_text' => '',
						'no_next_text' => '',
						'list_prev_text' => '...',
						'list_next_text' => '...',
						'list_span' => 11,
						'scroll_list_range' => 5,
					'footer_end' => "</div>\n\n",
					'no_results_start' => '<table class="grouped" cellspacing="0">'."\n",
					'no_results_end'   => '<tr class="lastline"><td class="firstcol lastcol">$no_results$</td></tr>'
								                .'</table>'."\n\n",
				'content_end' => '</div>',
				'after' => '</div>',
				'sort_type' => 'basic'
				);

			case 'compact_results':
				// Results list:
				return array(
					'page_url' => '', // All generated links will refer to the current page
					'before' => '<div class="compact_results">',
					'header_start' => '',
						'header_text' => '',
						'header_text_single' => '',
					'header_end' => '',
					'list_start' => '<table class="grouped" cellspacing="0">'."\n\n",
						'head_start' => "<thead>\n",
							'head_title' => '',
							'filters_start' => '<tr class="filters"><td colspan="$nb_cols$">',
							'filters_end' => '</td></tr>',
							'line_start_head' => '<tr>',  // TODO: fusionner avec colhead_start_first; mettre a jour admin_UI_general; utiliser colspan="$headspan$"
							'colhead_start' => '<th $class_attrib$ $title_attrib$>',
							'colhead_start_first' => '<th class="firstcol $class$" $title_attrib$>',
							'colhead_start_last' => '<th class="lastcol $class$" $title_attrib$>',
							'colhead_end' => "</th>\n",
							'sort_asc_off' => get_icon( 'sort_asc_off' ),
							'sort_asc_on' => get_icon( 'sort_asc_on' ),
							'sort_desc_off' => get_icon( 'sort_desc_off' ),
							'sort_desc_on' => get_icon( 'sort_desc_on' ),
							'basic_sort_off' => '',
							'basic_sort_asc' => get_icon( 'ascending' ),
							'basic_sort_desc' => get_icon( 'descending' ),
						'head_end' => "</thead>\n\n",
						'tfoot_start' => "<tfoot>\n",
						'tfoot_end' => "</tfoot>\n\n",
						'body_start' => "<tbody>\n",
							'line_start' => '<tr class="even">'."\n",
							'line_start_odd' => '<tr class="odd">'."\n",
							'line_start_last' => '<tr class="even lastline">'."\n",
							'line_start_odd_last' => '<tr class="odd lastline">'."\n",
								'col_start' => '<td $class_attrib$ $colspan_attrib$>',
								'col_start_first' => '<td class="firstcol $class$" $colspan_attrib$>',
								'col_start_last' => '<td class="lastcol $class$" $colspan_attrib$>',
								'col_end' => "</td>\n",
							'line_end' => "</tr>\n\n",
							'grp_line_start' => '<tr class="group">'."\n",
							'grp_line_start_odd' => '<tr class="odd">'."\n",
							'grp_line_start_last' => '<tr class="lastline">'."\n",
							'grp_line_start_odd_last' => '<tr class="odd lastline">'."\n",
										'grp_col_start' => '<td $class_attrib$ $colspan_attrib$>',
										'grp_col_start_first' => '<td class="firstcol $class$" $colspan_attrib$>',
										'grp_col_start_last' => '<td class="lastcol $class$" $colspan_attrib$>',
								'grp_col_end' => "</td>\n",
							'grp_line_end' => "</tr>\n\n",
						'body_end' => "</tbody>\n\n",
						'total_line_start' => '<tr class="total">'."\n",
							'total_col_start' => '<td $class_attrib$>',
							'total_col_start_first' => '<td class="firstcol $class$">',
							'total_col_start_last' => '<td class="lastcol $class$">',
							'total_col_end' => "</td>\n",
						'total_line_end' => "</tr>\n\n",
					'list_end' => "</table>\n\n",
					'footer_start' => '<div class="results_nav">',
					'footer_text' => '<strong>'.T_('Pages').'</strong>: $prev$ $first$ $list_prev$ $list$ $list_next$ $last$ $next$'
					                  /* T_('Page $scroll_list$ out of $total_pages$   $prev$ | $next$<br />'. */
					                  /* '<strong>$total_pages$ Pages</strong> : $prev$ $list$ $next$' */
					                  /* .' <br />$first$  $list_prev$  $list$  $list_next$  $last$ :: $prev$ | $next$') */,
					'footer_text_single' => '',
					'footer_text_no_limit' => '', // Text if theres no LIMIT and therefor only one page anyway
						'prev_text' => T_('Previous'),
						'next_text' => T_('Next'),
						'no_prev_text' => '',
						'no_next_text' => '',
						'list_prev_text' => '...',
						'list_next_text' => '...',
						'list_span' => 11,
						'scroll_list_range' => 5,
					'footer_end' => "</div>\n\n",
					'no_results_start' => '<table class="grouped" cellspacing="0"><tbody>'."\n\n",
					'no_results_end'   => '<tr class="lastline noresults"><td class="firstcol lastcol">$no_results$</td></tr>'
								                .'</tbody></table>'."\n\n",
				'after' => '</div>',
				'sort_type' => 'basic'
				);


			case 'blockspan_form':
				// blockspan Form settings:
				return array(
					'layout' => 'blockspan',		// Temporary dirty hack
					'formstart' => '',
					'title_fmt' => '$title$'."\n", // TODO: icons
					'no_title_fmt' => '',          //           "
					'fieldset_begin' => '<fieldset $fieldset_attribs$>'."\n"
															.'<legend $title_attribs$>$fieldset_title$</legend>'."\n",
					'fieldset_end' => '</fieldset>'."\n",
					'fieldstart' => '<span class="block" $ID$>',
					'labelclass' => '',
					'labelstart' => '',
					'labelend' => "\n",
					'labelempty' => '',
					'inputstart' => '',
					'infostart' => '',
					'infoend' => '',
					'inputend' => "\n",
					'fieldend' => '</span>'.get_icon( 'pixel' )."\n",
					'buttonsstart' => '',
					'buttonsend' => "\n",
					'customstart' => '',
					'customend' => "\n",
					'note_format' => ' <span class="notes">%s</span>',
					'bottom_note_format' => ' <div><span class="notes">%s</span></div>',
					'formend' => '',
				);

			case 'accordion_form':
			case 'compact_form':
			case 'Form':
				// Default Form settings:
				return array(
					'layout' => 'fieldset',
					'formstart' => '',
					'title_fmt' => '<span style="float:right">$global_icons$</span><h2>$title$</h2>'."\n",
					'no_title_fmt' => '<span style="float:right">$global_icons$</span>'."\n",
					'fieldset_begin' => '<div class="fieldset_wrapper $class$" id="fieldset_wrapper_$id$"><fieldset $fieldset_attribs$>'."\n"
															.'<legend $title_attribs$>$fieldset_title$</legend><div class="fieldset">'."\n",
					'fieldset_end' => '</div></fieldset></div>'."\n",
					'fieldstart' => '<fieldset $ID$>'."\n",
					'labelclass' => '',
					'labelstart' => '<div class="label">',
					'labelend' => "</div>\n",
					'labelempty' => '<div class="label"></div>', // so that IE6 aligns DIV.input correcctly
					'inputstart' => '<div class="input">',
					'infostart' => '<div class="info">',
					'infoend' => "</div>\n",
					'inputend' => "</div>\n",
					'fieldend' => "</fieldset>\n\n",
					'buttonsstart' => '<fieldset><div class="input">',
					'buttonsend' => "</div></fieldset>\n\n",
					'customstart' => '<div class="custom_content">',
					'customend' => "</div>\n",
					'note_format' => ' <span class="notes">%s</span>',
					'bottom_note_format' => ' <div><span class="notes">%s</span></div>',
					'formend' => '',
				);

			case 'file_browser':
				return array(
					'block_start' => '<div class="block_item"><h3><span style="float:right">$global_icons$</span>$title$</h3>',
					'block_end' => '</div>',
				);

			case 'block_item':
			case 'dash_item':
				return array(
					'block_start' => '<div class="block_item evo_content_block"><h3><span style="float:right">$global_icons$</span>$title$</h3>',
					'block_end' => '</div>',
				);

			case 'side_item':
				return array(
					'block_start' => '<div class="browse_side_item"><h3><span style="float:right">$global_icons$</span>$title$</h3>',
					'block_end' => '</div>',
				);

			case 'user_navigation':
				// The Prev/Next links of users, @see user_prevnext_links()
				return array();

			case 'button_classes':
				// Button classes, @see button_class()
				return array();

			case 'table_browse':
				// A browse table for items and comments
				return array(
					'table_start'     => '<table class="browse" cellspacing="0" cellpadding="0" border="0"><tr>',
					'full_col_start'  => '<td class="browse_left_col">',
					'left_col_start'  => '<td class="browse_left_col">',
					'left_col_end'    => '</td>',
					'right_col_start' => '<td class="browse_right_col">',
					'right_col_end'   => '</td>',
					'table_end'       => '</tr></table>',
				);

			case 'tooltip_plugin':
				// Plugin name for tooltips: 'bubbletip' or 'popover'
				return 'bubbletip';

			case 'autocomplete_plugin':
				// Plugin name to autocomplete the fields: 'hintbox', 'typeahead'
				return 'hintbox';

			case 'modal_window_js_func':
				// JavaScript function to initialize Modal windows, @see echo_user_ajaxwindow_js()
				return false; // Use standard functions

			case 'pagination':
				// Pagination, @see echo_comment_pages()
				return array();

			case 'blog_base.css':
				// File name of blog_base.css that are used on several back-office pages
				return 'blog_base.css';
				break;

			case 'colorbox_css_file':
				// CSS file of colorbox, @see require_js_helper( 'colorbox' )
				return 'colorbox-regular.min.css';

			default:
				if( $die_on_unknown )
				{ // Die because template name is unknown
					debug_die( 'Unknown $name for AdminUI::get_template(): '.var_export( $name, true ) );
				}
				else
				{ // Return NULL, if we want to know when template is not defined by current skin
					return NULL;
				}
		}
	}


	/**
	 * Get a path key by numeric key. Starts with 0.
	 *
	 * @param integer The numeric index of the path (0 is first).
	 * @param boolean Also return properties?
	 * @return string|array|false (depends on $withProps)
	 */
	function get_path( $which, $withProps = false )
	{
		if( $which === 'last' )
		{
			$which = count($this->path)-1;
		}
		if( !isset($this->path[$which]) )
		{
			return false;
		}

		if( $withProps )
		{
			return array(
				'path' => $this->path[$which],
				'props' => isset( $this->pathProps[$which] ) ? $this->pathProps[$which] : array(),
			);
		}

		return $this->path[$which];
	}


	/**
	 * Get the list of path keys in a given range.
	 *
	 * @param integer start index
	 * @param integer|NULL end index (NULL means same as start index)
	 * @return array List of path keys.
	 */
	function get_path_range( $start, $end = NULL )
	{
		if( is_null($end) )
		{
			$end = $start;
		}

		$r = array();
		for( $i = $start; $i <= $end; $i++ )
		{
			$r[] = isset($this->path[$i]) ? $this->path[$i] : NULL;
		}

		return $r;
	}


	/**
	 * Set a specific path level (specific depth).
	 *
	 * First level is 0, then the first subpath/submenu is level 1, etc.
	 *
	 * E.g., if plugins.php gets called, there could be a call to
	 * $AdminUI->set_path_level( 0, 'plugins' ), which selects this entry from the menu.
	 * If a specific tab is called inside of plugins.php, there could be a call to
	 * $AdminUI->set_path_level( 1, $tab )
	 *
	 * Though, it is recommended to call the wrapper functions:
	 *  - {@link append_path_level()}
	 *  - {@link set_path()}
	 *
	 * This also marks the parent node as selected and checks for permissions.
	 *
	 * @param integer Path level to set (starts at 0)
	 * @param array Either the key of the path or an array(keyname, propsArray).
	 * @param array Properties for this path entry.
	 * @return boolean DEPRECATED True if perm granted, false if not (and we're not exiting).
	 */
	function set_path_level( $level, $pathKey, $pathProps = array() )
	{
		// Get the parent node (the level above this one):
		if( $level == 0 )
		{ // first level in menu-path: parent node is NULL
			$parentNode = & $this->get_node_by_path( NULL );
		}
		else
		{ // parent node is the trunk from root to previous level
			$parentNode = & $this->get_node_by_path( $this->get_path_range( 0, $level-1 ) );
		}
		if( ! $parentNode )
		{ // parent node does not exist:
			return false;
		}

		// Store the selected entry name in the parent node:
		$parentNode['selected'] = $pathKey;

		$this->path[$level] = $pathKey;
		$this->pathProps[$level] = $pathProps;

		// pre_dump( 'set_path_level: ', $level, $pathKey, $this->pathProps[$level] );

		return true;
	}


	/**
	 * Append a selected menu entry to the current path of selected entries.
	 *
	 * @param string|array Either the key of the path or an array(keyname, propsArray).
	 */
	function append_path_level( $path, $pathProps = array() )
	{
		$search_path = $this->path;
		$search_path[] = $path;
		// auto-detect path props from menu entries
		if( $node = & $this->get_node_by_path( $search_path ) )
		{
			$pathProps = array_merge( $node, $pathProps );
		}

		// Set the path level right after the last existing one:
		return $this->set_path_level( count($this->path), $path, $pathProps );
	}


	/**
	 * Set the full selected path.
	 *
	 * For example, this selects the tab/submenu 'plugins' in the main menu 'options':
	 * <code>
	 * set_path( 'options', 'plugins' );
	 * </code>
	 *
	 * Use {@link append_path_level()} to append a single path element.
	 *
	 * This is an easy stub for {@link set_path_level()}.
	 *
	 * @param string|array,... VARIABLE NUMBER OF ARGUMENTS. Each is either the key of a path entry or an array(keyname, propsArray).
	 */
	function set_path(        )
	{
		$args = func_get_args();

		$i = 0;
		$prevPath = array();  // Remember the path we have walked through

		// Loop though all path levels to set:
		foreach( $args as $arg )
		{
			if( is_array($arg) )
			{ // Path name and properties given
				list( $pathName, $pathProps ) = $arg;
			}
			else
			{ // Just the path name
				$pathName = $arg;
				$pathProps = array();
			}

			$this->init_menus();

			if( $node = & $this->get_node_by_path( array_merge( $prevPath, array($pathName) ) ) )
			{ // the node exists in the menu entries: merge the properties
				$pathProps = array_merge( $node, $pathProps );
			}

			if( ! $this->set_path_level( $i++, $pathName, $pathProps ) )
			{
				return false;
			}

			$prevPath[] = $pathName;
		}

		return true;
	}


	/**
	 * Init the menus.
	 *
	 * Do this as late as possible. Especially after determining the blog ID we work on.
	 * This allows to check for proper permissions and build correct cross navigation links.
	 *
	 * Note: The menu structure is determined by the modules and the plugins.
	 * Individual Admin skins can still override the whole menu. In a cumbersome way though.
	 */
	function init_menus()
	{
		global $Plugins;

		if( !empty($this->_menus) )
		{	// Already initialized!
			return;
		}

		// Let the modules construct the menu:
		// Part 1:
		modules_call_method( 'build_menu_1' );

		// Part 2:
		modules_call_method( 'build_menu_2' );

		// Part 3:
		modules_call_method( 'build_menu_3' );

		// Call AdminAfterMenuInit to notify Plugins that the menu is initialized
		// E.g. the livehits_plugin and weather_plugin use it for adding a menu entry.
		$Plugins->trigger_event( 'AdminAfterMenuInit' );
	}


	/**
	 * Get the top of the HTML <body>.
	 *
	 * This is not called if {@link $mode} is set.
	 *
	 * @return string
	 */
	function get_body_top()
	{
		return '';
	}


	/**
	 * Get the end of the HTML <body>. Close open divs, etc...
	 *
	 * This is not called if {@link $mode} is set.
	 *
	 * @return string
	 */
	function get_body_bottom()
	{
		return '';
	}


	/**
	 * GLOBAL HEADER - APP TITLE, LOGOUT, ETC.
	 *
	 * @return string
	 */
	function get_page_head()
	{
		global $app_shortname, $app_version, $admin_url, $baseurl, $rsc_url;

		$r = '
		<div id="header">
			<div id="headinfo">
				<span id="headfunctions">'
					// Note: if we log in with another user, we may not have the perms to come back to the same place any more, thus: redirect to admin home.
					.'<a href="'.get_htsrv_url( 'login' ).'login.php?action=logout&amp;redirect_to='.rawurlencode( url_rel_to_same_host( $admin_url, get_htsrv_url( 'login' ) ) ).'">'.T_('Log out').'</a>
					<img src="'.$rsc_url.'icons/close.gif" width="14" height="14" border="0" class="top" alt="" title="'
					.T_('Log out').'" /></a>
				</span>

				'.$app_shortname.' v <strong>'.$app_version.'</strong>
			</div>

			<h1>'.$this->get_title_for_titlearea().'</h1>
		</div>
		';

		return $r;
	}


	/**
	 * Get the footer text
	 *
	 * @return string
	 */
	function get_footer_contents()
	{
		global $app_footer_text, $copyright_text;

		return '<div class="footer">'.$app_footer_text.' &ndash; '.$copyright_text."</div>\n\n";
	}


	/**
	 * Get colors for page elements that can't be controlled by CSS (charts)
	 *
	 * @return string
	 */
	function get_color( $what )
	{
		switch( $what )
		{
			case 'payload_background':
				return 'f1f6f8';
				break;
		}
		debug_die( 'unknown color' );
	}


	/**
	 * Display skin options.
	 * There is no common skin option.
	 */
	function display_skin_settings( $Form, $user_ID )
	{
	}


	/**
	 * Set skin specific options.
	 * There is no common skin option.
	 */
	function set_skin_settings( $user_ID )
	{
	}


	/**
	 * Get show evobar setting. Default true for every admin skin.
	 * @return boolean
	 */
	function get_show_evobar()
	{
		return true;
	}


	/**
	 * Display tabs for customizer mode in left iframe
	 *
	 * @param array Params
	 */
	function display_customizer_tabs( $params = array() )
	{
	}
}

?>
