<?php
namespace ThemeHouse\Reactions\XF\Template;
use XF\Mvc\Entity\Entity;
class Templater extends XFCP_Templater
{
public function addDefaultHandlers()
{
parent::addDefaultHandlers();
$this->addFunctions([
'th_reacts' => 'fnThReacts',
'th_reaction' => 'fnThReaction',
'th_reaction_list' => 'fnThReactionList',
'th_reaction_content_list' => 'fnThReactionContentList',
'th_reaction_bar_position' => 'fnThReactionBarPosition',
'th_reaction_trigger_icon' => 'fnThReactionTriggerIcon',
'th_react_handler_status' => 'fnThReactHandlerStatus',
'th_react_handlers' => 'fnThReactHandlers',
]);
}
public function fnThReactionTriggerIcon($templater, &$escape)
{
$escape = false;
$styleProperties = $this->getReactionStyleProperties();
switch ($styleProperties['triggerType']) {
case 'image_path':
// {{ base_url(property('th_triggerValue_reactions'), false) }}
return '<img src="' . $this->fnBaseUrl(
$templater,
$escape,
$styleProperties['triggerValue'],
false
) . '" class="th_reactions__trigger__image">';
case 'glyph_class':
return '<i class="' . $styleProperties['triggerValue'] . '" aria-hidden="true"></i>';
case 'template':
default:
return $this->renderTemplate($styleProperties['triggerValue']);
}
}
/**
* @param $templater
* @param $escape
* @param Entity $content
* @param $reacts
* @param array $contentDetails
* @param array $templateVars
* @return string
* @throws \Exception
*/
public function fnThReacts(
$templater,
&$escape,
Entity $content,
$reacts,
array $contentDetails,
$showReactLink = false,
array $templateVars = []
) {
$escape = false;
return $this->renderTemplate('public:th_display_bar_internal_reactions', array_merge([
'reactionList' => $this->preEscaped($this->fnThReactionList(
$templater,
$escape,
$content,
$reacts,
$contentDetails
), 'html'),
'reactionContentList' => $this->preEscaped($this->fnThReactionContentList(
$templater,
$escape,
$content,
$reacts,
$contentDetails,
$showReactLink
), 'html'),
], $templateVars));
}
/**
* @param $templater
* @param $escape
* @param $reaction
* @param string $class
* @return bool|mixed
*/
public function fnThReaction(
$templater,
&$escape,
$reaction,
$class = ''
) {
if (is_scalar($reaction)) {
$reactions = $this->app->container('thReactions');
if (array_key_exists($reaction, $reactions)) {
$reaction = $reactions[$reaction];
} else {
return false;
}
}
$escape = false;
/** @var \ThemeHouse\Reactions\XF\Str\Formatter $formatter */
$formatter = $this->app->stringFormatter();
return $formatter->getReactionHtml($reaction, $this->getReactionStyleProperties(), $class);
}
/**
* @param $templater
* @param $escape
* @param Entity $content
* @param $reactUsers
* @param array $contentDetails
* @return bool|string
* @throws \Exception
*/
public function fnThReactionList($templater, &$escape, Entity $content, $reactUsers, array $contentDetails)
{
$escape = false;
$this->processContentDetails($contentDetails);
$visitor = \XF::visitor();
if (!$this->fnThReactHandlerStatus(
$templater,
$escape,
$contentDetails['contentType']
) || !$visitor->user_id || $visitor->user_id == $contentDetails['contentUserId']) {
return false;
}
$reactHandler = $this->getReactHandlerByEntity($content);
$reactions = $reactHandler->buildValidReactionsList('react');
$userReacts = [];
$currentCount = $cannotUnreact = array();
if (!empty($reactUsers)) {
foreach ($reactUsers as $reactUser) {
if ($reactUser->react_user_id == $visitor->user_id) {
$userReacts[] = $reactUser;
if (!$reactHandler->canUnreactContent($content, $reactUser)) {
$cannotUnreact[] = $reactUser->reaction_id;
continue;
}
if (array_key_exists($reactUser->reaction_id, $reactions)) {
$currentCount[] = $reactUser->reaction_id;
unset($reactions[$reactUser->reaction_id]);
}
}
}
}
$exceedsMax = $this->getReactedContentRepo()->checkIfCurrentMaxExceedsPermissions($visitor, $content);
$output = [
'userReacts' => $userReacts,
'reactions' => false,
'unreactLink' => false,
'modifyReactLink' => false
];
/** @var \ThemeHouse\Reactions\XF\Str\Formatter $formatter */
$formatter = $this->app->stringFormatter();
if (!$exceedsMax) {
foreach ($reactions as $reactionId => $reaction) {
if ($reaction['random'] && count($reactions) === 1) {
continue;
}
if (in_array($reactionId, $cannotUnreact)) {
continue;
}
$output['reactions'][$reaction['reaction_id']] = [
'url' => $reactHandler->getReactLink($contentDetails['contentId'], $reactionId),
'rendered' => $this->preEscaped($formatter->getReactionHtml(
$reaction,
$this->getReactionStyleProperties()
), 'html'),
'is_default' => $reaction['is_default'],
];
}
}
if (!$exceedsMax && count($currentCount) && !empty($output['reactions'])) {
$output['modifyReactLink'] = $reactHandler->getModifyReactLink($contentDetails['contentId']);
}
if (($exceedsMax || empty($output['reactions'])) && !empty($userReacts)) {
$output['unreactLink'] = $reactHandler->getUnreactAllLink($contentDetails['contentId']);
}
return $this->renderTemplate('public:th_reaction_list_reactions', $output);
}
/**
* @param $templater
* @param $escape
* @param Entity $content
* @param $contentReactions
* @param array $contentDetails
* @param bool $showReactLink
* @param int $limit
* @param string $class
* @param bool $forceMinimal
* @return bool|string
* @throws \Exception
*/
public function fnThReactionContentList(
$templater,
&$escape,
Entity $content,
$contentReactions,
array $contentDetails,
$showReactLink = false,
$limit = 3,
$cutOff = 0,
$class = '',
$forceMinimal = false
) {
$escape = false;
$this->processContentDetails($contentDetails);
if (!$this->fnThReactHandlerStatus(
$templater,
$escape,
$contentDetails['contentType']
) || empty($contentReactions)) {
return false;
}
$output = [
'reacts',
'reactionListUrl' => (!$forceMinimal ? $this->getReactHandlerByEntity($content)->getListLink($contentDetails['contentId']) : false)
];
$reactions = \XF::app()->container('thReactions');
if (empty($reactions)) {
return false;
}
$reactionsCount = [];
foreach ($contentReactions as &$contentReaction) {
if (isset($reactionsCount[$contentReaction['reaction_id']])) {
$reactionsCount[$contentReaction['reaction_id']] += 1;
} else {
$reactionsCount[$contentReaction['reaction_id']] = 1;
}
}
if (empty($reactionsCount)) {
return false;
}
/** @var \ThemeHouse\Reactions\XF\Str\Formatter $formatter */
$formatter = $this->app->stringFormatter();
foreach ($reactionsCount as $reactionId => $count) {
if (!isset($reactions[$reactionId])) {
continue;
}
$reaction = $reactions[$reactionId];
$itemOutput = [
'count' => $count,
'rendered' => $this->preEscaped($formatter->getReactionHtml(
$reaction,
$this->getReactionStyleProperties(),
$class
), 'html'),
'title' => ((count($reactionsCount) < $limit && !$forceMinimal) ? $reaction['title'] : null),
];
if ($showReactLink) {
$reactHandler = $this->getReactHandlerByEntity($content);
$itemOutput['url'] = $reactHandler->getReactLink($contentDetails['contentId'], $reactionId);
$itemOutput['canReact'] = $reactHandler->canReactContent($content);
}
$output['reacts'][] = $itemOutput;
}
usort($output['reacts'], function ($a, $b) {
return $a['count'] <= $b['count'];
});
if ($cutOff) {
$output['reacts'] = array_slice($output['reacts'], 0, $cutOff);
}
return $this->renderTemplate('public:th_reaction_content_list_reactions', $output);
}
/**
* @param $templater
* @param $escape
* @param $contentType
* @return bool|int|null|string
* @throws \Exception
*/
public function fnThReactionBarPosition($templater, &$escape, $contentType)
{
$reactHandler = $this->getReactHandlerByType($contentType);
if (!$reactHandler) {
return false;
}
if (!$positions = $reactHandler->getReactionBarPositions()) {
return false;
}
/** @var array $positions */
$styleProperties = $this->getReactionStyleProperties();
if (empty($styleProperties['reactionBarPositions'][$contentType])) {
reset($positions);
return key($positions);
}
return $styleProperties['reactionBarPositions'][$contentType];
}
/**
* @param $templater
* @param $escape
* @param $contentType
* @return bool
* @throws \Exception
*/
public function fnThReactHandlerStatus($templater, &$escape, $contentType)
{
$reactHandler = $this->getReactHandlerByType($contentType);
if (!$reactHandler) {
return false;
}
return $reactHandler->getStatus();
}
/**
* @param $templater
* @param $escape
* @return \ThemeHouse\Reactions\React\AbstractHandler[]
* @throws \Exception
*/
public function fnThReactHandlers($templater, &$escape)
{
return $this->getReactHandlerRepo()->getReactHandlers();
}
/**
* @param array $contentDetails
*/
protected function processContentDetails(array &$contentDetails)
{
if (!count(array_filter(array_keys($contentDetails), 'is_string')) > 0) {
$contentDetails = [
'contentType' => $contentDetails[0],
'contentId' => $contentDetails[1],
'contentUserId' => (isset($contentDetails[2]) ? $contentDetails[2] : null)
];
}
}
/**
* @return array
*/
protected function getReactionStyleProperties()
{
$style = $this->getStyle();
if (!$style) {
$style = \XF::app()->container('style.fallback');
}
return [
'imageDimensions' => $style->getProperty('thReactionsImageDimensions'),
'triggerType' => $style->getProperty('th_triggerType_reactions'),
'triggerValue' => $style->getProperty('th_triggerValue_reactions'),
'reactionBarPositions' => $style->getProperty('th_reactionBar_location_reactions')
];
}
/**
* @return \ThemeHouse\Reactions\Repository\Reaction
*/
protected function getThReactionRepo()
{
/** @noinspection PhpIncompatibleReturnTypeInspection */
return \XF::Repository('ThemeHouse\Reactions:Reaction');
}
/**
* @return \ThemeHouse\Reactions\Repository\ReactedContent
*/
protected function getReactedContentRepo()
{
/** @noinspection PhpIncompatibleReturnTypeInspection */
return \XF::Repository('ThemeHouse\Reactions:ReactedContent');
}
/**
* @return \ThemeHouse\Reactions\Repository\ReactHandler
*/
protected function getReactHandlerRepo()
{
/** @noinspection PhpIncompatibleReturnTypeInspection */
return \XF::Repository('ThemeHouse\Reactions:ReactHandler');
}
/**
* @param $entity
* @return \ThemeHouse\Reactions\React\AbstractHandler
* @throws \Exception
*/
protected function getReactHandlerByEntity(Entity $entity)
{
return $this->getReactHandlerRepo()->getReactHandlerByEntity($entity, false);
}
/**
* @param $contentType
* @return \ThemeHouse\Reactions\React\AbstractHandler
* @throws \Exception
*/
protected function getReactHandlerByType($contentType)
{
return $this->getReactHandlerRepo()->getReactHandlerByType($contentType, false);
}
}