Seditio Source
Root |
./othercms/b2evolution_7.2.3/plugins/infodots_plugin/_infodots.plugin.php
<?php
/**
 * This file implements the Info dots renderer plugin for b2evolution
 *
 * Info dots formatting, like [infodot:1234:40:60:20ex]text of the info dot additional info text including <a href="/">link text</a>[enddot]
 *
 * b2evolution - {@link http://b2evolution.net/}
 * Released under GNU GPL License - {@link http://b2evolution.net/about/gnu-gpl-license}
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
 *
 * @package plugins
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );


/**
 * @package plugins
 */
class infodots_plugin extends Plugin
{
    var
$code = 'b2evoDot';
    var
$name = 'Info dots renderer';
    var
$priority = 95;
    var
$version = '7.2.3';
    var
$group = 'rendering';
    var
$short_desc;
    var
$long_desc;
    var
$help_topic = 'infodots-plugin';
    var
$number_of_installs = 1;

   
/*
     * Internal vars
     */
   
var $search_text;
    var
$replace_func;
    var
$dots = NULL;
    var
$object_ID = 0;
    var
$loaded_objects = NULL;
    var
$dot_numbers = NULL;

   
/**
     * Init
     */
   
function PluginInit( & $params )
    {
       
$this->short_desc = T_('Info dots formatting e-g [infodot:1234:40:60:20ex]html text[enddot]');
       
$this->long_desc = T_('This plugin allows to render info dots over images by using the syntax [infodot:1234:40:60:20ex]html text[enddot] for example');
    }


   
/**
     * Define here default custom settings that are to be made available
     *     in the backoffice for collections, private messages and newsletters.
     *
     * @param array Associative array of parameters.
     * @return array See {@link Plugin::get_custom_setting_definitions()}.
     */
   
function get_custom_setting_definitions( & $params )
    {
        return array(
           
'coll_min_width' => array(
                   
'label' => T_('Min width'),
                   
'type' => 'integer',
                   
'size' => 4,
                   
'defaultvalue' => 400,
                   
'note' => T_('Enter the minimum pixel width an image must have for dots to be displayed.')
                ),
        );
    }


   
/**
     * Define here default collection/blog settings that are to be made available in the backoffice.
     *
     * @return array See {@link Plugin::GetDefaultSettings()}.
     */
   
function get_coll_setting_definitions( & $params )
    {
       
$default_params = array_merge( $params, array(
               
'default_comment_rendering' => 'never',
               
'default_post_rendering' => 'opt-out'
           
) );

        return
parent::get_coll_setting_definitions( $default_params );
    }


   
/**
     * Include JS/CSS files in HTML head
     */
   
function init_html_head()
    {
        global
$Collection, $Blog;

        if( ! isset(
$Blog ) || (
           
$this->get_coll_setting( 'coll_apply_rendering', $Blog ) == 'never' &&
           
$this->get_coll_setting( 'coll_apply_comment_rendering', $Blog ) == 'never' ) )
        {    
// Don't load css/js files when plugin is not enabled
           
return;
        }

       
$this->require_css_async( 'infodots.css', false, 'footerlines' );

       
// Bubbletip
       
require_js_defer( '#jquery#', 'blog', false, '#', 'footerlines' );
       
require_js_defer( 'customized:jquery/bubbletip/js/jquery.bubbletip.min.js', 'blog', false, '#', 'footerlines' );
       
require_css_async( 'customized:jquery/bubbletip/css/jquery.bubbletip.css', 'blog', NULL, NULL, '#', false, 'footerlines' );
       
$this->require_js_defer( 'infodots.init.js', false, 'footerlines' );
    }


   
/**
     * Event handler: Called at the beginning of the skin's HTML HEAD section.
     *
     * Use this to add any HTML HEAD lines (like CSS styles or links to resource files (CSS, JavaScript, ..)).
     *
     * @param array Associative array of parameters
     */
   
function SkinBeginHtmlHead( & $params )
    {
       
$this->init_html_head();
    }


   
/**
     * Event handler: Called when ending the admin html head section.
     *
     * @param array Associative array of parameters
     * @return boolean did we do something?
     */
   
function AdminEndHtmlHead( & $params )
    {
       
$this->init_html_head();
    }


   
/**
     * Perform rendering
     *
     * Renders code:
     * from [infodot:1234:40:60:20ex]html text[enddot]
     * to <div class="infodots_info" id="infodot_1234_1" data-xy="40:60" style="width:20ex:>html text</div>
     * This html <div> is hidden by an inline CSS style="display: none;".
     * This plugin event is called only once to render content.
     * This only generates the content divs. The red dots themselves are added later to the evo_image_block divs, in the "RenderItemAttachment" event.
     *
     * This event also collects all dots in the array $this->dots.
     *
     * @see Plugin::RenderItemAsHtml()
     */
   
function RenderItemAsHtml( & $params )
    {
        if( empty(
$params['Item'] ) )
        {    
// This plugin can works only with items:
           
return false;
        }

       
$params['data'] = $this->render_infodot_captions( 'itm_'.$params['Item']->ID, $params['data'] );

        return
true;
    }


   
/**
     * Do the same as for HTML.
     *
     * @see RenderItemAsHtml()
     */
   
function RenderItemAsXml( & $params )
    {
       
$this->RenderItemAsHtml( $params );
    }


   
/**
     * Perform rendering of Message content
     *
     * NOTE: Use default coll settings of comments as messages settings
     *
     * @see Plugin::RenderMessageAsHtml()
     */
   
function RenderMessageAsHtml( & $params )
    {
       
// This plugin cannot works with messages:
       
return true;
    }


   
/**
     * Perform rendering of Email content
     *
     * NOTE: Use default coll settings of comments as messages settings
     *
     * @see Plugin::RenderEmailAsHtml()
     */
   
function RenderEmailAsHtml( & $params )
    {
       
// This plugin cannot works with emails:
       
return true;
    }


   
/**
     * Render comments if required
     *
     * Renders code:
     * from [infodot:1234:40:60:20ex]html text[enddot]
     * to <div class="infodots_info" id="infodot_1234_1" data-xy="40:60" style="width:20ex:>html text</div>
     * This html <div> is hidden by inline CSS style="display: none;".
     * This plugin event is called only once to render content.
     * This only generates the content divs. The red dots themselves are added later to the evo_image_block divs, in the "RenderCommentAttachment" event.
     *
     * This event also collects all dots in the array $this->dots.
     *
     * @see Plugin::FilterCommentContent()
     */
   
function FilterCommentContent( & $params )
    {
       
$Comment = & $params['Comment'];

        if(
in_array( $this->code, $Comment->get_renderers_validated() ) )
        {    
// If apply_comment_rendering is set to render:
           
$params['data'] = $this->render_infodot_captions( 'cmt_'.$Comment->ID, $params['data'] );
        }
    }


   
/**
     * Render infodots from like [infodot:1234:40:60:20ex]html text[enddot]
     *    to <div class="infodots_info" id="infodot_1234_1" data-xy="40:60" style="display:none">html text</div>
     *
     * @param string Object ID: for example, 'itm_123', 'cmt_456'.
     * @param string Source content
     * @return string Rendered content
     */
   
function render_infodot_captions( $object_ID, $content )
    {
       
$this->dot_numbers = NULL;
       
$this->object_ID = $object_ID;

       
$content = replace_outside_code_tags( '#((<br />|<p>)\r?\n?)?\[infodot:(\d+):(-?\d+[pxecm%]*):(-?\d+[pxecm%]*)(:\d+[pxecm%]*)?\](.+?)\[enddot\](\r?\n?(<br />|</p>))?#is',
                array(
$this, 'load_infodot_from_source' ),
               
$content, 'replace_content_callback' );

       
$this->loaded_objects[ $this->object_ID ] = 1;

        return
$content;
    }


   
/**
     * Callback function to load a dot from NOT rendered content
     *
     * @param array Matches
     * @param boolean TRUE is used only to load dot without returning of tooltip template
     * @return string Empty string to don't display the dot template in content, It is printed out before image tag
     */
   
function load_infodot_from_source( $matches, $only_load_dot = false )
    {
       
$link_ID = intval( $matches[3] );

        if( empty(
$link_ID ) || empty( $matches ) || empty( $this->object_ID ) )
        {    
// Skip this incorrect match
           
return;
        }

       
$LinkCache = & get_LinkCache();
       
$Link = & $LinkCache->get_by_ID( $link_ID, false, false );
        if( !
$Link )
        {    
// Inform about invalid Link ID
           
return '<div style="color:#F00"><b>'.T_('Invalid Link ID').' - '.$matches[0].'</b></div>';
        }

        if(
$this->dot_numbers === NULL )
        {    
// Init dot numbers array first time
           
$this->dot_numbers = array();
        }

        if( ! isset(
$this->dot_numbers[ $link_ID ] ) )
        {    
// Start to calculate number of the dots for current Link object
           
$this->dot_numbers[ $link_ID ] = 1;
        }

        if( ! isset(
$this->loaded_objects[ $this->object_ID ] ) )
        {    
// Load dots only once
           
if( $this->dots === NULL )
            {    
// Init dots array first time
               
$this->dots = array();
            }

            if( ! isset(
$this->dots[ $link_ID ] ) )
            {    
// Init sub array for each Link
               
$this->dots[ $link_ID ] = array();
            }

           
// Add dot
           
$this->dots[ $link_ID ][] = array(
                   
'x' => $matches[4].( strlen( intval( $matches[4] ) ) == strlen( $matches[4] ) ? 'px' : '' ), // Left
                   
'y' => $matches[5].( strlen( intval( $matches[5] ) ) == strlen( $matches[5] ) ? 'px' : '' ), // Top
               
);
        }

        if(
$only_load_dot )
        {    
// Exit here to don't execute a code below
           
return;
        }

       
// We inline style="display:none" to make sure this doesn't display on screen BEFORE the CSS files are defer-loaded:
       
$tooltip_styles = array( 'display:none' );

       
$dot_num = $this->dot_numbers[ $link_ID ];
        if( ! empty(
$matches[6] ) )
        {    
// Set css style for width:
           
$tooltip_width = substr( $matches[6], 1 );
           
$tooltip_width = ( strlen( intval( $tooltip_width ) ) == strlen( $tooltip_width ) ? $tooltip_width.'px' : $tooltip_width );
           
$tooltip_styles[] = 'width:'.$tooltip_width;
        }
       
$dot_xy = ' data-xy="'.$this->dots[ $link_ID ][ $dot_num - 1 ]['x'].':'.$this->dots[ $link_ID ][ $dot_num - 1 ]['y'].'"';

       
$this->dot_numbers[ $link_ID ]++;

       
// Print this element that will be used for tooltip of the dot
       
return '<div class="infodots_info" id="infodot_'.$link_ID.'_'.$dot_num.'"'.$dot_xy
               
.' style="'.implode( ';', $tooltip_styles ).'">'
               
.balance_tags( $matches[7] )
            .
'</div>'."\n";
    }


   
/**
     * Callback function to load a dot from the rendered content
     *
     * @param array Matches
     */
   
function load_infodot_from_rendered_content( $matches )
    {
       
// Load a dot from the rendered content
       
$this->load_infodot_from_source( array(
               
0 => $matches[0],
               
3 => $matches[1],
               
4 => $matches[4],
               
5 => $matches[5]
            ),
true );
    }


   
/**
     * Render the dots before <img> tag
     *
     * Renders code:
     * from the before collected array $this->dots
     * OR from the prerendered content <div class="infodots_info" id="infodot_1234_1" data-xy="40:60">html text</div>
     * to <div class="infodots_dot" rel="infodot_1234_1" style="left:40px;top:60px"></div>
     *
     * @param array Associative array of parameters. $params['File'] - attachment, $params['data'] - output
     * @param string Content of the Item/Comment
     */
   
function render_infodots( & $params, $content )
    {
        if( empty(
$params['File'] ) || empty( $params['Link'] ) )
        {    
// Check input data
           
return;
        }

       
$File = $params['File'];
       
$Link = $params['Link'];

        if( !
$File->is_image() )
        {    
// This plugin works only with image files
           
return;
        }

        if( (
$LinkOwner = & $Link->get_LinkOwner() ) === false || ( $Collection = $Blog = & $LinkOwner->get_Blog() ) === false )
        {    
// Couldn't get Blog object
           
return;
        }

        global
$thumbnail_sizes;
       
$thumbnail_width = isset( $thumbnail_sizes[ $params['image_size'] ] ) ? $thumbnail_sizes[ $params['image_size'] ][1] : 0;

        if(
$File->get_image_size( 'width' ) < $this->get_coll_setting( 'coll_min_width', $Blog ) ||
           
$thumbnail_width < $this->get_coll_setting( 'coll_min_width', $Blog ) )
        {    
// Don't draw a dot on image if width is less than setting value
           
return;
        }

        if( ! isset(
$this->loaded_objects[ $this->object_ID ] ) )
        {    
// Load the info dots if they were not loaded before:
           
replace_outside_code_tags( '#<div class="infodots_info" id="infodot_(\d+)_(\d+)" (data-)?xy="(-?\d+[pxecm%]*):(-?\d+[pxecm%]*)"[^>]*>(.+?)</div>#is',
                array(
$this, 'load_infodot_from_rendered_content' ), $content, 'replace_content_callback' );
           
$this->loaded_objects[ $this->object_ID ] = 1;
        }

        if( empty(
$this->dots[ $Link->ID ] ) )
        {    
// No dots for this Link
           
return;
        }

       
$before_image = '<div class="infodots_image">'."\n";
        foreach(
$this->dots[ $Link->ID ] as $d => $dot )
        {    
// Init html element for each dot
           
$before_image .= '<div class="infodots_dot" rel="infodot_'.$Link->ID.'_'.( $d + 1 ).'" style="left:'.$dot['x'].';top:'.$dot['y'].'"></div>'."\n";
        }

       
// Append info dots html to current image tag
       
$params['before_image'] = $params['before_image'].$before_image;
       
$params['after_image'] = '</div>'.$params['after_image'];
    }


   
/**
     * Event handler: Called prepare params before rendering attachments of item contents.
     *
     * This will render the red dots in the same <div> as the image, which is important for using relative positioning based on the image coordinates.
     *
     * Renders the red dots before each image by getting them from array $this->dots if it was initialized during RenderItemAsHtml().
     * If RenderItemAsHtml() was not called (which happens if content was already pre-rendered) then the array $this->dots is built by preg_match from prerendered content
     * from hidden div <div class="infodots_info" id="infodot_1234_1" data-xy="40:60" style="width:20ex:>html text</div>
     *
     * The dots are rendered as <div class="infodots_image"> <div class="infodots_dot" rel="infodot_1234_1" style="left:40px;top:60px"></div> </div> before attached image.
     *
     * @param array Associative array of parameters. $params['File'] - attachment, $params['data'] - output
     * @param boolean TRUE - when render in comments
     * @return boolean true if plugin rendered this attachment
     */
   
function PrepareForRenderItemAttachment( & $params )
    {
        if( empty(
$params['Item'] ) || empty( $params['Link'] ) )
        {    
// Wrong input data, Exit here:
           
return false;
        }

       
$Item = & $params['Item'];

       
$this->dot_numbers = NULL;
       
$this->object_ID = 'itm_'.$Item->ID;

       
// Render dots:
       
$this->render_infodots( $params, $Item->get_prerendered_content( 'htmlbody' ) );

        return
true;
    }


   
/**
     * Event handler: Called prepare params before rendering attachments of comment content.
     *
     * See sister function RenderItemAttachment() above for more details.
     *
     * @param array Associative array of parameters. $params['File'] - attachment, $params['data'] - output
     * @return boolean true if plugin rendered this attachment
     */
   
function PrepareForRenderCommentAttachment( & $params )
    {
        if( empty(
$params['Comment'] ) || empty( $params['Link'] ) )
        {    
// Wrong input data, Exit here:
           
return false;
        }

       
$Comment = & $params['Comment'];

       
$this->dot_numbers = NULL;
       
$this->object_ID = 'cmt_'.$Comment->ID;

       
// Render dots:
       
$this->render_infodots( $params, $Comment->get_prerendered_content( 'htmlbody' ) );

        return
true;
    }
}

?>