XF 2.1 Reactions

Ozzy47

Well-known member
Where could I find the template, file or js that handles the reaction menu/list found in XF? <xf:react content="{$post}" link="posts/react" list="< .js-post | .js-reactionsList" />

Using the inspector, this is what I am after:
<span class="reaction-text js-reactionText"><bdi>Like</bdi></span>
 
Okay, I was able to get done what I was after but I think it is not correct, as I have to fully replace the function fnReaction to get what I want to work, and that is not acceptable.

PHP:
<?php

namespace OzzModz\ReactionLike\XF\Template;

class Templater extends XFCP_Templater
{

    protected $defaultFunctions = [
        'anchor_target'       => 'fnAnchorTarget',
        'array_keys'          => 'fnArrayKeys',
        'array_merge'         => 'fnArrayMerge',
        'array_values'        => 'fnArrayValues',
        'attributes'          => 'fnAttributes',
        'avatar'              => 'fnAvatar',
        'base_url'            => 'fnBaseUrl',
        'bb_code'             => 'fnBbCode',
        'bb_code_type'        => 'fnBbCodeType',
        'button_icon'         => 'fnButtonIcon',
        'callable'            => 'fnCallable',
        'captcha'             => 'fnCaptcha',
        'ceil'                => 'fnCeil',
        'contains'            => 'fnContains',
        'copyright'           => 'fnCopyright',
        'core_js'             => 'fnCoreJs',
        'count'               => 'fnCount',
        'csrf_input'          => 'fnCsrfInput',
        'csrf_token'          => 'fnCsrfToken',
        'css_url'             => 'fnCssUrl',
        'date'                => 'fnDate',
        'date_from_format'    => 'fnDateFromFormat',
        'date_dynamic'        => 'fnDateDynamic',
        'date_time'           => 'fnDateTime',
        'debug_url'           => 'fnDebugUrl',
        'display_totals'      => 'fnDisplayTotals',
        'dump'                => 'fnDump',
        'dump_simple'         => 'fnDumpSimple',
        'empty'               => 'fnEmpty',
        'fa_weight'           => 'fnFaWeight',
        'file_size'           => 'fnFileSize',
        'floor'               => 'fnFloor',
        'gravatar_url'        => 'fnGravatarUrl',
        'highlight'           => 'fnHighlight',
        'in_array'            => 'fnInArray',
        'is_array'            => 'fnIsArray',
        'is_scalar'           => 'fnIsScalar',
        'is_addon_active'     => 'fnIsAddonActive',
        'is_editor_capable'   => 'fnIsEditorCapable',
        'is_toggled'          => 'fnIsToggled',
        'js_url'              => 'fnJsUrl',
        'key_exists'          => 'fnKeyExists',
        'last_pages'          => 'fnLastPages',
        'likes'               => 'fnLikes',
        'likes_content'       => 'fnLikesContent',
        'link'                => 'fnLink',
        'link_type'           => 'fnLinkType',
        'min'                 => 'fnMin',
        'max'                 => 'fnMax',
        'max_length'          => 'fnMaxLength',
        'media_sites'         => 'fnMediaSites',
        'mustache'            => 'fnMustache',
        'number'              => 'fnNumber',
        'number_short'        => 'fnNumberShort',
        'named_colors'        => 'fnNamedColors',
        'page_description'    => 'fnPageDescription',
        'page_h1'             => 'fnPageH1',
        'page_nav'            => 'fnPageNav',
        'page_title'          => 'fnPageTitle',
        'parens'              => 'fnParens',
        'parse_less_color'    => 'fnParseLessColor',
        'prefix'              => 'fnPrefix',
        'prefix_group'        => 'fnPrefixGroup',
        'prefix_title'        => 'fnPrefixTitle',
        'property'            => 'fnProperty',
        'rand'                => 'fnRand',
        'range'               => 'fnRange',
        'react'               => 'fnReact',
        'alert_reaction'      => 'fnAlertReaction',
        'reaction'            => 'fnReaction',
        'reaction_title'      => 'fnReactionTitle',
          'reaction_titles'     => 'fnReactionTitles',
        'reactions'           => 'fnReactions',
        'reactions_content'   => 'fnReactionsContent',
        'reactions_summary'   => 'fnReactionsSummary',
        'redirect_input'      => 'fnRedirectInput',
        'repeat'              => 'fnRepeat',
        'repeat_raw'          => 'fnRepeatRaw',
        'short_to_emoji'      => 'fnShortToEmoji',
        'show_ignored'        => 'fnShowIgnored',
        'smilie'              => 'fnSmilie',
        'snippet'             => 'fnSnippet',
        'strlen'              => 'fnStrlen',
        'structured_text'     => 'fnStructuredText',
        'templater'           => 'fnTemplater',
        'time'                => 'fnTime',
        'transparent_img'     => 'fnTransparentImg',
        'trim'                => 'fnTrim',
        'unique_id'           => 'fnUniqueId',
        'user_activity'       => 'fnUserActivity',
        'user_banners'        => 'fnUserBanners',
        'user_blurb'          => 'fnUserBlurb',
        'user_title'          => 'fnUserTitle',
        'username_link'       => 'fnUsernameLink',
        'username_link_email' => 'fnUsernameLinkEmail',
        'widget_data'         => 'fnWidgetData'
    ];
 
