BxDolVoteReactions.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. /**
  10. * Like based votes for any content
  11. */
  12. class BxDolVoteReactions extends BxTemplVote
  13. {
  14. protected static $_sCounterStyleSimple = 'simple'; // total counter only.
  15. protected static $_sCounterStyleDivided = 'divided'; // counters [icon + counter] divided by reactions.
  16. protected static $_sCounterStyleCompound = 'compound'; // total counter with a list of reaction icons.
  17. public static $sModeSolo = 'solo'; // one reaction (the default one), cast with a click.
  18. public static $sModeFew = 'few'; // the reactions list.
  19. public static $sModeMany = 'many'; // the reactions list plus any emoji.
  20. public static $iPerUserMax = 5; // most reactions one person may give to one object.
  21. protected static $_sEmojiCodePrefix = 'e_'; // free-picked emoji are stored as e_<hex code points>.
  22. protected static $_iReactionLengthMax = 32; // the `reaction` column is varchar(32).
  23. protected $_sMenuDoVote; //--- Do vote (reaction) menu name.
  24. protected $_sDataList; //--- Reactions list name.
  25. protected $_aDataList; //--- Reactions list with all reactions.
  26. protected $_aDataListAll; //--- The whole Reactions list, also the reactions Solo mode leaves out.
  27. protected $_sDefault; //--- Default reaction name.
  28. protected $_bQuickMode; //--- Give 'default' reaction when clicked (a default reaction is set).
  29. protected $_bSingleMode; //--- Only one reaction is available (Solo mode, or one item in Reactions list).
  30. protected $_sMode; //--- Reactions mode: solo, few or many.
  31. protected $_sDefaultReaction; //--- Default reaction name from settings, '' when there is none.
  32. protected $_iPerUser; //--- How many different reactions one person may give to one object.
  33. protected $_bSplitCounters; //--- Show a counter per reaction instead of one total.
  34. public function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
  35. {
  36. parent::__construct($sSystem, $iId, $iInit, $oTemplate);
  37. $this->_oQuery = new BxDolVoteReactionsQuery($this);
  38. $this->_sType = BX_DOL_VOTE_TYPE_REACTIONS;
  39. $this->_sMenuDoVote = 'sys_vote_reactions_do';
  40. $this->_sDataList = 'sys_vote_reactions';
  41. $this->_aDataList = [];
  42. $this->_aDataListAll = [];
  43. $this->_sDefault = 'default';
  44. $this->_sMode = getParam('sys_vote_reactions_mode');
  45. if(!in_array($this->_sMode, [self::$sModeSolo, self::$sModeFew, self::$sModeMany]))
  46. $this->_sMode = self::$sModeFew;
  47. $this->_sDefaultReaction = trim((string)getParam('sys_vote_reactions_default'));
  48. $this->_iPerUser = $this->_sMode == self::$sModeSolo ? 1 : max(1, min(self::$iPerUserMax, (int)getParam('sys_vote_reactions_per_user')));
  49. $this->_bSplitCounters = getParam('sys_vote_reactions_split_counters') == 'on';
  50. }
  51. public function init($iId)
  52. {
  53. if(!parent::init($iId))
  54. return false;
  55. $aReactions = BxDolFormQuery::getDataItems($this->_sDataList, false, BX_DATA_VALUES_ALL);
  56. $sPlaceholder = ''; //--- The reaction which carries the 'not voted yet' look in its data.
  57. foreach($aReactions as $sReaction => $aReaction) {
  58. $aData = !empty($aReaction['Data']) ? unserialize($aReaction['Data']) : array();
  59. if(!empty($aData['default']))
  60. $sPlaceholder = $sReaction;
  61. $this->_aDataList[$sReaction] = array(
  62. 'name' => $sReaction,
  63. 'title' => $aReaction['LKey'],
  64. 'title_aux' => $aReaction['LKey2'],
  65. 'use' => isset($aData['use']) ? $aData['use'] : 'emoji',
  66. 'icon' => isset($aData['icon']) ? $aData['icon'] : '',
  67. 'emoji' => isset($aData['emoji']) ? $aData['emoji'] : '',
  68. 'image' => isset($aData['image']) ? $aData['image'] : '',
  69. 'color' => isset($aData['color']) ? $aData['color'] : '',
  70. 'weight' => isset($aData['weight']) ? $aData['weight'] : 1,
  71. 'default' => isset($aData['default']) ? $aData['default'] : '',
  72. );
  73. }
  74. //--- The default reaction must be on the list.
  75. if($this->_sDefaultReaction !== '' && !isset($this->_aDataList[$this->_sDefaultReaction]))
  76. $this->_sDefaultReaction = '';
  77. $this->_aDataListAll = $this->_aDataList;
  78. //--- Solo: only the default reaction (or the first one on the list) is available.
  79. if($this->_sMode == self::$sModeSolo && !empty($this->_aDataList)) {
  80. if($this->_sDefaultReaction === '')
  81. $this->_sDefaultReaction = (string)key($this->_aDataList);
  82. $this->_aDataList = [$this->_sDefaultReaction => $this->_aDataList[$this->_sDefaultReaction]];
  83. }
  84. $this->_bSingleMode = count($this->_aDataList) == 1;
  85. $this->_bQuickMode = $this->_sDefaultReaction !== '';
  86. //--- The 'default' item is what the do-button shows before a vote: the default
  87. //--- reaction, or the 'not voted yet' look (e.g. 'React' with a smile) when there is none.
  88. $sBase = $this->_sDefaultReaction;
  89. if($sBase === '')
  90. $sBase = $sPlaceholder !== '' && isset($this->_aDataList[$sPlaceholder]) ? $sPlaceholder : (string)key($this->_aDataList);
  91. if($sBase !== '' && isset($this->_aDataList[$sBase])) {
  92. $aDefault = $this->_aDataList[$sBase];
  93. if(!$this->_bQuickMode && !$this->_bSingleMode && !empty($aDefault['default'])) {
  94. if(is_array($aDefault['default']))
  95. $aDefault = array_merge ($aDefault, $aDefault['default']);
  96. else
  97. $aDefault['icon'] = $aDefault['default'];
  98. }
  99. $this->_aDataList[$this->_sDefault] = $aDefault;
  100. }
  101. }
  102. /**
  103. * Interface functions for outer usage
  104. */
  105. public function getValue()
  106. {
  107. return (int)$this->_aSystem['min_value'];
  108. }
  109. public function getDefault()
  110. {
  111. return $this->_sDefault;
  112. }
  113. /**
  114. * Default reaction name from settings, '' when there is none.
  115. */
  116. public function getDefaultReaction()
  117. {
  118. return $this->_sDefaultReaction;
  119. }
  120. public function getMode()
  121. {
  122. return $this->_sMode;
  123. }
  124. public function getPerUser()
  125. {
  126. return $this->_iPerUser;
  127. }
  128. public function isSplitCounters()
  129. {
  130. return $this->_bSplitCounters;
  131. }
  132. /**
  133. * Any emoji may be given, not only the reactions on the list.
  134. */
  135. public function isFreeEmojiAllowed()
  136. {
  137. return $this->_sMode == self::$sModeMany;
  138. }
  139. public function getReaction($sName)
  140. {
  141. if(!empty($this->_aDataList[$sName]))
  142. return $this->_aDataList[$sName];
  143. //--- Left out by Solo mode, but people may still have it.
  144. if(!empty($this->_aDataListAll[$sName]))
  145. return $this->_aDataListAll[$sName];
  146. //--- A free-picked emoji (Many mode), stored under its code.
  147. $sEmoji = self::decodeEmoji($sName);
  148. if($sEmoji === false)
  149. return false;
  150. return [
  151. 'name' => $sName,
  152. 'title' => $sEmoji,
  153. 'title_aux' => '',
  154. 'use' => 'emoji',
  155. 'icon' => '',
  156. 'emoji' => $sEmoji,
  157. 'image' => '',
  158. 'color' => '',
  159. 'weight' => 1,
  160. 'default' => '',
  161. ];
  162. }
  163. public function getReactions($bFullInfo = false)
  164. {
  165. return $bFullInfo ? $this->_aDataList : array_keys($this->_aDataList);
  166. }
  167. public function getTrackBy($aParams)
  168. {
  169. return $this->_oQuery->getTrackBy($aParams);
  170. }
  171. /**
  172. * Reactions which have a counter in the given vote data: the list, then free-picked emoji (Many mode).
  173. */
  174. public function getCountedReactions($aVote)
  175. {
  176. $aNames = [];
  177. foreach(array_keys($aVote) as $sKey)
  178. if(strncmp($sKey, 'count_', 6) === 0)
  179. $aNames[] = substr($sKey, 6);
  180. return $aNames;
  181. }
  182. /**
  183. * The author's reactions to the object, oldest first.
  184. */
  185. public function getReactionsBy($iObjectId, $iAuthorId)
  186. {
  187. return $this->_oQuery->getReactionsBy($iObjectId, $iAuthorId);
  188. }
  189. /**
  190. * Turn a requested reaction into the name it is stored under, or false when it may not be given:
  191. * a name from the list, or (Many mode) a single emoji, given as is or as its e_<hex> code.
  192. * An emoji which is on the list is that reaction.
  193. */
  194. public function normalizeReaction($sReaction)
  195. {
  196. $sReaction = trim((string)$sReaction);
  197. if($sReaction === '')
  198. return false;
  199. if($sReaction === $this->_sDefault)
  200. return isset($this->_aDataList[$this->_sDefault]) ? $this->_aDataList[$this->_sDefault]['name'] : false;
  201. if(isset($this->_aDataList[$sReaction]))
  202. return $sReaction;
  203. if(!$this->isFreeEmojiAllowed())
  204. return false;
  205. $sEmoji = self::decodeEmoji($sReaction);
  206. if($sEmoji === false)
  207. $sEmoji = $sReaction;
  208. if(!self::isEmoji($sEmoji))
  209. return false;
  210. $sKey = self::_emojiKey($sEmoji);
  211. foreach($this->_aDataList as $sName => $aReaction)
  212. if($sName !== $this->_sDefault && !empty($aReaction['emoji']) && self::_emojiKey($aReaction['emoji']) === $sKey)
  213. return $sName;
  214. $sCode = self::encodeEmoji($sEmoji);
  215. return get_mb_len($sCode) <= self::$_iReactionLengthMax ? $sCode : false;
  216. }
  217. /**
  218. * One emoji: a pictograph (with optional variation selector, skin tone and ZWJ parts),
  219. * a keycap or a flag.
  220. */
  221. public static function isEmoji($s)
  222. {
  223. $sPart = '(?:\p{Extended_Pictographic}(?:\x{FE0F}|[\x{1F3FB}-\x{1F3FF}])?)';
  224. return preg_match('/^(?:' . $sPart . '(?:\x{200D}' . $sPart . ')*|[#*0-9]\x{FE0F}?\x{20E3}|\p{Regional_Indicator}{2})$/u', (string)$s) === 1;
  225. }
  226. /**
  227. * e_<hex code points joined with _>, e.g. e_2764_fe0f. Stored as ASCII because the
  228. * tables' unicode collation gives every emoji above U+FFFF the same weight.
  229. */
  230. public static function encodeEmoji($sEmoji)
  231. {
  232. $aCodes = [];
  233. foreach(mb_str_split($sEmoji, 1, 'UTF-8') as $sChar)
  234. $aCodes[] = dechex(mb_ord($sChar, 'UTF-8'));
  235. return self::$_sEmojiCodePrefix . implode('_', $aCodes);
  236. }
  237. /**
  238. * The emoji for an e_<hex> code, or false.
  239. */
  240. public static function decodeEmoji($sCode)
  241. {
  242. if(!is_string($sCode) || !preg_match('/^e(?:_[0-9a-f]{1,6})+$/', $sCode))
  243. return false;
  244. $aHex = explode('_', $sCode);
  245. array_shift($aHex); //--- the 'e' of the prefix
  246. $sEmoji = '';
  247. foreach($aHex as $sHex) {
  248. $iCode = hexdec($sHex);
  249. if($iCode > 0x10FFFF || ($iCode >= 0xD800 && $iCode <= 0xDFFF))
  250. return false;
  251. $sEmoji .= mb_chr($iCode, 'UTF-8');
  252. }
  253. return self::isEmoji($sEmoji) ? $sEmoji : false;
  254. }
  255. /**
  256. * Compare emoji without variation selectors (U+FE0E/U+FE0F).
  257. */
  258. protected static function _emojiKey($sEmoji)
  259. {
  260. return preg_replace('/[\x{FE0E}\x{FE0F}]/u', '', (string)$sEmoji);
  261. }
  262. protected function _getReactionOrDefault($sReaction)
  263. {
  264. $aReaction = $this->getReaction($sReaction);
  265. return $aReaction !== false ? $aReaction : $this->_aDataList[$this->_sDefault];
  266. }
  267. public function getIcon($sReaction, $bWithColor = true)
  268. {
  269. $aReaction = $this->_getReactionOrDefault($sReaction);
  270. return $aReaction['icon'] . ($bWithColor && !empty($aReaction['color']) ? ' ' . $aReaction['color'] : '');
  271. }
  272. public function getEmoji($sReaction, $bWithColor = true)
  273. {
  274. $aReaction = $this->_getReactionOrDefault($sReaction);
  275. return $aReaction['emoji'];
  276. }
  277. public function getImage($sReaction, $bWithColor = true)
  278. {
  279. $aReaction = $this->_getReactionOrDefault($sReaction);
  280. list($sIconFont, $sIconUrl, $sIconA, $sIconHtml) = BxTemplFunctions::getInstanceWithTemplate($this->_oTemplate)->getIcon($aReaction['image']);
  281. if ($sIconUrl)
  282. return $this->_oTemplate->parseImage($sIconUrl, ['class' => 'sys-icon-image']);
  283. return $sIconHtml;
  284. }
  285. /**
  286. * Actions functions
  287. */
  288. public function actionGetDoVotePopup()
  289. {
  290. if(!$this->isEnabled())
  291. return '';
  292. return $this->_getDoVotePopup((int)bx_get('value'));
  293. }
  294. public function actionGetVotedBy()
  295. {
  296. if(!$this->isEnabled())
  297. return '';
  298. $aParams = array();
  299. $sReaction = bx_get('reaction');
  300. if($sReaction !== false) {
  301. $sReaction = bx_process_input($sReaction);
  302. if($this->getReaction($sReaction) === false)
  303. $sReaction = $this->_sDefault;
  304. $aParams['reaction'] = $sReaction;
  305. }
  306. return $this->_getVotedBy($aParams);
  307. }
  308. /**
  309. * Give or take back one reaction. A person may hold up to getPerUser() different reactions;
  310. * with one allowed, a new reaction replaces the old one. Giving a reaction one already has
  311. * takes it back.
  312. */
  313. public function vote($aVoteData = [], $aRequestParamsData = [])
  314. {
  315. $iObjectId = $this->getId();
  316. $iAuthorId = $this->_getAuthorId();
  317. $iAuthorIp = $this->_getAuthorIp();
  318. $sRequested = isset($aVoteData['reaction']) ? trim((string)$aVoteData['reaction']) : '';
  319. $aReactions = $this->getReactionsBy($iObjectId, $iAuthorId);
  320. //--- One's own reaction can be taken back even when the settings no longer offer it
  321. //--- (an emoji picked before Many mode was turned off, a reaction Solo mode leaves out).
  322. $sReaction = $this->_getOwnReaction($sRequested, $aReactions);
  323. if($sReaction === false)
  324. $sReaction = $this->normalizeReaction($sRequested);
  325. if($sReaction === false)
  326. return ['code' => BX_DOL_OBJECT_ERR_WRONG_DATE, 'message' => _t('_vote_err_reaction_not_available')];
  327. $aVoteData = ['reaction' => $sReaction, 'value' => $this->getValue()];
  328. $bUndo = $this->isUndo();
  329. //--- Take back a reaction one already has.
  330. if(in_array($sReaction, $aReactions)) {
  331. if(!$bUndo)
  332. return ['code' => BX_DOL_OBJECT_ERR_DUPLICATE, 'message' => _t('_vote_err_duplicate_vote')];
  333. $aTrack = $this->_oQuery->getTrackReaction($iObjectId, $iAuthorId, $sReaction);
  334. if(!empty($aTrack['value']))
  335. $aVoteData['value'] = (int)$aTrack['value'];
  336. return $this->_vote($iObjectId, $iAuthorId, $iAuthorIp, $aVoteData, true, $aRequestParamsData);
  337. }
  338. if(!$this->isAllowedVote())
  339. return ['code' => BX_DOL_OBJECT_ERR_ACCESS_DENIED, 'message' => $this->msgErrAllowedVote()];
  340. if(count($aReactions) >= $this->_iPerUser) {
  341. if(!$bUndo)
  342. return ['code' => BX_DOL_OBJECT_ERR_DUPLICATE, 'message' => _t('_vote_err_duplicate_vote')];
  343. if($this->_iPerUser > 1)
  344. return ['code' => BX_DOL_OBJECT_ERR_DUPLICATE, 'message' => _t('_vote_err_reactions_limit', $this->_iPerUser)];
  345. //--- One reaction per person: the new one replaces the old one.
  346. foreach($aReactions as $sReactionOld) {
  347. $aTrack = $this->_oQuery->getTrackReaction($iObjectId, $iAuthorId, $sReactionOld);
  348. $aResult = $this->_vote($iObjectId, $iAuthorId, $iAuthorIp, [
  349. 'reaction' => $sReactionOld,
  350. 'value' => !empty($aTrack['value']) ? (int)$aTrack['value'] : $this->getValue()
  351. ], true, $aRequestParamsData);
  352. if((int)$aResult['code'] != 0)
  353. return $aResult;
  354. }
  355. }
  356. return $this->_vote($iObjectId, $iAuthorId, $iAuthorIp, $aVoteData, false, $aRequestParamsData);
  357. }
  358. /**
  359. * Internal functions
  360. */
  361. /**
  362. * The track the do-button reflects: the author's latest reaction.
  363. */
  364. protected function _getTrack($iObjectId, $iAuthorId)
  365. {
  366. return $this->_oQuery->getTrackLatest($iObjectId, $iAuthorId);
  367. }
  368. protected function _isVote($iObjectId = 0, $bForceGet = false)
  369. {
  370. $aVote = $this->_getVote($iObjectId, $bForceGet);
  371. foreach($aVote as $sKey => $iValue)
  372. if(strpos($sKey, 'count_') !== false && !empty($iValue))
  373. return true;
  374. return false;
  375. }
  376. protected function _isDuplicate($iObjectId, $iAuthorId, $iAuthorIp, $bVoted)
  377. {
  378. return $bVoted && !$this->isUndo();
  379. }
  380. protected function _isCount($aVote = array())
  381. {
  382. if(empty($aVote))
  383. $aVote = $this->_getVote();
  384. foreach($aVote as $sKey => $mixedValue)
  385. if(substr($sKey, 0, 5) == 'count' && (int)$mixedValue != 0)
  386. return true;
  387. return false;
  388. }
  389. protected function _getVoteData()
  390. {
  391. $aData = parent::_getVoteData();
  392. if($aData === false)
  393. return false;
  394. $sReaction = bx_get('reaction');
  395. if($sReaction === false)
  396. return false;
  397. $sRequested = trim((string)bx_process_input($sReaction));
  398. $sReaction = $this->normalizeReaction($sRequested);
  399. if($sReaction === false)
  400. $sReaction = $this->_getOwnReaction($sRequested, $this->getReactionsBy($this->getId(), $this->_getAuthorId()));
  401. if($sReaction === false)
  402. return false;
  403. $aData['reaction'] = $sReaction;
  404. return $aData;
  405. }
  406. /**
  407. * The stored name of the author's own reaction which `$sRequested` names (by its name, e_<hex> code or emoji),
  408. * or false. One's own reaction can always be taken back, even when the settings no longer offer it.
  409. */
  410. protected function _getOwnReaction($sRequested, $aReactions)
  411. {
  412. if(in_array($sRequested, $aReactions, true))
  413. return $sRequested;
  414. if(self::isEmoji($sRequested) && in_array(($sCode = self::encodeEmoji($sRequested)), $aReactions, true))
  415. return $sCode;
  416. return false;
  417. }
  418. protected function _returnVoteData($iObjectId, $iAuthorId, $iAuthorIp, $aData, $bVoted, $aParams = [])
  419. {
  420. $sReaction = $aData['reaction']; //--- the reaction just given or taken back
  421. $bUndo = $this->isUndo();
  422. //--- The author's state after the change: all their reactions, the latest one on the button.
  423. $aReactionsBy = $this->getReactionsBy($iObjectId, $iAuthorId);
  424. $bVotedAny = !empty($aReactionsBy);
  425. $bDisabled = $bVotedAny && !$bUndo && count($aReactionsBy) >= $this->_iPerUser;
  426. $aTrack = $bVotedAny ? $this->_getTrack($iObjectId, $iAuthorId) : [];
  427. $sShown = $bVotedAny ? $aTrack['reaction'] : $this->_sDefault;
  428. $aShown = $this->_getReactionOrDefault($sShown);
  429. $aVote = $this->_getVote($iObjectId, true);
  430. $sLabelUse = !empty($aShown['use']) ? $aShown['use'] : 'emoji';
  431. $sLabelIcon = $this->_getIconDoWithTrack($bVotedAny, $aTrack);
  432. if(!$bVotedAny && $sLabelUse != 'icon') {
  433. $sLabelUse = 'icon';
  434. $sLabelIcon = $this->_oTemplate->parseIcon($sLabelIcon);
  435. }
  436. $sJsClick = '';
  437. if(!$bDisabled)
  438. $sJsClick = $bVotedAny && $bUndo ? $this->getJsClickDo($sShown) : $this->getJsClick();
  439. $iTotalC = $iTotalS = 0;
  440. foreach($aVote as $sKey => $mixedValue)
  441. if(strncmp($sKey, 'count_', 6) === 0)
  442. $iTotalC += (int)$mixedValue;
  443. else if(strncmp($sKey, 'sum_', 4) === 0)
  444. $iTotalS += (int)$mixedValue;
  445. $fTotalR = $iTotalC != 0 ? round($iTotalS / $iTotalC, 2) : 0;
  446. $iCount = isset($aVote['count_' . $sReaction]) ? (int)$aVote['count_' . $sReaction] : 0;
  447. $aResult = [
  448. 'code' => 0,
  449. 'reaction' => $this->_bIsApi ? $aShown['name'] : $sReaction,
  450. 'rate' => isset($aVote['rate_' . $sReaction]) ? $aVote['rate_' . $sReaction] : 0,
  451. 'count' => $iCount,
  452. 'countf' => $iCount > 0 ? $this->_getCounterLabel($iCount, array('reaction' => $sReaction)) : '',
  453. 'label_use' => $sLabelUse,
  454. 'label_icon' => $sLabelIcon,
  455. 'label_emoji' => $this->_getEmojiDoWithTrack($bVotedAny, $aTrack),
  456. 'label_image' => $this->_getImageDoWithTrack($bVotedAny, $aTrack),
  457. 'label_title' => _t($this->_getTitleDoWithTrack($bVotedAny, $aTrack)),
  458. 'label_click' => $sJsClick,
  459. 'voted' => $bVotedAny,
  460. 'disabled' => $bDisabled,
  461. 'total' => [
  462. 'rate' => $fTotalR,
  463. 'count' => $iTotalC,
  464. 'countf' => $iTotalC > 0 ? $this->_getCounterLabel($iTotalC, ['show_counter_label_icon' => false, 'reaction' => '']) : '',
  465. ]
  466. ];
  467. $aResult['api'] = [
  468. 'ts' => microtime(true),
  469. 'performer_id' => $iAuthorId,
  470. 'is_voted' => $bVotedAny,
  471. 'is_disabled' => $bDisabled,
  472. 'reaction' => $bVotedAny ? $sShown : $this->_sDefault,
  473. 'reactions' => $aReactionsBy,
  474. 'default' => $this->_sDefaultReaction,
  475. 'icon' => !empty($aResult['label_emoji']) ? $aResult['label_emoji'] : $aShown['emoji'],
  476. 'title' => !empty($aResult['label_title']) ? $aResult['label_title'] : '',
  477. 'counter' => $aVote
  478. ];
  479. return $aResult;
  480. }
  481. protected function _returnVoteDataForSocket($aData, $aMask = [])
  482. {
  483. return parent::_returnVoteDataForSocket($aData, ['code', 'reaction', 'rate', 'count', 'countf', 'total', 'api']);
  484. }
  485. protected function _getIconDoWithTrack($bVoted, $aTrack = [])
  486. {
  487. $sReaction = $bVoted ? $aTrack['reaction'] : $this->_sDefault;
  488. return $this->getIcon($sReaction);
  489. }
  490. protected function _getEmojiDoWithTrack($bVoted, $aTrack = [])
  491. {
  492. $sReaction = $bVoted ? $aTrack['reaction'] : $this->_sDefault;
  493. return $this->getEmoji($sReaction);
  494. }
  495. protected function _getImageDoWithTrack($bVoted, $aTrack = [])
  496. {
  497. $sReaction = $bVoted ? $aTrack['reaction'] : $this->_sDefault;
  498. return $this->getImage($sReaction);
  499. }
  500. protected function _getTitleDoWithTrack($bVoted, $aTrack = [])
  501. {
  502. $sReaction = $bVoted ? $aTrack['reaction'] : $this->_sDefault;
  503. return $this->_getReactionOrDefault($sReaction)['title'];
  504. }
  505. protected function _getTitleDoBy($aParams = [])
  506. {
  507. if(isset($aParams['show_counter_style']) && $aParams['show_counter_style'] == self::$_sCounterStyleCompound)
  508. return _t('_vote_do_by_reactions');
  509. $sReaction = !empty($aParams['reaction']) ? $aParams['reaction'] : $this->_sDefault;
  510. return _t('_vote_do_by_x_reaction', _t($this->_getReactionOrDefault($sReaction)['title']));
  511. }
  512. }
  513. /** @} */