Seditio Source
Root |
./othercms/b2evolution_7.2.3/inc/widgets/model/_widget.class.php
<?php
/**
 * This file implements the Widget class.
 *
 * This file is part of the evoCore framework - {@link http://evocore.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/}
 *
 * @package evocore
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );

load_class( '_core/model/dataobjects/_dataobject.class.php', 'DataObject' );

// Load functions for widget layout:
load_funcs( 'widgets/_widgets.funcs.php' );

/**
 * ComponentWidget Class
 *
 * A ComponentWidget is a displayable entity that can be placed into a Container on a web page.
 *
 * @package evocore
 */
class ComponentWidget extends DataObject
{
   
/**
     * Widget container ID
     */
   
var $wico_ID;

    var
$order;
   
/**
     * @var string Type of the plugin ("core" or "plugin")
     */
   
var $type;
    var
$code;
    var
$params;

   
/**
     * Indicates whether the widget is enabled.
     *
     * @var boolean
     */
   
var $enabled;

   
/**
     * Array of params which have been customized for this widget instance
     *
     * This is saved to the DB as a serialized string ($params)
     */
   
var $param_array = NULL;

   
/**
     * Array of params used during display()
     */
   
var $disp_params = NULL;

   
/**
     * Lazy instantiated.
     *
     * This gets set/used for widget plugins (those that hook into SkinTag).
     * (false if this Widget is not handled by a Plugin)
     * @see get_Plugin()
     * @var Plugin
     */
   
var $Plugin;

   
/**
    * @var BlockCache
    */
   
var $BlockCache;

   
/**
     * The widget container where this widget belongs to
     *
     * Lazy instantiated.
     *
     * @var WidgetContainer
     */
   
var $WidgetContainer;

   
/**
    * @var Blog
    */
   
var $Blog = NULL;

   
/**
    * @var target User that should be used depending on context
    */
   
var $target_User = NULL;

   
/**
     * Widget icon name.
     * Use icon name from http://fontawesome.io/icons/
     *
     * @var string
     */
   
var $icon = 'cube';

   
/**
     * @var Mode: 'designer' or 'normal'
     */
   
var $mode = 'normal';


   
/**
     * Constructor
     *
     * @param object data row from db
     */
   
function __construct( $db_row = NULL, $type = 'core', $code = NULL )
    {
       
// Call parent constructor:
       
parent::__construct( 'T_widget__widget', 'wi_', 'wi_ID' );

        if(
is_null($db_row) )
        {    
// We are creating an object here:
            // Using parent:: instead of $this-> in order to fix http://forums.b2evolution.net//viewtopic.php?p=94778
           
parent::set( 'type', $type );
           
parent::set( 'code', $code );
        }
        else
        {    
// We are loading an object:
           
$this->ID       = $db_row->wi_ID;
           
$this->wico_ID  = $db_row->wi_wico_ID;
           
$this->type     = $db_row->wi_type;
           
$this->code     = $db_row->wi_code;
           
$this->params   = $db_row->wi_params;
           
$this->order    = $db_row->wi_order;
           
$this->enabled  = $db_row->wi_enabled;
        }
    }


   
/**
     * Get a member param by its name
     *
     * @param mixed Name of parameter
     * @return mixed Value of parameter
     */
   
function get( $parname )
    {
        if(
$parname == 'coll_ID' )
        {
            return
$this->get_coll_ID();
        }

        return
parent::get( $parname );
    }


   
/**
     * Get param prefix with is used on edit forms and submit data
     *
     * @return string
     */
   
function get_param_prefix()
    {
        return
'edit_widget_'.( empty( $this->ID ) ? '0' : $this->ID ).'_set_';
    }


   
/**
     * Get ref to the Plugin handling this Widget.
     *
     * @return Plugin
     */
   
function & get_Plugin()
    {
        global
$Plugins;

        if(
is_null( $this->Plugin ) )
        {
            if(
$this->type != 'plugin' )
            {
               
$this->Plugin = false;
            }
            else
            {
               
$this->Plugin = & $Plugins->get_by_code( $this->code );
            }
        }

        return
$this->Plugin;
    }


   
/**
     * Get WidgetContainer
     */
   
function & get_WidgetContainer()
    {
        if( ! isset(
$this->WidgetContainer ) )
        {
           
$WidgetContainerCache = & get_WidgetContainerCache();
           
$this->WidgetContainer = & $WidgetContainerCache->get_by_ID( $this->wico_ID, false, false );
        }
        return
$this->WidgetContainer;
    }


   
/**
     * Get the collection ID where this widget belongs to
     *
     * @return integer Collection ID
     */
   
function get_coll_ID()
    {
        return
$this->get_container_param( 'coll_ID' );
    }


   
/**
     * Get param value of container
     *
     * @param string Param name
     * @return string Param value
     */
   
function get_container_param( $param )
    {
       
$WidgetContainer = & $this->get_WidgetContainer();

        if( empty(
$WidgetContainer ) )
        {
            return
NULL;
        }

        return
$WidgetContainer->get( $param );
    }


   
/**
     * Load params
     */
   
function load_from_Request()
    {
       
load_funcs('plugins/_plugin.funcs.php');

       
// Loop through all widget params:
       
foreach( $this->get_param_definitions( array( 'for_editing' => true, 'for_updating' => true  ) ) as $parname => $parmeta )
        {
           
$parvalue = NULL;
            if(
$parname == 'allow_blockcache'
                   
&& isset( $parmeta['disabled'] )
                    && (
$parmeta['disabled'] == 'disabled' ) )
            {
// Force checkbox "Allow caching" to unchecked when it is disallowed from widget config
               
$parvalue = 0;
            }
           
autoform_set_param_from_request( $parname, $parmeta, $this, 'Widget', NULL, $parvalue );
        }
    }


   
/**
     * Get name of widget
     *
     * Should be overriden by core widgets
     */
   
function get_name()
    {
        if(
$this->type == 'plugin' )
        {
           
// Make sure Plugin is loaded:
           
if( $this->get_Plugin() )
            {
                return
$this->Plugin->name;
            }
            return
T_('Inactive / Uninstalled plugin').': "'.$this->code.'"';
        }
        elseif(
$this->type == 'wrong' )
        {
            return
T_('Wrong widget / Invalid code').': "'.$this->code.'"';
        }

        return
T_('Unknown');
    }


   
/**
     * Get a very short desc. Used in the widget list.
     *
     * MAY be overriden by core widgets. Example: menu link widget.
     */
   
function get_short_desc()
    {
        return
$this->get_name();
    }


   
/**
     * Get widget icon
     *
     * @return string
     */
   
function get_icon()
    {
        if(
$this->type == 'plugin' )
        {    
// Use widget icon from plugin:
           
if( $this->get_Plugin() )
            {    
// Get widget icon from plugin:
               
return $this->Plugin->get_widget_icon();
            }
            else
            {    
// Set icon for inactive / uninstalled plugin:
               
$this->icon = 'warning';
            }
        }

        if( empty(
$this->icon ) )
        {
            return
'';
        }

        return
'<span class="label label-info evo_widget_icon"><span class="fa fa-'.$this->icon.'"></span></span>';
    }


   
/**
     * Get a clean description to display in the widget list.
     * @return string
     */
   
function get_desc_for_list()
    {
       
$name = $this->get_name();

        if(
$this->type == 'plugin' )
        {    
// Plugin widget:
           
$widget_Plugin = & $this->get_Plugin();

            if(
$widget_Plugin )
            {
                if( isset(
$this->disp_params['title'] ) && ! empty( $this->disp_params['title'] ) )
                {
                    return
$widget_Plugin->get_widget_icon().' <strong>'.$this->disp_params['title'].'</strong> ('.$name. ' - ' .T_('Plugin').')';
                }

                return
$widget_Plugin->get_widget_icon().' <strong>'.$name.'</strong> ('.T_('Plugin').')';
            }
            else
            {
               
$icon = '<span class="label label-info evo_widget_icon"><span class="fa fa-warning"></span></span>';
                return
$icon.' <strong>'.$name.'</strong> ('.T_('Plugin').')';
            }
        }

       
// Normal widget:
       
$short_desc = $this->get_short_desc();
       
$icon = $this->get_icon();

        if(
$name == $short_desc || empty( $short_desc ) )
        {
            return
$icon.' <strong>'.$name.'</strong>';
        }

        return
$icon.' <strong>'.$short_desc.'</strong> ('.$name.')';
    }


   
/**
     * Get desc of widget
     *
     * Should be overriden by core widgets
     */
   
function get_desc()
    {
        if(
$this->type == 'plugin' )
        {
           
// Make sure Plugin is loaded:
           
if( $this->get_Plugin() )
            {
                return
$this->Plugin->short_desc;
            }
            return
T_('Inactive / Uninstalled plugin').': "'.$this->code.'"';
        }

        return
T_('Unknown');
    }


   
/**
     * Get help URL
     *
     * @return string|NULL URL, NULL - when core widget doesn't define the url yet
     */
   
function get_help_url()
    {
        if(
$widget_Plugin = & $this->get_Plugin() )
        {
// Get url of the plugin widget
           
$help_url = $widget_Plugin->get_help_url( '$widget_url' );
        }
        else
        {
// Core widget must defines this URL
           
$help_url = NULL;
        }

        return
$help_url;
    }


   
/**
     * Get help link
     *
     * @param string Icon
     * @param boolean TRUE - to add info to display it in tooltip on mouseover
     * @return string icon
     */
   
function get_help_link( $icon = 'help', $use_tooltip = true )
    {
       
$widget_url = $this->get_help_url();

        if( empty(
$widget_url ) )
        {
// Return empty string when widget URL is not defined
           
return '';
        }

       
$link_attrs = array( 'target' => '_blank' );

        if(
$use_tooltip )
        {
// Add these data only for tooltip
           
$link_attrs['class'] = 'action_icon help_plugin_icon';
           
$link_attrs['data-popover'] = format_to_output( $this->get_desc(), 'htmlattr' );
        }

        return
action_icon( '', $icon, $widget_url, NULL, NULL, NULL, $link_attrs );
    }


   
/**
     * Get definitions for editable params.
     *
     * @see Plugin::GetDefaultSettings()
     *
     * @param array Local params like 'for_editing' => true
     */
   
function get_param_definitions( $params )
    {
       
$r = array();

        if(
$this->type == 'plugin' )
        {
           
// Make sure Plugin is loaded:
           
if( $this->get_Plugin() )
            {
               
$r = $this->Plugin->get_widget_param_definitions( $params );
            }
        }

        if( ! isset(
$r['widget_css_class'] ) ||
            ! isset(
$r['widget_ID'] ) ||
            ! isset(
$r['allow_blockcache'] ) )
        {    
// Start fieldset of advanced settings:
           
$r['advanced_layout_start'] = array(
                   
'layout' => 'begin_fieldset',
                   
'label'  => T_('Advanced'),
                );
           
$advanced_layout_is_started = true;
        }

       
// Add advanced definitions if they are provided in a widget:
       
$r = array_merge( $r, $this->get_advanced_param_definitions() );

        if( ! empty(
$this->allow_link_css_params ) )
        {    
// Enable link/button CSS classes only for specific widgets like menu widgets:
           
$r['widget_link_class'] = array(
                   
'label' => '<span class="dimmed">'.T_('Link/Button Class').'</span>',
                   
'size' => 20,
                   
'note' => sprintf( T_('Replaces %s in class attribute of link/button.'), '<code>$link_class$</code>' ).' '.T_('Leave empty to use default values from skin or from widget.'),
                );
           
$r['widget_active_link_class'] = array(
                   
'label' => '<span class="dimmed">'.T_('Active Link/Button Class').'</span>',
                   
'size' => 20,
                   
'note' => sprintf( T_('Replaces %s in class attribute of active link/button.'), '<code>$link_class$</code>' ).' '.T_('Leave empty to use default values from skin or from widget.'),
                );
        }

        if( ! isset(
$r['widget_css_class'] ) )
        {    
// Widget CSS class:
           
$r['widget_css_class'] = array(
                   
'label' => '<span class="dimmed">'.T_( 'Widget CSS Class' ).'</span>',
                   
'size' => 20,
                   
'note' => sprintf( T_('Will be injected into %s in your skin containers (along with required system classes).'), '<code>$wi_class$</code>' ),
                );
        }

        if( ! isset(
$r['widget_ID'] ) )
        {    
// Widget ID:
           
$r['widget_ID'] = array(
                   
'label' => '<span class="dimmed">'.T_( 'Widget DOM ID' ).'</span>',
                   
'size' => 20,
                   
'note' => sprintf( T_('Replaces %s in your skins containers.'), '<code>$wi_ID$</code>' ).' '.sprintf( T_('Leave empty to use default value: %s.'), '<code>widget_'.$this->type.'_'.$this->code.'_'.$this->ID.'</code>' ),
                );
        }

        if( ! isset(
$r['allow_blockcache'] ) )
        {    
// Allow widget/block caching:
           
$widget_Blog = & $this->get_Blog();
           
$r['allow_blockcache'] = array(
                   
'label' => T_( 'Allow caching' ),
                   
'note' => ( $widget_Blog && $widget_Blog->get_setting( 'cache_enabled_widgets' ) ) ?
                           
T_('Uncheck to prevent this widget from ever being cached in the block cache. (The whole page may still be cached.) This is only needed when a widget is poorly handling caching and cache keys.') :
                           
T_('Block caching is disabled for this collection.'),
                   
'type' => 'checkbox',
                   
'defaultvalue' => true,
                );
        }

        if( ! empty(
$advanced_layout_is_started ) )
        {    
// End fieldset of advanced settings:
           
$r['advanced_layout_end'] = array(
                   
'layout' => 'end_fieldset',
                );
           
$advanced_layout_is_started = false;
        }

        return
$r;
    }


   
/**
     * Get advanced definitions for editable params.
     *
     * @see Plugin::GetDefaultSettings()
     *
     * @return array Advanced params
     */
   
function get_advanced_param_definitions()
    {
        return array();
    }


   
/**
     * Load param array.
     */
   
function load_param_array()
    {
        if(
is_null( $this->param_array ) )
        {    
// Param array has not been loaded yet
           
$this->param_array = @unserialize( $this->params );

            if( empty(
$this->param_array ) )
            {    
// No saved param values were found:
               
$this->param_array = array();
            }
        }
    }


   
/**
     * Get param value.
     *
     * @param string Parameter name
     * @param mixed Default value, Set to different than NULL only if it is called from a widget::get_param_definition() function to avoid infinite loop
     * @param string|NULL Group name
     * @return mixed
     */
   
function get_param( $parname, $default_value = NULL, $group = NULL )
    {
       
$this->load_param_array();

        if(
strpos( $parname, '[' ) !== false )
        {    
// Get value for array setting like "sample_sets[0][group_name_param_name]":
           
$setting_names = explode( '[', $parname );
            if( isset(
$this->param_array[ $setting_names[0] ] ) )
            {
               
$setting_value = $this->param_array[ $setting_names[0] ];
                unset(
$setting_names[0] );
                foreach(
$setting_names as $setting_name )
                {
                   
$setting_name = trim( $setting_name, ']' );
                    if( isset(
$setting_value[ $setting_name ] ) )
                    {
                       
$setting_value = $setting_value[ $setting_name ];
                    }
                    else
                    {
                       
$setting_value = NULL;
                        break;
                    }
                }
                return
$setting_value;
            }
        }
        else
        {    
// Get normal(not array) setting value:
           
if( isset( $this->disp_params[ $parname ] ) )
            {    
// Get an overridden value from skin:
               
return $this->disp_params[ $parname ];
            }
            elseif( isset(
$this->param_array[ $parname ] ) )
            {    
// Get value from DB:
               
return $this->param_array[ $parname ];
            }
        }

        if(
$default_value !== NULL )
        {    
// Use defined default value when it is not saved in DB yet:
            // (This call is used to get a value from function widget::get_param_definition() to avoid infinite loop)
           
return $default_value;
        }

       
// Try default values from widget config:
       
$params = $this->get_param_definitions( NULL );

        if(
$group === NULL )
        {    
// Get param from simple field:
           
if( isset( $params[$parname]['defaultvalue'] ) )
            {    
// We have a default value:
               
return $params[$parname]['defaultvalue'] ;
            }
        }
        else
        {    
// Get param from group field:
           
$parname = substr( $parname, strlen( $group ) );
            if( isset(
$params[$group]['inputs'][$parname]['defaultvalue'] ) )
            {    
// We have a default value:
               
return $params[$group]['inputs'][$parname]['defaultvalue'] ;
            }
        }

        return
NULL;
    }


   
/**
     * Set param value
     *
     * @param string parameter name
     * @param mixed parameter value
     * @param boolean true to set to NULL if empty value
     * @return boolean true, if a value has been set; false if it has not changed
     */
   
function set( $parname, $parvalue, $make_null = false, $group = NULL )
    {
       
$params = $this->get_param_definitions( NULL );

        if( isset(
$params[$parname] ) ||
            (
$group !== NULL && isset( $params[ $group ]['inputs'][ substr( $parname, strlen( $group ) ) ] ) ) )
        {
// This is a widget specific param:
            // Make sure param_array is loaded before set the param value
           
$this->load_param_array();
           
$this->param_array[$parname] = $parvalue;
           
// This is what'll be saved to the DB:
           
return $this->set_param( 'params', 'string', serialize($this->param_array), $make_null );
        }

        switch(
$parname )
        {
            default:
                return
$this->set_param( $parname, 'string', $parvalue, $make_null );
        }
    }


   
/**
     * Request all required css and js files for this widget
     */
   
function request_required_files()
    {
    }


   
/**
     * Prepare display params
     *
     * @todo Document default params and default values.
     * @todo fp> do NOT call this when just listing widget names in the back-office. It's overkill!
     *
     * @param array MUST contain at least the basic display params
     */
   
function init_display( $params )
    {
        global
$admin_url, $debug;

        if( !
is_null($this->disp_params) )
        {
// Params have been initialized before...
           
return;
        }

       
// Generate widget defaults (editable params) array:
       
$widget_defaults = array();
       
$defs = $this->get_param_definitions( array() );
        foreach(
$defs as $parname => $parmeta )
        {
            if( isset(
$parmeta['type'] ) && $parmeta['type'] == 'checklist' )
            {
               
$widget_defaults[ $parname ] = array();
                foreach(
$parmeta['options'] as $parmeta_option )
                {
                   
$widget_defaults[ $parname ][ $parmeta_option[0] ] = $parmeta_option[2];
                }
            }
            else
            {
               
$widget_defaults[ $parname ] = ( isset( $parmeta['defaultvalue'] ) ) ? $parmeta['defaultvalue'] : NULL;
            }
        }

       
// Load DB configuration:
       
$this->load_param_array();

       
// DEFAULT CONTAINER PARAMS:
        // Merge basic defaults < widget defaults (editable params) < container params < DB params
        // note: when called with skin_widget it falls back to basic defaults < widget defaults < calltime params < array()
       
$params = array_merge( array(
                   
'widget_context' => 'general',        // general | item | user
                   
'block_start' => '<div class="evo_widget widget $wi_class$">',
                   
'block_end' => '</div>',
                   
'block_display_title' => true,
                   
'block_title_start' => '<h3>',
                   
'block_title_end' => '</h3>',
                   
'block_body_start' => '',
                   
'block_body_end' => '',
                   
'collist_start' => '',
                   
'collist_end' => '',
                   
'coll_start' => '<h4>',
                   
'coll_end' => '</h4>',
                   
'list_start' => '<ul>',
                   
'list_end' => '</ul>',
                   
'item_start' => '<li>',
                   
'item_end' => '</li>',
                       
'item_title_start' => '<strong>',
                       
'item_title_end' => ':</strong> ',
                       
'link_default_class' => 'default',
                       
'link_selected_class' => 'selected',
                       
'item_text_start' => '',
                       
'item_text_end' => '',
                       
'item_text' => '%s',
                   
'item_selected_start' => '<li class="selected">',
                   
'item_selected_end' => '</li>',
                   
'item_selected_text' => '%s',

                   
// Automatically detect whether we are displaying menu links as list elements or as standalone buttons:
                   
'inlist' => 'auto',        // auto is based on 'list_start'; may also be true or false
                    // Button styles used for Menu Links / Buttons widgets:
                   
'button_default_class' => 'btn btn-default btn-margin-right',
                   
'button_selected_class' => 'btn btn-default btn-margin-right active',
                   
'button_group_start' => '<span class="btn-group">',
                   
'button_group_end' => '</span>',
                   
// Tabs style:
                   
'tabs_start'         => '<ul class="nav nav-tabs">',
                   
'tabs_end'           => '</ul>',
                   
'tab_start'          => '<li>',
                   
'tab_end'            => '</li>',
                   
'tab_selected_start' => '<li class="active">',
                   
'tab_selected_end'   => '</li>',
                   
'tab_default_class'  => '',
                   
'tab_selected_class' => 'active',

                   
'grid_start' => '<table cellspacing="1" class="widget_grid">',
                       
'grid_colstart' => '<tr>',
                           
'grid_cellstart' => '<td>',
                           
'grid_cellend' => '</td>',
                       
'grid_colend' => '</tr>',
                   
'grid_end' => '</table>',
                   
'grid_nb_cols' => 2,
                   
'flow_start' => '<div class="widget_flow_blocks">',
                       
'flow_block_start' => '<div>',
                       
'flow_block_end' => '</div>',
                   
'flow_end' => '</div>',
                   
'rwd_start' => '<div class="widget_rwd_blocks row">',
                       
'rwd_block_start' => '<div class="$wi_rwd_block_class$"><div class="widget_rwd_content clearfix">',
                       
'rwd_block_end' => '</div></div>',
                   
'rwd_end' => '</div>',
                   
'thumb_size' => 'crop-80x80',
                   
'link_type' => 'canonic',        // 'canonic' | 'context' (context will regenrate URL injecting/replacing a single filter)
                   
'item_selected_text_start' => '',
                   
'item_selected_text_end' => '',
                   
'group_start' => '<ul>',
                   
'group_end' => '</ul>',
                   
'group_item_start' => '<li>',
                   
'group_item_end' => '</li>',
                   
'notes_start' => '<div class="notes">',
                   
'notes_end' => '</div>',
                   
'tag_cloud_start' => '<p class="tag_cloud">',
                   
'tag_cloud_end' => '</p>',
                   
'limit' => 100,
                ),
$widget_defaults, $params, $this->param_array );

        if( isset(
$params['override_params_for_'.$this->code] ) )
        {    
// Use specific widget params if they are defined for this widget by code:
           
$params = array_merge( $params, $params['override_params_for_'.$this->code] );
        }

       
// Customize params to the current widget:

        // Add additional css classes if required:
       
$widget_css_class = 'widget_'.$this->type.'_'.$this->code.( empty( $params[ 'widget_css_class' ] ) ? '' : ' '.$params[ 'widget_css_class' ] );

       
// Set additional css class depending on layout:
       
$layout = isset( $params['layout'] ) ? $params['layout'] : ( isset( $params['thumb_layout'] ) ? $params['thumb_layout'] : NULL );
        switch(
$layout )
        {
            case
'rwd':
               
$widget_css_class .= ' evo_layout_rwd';
                break;
            case
'flow':
               
$widget_css_class .= ' evo_layout_flow';
                break;
            case
'list':
               
$widget_css_class .= ' evo_layout_list';
                break;
            case
'grid':
               
$widget_css_class .= ' evo_layout_grid';
                break;
        }

       
// Add custom id if required, default to generic id for validation purposes:
       
$widget_ID = ( !empty($params[ 'widget_ID' ]) ? $params[ 'widget_ID' ] : 'widget_'.$this->type.'_'.$this->code.'_'.$this->ID );

       
// Replace the values:
       
$this->disp_params = str_replace( array( '$wi_ID$', '$wi_class$' ), array( $widget_ID, $widget_css_class ), $params );
    }


   
/**
     * Convert old display params to new name.
     *
      * Use this function if some params were renamed.
     * This function will look for the old params and convert them if no new param is present
     */
   
function convert_legacy_param( $old_name, $new_name )
    {
       
//pre_dump( $this->disp_params );
       
if( isset($this->disp_params[$old_name]) && !isset($this->disp_params[$new_name]) )
        {    
// We have old param but NOT new param, duplicate old to new:
           
$this->disp_params[$new_name] = $this->disp_params[$old_name];
        }
    }


   
/**
     * Display the widget!
     *
     * Should be overriden by core widgets
     *
     * @todo fp> handle custom params for each widget
     *
     * @param array MUST contain at least the basic display params
     * @return bool true if the widget displayed something (other than a debug message)
     */
   
function display( $params )
    {
        global
$Collection, $Blog;
        global
$Plugins;
        global
$rsc_url;

       
// prepare for display:
       
$this->init_display( $params );

        switch(
$this->type )
        {
            case
'plugin':
               
// Set widget ID param to make it available in plugin function SkinTag():
               
$this->disp_params['wi_ID'] = $this->ID;
               
// Call plugin (will return false if Plugin is not enabled):
               
if( $Plugins->call_by_code( $this->code, $this->disp_params ) )
                {
                    return
true;
                }
                else
                {    
// Plugin failed (happens when a plugin has been disabled for example):
                   
if( $this->mode == 'designer' )
                    {    
// Display red text in customizer widget designer mode in order to make this plugin visible for editing:
                       
echo $this->disp_params['block_start'].get_rendering_error( T_('Inactive / Uninstalled plugin').': "'.$this->code.'"', 'span' ).$this->disp_params['block_end'];
                    }
                    return
false;
                }
        }

        echo
"Widget $this->type : $this->code did not provide a display() method! ";

        return
false;
    }


   
/**
     * Wraps display in a cacheable block.
     *
     * @param array MUST contain at least the basic display params
     * @param array of extra keys to be used for cache keying
     */
   
function display_with_cache( $params, $keys = array() )
    {
        global
$Collection, $Blog, $Timer, $debug, $admin_url, $Session;

       
$this->init_display( $params );

       
// Display the debug conatainers when $debug = 2 OR when it is turned on from evo menu under "Collection" -> "Show/Hide containers"
       
$display_containers = ( $debug == 2 ) || ( is_logged_in() && $Session->get( 'display_containers_'.$Blog->ID ) );

       
$force_nocaching = false;

       
// Enable the desinger mode when it is turned on from evo menu under "Designer Mode/Exit Designer" or "Collection" -> "Enable/Disable designer mode"
       
if( is_logged_in() && $Session->get( 'designer_mode_'.$Blog->ID ) )
        {    
// Initialize data which is used by JavaScript to build overlay designer mode html elements:
           
$designer_mode_data = array(
                   
'data-id'        => $this->ID,
                   
'data-type'      => $this->get_name(),
                   
'data-container' => $this->get_container_param( 'code' ),
                );
            if(
$this->get( 'code' ) == 'subcontainer' &&
                (
$sub_WidgetContainer = & $this->get_sub_WidgetContainer() ) )
            {    
// For Sub-Container widget we should know what sub-container is used in order to list and add widgets on customizer mode:
               
$designer_mode_data['data-subcontainer-name'] = $sub_WidgetContainer->get( 'name' );
               
$designer_mode_data['data-subcontainer-code'] = $this->get_param( 'container' );
            }
           
// Set data to know current user has a permission to edit this widget:
           
$designer_mode_data['data-can-edit'] = check_user_perm( 'blog_properties', 'edit', false, $Blog->ID ) ? 1 : 0;
           
// Don't load a widget content from cache when designer mode is enabled:
           
$force_nocaching = true;
           
// Set designer mode:
           
$this->mode = 'designer';
           
// Set this param for plugin widgets:
           
$this->disp_params['debug_mode'] = $this->mode;
        }

        if(
$force_nocaching
           
|| ! $Blog->get_setting( 'cache_enabled_widgets' )
            || !
$this->disp_params['allow_blockcache']
            ||
$this->get_cache_status() == 'disallowed' )
        {
// NO CACHING - We do NOT want caching for this collection or for this specific widget:

           
if( $display_containers )
            {
// DEBUG:
               
$is_subcontainer = ( $this->get( 'code' ) == 'subcontainer' || $this->get( 'code' ) == 'subcontainer_row' );
                echo
'<div class="dev-blocks '.( $is_subcontainer ? 'dev-blocks--subcontainer' : 'dev-blocks--widget' ).'"><div class="dev-blocks-name" title="'.
                            (
$Blog->get_setting('cache_enabled_widgets') ? 'Widget params have BlockCache turned off' : 'Collection params have BlockCache turned off' ).'">';
                if(
check_user_perm( 'blog_properties', 'edit', false, $Blog->ID ) )
                {    
// Display a link to edit this widget only if current user has a permission:
                   
echo '<span class="dev-blocks-action"><a href="'.$admin_url.'?ctrl=widgets&amp;action=edit&amp;wi_ID='.$this->ID.'">Edit</a></span>';
                }
                echo
'Widget: <b>'.$this->get_name().'</b> - Cache OFF <i class="fa fa-info">?</i></div>'."\n";
            }

           
// Start to collect output buffer in order to can clean up rendering errors when it need below:
           
ob_start();

            if( ! empty(
$designer_mode_data ) )
            {    
// Append designer mode html tag attributes to first not empty widget wrapper/container:
               
$widget_wrappers = array(
                       
'block_start',
                       
'block_body_start',
                       
'list_start',
                        array(
'item_start', 'item_selected_start' ),
                    );
                foreach(
$widget_wrappers as $widget_wrapper_items )
                {
                    if( !
is_array( $widget_wrapper_items ) )
                    {
                       
$widget_wrapper_items = array( $widget_wrapper_items );
                    }
                   
$wrapper_is_found = false;
                    foreach(
$widget_wrapper_items as $widget_wrapper )
                    {
                        if( !empty(
$params[ $widget_wrapper ] ) || !empty( $params['override_params_for_'.$this->code][ $widget_wrapper ] ) )
                        {    
// Append new data for widget wrapper:
                           
$attrib_actions = array(
                                   
'data-id'        => 'replace',
                                   
'data-type'      => 'replace',
                                   
'data-container' => 'replace',
                                   
'data-can-edit'  => 'replace',
                                );
                            if( !empty(
$params[ $widget_wrapper ] ) )
                            {    
// If this wrapper is filled and used with current widget,
                               
$params[ $widget_wrapper ] = update_html_tag_attribs( $params[ $widget_wrapper ], $designer_mode_data, $attrib_actions );
                            }

                            if( !empty(
$params['override_params_for_'.$this->code][ $widget_wrapper ] ) )
                            {    
// Also update override params:
                               
$params['override_params_for_'.$this->code][ $widget_wrapper ] = update_html_tag_attribs( $params['override_params_for_'.$this->code][ $widget_wrapper ], $designer_mode_data, $attrib_actions );
                            }

                            if( isset(
$this->disp_params[ $widget_wrapper ] ) )
                            {    
// Also update params if they already have been initialized before:
                               
$this->disp_params[ $widget_wrapper ] = update_html_tag_attribs( $this->disp_params[ $widget_wrapper ], $designer_mode_data, $attrib_actions );
                            }
                           
$wrapper_is_found = true;
                        }
                    }
                    if(
$wrapper_is_found )
                    {    
// Stop search other wrapper in order to use only first filled wrapper:
                       
break;
                    }
                }
                if( !
$wrapper_is_found )
                {    
// Display error if widget has no wrappers to enable designer mode:
                   
echo ' '.get_rendering_error( 'Widget <code>'.$this->code.'</code> cannot be manipulated because it lacks a wrapper tag.', 'span' ).' ';
                }
            }

            if( ! isset(
$params['widget_'.$this->code.'_display'] ) || ! empty( $params['widget_'.$this->code.'_display'] ) )
            {    
// Display widget content:
               
$this->display( $params );
            }
            else
            {    
// Hide the widget by code if it is requsted from skin:
               
$this->display_debug_message( 'Widget "'.$this->get_name().'" is hidden by code <code>'.$this->code.'</code> from skin template.' );
            }

           
$widget_content = ob_get_clean();

            if( !
check_user_perm( 'blog_admin', 'edit', false, $Blog->ID ) )
            {    
// Clean up rendering errors from content if current User is not collection admin:
               
$widget_content = clear_rendering_errors( $widget_content );
            }

            echo
$widget_content;

            if(
$display_containers )
            {
// DEBUG:
               
echo "</div>\n";
            }
        }
        else
        {
// Instantiate BlockCache:
           
$Timer->resume( 'BlockCache' );
           
// Extend cache keys:
           
$keys += $this->get_cache_keys();

           
$this->BlockCache = new BlockCache( 'widget', $keys );

           
$content = $this->BlockCache->check();

           
$Timer->pause( 'BlockCache' );

            if(
$content !== false )
            {
// cache hit, let's display:

               
if( $display_containers )
                {
// DEBUG:
                   
echo '<div class="dev-blocks dev-blocks--widget dev-blocks--widget--incache"><div class="dev-blocks-name" title="Cache key = '.$this->BlockCache->serialized_keys.'">';
                    if(
check_user_perm( 'blog_properties', 'edit', false, $Blog->ID ) )
                    {    
// Display a link to edit this widget only if current user has a permission:
                       
echo '<span class="dev-blocks-action"><a href="'.$admin_url.'?ctrl=widgets&amp;action=edit&amp;wi_ID='.$this->ID.'">Edit</a></span>';
                    }
                    echo
'Widget: <b>'.$this->get_name().'</b> - FROM cache <i class="fa fa-info">?</i></div>'."\n";
                }

                if( !
check_user_perm( 'blog_admin', 'edit', false, $Blog->ID ) )
                {    
// Clean up rendering errors from content if current User is not collection admin:
                   
$content = clear_rendering_errors( $content );
                }

                echo
$content;

                if(
$display_containers )
                {
// DEBUG:
                   
echo "</div>\n";
                }

            }
            else
            {
// Cache miss, we have to generate:

               
if( $display_containers )
                {
// DEBUG:
                   
echo '<div class="dev-blocks dev-blocks--widget dev-blocks--widget--notincache"><div class="dev-blocks-name" title="Cache key = '.$this->BlockCache->serialized_keys.'">';
                    if(
check_user_perm( 'blog_properties', 'edit', false, $Blog->ID ) )
                    {    
// Display a link to edit this widget only if current user has a permission:
                       
echo '<span class="dev-blocks-action"><a href="'.$admin_url.'?ctrl=widgets&amp;action=edit&amp;wi_ID='.$this->ID.'">Edit</a></span>';
                    }
                    echo
'Widget: <b>'.$this->get_name().'</b> - NOT in cache <i class="fa fa-info">?</i></div>'."\n";
                }

               
$this->BlockCache->start_collect();

                if( ! isset(
$params['widget_'.$this->code.'_display'] ) || ! empty( $params['widget_'.$this->code.'_display'] ) )
                {    
// Display widget content:
                   
$this->display( $params );
                }
                else
                {    
// Hide the widget by code if it is requsted from skin:
                   
$this->display_debug_message( 'Widget "'.$this->get_name().'" is hidden by code <code>'.$this->code.'</code> from skin template.' );
                }

               
// Save collected cached data if needed:
               
$content = $this->BlockCache->end_collect( false );

                if( !
check_user_perm( 'blog_admin', 'edit', false, $Blog->ID ) )
                {    
// Clean up rendering errors from content if current User is not collection admin:
                   
$content = clear_rendering_errors( $content );
                }

                echo
$content;

                if(
$display_containers )
                {
// DEBUG:
                   
echo "</div>\n";
                }

            }
        }
    }


   
/**
     * Maybe be overriden by some widgets, depending on what THEY depend on..
     *
     * @return array of keys this widget depends on
     */
   
function get_cache_keys()
    {
        global
$Collection, $Blog;

        if(
$this->type == 'plugin' && $this->get_Plugin() )
        {    
// Get widget cache keys from plugin:
           
return $this->Plugin->get_widget_cache_keys( $this->ID );
        }

        return array(
               
'wi_ID'       => $this->ID, // Have the widget settings changed ?
               
'set_coll_ID' => $Blog->ID, // Have the settings of the blog changed ? (ex: new skin)
           
);
    }


   
/**
     * Get cache status
     *
     * @param boolean TRUE to check if blog allows a caching for widgets
     * @return string 'enabled', 'disabled', 'disallowed', 'denied'
     */
   
function get_cache_status( $check_blog_restriction  = false )
    {
       
$default_widget_params = $this->get_param_definitions( array() );
        if( ! empty(
$default_widget_params )
            && isset(
$default_widget_params['allow_blockcache'] )
            && isset(
$default_widget_params['allow_blockcache']['disabled'] )
            && (
$default_widget_params['allow_blockcache']['disabled'] == 'disabled' ) )
        {
// Widget cache is NOT allowed by widget config
           
return 'disallowed';
        }
        else
        {
// Check current cache status if it is allowed
           
if( $check_blog_restriction )
            {
// Check blog restriction for widget caching
               
$widget_Blog = & $this->get_Blog();
                if(
$widget_Blog && ! $widget_Blog->get_setting( 'cache_enabled_widgets' ) )
                {    
// Widget/block cache is not allowed by collection setting:
                   
return 'denied';
                }
            }

            if(
$this->get_param( 'allow_blockcache' ) )
            {
// Enabled
               
return 'enabled';
            }
            else
            {
// Disabled
               
return 'disabled';
            }
        }
    }


   
/**
     * Note: a container can prevent display of titles with 'block_display_title'
     * This is useful for the lists in the headers
     * fp> I'm not sure if this param should be overridable by widgets themselves (priority problem)
     * Maybe an "auto" setting.
     *
     * @access protected
     */
   
function disp_title( $title = NULL, $display = true )
    {
        if(
is_null($title) )
        {
           
$title = & $this->disp_params['title'];
        }

        if(
$this->disp_params['block_display_title'] && !empty( $title ) )
        {
           
$r = $this->disp_params['block_title_start'];
            if( ! isset(
$this->disp_params['hide_header_title'] ) )
            {
               
$r .= format_to_output( $title );
            }
           
$r .= $this->disp_params['block_title_end'];

            if(
$display ) echo $r;

            return
$r;
        }
    }


   
/**
     * List of collections/blogs
     *
     * @param array MUST contain at least the basic display params
     */
   
function disp_coll_list( $filter = 'public', $order_by = 'ID', $order_dir = 'ASC' )
    {
       
/**
         * @var Blog
         */
       
global $Collection, $Blog, $baseurl;

        echo
$this->disp_params['block_start'];

       
$this->disp_title();

       
/**
         * @var BlogCache
         */
       
$BlogCache = & get_BlogCache();

        if(
$filter == 'owner' )
        {    
// Load blogs of same owner
           
$blog_array = $BlogCache->load_owner_blogs( $Blog->owner_user_ID, $order_by, $order_dir );
        }
        else
        {    
// Load all public blogs
           
$blog_array = $BlogCache->load_public( $order_by, $order_dir );
        }

       
// 3.3? if( $this->disp_params['list_type'] == 'list' )
        // fp> TODO: init default value for $this->disp_params['list_type'] to avoid error
       
{
            echo
$this->disp_params['list_start'];

            foreach(
$blog_array as $l_blog_ID )
            {    
// Loop through all public blogs:

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

                if(
$Blog && $l_blog_ID == $Blog->ID )
                {
// This is the blog being displayed on this page:
                   
echo $this->disp_params['item_selected_start'];
                   
$link_class = empty( $this->disp_params['widget_active_link_class'] ) ? $this->disp_params['link_selected_class'] : $this->disp_params['widget_active_link_class'];
                }
                else
                {
                    echo
$this->disp_params['item_start'];
                   
$link_class = empty( $this->disp_params['widget_link_class'] ) ? $this->disp_params['link_default_class'] : $this->disp_params['widget_link_class'];
                }

                echo
'<a href="'.$l_Blog->gen_blogurl().'" class="'.$link_class.'" title="'
                                           
.$l_Blog->dget( 'name', 'htmlattr' ).'">';

                if(
$Blog && $l_blog_ID == $Blog->ID )
                {
// This is the blog being displayed on this page:
                   
echo $this->disp_params['item_selected_text_start'];
                   
printf( $this->disp_params['item_selected_text'], $l_Blog->dget( 'shortname', 'htmlbody' ) );
                    echo
$this->disp_params['item_selected_text_end'];
                    echo
'</a>';
                    echo
$this->disp_params['item_selected_end'];
                }
                else
                {
                    echo
$this->disp_params['item_text_start'];
                   
printf( $this->disp_params['item_text'], $l_Blog->dget( 'shortname', 'htmlbody' ) );
                    echo
$this->disp_params['item_text_end'];
                    echo
'</a>';
                    echo
$this->disp_params['item_end'];
                }
            }

            echo
$this->disp_params['list_end'];
        }
       
/* 3.3?
            Problems:
            -In FF3/XP with skin evoCamp, I click to drop down and it already reloads the page on the same blog.
            -Missing appropriate CSS so it displays at least half nicely in most of teh default skins
        {
            $select_options = '';
            foreach( $blog_array as $l_blog_ID )
            {    // Loop through all public blogs:
                $l_Blog = & $BlogCache->get_by_ID( $l_blog_ID );

                // Add item select list:
                $select_options .= '<option value="'.$l_blog_ID.'"';
                if( $Blog && $l_blog_ID == $Blog->ID )
                {
                    $select_options .= ' selected="selected"';
                }
                $select_options .= '>'.$l_Blog->dget( 'shortname', 'formvalue' ).'</option>'."\n";
            }

            if( !empty($select_options) )
            {
                echo '<form action="'.$baseurl.'" method="get">';
                echo '<select name="blog" onchange="this.form.submit();">'.$select_options.'</select>';
                echo '<noscript><input type="submit" value="'.T_('Go').'" /></noscript></form>';
            }
        }
        */
       
echo $this->disp_params['block_end'];
    }


   
/**
     * Insert object into DB based on previously recorded changes.
     *
     * @return boolean true on success
     */
   
function dbinsert()
    {
        global
$DB;

        if(
$this->ID != 0 )
        {
           
debug_die( 'Existing object cannot be inserted!' );
        }

       
$DB->begin();

        if( ! isset(
$this->order ) )
        {
           
$order_max = $DB->get_var(
               
'SELECT MAX(wi_order)
                    FROM T_widget__widget
                    WHERE wi_wico_ID = '
.$this->wico_ID, 0, 0, 'Get current max order' );

           
$this->set( 'order', $order_max+1 );
        }

       
$res = parent::dbinsert();

       
$DB->commit();

        return
$res;
    }


   
/**
     * Update the DB based on previously recorded changes
     */
   
function dbupdate()
    {
       
$result = parent::dbupdate();

        if(
$result )
        {    
// If widget has been really updated
            // BLOCK CACHE INVALIDATION:
            // This widget has been modified, cached content depending on it should be invalidated:
           
BlockCache::invalidate_key( 'wi_ID', $this->ID );
        }

        return
$result;
    }


   
/**
     * Get Blog
     *
     * @return object Blog
     */
   
function & get_Blog()
    {
        if(
$this->Blog === NULL )
        {
// Get blog only first time
           
$BlogCache = & get_BlogCache();
           
$this->Blog = & $BlogCache->get_by_ID( $this->get_coll_ID(), false, false );
        }

        return
$this->Blog;
    }


   
/**
     * Get current layout
     *
     * @return string|NULL Widget layout | NULL - if widget has no layout setting
     */
   
function get_layout()
    {
        return
get_widget_layout( $this->disp_params );
    }


   
/**
     * Get start of layout
     *
     * @return string
     */
   
function get_layout_start()
    {
        return
get_widget_layout_start( $this->disp_params );
    }


   
/**
     * Get end of layout
     *
     * @param integer Cell index (used for grid/table layout)
     * @return string
     */
   
function get_layout_end( $cell_index = 0 )
    {
        return
get_widget_layout_end( $cell_index, $this->disp_params );
    }


   
/**
     * Get item start of layout
     *
     * @param integer Cell index (used for grid/table layout)
     * @param boolean TRUE if current item/cell is selected
     * @param string Prefix for param
     * @return string
     */
   
function get_layout_item_start( $cell_index = 0, $is_selected = false, $disp_param_prefix = '' )
    {
        return
get_widget_layout_item_start( $cell_index, $is_selected, $disp_param_prefix, $this->disp_params );
    }


   
/**
     * Get item end of layout
     *
     * @param integer Cell index (used for grid/table layout)
     * @param boolean TRUE if current item/cell is selected
     * @param string Prefix for param
     * @return string
     */
   
function get_layout_item_end( $cell_index = 0, $is_selected = false, $disp_param_prefix = '' )
    {
        return
get_widget_layout_item_end($cell_index, $is_selected, $disp_param_prefix, $this->disp_params );
    }


   
/**
     * Get User that should be used for this widget currently depending on context
     *
     * @return object User
     */
   
function & get_target_User()
    {
        if(
$this->target_User === NULL )
        {    
// Initialize target User only first time:
           
global $Item, $Blog;

            if(
$this->disp_params['widget_context'] == 'user' )
            {    
// Use an user of current page disp=user (Only if we are in the context of displaying an User, not if $User is set from before):
               
$user_ID = get_param( 'user_ID' );
                if( empty(
$user_ID ) && is_logged_in() )
                {    
// Use current logged in User:
                   
global $current_User;
                   
$user_ID = $current_User->ID;
                }
                if( ! empty(
$user_ID ) )
                {    
// Try to get User by ID:
                   
$UserCache = & get_UserCache();
                   
$this->target_User = & $UserCache->get_by_ID( $user_ID, false, false );
                }
            }

            if( empty(
$this->target_User ) && $this->disp_params['widget_context'] == 'item' && ! empty( $Item ) )
            {    
// Use an author of the current $Item (Only if we are in the context of displaying an Item, not if $Item is set from before):
               
$this->target_User = & $Item->get_creator_User();
            }

            if( empty(
$this->target_User ) && ! empty( $Blog ) )
            {    
// Use an owner of the current $Blog:
               
$this->target_User = & $Blog->get_owner_User();
            }
        }

        return
$this->target_User;
    }


   
/**
     * Get the list of validated renderers for this Widget. This includes stealth plugins etc.
     *
     * @return array List of validated renderer codes
     */
   
function get_renderers_validated()
    {
        if( ! isset(
$this->renderers_validated ) )
        {
            global
$Plugins;

           
$widget_Blog = & $this->get_Blog();

           
// Convert active renderers options for plugin functions below:
           
$widget_renderers = array_keys( $this->disp_params['renderers'] );

           
$this->renderers_validated = $Plugins->validate_renderer_list( $widget_renderers, array(
                   
'Blog'         => & $widget_Blog,
                   
'setting_name' => 'shared_apply_rendering'
               
) );
        }

        return
$this->renderers_validated;
    }


   
/**
     * Get content which is rendered with plugins
     *
     * @param string Source content
     * @return string Rendered content
     */
   
function get_rendered_content( $content )
    {
        if( ! isset(
$this->disp_params['renderers'] ) )
        {    
// This widget has no render settings, Return original content:
           
return $content;
        }

        global
$Plugins;

       
$widget_Blog = & $this->get_Blog();
        if( empty(
$widget_Blog ) )
        {    
// Use current collection if it is not defined, e.g. for shared widget containers:
           
global $Blog;
           
$widget_Blog = $Blog;
        }
       
$widget_renderers = $this->get_renderers_validated();

       
// Do some optional filtering on the content
        // Typically stuff that will help the content to validate
        // Useful for code display.
        // Will probably be used for validation also.
        // + APPLY RENDERING from Rendering Plugins:
       
$Plugins_admin = & get_Plugins_admin();
       
$params = array(
               
'object_type' => 'Widget',
               
'object'      => & $this,
               
'object_Blog' => & $widget_Blog
           
);
       
$fake_title = '';
       
$Plugins_admin->filter_contents( $fake_title /* by ref */, $content /* by ref */, $widget_renderers, $params /* by ref */ );

       
// Render block content with selected plugins:
       
$Plugins->render( $content, $widget_renderers, 'htmlbody', array( 'Blog' => & $widget_Blog, 'Widget' => $this ), 'Render' );
       
$Plugins->render( $content, $widget_renderers, 'htmlbody', array( 'Blog' => & $widget_Blog, 'Widget' => $this ), 'Display' );

        return
$content;
    }


   
/**
     * Get JavaScript code which helps to edit widget form
     *
     * @return string
     */
   
function get_edit_form_javascript()
    {
        return
false;
    }


   
/**
     * Get a form display mode depending on requested skin param 'form_display'
     *
     * @param string Default value for form display: 'standard', 'compact', 'nolabels', 'inline', 'grouped'
     * @return string Current form display: 'standard', 'compact', 'nolabels', 'inline', 'grouped' or another default of the widget
     */
   
function get_form_display( $default_form_display = 'standard' )
    {
       
$form_display = isset( $this->disp_params['form_display'] ) ? $this->disp_params['form_display'] : $default_form_display;
        return
in_array( $form_display, array( 'standard', 'compact', 'nolabels', 'inline', 'grouped' ) ) ? $form_display : $default_form_display;
    }


   
/**
     * Get Item's info from param by ID or slug
     *
     * @param string Param name
     * @return string
     */
   
function get_param_item_info( $param_name )
    {
       
$param_value = $this->get_param( $param_name, '' );
        if( empty(
$param_value ) )
        {    
// Param is not defined:
           
return '';
        }

       
$ItemCache = & get_ItemCache();
       
$param_value_is_ID = is_number( $param_value );
        if( ! (
$param_value_is_ID && $param_Item = & $ItemCache->get_by_ID( $param_value, false, false ) ) &&
            ! ( !
$param_value_is_ID && $param_Item = & $ItemCache->get_by_urltitle( $param_value, false, false ) ) )
        {    
// Item is not detected:
           
return get_rendering_error( T_('Item is not found.'), 'span' );
        }

       
$item_info = '';
       
$status_icons = get_visibility_statuses( 'icons' );
        if( isset(
$status_icons[ $param_Item->get( 'status' ) ] ) )
        {    
// Status colored icon:
           
$item_info .= $status_icons[ $param_Item->get( 'status' ) ];
        }
       
// Title with link to permament url:
       
$item_info .= ' '.$param_Item->get_title( array( 'link_type' => 'admin_view' ) );
       
// Icon to edit:
       
$item_info .= ' '.$param_Item->get_edit_link( array( 'text' => '#icon#' ) );

        return
$item_info;
    }


   
/**
     * Display debug message e-g on designer mode when we need to show widget when nothing to display currently
     *
     * @param string Message
     */
   
function display_debug_message( $message = NULL )
    {
        if(
$this->mode == 'designer' )
        {    
// Display message on designer mode:
           
if( $message === NULL )
            {    
// Set default message:
               
$message = 'Widget "'.$this->get_name().'" is hidden because there is nothing to display.';
            }

            if(
preg_match( '#class="[^"]*evo_widget[^"]*"#i', $this->disp_params['block_start'].$this->disp_params['block_body_start'] ) )
            {    
// If standard widget wrappers have special style class "evo_widget" we can use it:
               
echo $this->disp_params['block_start'];
               
$this->disp_title();
                echo
$this->disp_params['block_body_start'];
                echo
$message;
                echo
$this->disp_params['block_body_end'];
                echo
$this->disp_params['block_end'];
            }
            else
            {    
// Otherwise we should use more wrappers to design widgets correctly, e-g for Menu container:
               
echo $this->disp_params['block_start'];
               
$this->disp_title();
                echo
$this->disp_params['block_body_start'];
                echo
$this->get_layout_start();
                echo
$this->get_layout_item_start();
                echo
'<a href="#">(...)</a>';
                echo
$this->get_layout_item_end();
                echo
$this->get_layout_end();
                echo
$this->disp_params['block_body_end'];
                echo
$this->disp_params['block_end'];
            }
        }
    }


   
/**
     * Display an error message
     *
     * @param string Message
     */
   
function display_error_message( $message = NULL )
    {
        global
$Blog;

        if( isset(
$this->BlockCache ) )
        {    
// Do NOT cache because this widget has an error which is dispalyed only for collection admin:
           
$this->BlockCache->abort_collect();
        }

        if(
$message === NULL )
        {
           
$message = 'Unable to display widget '.$this->get_name();
        }

        echo
$this->disp_params['block_start'];
       
$this->disp_title();
        echo
$this->disp_params['block_body_start'];
        if(
check_user_perm( 'blog_admin', 'edit', false, $Blog->ID ) )
        {    
// Display error only for collection admin:
           
display_rendering_error( $message, 'span' );
        }
        echo
$this->disp_params['block_body_end'];
        echo
$this->disp_params['block_end'];
    }


   
/**
     * Create new sub-container automatically
     *
     * @param string Suffix for new sub-container
     * @return string|boolean Code of new created sub-container OR FALSE on fail
     */
   
function create_auto_subcontainer( $name_suffix = '' )
    {
        if( ! isset(
$this->cached_existing_containers ) )
        {    
// Get existing containers to avoid duplicate error on inserting:
           
global $DB;
           
$SQL = new SQL( 'Get existing widget containers before auto create new' );
           
$SQL->SELECT( 'wico_code' );
           
$SQL->FROM( 'T_widget__container' );
           
$SQL->WHERE( 'wico_coll_ID = '.$this->get_coll_ID() );
           
$SQL->WHERE_and( 'wico_skin_type = '.$DB->quote( $this->get_container_param( 'skin_type' ) ) );
           
$this->cached_existing_containers = array_map( 'strtolower', $DB->get_col( $SQL ) );
        }

       
// Set data for new creating sub-container:
       
$new_WidgetContainer = new WidgetContainer();
       
$new_WidgetContainer->set( 'coll_ID', $this->get_coll_ID() );
       
$auto_container_name = $this->get_container_param( 'name' ).$name_suffix;
       
$new_container_name = $auto_container_name;
       
$auto_container_code = strtolower( preg_replace( '/[^0-9a-z\-]+/i', '_', $new_container_name ) );
        if(
strlen( $auto_container_code ) > 125 )
        {    
// Limit widget code to avoid mysql error of long data:
           
$auto_container_code = substr( $auto_container_code, strlen( $auto_container_code ) - 123 );
        }
       
$new_container_code = $auto_container_code;
       
$c = 1;
        while(
in_array( $new_container_code, $this->cached_existing_containers ) )
        {    
// Find unique container code per collection and skin type:
           
$new_container_code = $auto_container_code.'_'.$c;
           
$new_container_name = $auto_container_name.' '.$c;
           
$c++;
        }
       
$new_WidgetContainer->set( 'code', $new_container_code );
       
$new_WidgetContainer->set( 'name', utf8_substr( $new_container_name, 0, 128 ) );
       
$new_WidgetContainer->set( 'skin_type', $this->get_container_param( 'skin_type' ) );
       
$new_WidgetContainer->set( 'main', 0 );

       
// Insert new sub-container:
       
if( ! $new_WidgetContainer->dbinsert() )
        {    
// Stop updating if some new container cannot be created:
           
return false;
        }

       
// Cache new created sub-container:
       
$this->cached_existing_containers[] = $new_container_code;
       
// Set this temp flag to update widget form with new created sub-container:
       
$this->reload_page_after_update = true;

        return
$new_container_code;
    }
}
?>