 public function fnReactionTitles($templater, &$escape, $reactionId)
    {
        return \XF::phrase('ozzmodz_reactionlike_react');
    }
 
    public function fnReaction($templater, &$escape, array $config)
    {
        $escape = false;

        $baseConfig = [
            'id' => null,
            'class' => '',
            'content' => null,
            'link' => '',
            'params' => [],
            'list' => null,
            'hasreaction' => false,
            'init' => false,
            'showtitle' => false,
            'appendtitle' => '',
            'small' => false,
            'medium' => false,
            'tooltip' => false,
            'routerType' => 'public'
        ];
        $config = array_replace($baseConfig, $config);

        $hasReaction = $config['hasreaction'];
        if (is_string($config['hasreaction']) && $config['hasreaction'] == 'false')
        {
            $hasReaction = false;
        }

        $reactionId = $config['id'];
        if (!is_int($reactionId))
        {
            $reactionId = $reactionId['reaction_id'];
        }

        if (!$reactionId)
        {
            return '';
        }

        $reactionCache = $this->app->container('reactions');
        if (!isset($reactionCache[$reactionId]))
        {
            return '';
        }
        $reaction = $reactionCache[$reactionId];

        $reactionTitle = htmlspecialchars($this->fn('reaction_title', [$reaction]));
        $reactionTitles = htmlspecialchars($this->fn('reaction_titles', [$reaction]));
        $pather = $this->pather;

        $tooltip = '';
        if ($config['tooltip'])
        {
            $tooltip = ' data-xf-init="tooltip"';
        }

        $html = '<i aria-hidden="true"></i>';
        if (empty($reaction['sprite_params']))
        {
            $url = htmlspecialchars($pather ? $pather($reaction['image_url'], 'base') : $reaction['image_url']);
            $srcSet = '';
            if (!empty($reaction['image_url_2x']))
            {
                $url2x = htmlspecialchars($pather ? $pather($reaction['image_url_2x'], 'base') : $reaction['image_url_2x']);
                $srcSet = 'srcset="' . $url2x . ' 2x"';
            }

            $html .= '<img src="' . $url . '" ' . $srcSet . ' class="reaction-image js-reaction" alt="' . $reactionTitle . '" title="' . $reactionTitle . '"' . $tooltip . ' />';
        }
        else
        {
            // embed a data URI to avoid a request that doesn't respect paths fully
            $html .= '<img src="' . self::TRANSPARENT_IMG_URI . '" class="reaction-sprite js-reaction" alt="' . $reactionTitle . '" title="' . $reactionTitle . '"' . $tooltip . ' />';
        }

        if ($config['showtitle'])
        {
            $displayTitle = '<bdi>' . $reactionTitles . '</bdi>';
            if ($config['appendtitle'])
            {
                $displayTitle .= ' ' . $config['appendtitle'];
            }
            $html .= ' <span class="reaction-text js-reactionText">' . $displayTitle . '</span>';
        }

        $init = '';
        if ($config['init'])
        {
            $init = ' data-xf-init="reaction"';
            if ($config['list'])
            {
                $init .= ' data-reaction-list="' . $config['list'] . '"';
            }
        }

        $unhandledAttrs = $this->processUnhandledAttributes(array_diff_key($config, $baseConfig));

        $tag = 'span';
        $href = '';
        if ($config['link'])
        {
            if (is_array($config['link']))
            {
                $link = $config['link'];
                $config['link'] = $link[0];
                if (!$config['params'])
                {
                    $config['params'] = $link[1];
                }
            }

            $tag = 'a';
            $href = $this->app->router($config['routerType'])->buildLink(
                $config['link'], $config['content'], $config['params']
            );
        }

        if ($config['tooltip'] && !$config['link'])
        {
            $tag = 'a';
            $href = '#';
        }

        return '<' . $tag . ($href ? ' href="' . $href . '"' : '')
            . $unhandledAttrs
            . ' class="reaction' . ($config['small'] ? ' reaction--small' : '') . ($config['medium'] ? ' reaction--medium' : '') . ($config['class'] ? ' ' . $config['class'] : '') . ($hasReaction ? ' has-reaction' : '') . (!$hasReaction && $config['init'] ? ' reaction--imageHidden' : '') . ' reaction--' . $reactionId . '"'
            . ' data-reaction-id="' . $reactionId . '"' . $init . '>' . $html . '</' . $tag . '>';
    }
}

