Seditio Source
Root |
./othercms/b2evolution_7.2.3/skins_adm/_adminUI_general.class.php
<?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() )
    {
    }
}

?>