Now in the function fnReaction I added $reactionTitles = htmlspecialchars($this->fn('reaction_titles', [$reaction])); and changed this, $displayTitle = '<bdi>' . $reactionTitle . '</bdi>'; to this, $displayTitle = '<bdi>' . $reactionTitles . '</bdi>';

I tried removing all un-necessary code and return to parent function, but removing any code results in the menu not working or not showing up.
 
Are you sure you need fnReactionTitles() inside the $defaultFunctions array?

If yes, you should be able to do something like:

PHP:
protected $defaultFunctions = array_merge(parent::$this->defaultFunctions, ['reaction_titles'     => 'fnReactionTitles');
 
Are you sure you need fnReactionTitles() inside the $defaultFunctions array?

If yes, you should be able to do something like:

PHP:
protected $defaultFunctions = array_merge(parent::$this->defaultFunctions, ['reaction_titles'     => 'fnReactionTitles');

Thanks, I'll try this out in a bit.
 
I am not sure what you really want to do, but maybe this code is sufficient:

PHP:
public function fnReaction($templater, &$escape, array $config)
{
    $parent = parent::fnReaction($templater, &$escape, $config);

    $pattern = '#<span class="reaction-text js-reactionText">(.*)</span>#U';

    $peplace = '<span class="reaction-text js-reactionText">$0 ' . \XF::phrase('ozzmodz_reactionlike_react') .'</span>';

    $output = preg_replace($pattern, $replace, $parent);

    return $output;
}

Code is untested, but I think it should work similar to this..
 
That throws an error:
An exception occurred: [ParseError] syntax error, unexpected '&' in src/addons/OzzModz/ReactionLike/XF/Template/Templater.php on line 116

Line 116 is $parent = parent::fnReaction($templater, &$escape, $config);
 
Another try ($1 instead of $0 in $replace):

PHP:
public function fnReaction($templater, &$escape, array $config)
{
    $parent = parent::fnReaction($templater, $escape, $config);

    $pattern = '#<span class="reaction-text js-reactionText">(.*)</span>#U';

    $peplace = '<span class="reaction-text js-reactionText">$1 ' . \XF::phrase('ozzmodz_reactionlike_react') .'</span>';

    $output = preg_replace($pattern, $replace, $parent);

    return $output;
}
 
Without:
HTML:
<a href="/posts/81087/react?reaction_id=1" class="reaction reaction--small actionBar-action actionBar-action--reaction reaction--imageHidden reaction--1" data-reaction-id="1" data-xf-init="reaction" data-reaction-list="< .js-post | .js-reactionsList"><i aria-hidden="true"></i><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="reaction-sprite js-reaction" alt="Like" title="Like" /> <span class="reaction-text js-reactionText"><bdi>Like</bdi></span></a>

With:
HTML:
<a href="/posts/81087/react?reaction_id=1" class="reaction reaction--small actionBar-action actionBar-action--reaction reaction--imageHidden reaction--1" data-reaction-id="1" data-xf-init="reaction" data-reaction-list="< .js-post | .js-reactionsList"><i aria-hidden="true"></i><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="reaction-sprite js-reaction" alt="Like" title="Like" /> </a>

If you look in the without example, you see <bdi>Like</bdi>, that is where I want to replace the Like phrase with my custom one.
 
o.k. there was another typo :D

Replace $peplace with $replace.

P.S. If you want to remove the original phrase, just remove the $1 ;)
 
Okay, getting closer. now it is showing "Like React", both phrases.

Remove the $0 . part:

PHP:
public function fnReaction($templater, &$escape, array $config)
{
    $parent = parent::fnReaction($templater, $escape, $config);

    $pattern = '#<span class="reaction-text js-reactionText">(.*)</span>#U';

    $replace = '<span class="reaction-text js-reactionText">' . \XF::phrase('ozzmodz_reactionlike_react') . '</span>';

    $output = preg_replace($pattern, $replace, $parent);

    return $output;
}
 
Remove the $0 . part:

PHP:
public function fnReaction($templater, &$escape, array $config)
{
    $parent = parent::fnReaction($templater, $escape, $config);

    $pattern = '#<span class="reaction-text js-reactionText">(.*)</span>#U';

    $replace = '<span class="reaction-text js-reactionText">' . \XF::phrase('ozzmodz_reactionlike_react') . '</span>';

    $output = preg_replace($pattern, $replace, $parent);

    return $output;
}

OMG, we finally got it, thank you both very much. :) There is no way I could have figured that out on my own.
 
Back
Top Bottom