Ver código fonte

Ticket #1691 -
1. Refactoring
2. Division into Likes, Stars and Reactions.
3. Reactions itself.

AntonLV 7 anos atrás
pai
commit
9a5fffad24
55 arquivos alterados com 2051 adições e 515 exclusões
  1. 23 10
      inc/classes/BxDolObjectQuery.php
  2. 115 83
      inc/classes/BxDolVote.php
  3. 50 0
      inc/classes/BxDolVoteLikes.php
  4. 21 0
      inc/classes/BxDolVoteLikesQuery.php
  5. 32 20
      inc/classes/BxDolVoteQuery.php
  6. 184 0
      inc/classes/BxDolVoteReactions.php
  7. 159 0
      inc/classes/BxDolVoteReactionsQuery.php
  8. 32 0
      inc/classes/BxDolVoteStars.php
  9. 28 0
      inc/classes/BxDolVoteStarsQuery.php
  10. 54 126
      inc/js/classes/BxDolVote.js
  11. 36 0
      inc/js/classes/BxDolVoteLikes.js
  12. 112 0
      inc/js/classes/BxDolVoteReactions.js
  13. 110 0
      inc/js/classes/BxDolVoteStars.js
  14. 13 0
      install/sql/system.sql
  15. 27 0
      modules/base/general/classes/BxBaseModGeneralMenuViewActions.php
  16. 1 1
      modules/base/profile/classes/BxBaseModProfileVote.php
  17. 7 10
      modules/base/text/classes/BxBaseModTextVotePollAnswers.php
  18. 1 1
      modules/base/text/template/poll_answer_ve_block.html
  19. 21 0
      modules/boonex/decorous/data/template/system/scripts/BxTemplVoteLikes.php
  20. 21 0
      modules/boonex/decorous/data/template/system/scripts/BxTemplVoteReactions.php
  21. 21 0
      modules/boonex/decorous/data/template/system/scripts/BxTemplVoteStars.php
  22. 9 0
      modules/boonex/english/data/langs/system/en.xml
  23. 21 0
      modules/boonex/lucid/data/template/system/scripts/BxTemplVoteLikes.php
  24. 21 0
      modules/boonex/lucid/data/template/system/scripts/BxTemplVoteReactions.php
  25. 21 0
      modules/boonex/lucid/data/template/system/scripts/BxTemplVoteStars.php
  26. 0 0
      modules/boonex/market/classes/BxMarketVoteStars.php
  27. 1 1
      modules/boonex/market/install/sql/install.sql
  28. 21 0
      modules/boonex/ocean/data/template/system/scripts/BxTemplVoteLikes.php
  29. 21 0
      modules/boonex/ocean/data/template/system/scripts/BxTemplVoteReactions.php
  30. 21 0
      modules/boonex/ocean/data/template/system/scripts/BxTemplVoteStars.php
  31. 20 0
      modules/boonex/photos/classes/BxPhotosVoteStars.php
  32. 1 1
      modules/boonex/photos/install/sql/install.sql
  33. 7 10
      modules/boonex/polls/classes/BxPollsVoteSubentries.php
  34. 1 1
      modules/boonex/polls/template/subentries_ve_block.html
  35. 1 0
      modules/boonex/posts/classes/BxPostsConfig.php
  36. 1 0
      modules/boonex/posts/install/sql/enable.sql
  37. 23 0
      modules/boonex/posts/install/sql/install.sql
  38. 2 2
      modules/boonex/posts/install/sql/uninstall.sql
  39. 21 0
      modules/boonex/protean/data/template/system/scripts/BxTemplVoteLikes.php
  40. 21 0
      modules/boonex/protean/data/template/system/scripts/BxTemplVoteReactions.php
  41. 21 0
      modules/boonex/protean/data/template/system/scripts/BxTemplVoteStars.php
  42. 9 0
      modules/boonex/russian/data/langs/system/ru.xml
  43. 15 7
      modules/boonex/timeline/classes/BxTimelineModule.php
  44. 0 0
      modules/boonex/timeline/classes/BxTimelineVoteLikes.php
  45. 1 1
      modules/boonex/timeline/install/sql/install.sql
  46. 44 0
      template/css/vote.css
  47. 106 236
      template/scripts/BxBaseVote.php
  48. 138 0
      template/scripts/BxBaseVoteLikes.php
  49. 258 0
      template/scripts/BxBaseVoteReactions.php
  50. 149 0
      template/scripts/BxBaseVoteStars.php
  51. 1 0
      template/vote_counter_label_reactions.html
  52. 1 0
      template/vote_counter_wrapper_reactions.html
  53. 1 0
      template/vote_do_vote_reactions_label.html
  54. 3 3
      template/vote_element_block.html
  55. 2 2
      template/vote_element_inline.html

+ 23 - 10
inc/classes/BxDolObjectQuery.php

@@ -73,7 +73,7 @@ class BxDolObjectQuery extends BxDolDb
         );
     }
 
-	public function isPerformed($iObjectId, $iAuthorId)
+    public function isPerformed($iObjectId, $iAuthorId)
     {
         $sQuery = $this->prepare("SELECT `object_id` FROM `{$this->_sTableTrack}` WHERE `object_id` = ? AND `author_id` = ? LIMIT 1", $iObjectId, $iAuthorId);
         return (int)$this->getOne($sQuery) != 0;
@@ -85,18 +85,26 @@ class BxDolObjectQuery extends BxDolDb
         return $this->getColumn($sQuery);
     }
 
+    public function getTrack($iObjectId, $iAuthorId)
+    {
+        return $this->getRow("SELECT * FROM `{$this->_sTableTrack}` WHERE `object_id` = :object_id AND `author_id` = :author_id LIMIT 1", array(
+            'object_id' => $iObjectId,
+            'author_id' => $iAuthorId
+        ));
+    }
+
     public function deleteObjectEntries($iObjectId)
     {
     	if(!empty($this->_sTable)) {
-	        $sQuery = $this->prepare("DELETE FROM `{$this->_sTable}` WHERE `object_id` = ?", $iObjectId);
-	        if($this->query($sQuery))
-	        	$this->query("OPTIMIZE TABLE `{$this->_sTable}`");
+            $sQuery = $this->prepare("DELETE FROM `{$this->_sTable}` WHERE `object_id` = ?", $iObjectId);
+            if($this->query($sQuery))
+                $this->query("OPTIMIZE TABLE `{$this->_sTable}`");
     	}
 
     	if(!empty($this->_sTableTrack)) {
-	        $sQuery = $this->prepare("DELETE FROM `{$this->_sTableTrack}` WHERE `object_id` = ?", $iObjectId);
-	        if($this->query($sQuery))
-	        	$this->query ("OPTIMIZE TABLE `{$this->_sTableTrack}`");
+            $sQuery = $this->prepare("DELETE FROM `{$this->_sTableTrack}` WHERE `object_id` = ?", $iObjectId);
+            if($this->query($sQuery))
+                $this->query ("OPTIMIZE TABLE `{$this->_sTableTrack}`");
     	}
     }
     public function deleteAuthorEntries($iAuthorId)
@@ -124,10 +132,10 @@ class BxDolObjectQuery extends BxDolDb
 
         $sQuery = $this->prepare("DELETE FROM `{$this->_sTableTrack}` WHERE `{$this->_sTableTrackFieldAuthor}`=?", $iAuthorId);
         if($this->query($sQuery))
-        	$this->query("OPTIMIZE TABLE `{$this->_sTableTrack}`");
+            $this->query("OPTIMIZE TABLE `{$this->_sTableTrack}`");
     }
 
-	public function getObjectAuthorId($iId)
+    public function getObjectAuthorId($iId)
     {
         if(empty($this->_sTriggerFieldAuthor))
             return 0;
@@ -151,7 +159,7 @@ class BxDolObjectQuery extends BxDolDb
     public function updateTriggerTable($iObjectId)
     {
     	if(empty($this->_sMethodGetEntry))
-    		return false;
+            return false;
 
         $aEntry = $this->{$this->_sMethodGetEntry}($iObjectId);
         if(empty($aEntry) || !is_array($aEntry))
@@ -160,6 +168,11 @@ class BxDolObjectQuery extends BxDolDb
         return $this->_updateTriggerTable($iObjectId, $aEntry);
     }
 
+    /**
+     * Is used instead of 'updateTriggerTable' when trigger table contains 
+     * only one field related to this object (View, Vote, Comment, etc).
+     * For example, it's used in Favourite and Feature objects.
+     */
     public function updateTriggerTableValue($iObjectId, $iValue)
     {
         $sQuery = $this->prepare("UPDATE `{$this->_sTriggerTable}` SET `{$this->_sTriggerFieldCount}` = `{$this->_sTriggerFieldCount}` + ? WHERE `{$this->_sTriggerFieldId}` = ?", (int)$iValue, $iObjectId);

+ 115 - 83
inc/classes/BxDolVote.php

@@ -11,6 +11,7 @@ bx_import('BxDolAcl');
 
 define('BX_DOL_VOTE_TYPE_STARS', 'stars');
 define('BX_DOL_VOTE_TYPE_LIKES', 'likes');
+define('BX_DOL_VOTE_TYPE_REACTIONS', 'reactions');
 
 define('BX_DOL_VOTE_USAGE_BLOCK', 'block');
 define('BX_DOL_VOTE_USAGE_INLINE', 'inline');
@@ -78,8 +79,8 @@ define('BX_DOL_VOTE_USAGE_DEFAULT', BX_DOL_VOTE_USAGE_BLOCK);
 
 class BxDolVote extends BxDolObject
 {
-	protected $_bLike = true;
-	protected $_sType = BX_DOL_VOTE_TYPE_LIKES;
+    protected $_sType;
+    protected $_aVote;
 
     protected function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
     {
@@ -87,10 +88,7 @@ class BxDolVote extends BxDolObject
         if(empty($this->_sSystem))
             return;
 
-        $this->_oQuery = new BxDolVoteQuery($this);
-
-		$this->_bLike = $this->isLikeMode();
-        $this->_sType = $this->_bLike ? BX_DOL_VOTE_TYPE_LIKES : BX_DOL_VOTE_TYPE_STARS;
+        $this->_aVote = array();
     }
 
     /**
@@ -109,14 +107,17 @@ class BxDolVote extends BxDolObject
         if(!isset($aSystems[$sSys]))
             return null;
 
-        $sClassName = 'BxTemplVote';
+        $sClassName = 'BxTemplVoteLikes';
         if(!empty($aSystems[$sSys]['class_name'])) {
             $sClassName = $aSystems[$sSys]['class_name'];
             if(!empty($aSystems[$sSys]['class_file']))
                 require_once(BX_DIRECTORY_PATH_ROOT . $aSystems[$sSys]['class_file']);
         }
 
-        $o = new $sClassName($sSys, $iId, $iInit, $oTemplate);
+        $o = new $sClassName($sSys, $iId, false, $oTemplate);
+        if($iInit && method_exists($o, 'init'))
+            $o->init($iId);
+
         return ($GLOBALS['bxDolClasses']['BxDolVote!' . $sSys . $iId] = $o);
     }
 
@@ -157,17 +158,20 @@ class BxDolVote extends BxDolObject
         return true;
     }
 
-    public function isUndo()
+    public function getObjectAuthorId($iObjectId = 0)
     {
-        return (int)$this->_aSystem['is_undo'] == 1;
+    	if(empty($this->_aSystem['trigger_field_author']))
+            return 0;
+
+        return $this->_oQuery->getObjectAuthorId($iObjectId ? $iObjectId : $this->getId());
     }
 
-    public function isLikeMode()
+    /**
+     * Interface functions for outer usage
+     */
+    public function isUndo()
     {
-        $iMinValue = $this->getMinValue();
-        $iMaxValue = $this->getMaxValue();
-
-        return $iMinValue == $iMaxValue;
+        return (int)$this->_aSystem['is_undo'] == 1;
     }
 
     public function getMinValue()
@@ -180,26 +184,15 @@ class BxDolVote extends BxDolObject
         return (int)$this->_aSystem['max_value'];
     }
 
-	public function getObjectAuthorId($iObjectId = 0)
-    {
-    	if(empty($this->_aSystem['trigger_field_author']))
-    		return 0;
-
-        return $this->_oQuery->getObjectAuthorId($iObjectId ? $iObjectId : $this->getId());
-    }
-
-    /**
-     * Interface functions for outer usage
-     */
     public function getStatCounter()
     {
-        $aVote = $this->_oQuery->getVote($this->getId());
+        $aVote = $this->_getVote();
         return $aVote['count'];
     }
 
     public function getStatRate()
     {
-        $aVote = $this->_oQuery->getVote($this->getId());
+        $aVote = $this->_getVote();
         return $aVote['rate'];
     }
 
@@ -208,10 +201,8 @@ class BxDolVote extends BxDolObject
      */
     public function actionVote()
     {
-        if(!$this->isEnabled()) {
-            echoJson(array('code' => 1, 'message' => _t('_vote_err_not_enabled')));
-            return;
-        }
+        if(!$this->isEnabled())
+            return echoJson(array('code' => 1, 'message' => _t('_vote_err_not_enabled')));
 
         $iObjectId = $this->getId();
         $iObjectAuthorId = $this->getObjectAuthorId($iObjectId);
@@ -219,61 +210,32 @@ class BxDolVote extends BxDolObject
         $iAuthorIp = $this->_getAuthorIp();
 
         $bUndo = $this->isUndo();
-        $bLikeMode = $this->isLikeMode();
-
         $bVoted = $this->isPerformed($iObjectId, $iAuthorId);
-        $bPerformUndo = $bVoted && $bUndo ? true : false;
+        $bPerformUndo = $bVoted && $bUndo;
 
-        if(!$bPerformUndo && !$this->isAllowedVote(true)) {
-            echoJson(array('code' => 2, 'message' => $this->msgErrAllowedVote()));
-            return;
-        }
+        if(!$bPerformUndo && !$this->isAllowedVote())
+            return echoJson(array('code' => 2, 'message' => $this->msgErrAllowedVote()));
 
-        if((!$bLikeMode && !$this->_oQuery->isPostTimeoutEnded($iObjectId, $iAuthorIp)) || ($bLikeMode && $bVoted && !$bUndo)) {
-            echoJson(array('code' => 3, 'message' => _t('_vote_err_duplicate_vote')));
-            return;
-        }
+        if($this->_isDuplicate($iObjectId, $iAuthorId, $iAuthorIp, $bVoted))
+            return echoJson(array('code' => 3, 'message' => _t('_vote_err_duplicate_vote')));
 
-        $iValue = bx_get('value');
-        if($iValue === false) {
-            echoJson(array('code' => 4));
-            return;
-        }
+        $aData = $this->_getVoteData();
+        if($aData === false)
+            return echoJson(array('code' => 4));
 
-        $iValue = bx_process_input($iValue, BX_DATA_INT);
+        $iId = $this->_putVoteData($iObjectId, $iAuthorId, $iAuthorIp, $aData, $bPerformUndo);
+        if($iId === false)
+            return echoJson(array('code' => 5));
 
-        $iMinValue = $this->getMinValue();
-        if($iValue < $iMinValue)
-            $iValue = $iMinValue;
-
-        $iMaxValue = $this->getMaxValue();
-        if($iValue > $iMaxValue)
-            $iValue = $iMaxValue;
-
-		$iId = $this->_oQuery->putVote($iObjectId, $iAuthorId, $iAuthorIp, $iValue, $bPerformUndo);
-        if($iId === false) {
-            echoJson(array('code' => 5));
-            return;
-        }
+        if(!$bPerformUndo)
+            $this->isAllowedVote(true);
 
         $this->_trigger();
 
-        $oZ = new BxDolAlerts($this->_sSystem, ($bPerformUndo ? 'un' : '') . 'doVote', $iObjectId, $iAuthorId, array('vote_id' => $iId, 'vote_author_id' => $iAuthorId, 'object_author_id' => $iObjectAuthorId, 'value' => $iValue));
-        $oZ->alert();
-
-        $oZ = new BxDolAlerts('vote', ($bPerformUndo ? 'un' : '') . 'do', $iId, $iAuthorId, array('object_system' => $this->_sSystem, 'object_id' => $iObjectId, 'object_author_id' => $iObjectAuthorId, 'value' => $iValue));
-        $oZ->alert();
+        bx_alert($this->_sSystem, ($bPerformUndo ? 'un' : '') . 'doVote', $iObjectId, $iAuthorId, array_merge(array('vote_id' => $iId, 'vote_author_id' => $iAuthorId, 'object_author_id' => $iObjectAuthorId), $aData));
+        bx_alert('vote', ($bPerformUndo ? 'un' : '') . 'do', $iId, $iAuthorId, array_merge(array('object_system' => $this->_sSystem, 'object_id' => $iObjectId, 'object_author_id' => $iObjectAuthorId), $aData));
 
-        $aVote = $this->_oQuery->getVote($iObjectId);
-        echoJson(array(
-            'code' => 0,
-            'rate' => $aVote['rate'],
-            'count' => $aVote['count'],
-            'countf' => (int)$aVote['count'] > 0 ? $this->_getLabelCounter($aVote['count']) : '',
-        	'label_icon' => $bLikeMode ? $this->_getIconDoLike(!$bVoted) : '',
-        	'label_title' => $bLikeMode ? _t($this->_getTitleDoLike(!$bVoted)) : '',
-        	'disabled' => !$bVoted && !$bUndo,
-        ));
+        echoJson($this->_returnVoteData($iObjectId, $iAuthorId, $iAuthorIp, $aData, !$bVoted));
     }
 
     public function actionGetVotedBy()
@@ -303,19 +265,89 @@ class BxDolVote extends BxDolObject
     /**
      * Internal functions
      */
-    protected function _getIconDoLike($bVoted)
+    protected function _isDuplicate($iObjectId, $iAuthorId, $iAuthorIp, $bVoted)
+    {
+        return false;
+    }
+
+    protected function _isCount($aVote = array())
+    {
+        if(empty($aVote))
+            $aVote = $this->_getVote();
+
+        return isset($aVote['count']) && (int)$aVote['count'] != 0;
+    }
+
+    protected function _getVoteData()
+    {
+        $iValue = bx_get('value');
+        if($iValue === false)
+            return false;
+
+        $iValue = bx_process_input($iValue, BX_DATA_INT);
+
+        $iMinValue = $this->getMinValue();
+        if($iValue < $iMinValue)
+            $iValue = $iMinValue;
+
+        $iMaxValue = $this->getMaxValue();
+        if($iValue > $iMaxValue)
+            $iValue = $iMaxValue;
+
+        return array('value' => $iValue);
+    }
+
+    protected function _putVoteData($iObjectId, $iAuthorId, $iAuthorIp, $aData, $bPerformUndo)
+    {
+        return $this->_oQuery->putVote($iObjectId, $iAuthorId, $iAuthorIp, $aData, $bPerformUndo);
+    }
+
+    protected function _returnVoteData($iObjectId, $iAuthorId, $iAuthorIp, $aData, $bVoted)
+    {
+        $bUndo = $this->isUndo();
+        $aVote = $this->_getVote($iObjectId, true);
+
+        return array(
+            'code' => 0,
+            'rate' => $aVote['rate'],
+            'count' => $aVote['count'],
+            'countf' => (int)$aVote['count'] > 0 ? $this->_getLabelCounter($aVote['count']) : '',
+            'label_icon' => $this->_getIconDo($bVoted),
+            'label_title' => _t($this->_getTitleDo($bVoted)),
+            'disabled' => $bVoted && !$bUndo,
+        );
+    }
+
+    protected function _getVote($iObjectId = 0, $bForceGet = false)
+    {
+        if(!empty($this->_aVote) && !$bForceGet)
+            return $this->_aVote;
+
+        if(empty($iObjectId))
+            $iObjectId = $this->getId();
+
+        $this->_aVote = $this->_oQuery->getVote($iObjectId);
+        return $this->_aVote;
+    }
+
+    protected function _getTrack($iObjectId, $iAuthorId)
+    {
+        return $this->_oQuery->getTrack($iObjectId, $iAuthorId);
+    }
+
+    protected function _getIconDo($bVoted)
     {
-    	return $bVoted && $this->isUndo() ?  'thumbs-up' : 'thumbs-up';
+    	return '';
     }
 
-    protected function _getTitleDoLike($bVoted)
+    protected function _getTitleDo($bVoted)
     {
-    	return $bVoted && $this->isUndo() ? '_vote_do_unlike' : '_vote_do_like';
+    	return '';
     }
 
-    protected function _getTitleDoBy()
+    protected function _getTitleDoBy($aParams = array())
     {
-    	return '_vote_do_by';
+    	return _t('_vote_do_by');
     }
 }
 

+ 50 - 0
inc/classes/BxDolVoteLikes.php

@@ -0,0 +1,50 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * Like based votes for any content
+ */
+class BxDolVoteLikes extends BxTemplVote
+{
+    protected function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+
+        $this->_oQuery = new BxDolVoteLikesQuery($this);
+        $this->_sType = BX_DOL_VOTE_TYPE_LIKES;
+    }
+
+    /**
+     * Interface functions for outer usage
+     */
+    public function getValue()
+    {
+        return (int)$this->_aSystem['min_value'];
+    }
+
+    /**
+     * Internal functions
+     */
+    protected function _isDuplicate($iObjectId, $iAuthorId, $iAuthorIp, $bVoted)
+    {
+        return $bVoted && !$this->isUndo();
+    }
+
+    protected function _getIconDo($bVoted)
+    {
+    	return $bVoted && $this->isUndo() ?  'thumbs-up' : 'thumbs-up';
+    }
+
+    protected function _getTitleDo($bVoted)
+    {
+    	return $bVoted && $this->isUndo() ? '_vote_do_unlike' : '_vote_do_like';
+    }
+}
+
+/** @} */

+ 21 - 0
inc/classes/BxDolVoteLikesQuery.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxDolVoteLikesQuery extends BxDolVoteQuery
+{
+    public function __construct(&$oModule)
+    {
+        parent::__construct($oModule);
+    }
+}
+
+/** @} */

+ 32 - 20
inc/classes/BxDolVoteQuery.php

@@ -12,7 +12,7 @@
  */
 class BxDolVoteQuery extends BxDolObjectQuery
 {
-	protected $_sTriggerFieldRate;
+    protected $_sTriggerFieldRate;
 
     protected $_iPostTimeout;
 
@@ -50,13 +50,15 @@ class BxDolVoteQuery extends BxDolObjectQuery
         return $aResult;
     }
 
-    public function putVote($iObjectId, $iAuthorId, $sAuthorIp, $iValue, $bUndo = false)
+    public function putVote($iObjectId, $iAuthorId, $sAuthorIp, $aData, $bUndo = false)
     {
         $sQuery = $this->prepare("SELECT `object_id` FROM `{$this->_sTable}` WHERE `object_id` = ? LIMIT 1", $iObjectId);
         $bExists = (int)$this->getOne($sQuery) != 0;
         if(!$bExists && $bUndo)
             return false;
 
+        $iValue = $aData['value'];
+
         if(!$bExists)
             $sQuery = $this->prepare("INSERT INTO {$this->_sTable} SET `object_id` = ?, `count` = '1', `sum` = ?", $iObjectId, $iValue);
         else
@@ -65,21 +67,14 @@ class BxDolVoteQuery extends BxDolObjectQuery
         if((int)$this->query($sQuery) == 0)
             return false;
 
-        if($bUndo) {
-        	$sQuery = $this->prepare("SELECT `id` FROM `{$this->_sTableTrack}` WHERE `object_id` = ? AND `author_id` = ? LIMIT 1", $iObjectId, $iAuthorId);
-        	$iId = (int)$this->getOne($sQuery);
-
-            $sQuery = $this->prepare("DELETE FROM `{$this->_sTableTrack}` WHERE `id` = ? LIMIT 1", $iId);
-            if((int)$this->query($sQuery) > 0)
-            	return $iId;
-        }
-        else {
-            $iNow = time();
-            $iAuthorNip = ip2long($sAuthorIp);
-            $sQuery = $this->prepare("INSERT INTO `{$this->_sTableTrack}` SET `object_id` = ?, `author_id` = ?, `author_nip` = ?, `value` = ?, `date` = ?", $iObjectId, $iAuthorId, $iAuthorNip, $iValue, $iNow);
-            if((int)$this->query($sQuery) > 0)
-            	return $this->lastId();
-        }
+        if($bUndo)
+            return $this->_deleteTrack($iObjectId, $iAuthorId);
+
+        $iNow = time();
+        $iAuthorNip = ip2long($sAuthorIp);
+        $sQuery = $this->prepare("INSERT INTO `{$this->_sTableTrack}` SET `object_id` = ?, `author_id` = ?, `author_nip` = ?, `value` = ?, `date` = ?", $iObjectId, $iAuthorId, $iAuthorNip, $iValue, $iNow);
+        if((int)$this->query($sQuery) > 0)
+            return $this->lastId();
 
         return false;
     }
@@ -97,11 +92,28 @@ class BxDolVoteQuery extends BxDolObjectQuery
         if(empty($aResult))
             return $aResult;
 
-		$aResult['fields'] = ",`{$this->_sTable}`.`count` as `vote_count`, (`{$this->_sTable}`.`sum` / `{$this->_sTable}`.`count`) AS `vote_rate` ";
+        $aResult['fields'] = ", `{$this->_sTable}`.`count` as `vote_count`, (`{$this->_sTable}`.`sum` / `{$this->_sTable}`.`count`) AS `vote_rate` ";
         return $aResult;
     }
 
-	protected function _updateTriggerTable($iObjectId, $aEntry)
+    public function updateTriggerTableValue($iObjectId, $iValue)
+    {
+        return false;
+    }
+
+    protected function _deleteTrack($iObjectId, $iAuthorId)
+    {
+        $sQuery = $this->prepare("SELECT `id` FROM `{$this->_sTableTrack}` WHERE `object_id` = ? AND `author_id` = ? LIMIT 1", $iObjectId, $iAuthorId);
+        $iId = (int)$this->getOne($sQuery);
+
+        $sQuery = $this->prepare("DELETE FROM `{$this->_sTableTrack}` WHERE `id` = ? LIMIT 1", $iId);
+        if((int)$this->query($sQuery) > 0)
+            return $iId;
+
+        return false;
+    }
+    
+    protected function _updateTriggerTable($iObjectId, $aEntry)
     {
     	$sQuery = $this->prepare("UPDATE `{$this->_sTriggerTable}` SET `{$this->_sTriggerFieldCount}` = ?, `{$this->_sTriggerFieldRate}` = ? WHERE `{$this->_sTriggerFieldId}` = ?", $aEntry['count'], $aEntry['rate'], $iObjectId);
         return (int)$this->query($sQuery) > 0;
@@ -110,7 +122,7 @@ class BxDolVoteQuery extends BxDolObjectQuery
     protected function _deleteAuthorEntriesTableMain($aTrack)
     {
         return $this->query("UPDATE `{$this->_sTable}` SET `count`=`count`-1, `sum`=`sum`-:value WHERE `object_id`=:object_id", array(
-        	'object_id' => $aTrack['object_id'],
+            'object_id' => $aTrack['object_id'],
             'value' => $aTrack['value']
         ));
     }

+ 184 - 0
inc/classes/BxDolVoteReactions.php

@@ -0,0 +1,184 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * Like based votes for any content
+ */
+class BxDolVoteReactions extends BxTemplVote
+{
+    protected $_sDataList; //--- Reactions list name.
+    protected $_aDataList; //--- Reactions list with all reactions.
+
+    protected $_sDefault; //--- Default reaction name.
+
+    protected function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+
+        $this->_oQuery = new BxDolVoteReactionsQuery($this);
+        $this->_sType = BX_DOL_VOTE_TYPE_REACTIONS;
+
+        $this->_sDataList = 'sys_vote_reactions';
+        $this->_aDataList = array();
+
+        $this->_sDefault = '';
+    }
+
+    public function init($iId)
+    {
+        if(!parent::init($iId))
+            return false;
+
+        $aReactions = BxDolFormQuery::getDataItems($this->_sDataList, false, BX_DATA_VALUES_ALL);
+
+        foreach($aReactions as $sReaction => $aReaction) {
+            $aData = !empty($aReaction['Data']) ? unserialize($aReaction['Data']) : array();
+
+            $this->_aDataList[$sReaction] = array(
+                'name' => $sReaction,
+                'title' => $aReaction['LKey'],
+                'title_aux' => $aReaction['LKey2'],
+                'icon' => isset($aData['icon']) ? $aData['icon'] : '',
+                'weight' => isset($aData['weight']) ? $aData['weight'] : 1,
+            );
+        }
+
+        if(!empty($this->_aDataList)) {
+            $aNames = array_keys($this->_aDataList);
+            $this->_sDefault = array_shift($aNames);
+        }
+    }
+    /**
+     * Interface functions for outer usage
+     */
+    public function getValue()
+    {
+        return (int)$this->_aSystem['min_value'];
+    }
+
+    public function getReaction($sName)
+    {
+        if(empty($this->_aDataList))
+            $this->getReactions();
+
+        return !empty($this->_aDataList[$sName]) ? $this->_aDataList[$sName] : false;
+    }
+
+    public function getReactions($bFullInfo = false)
+    {
+        return $bFullInfo ? $this->_aDataList : array_keys($this->_aDataList);
+    }
+
+    /**
+     * Actions functions
+     */
+    public function actionGetDoVotePopup()
+    {
+        if(!$this->isEnabled())
+           return '';
+
+        return $this->_getDoVotePopup((int)bx_get('value'));
+    }
+
+    public function actionGetVotedBy()
+    {
+        if (!$this->isEnabled())
+           return '';
+
+        $sReaction = bx_get('reaction');
+        $sReaction = $sReaction !== false ? bx_process_input($sReaction) : $this->_sDefault;
+
+        return $this->_getVotedBy(array('reaction' => $sReaction));
+    }
+
+    /**
+     * Internal functions
+     */
+    protected function _isDuplicate($iObjectId, $iAuthorId, $iAuthorIp, $bVoted)
+    {
+        return $bVoted && !$this->isUndo();
+    }
+
+    protected function _isCount($aVote = array())
+    {
+        if(empty($aVote))
+            $aVote = $this->_getVote();
+
+        foreach($aVote as $sKey => $mixedValue)
+            if(substr($sKey, 0, 5) == 'count' && (int)$mixedValue != 0)
+                return true;
+
+        return false;
+    }
+
+    protected function _getVoteData()
+    {
+        $aData = parent::_getVoteData();
+        if($aData === false)
+            return false;
+
+        $sReaction = bx_get('reaction');
+        if($sReaction === false)
+            return false;
+
+        $aData['reaction'] = bx_process_input($sReaction);
+        return $aData;
+    }
+
+    protected function _returnVoteData($iObjectId, $iAuthorId, $iAuthorIp, $aData, $bVoted)
+    {
+        $sReaction = $aData['reaction'];
+
+        $bUndo = $this->isUndo();
+        $bDisabled = $bVoted && !$bUndo;
+
+        $aVote = $this->_getVote($iObjectId, true);
+        $aTrack = $bVoted ? $this->_getTrack($iObjectId, $iAuthorId) : array();
+
+        $sJsClick = '';
+        if(!$bDisabled)
+            $sJsClick = $bVoted && $bUndo ? $this->getJsClickDo($sReaction) : $this->getJsClick();
+
+        $iCount = (int)$aVote['count_' . $sReaction];
+        return array(
+            'code' => 0,
+            'reaction' => $sReaction,
+            'rate' => $aVote['rate_' . $sReaction],
+            'count' => $iCount,
+            'countf' => $iCount > 0 ? $this->_getLabelCounter($iCount, array('reaction' => $sReaction)) : '',
+            'label_icon' => $this->_getIconDoWithTrack($bVoted, $aTrack),
+            'label_title' => _t($this->_getTitleDoWithTrack($bVoted, $aTrack)),
+            'label_click' => $sJsClick,
+            'disabled' => $bVoted && !$this->isUndo(),
+        );
+    }
+
+    protected function _getIconDoWithTrack($bVoted, $aTrack = array())
+    {
+        $sReaction = $bVoted ? $aTrack['reaction'] : $this->_sDefault;
+
+    	return $this->_aDataList[$sReaction]['icon'];
+    }
+
+    protected function _getTitleDoWithTrack($bVoted, $aTrack = array())
+    {
+        $sReaction = $bVoted ? $aTrack['reaction'] : $this->_sDefault;
+
+    	return $this->_aDataList[$sReaction]['title'];
+    }
+    
+    protected function _getTitleDoBy($aParams = array())
+    {
+        $sReaction = !empty($aParams['reaction']) ? $aParams['reaction'] : $this->_sDefault;
+
+    	return _t('_vote_do_by_reactions', _t($this->_aDataList[$sReaction]['title']));
+    }
+}
+
+/** @} */

+ 159 - 0
inc/classes/BxDolVoteReactionsQuery.php

@@ -0,0 +1,159 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxDolVoteReactionsQuery extends BxDolVoteQuery
+{
+    public function __construct(&$oModule)
+    {
+        parent::__construct($oModule);
+    }
+
+    public function getVote($iObjectId)
+    {
+        $aReactions = $this->_oModule->getReactions();
+        $aReactionsGot = $this->getAllWithKey('SELECT * FROM ' . $this->_sTable . ' WHERE `object_id` = :object_id', 'reaction', array('object_id' => $iObjectId));
+
+        $aResult = array();
+        foreach($aReactions as $sName) {
+            $iCount = $iSum = 0;
+            if(!empty($aReactionsGot[$sName])) {
+                $iCount = (int)$aReactionsGot[$sName]['count'];
+                $iSum = (int)$aReactionsGot[$sName]['sum'];
+            }
+
+            $aResult['count_' . $sName] = $iCount;
+            $aResult['sum_' . $sName] = $iSum;
+            $aResult['rate_' . $sName] = $iCount != 0 ? $iSum / $iCount : 0;
+        }
+
+        return $aResult;
+    }
+
+    public function putVote($iObjectId, $iAuthorId, $sAuthorIp, $aData, $bUndo = false)
+    {
+        $sReaction = $aData['reaction'];
+        $aReaction = $this->_oModule->getReaction($sReaction);
+        if($aReaction === false)
+            return false;
+
+        $iValue = (int)$aData['value'];
+        $fValueFinal = $iValue * (float)$aReaction['weight'];
+
+        $sQuery = "SELECT `object_id` FROM `{$this->_sTable}` WHERE `object_id` = :object_id AND `reaction` = :reaction LIMIT 1";
+        $bExists = (int)$this->getOne($sQuery, array('object_id' => $iObjectId, 'reaction' => $sReaction)) != 0;
+        if(!$bExists && $bUndo)
+            return false;      
+
+        if(!$bExists)
+            $sQuery = $this->prepare("INSERT INTO {$this->_sTable} SET `object_id` = ?, `reaction` = ?, `count` = '1', `sum` = ?", $iObjectId, $sReaction, $fValueFinal);
+        else
+            $sQuery = $this->prepare("UPDATE `{$this->_sTable}` SET `count` = `count` " . ($bUndo ? "-" : "+") . " 1, `sum` = `sum` " . ($bUndo ? "-" : "+") . " ? WHERE `object_id` = ? AND `reaction` = ?", $fValueFinal, $iObjectId, $sReaction);
+
+        if((int)$this->query($sQuery) == 0)
+            return false;
+
+        if($bUndo)
+            return $this->_deleteTrack($iObjectId, $iAuthorId);
+
+        $iNow = time();
+        $iAuthorNip = ip2long($sAuthorIp);
+        $sQuery = $this->prepare("INSERT INTO `{$this->_sTableTrack}` SET `object_id` = ?, `author_id` = ?, `author_nip` = ?, `reaction` = ?, `value` = ?, `date` = ?", $iObjectId, $iAuthorId, $iAuthorNip, $sReaction, $iValue, $iNow);
+        if((int)$this->query($sQuery) > 0)
+            return $this->lastId();
+
+        return false;
+    }
+
+    public function getSqlParts($sMainTable, $sMainField)
+    {
+        if(empty($this->_sTable) || empty($sMainTable) || empty($sMainField))
+            return array();
+
+        $sFields = $sJoin = '';
+        $aReactions = $this->_oModule->getReactions();
+        foreach($aReactions as $iIndex => $sName) {
+            $sFields .= ", `t{$iIndex}`.`count` as `vote_count_{$sName}`, (`t{$iIndex}`.`sum` / `t{$iIndex}`.`count`) AS `vote_rate_{$sName}` ";
+            $sJoin .= " LEFT JOIN `{$this->_sTable}` AS `t{$iIndex}` ON (`t{$iIndex}`.`object_id` = `{$sMainTable}`.`{$sMainField}` AND `t{$iIndex}`.`reaction` = '{$sName}') ";
+        }
+
+        return array (
+            'fields' => $sFields,
+            'join' => $sJoin,
+        );
+    }
+
+    public function getPerformed($aParams = array())
+    {
+    	$aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
+        $aBindings = array();
+
+    	$sSelectClause = '*';
+    	$sWhereClause = '';
+    	$sLimitClause = isset($aParams['start']) && !empty($aParams['per_page']) ? "LIMIT " . $aParams['start'] . ", " . $aParams['per_page'] : "";
+
+    	if(!empty($aParams['type']))
+            switch($aParams['type']) {
+                case 'by':
+                    $aMethod['name'] = 'getColumn';
+                    $aBindings['object_id'] = $aParams['object_id'];
+                    $aBindings['reaction'] = $aParams['reaction'];
+
+                    $sSelectClause = '`author_id`';
+                    $sWhereClause = 'AND `object_id` = :object_id AND `reaction`=:reaction';
+                    break;
+            }
+
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $this->_sTableTrack . "` WHERE 1 " . $sWhereClause . $sLimitClause;
+        $aMethod['params'][] = $aBindings;
+
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+
+    /**
+     * Update trigger table.
+     * @param integer $iObjectId - object ID;
+     * @param array $aEntry - contains data received from BxDolVoteReactionsQuery::getVote method;
+     * @return boolean - operation result.
+     */
+    protected function _updateTriggerTable($iObjectId, $aEntry)
+    {
+        $iCounted = $iTotalCount = $fTotalRate = 0;
+        $aReactions = $this->_oModule->getReactions();
+        foreach($aReactions as $sName) {
+            $iCount = (int)$aEntry['count_' . $sName];
+            if($iCount == 0)
+                continue;
+
+            $iTotalCount += $iCount;
+            $fTotalRate += (float)$aEntry['rate_' . $sName];
+            $iCounted += 1;
+        }
+
+        return (int)$this->query("UPDATE `{$this->_sTriggerTable}` SET `{$this->_sTriggerFieldCount}`=:count, `{$this->_sTriggerFieldRate}`=:rate  WHERE `{$this->_sTriggerFieldId}`=:id", array(
+            'count' => $iTotalCount, 
+            'rate' => $iCounted != 0 ? $fTotalRate / $iCounted : 0, 
+            'id' => $iObjectId
+        )) > 0;
+    }
+
+    protected function _deleteAuthorEntriesTableMain($aTrack)
+    {
+        $aReaction = $this->_oModule->getReaction($aTrack['reaction']);
+        return $this->query("UPDATE `{$this->_sTable}` SET `count`=`count`-1, `sum`=`sum`-:value WHERE `object_id`=:object_id AND `reaction`=:reaction", array(
+            'value' => (int)$aTrack['value'] * (float)$aReaction['weight'],
+            'object_id' => $aTrack['object_id'],
+            'reaction' => $aTrack['reaction']
+        ));
+    }
+}
+
+/** @} */

+ 32 - 0
inc/classes/BxDolVoteStars.php

@@ -0,0 +1,32 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * Star based votes for any content
+ */
+class BxDolVoteStars extends BxTemplVote
+{
+    protected function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+
+        $this->_oQuery = new BxDolVoteStarsQuery($this);
+        $this->_sType = BX_DOL_VOTE_TYPE_STARS;
+    }
+
+    /**
+     * Internal functions
+     */
+    protected function _isDuplicate($iObjectId, $iAuthorId, $iAuthorIp, $bVoted)
+    {
+        return !$this->_oQuery->isPostTimeoutEnded($iObjectId, $iAuthorIp);
+    }
+}
+
+/** @} */

+ 28 - 0
inc/classes/BxDolVoteStarsQuery.php

@@ -0,0 +1,28 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxDolVoteStarsQuery extends BxDolVoteQuery
+{
+    public function __construct(&$oModule)
+    {
+        parent::__construct($oModule);
+    }
+
+    public function getLegend($iObjectId)
+    {
+    	$sQuery = $this->prepare("SELECT `value` AS `value`, COUNT(`value`) AS `count` FROM `{$this->_sTableTrack}` WHERE `object_id` = ? GROUP BY `value`", $iObjectId);
+
+    	return $this->getAllWithKey($sQuery, 'value');
+    }
+}
+
+/** @} */

+ 54 - 126
inc/js/classes/BxDolVote.js

@@ -6,76 +6,42 @@
  * @{
  */
 
-function BxDolVote(options)
+function BxDolVote(oOptions)
 {
-	this._sObjName = undefined == options.sObjName ? 'oVote' : options.sObjName; // javascript object name, to run current object instance from onTimer
-	this._sSystem = options.sSystem; // current comment system
-	this._iAuthorId = options.iAuthorId; // this comment's author ID.
-    this._iObjId = options.iObjId; // this object id comments
-    this._iLikeMode = undefined == options.iLikeMode ? 0 : options.iLikeMode;
+    if(typeof oOptions === 'undefined')
+        return;
+
+    this._sObjName = oOptions.sObjName === undefined ? 'oVote' : oOptions.sObjName; // javascript object name, to run current object instance from onTimer
+    this._sSystem = oOptions.sSystem; // current comment system
+    this._iAuthorId = oOptions.iAuthorId; // this comment's author ID.
+    this._iObjId = oOptions.iObjId; // this object id comments
 
     this._sActionsUri = 'vote.php';
-    this._sActionsUrl = options.sRootUrl + this._sActionsUri; // actions url address
+    this._sActionsUrl = oOptions.sRootUrl + this._sActionsUri; // actions url address
 
     this._sAnimationEffect = 'fade';
     this._iAnimationSpeed = 'slow';
-    this._sSP = undefined == options.sStylePrefix ? 'bx-vote' : options.sStylePrefix;
-    this._aHtmlIds = options.aHtmlIds;
-
-    this._iSaveWidth = -1;
-
-    //--- Init stars based vote ---//
-    var $this = this;
-    if(!this._iLikeMode) {
-    	$('.' + this._sSP + '.' + this._sSP + '-stars').each(function() {
-    		var oDoVote = $(this);
-	    	var fRate = oDoVote.attr('bx_vote_data_rate');
-	    	var iStarWidth = $this._getStarWidthDo(oDoVote);
-
-	    	$this._getSliderDo(oDoVote).width(Math.round(fRate * iStarWidth));
-    	});
-
-    	$('.' + this._sSP + '-legend.' + this._sSP + '-legend-stars').each(function() {
-    		var oLegend = $(this);
-    		var iStarWidth = $this._getStarWidthLegend(oLegend);
-
-	    	oLegend.find('.' + $this._sSP + '-legend-item').each(function() {
-	    		var oItem = $(this);
-
-	    		oItem.find('.' + $this._sSP + '-slider').width(parseInt(oItem.attr('bx_vote_item_value')) * iStarWidth); 
-	    	});
-    	});
-    }
+    this._sSP = oOptions.sStylePrefix === undefined ? 'bx-vote' : oOptions.sStylePrefix;
+    this._aHtmlIds = oOptions.aHtmlIds;
 }
 
-BxDolVote.prototype.toggleByPopup = function(oLink) {
-	var $this = this;
-    var oData = this._getDefaultParams();
-    oData['action'] = 'GetVotedBy';
-
-	$(oLink).dolPopupAjax({
-		id: this._aHtmlIds['by_popup'], 
-		url: bx_append_url_params(this._sActionsUri, oData)
-	});
-};
-
-BxDolVote.prototype.over = function (oLink)
+BxDolVote.prototype.init = function()
 {
-	var oSlider = this._getSliderDo(oLink);
-	var iIndex = this._getButtons(oLink).index(oLink);
-
-    this._iSaveWidth = parseInt(oSlider.width());
-    oSlider.width((iIndex + 1) * this._getStarWidthDo(oLink));
+    return;
 };
 
-BxDolVote.prototype.out = function (oLink)
+BxDolVote.prototype.toggleByPopup = function(oLink)
 {
-	var oSlider = this._getSliderDo(oLink);
+    var oData = this._getDefaultParams();
+    oData['action'] = 'GetVotedBy';
 
-	oSlider.width(this._iSaveWidth);
+    $(oLink).dolPopupAjax({
+        id: this._aHtmlIds['by_popup'], 
+        url: bx_append_url_params(this._sActionsUri, oData)
+    });
 };
 
-BxDolVote.prototype.vote = function (oLink, iValue, onComplete)
+BxDolVote.prototype.vote = function(oLink, iValue, onComplete)
 {
     var $this = this;
     var oParams = this._getDefaultParams();
@@ -86,90 +52,52 @@ BxDolVote.prototype.vote = function (oLink, iValue, onComplete)
     	this._sActionsUrl,
     	oParams,
     	function(oData) {
-    		var fContinue = function() {
-    			if(oData && oData.code != 0)
-                    return;
-
-        		if($this._iLikeMode) {
-    	    		if(oData && oData.label_icon)
-    	    			$(oLink).find('.sys-icon').attr('class', 'sys-icon ' + oData.label_icon);
-
-    	    		if(oData && oData.label_title) {
-    	    			$(oLink).attr('title', oData.label_title);
-    	    			$(oLink).find('span').html(oData.label_title);
-    	    		}
-
-    	    		if(oData && oData.disabled)
-    	    			$(oLink).removeAttr('onclick').addClass($(oLink).hasClass('bx-btn') ? 'bx-btn-disabled' : 'bx-vote-disabled');
-        		}
-
-        		if(!$this._iLikeMode)
-        			$this._iSaveWidth = Math.round(oData.rate * $this._getStarWidthDo(oLink));
-
-                var oCounter = $this._getCounter(oLink);
-                if(oCounter && oCounter.length > 0) {
-                	oCounter.html(oData.countf);
-
-                	oCounter.parents('.' + $this._sSP + '-counter-holder:first').bx_anim(oData.count > 0 ? 'show' : 'hide');
-                }
-
-                if(typeof onComplete == 'function')
-                	onComplete(oLink, oData);
-    		};
-
-    		if(oData && oData.message != undefined)
-                bx_alert(oData.message, fContinue);
-    		else
-    			fContinue();
+            if(oData && oData.message != undefined)
+                bx_alert(oData.message, function() {
+                    $this.onVote(oLink, oData, onComplete);
+                });
+            else
+                $this.onVote(oLink, oData, onComplete);
         },
         'json'
     );
 };
 
-BxDolVote.prototype._getButtons = function(oElement) {
-	if($(oElement).hasClass(this._sSP))
-		return $(oElement).find('.' + this._sSP + '-button');
-	else
-		return $(oElement).parents('.' + this._sSP + ':first').find('.' + this._sSP + '-button');
-};
+BxDolVote.prototype.onVote = function(oLink, oData, onComplete)
+{
+    if(oData && oData.code != 0)
+        return;
 
-BxDolVote.prototype._getSliderDo = function(oElement) {
-	return this._getSlider(oElement, '.' + this._sSP + '-do');
-};
-BxDolVote.prototype._getSliderLegend = function(oElement) {
-	return this._getSlider(oElement, '.' + this._sSP + '-legend');
-};
-BxDolVote.prototype._getSlider = function(oElement, sParent) {
-	var sSlider = (sParent.length > 0 ? sParent + ' ' : '') + '.' + this._sSP + '-slider';
-	if($(oElement).hasClass(this._sSP))
-		return $(oElement).find(sSlider);
-	else
-		return $(oElement).parents('.' + this._sSP + ':first').find(sSlider);
-};
+    var oCounter = this._getCounter(oLink);
+    if(oCounter && oCounter.length > 0) {
+        oCounter.html(oData.countf);
 
-BxDolVote.prototype._getStarWidthDo = function(oElement) {
-	return this._getSliderDo(oElement).find('.sys-icon').width();
-};
-BxDolVote.prototype._getStarWidthLegend = function(oElement) {
-	return this._getSliderLegend(oElement).find('.sys-icon').width();
+        oCounter.parents('.' + this._sSP + '-counter-holder:first').bx_anim(oData.count > 0 ? 'show' : 'hide');
+    }
+
+    if(typeof onComplete == 'function')
+        onComplete(oLink, oData);
 };
 
-BxDolVote.prototype._getCounter = function(oElement) {
-	if($(oElement).hasClass(this._sSP))
-		return $(oElement).find('.' + this._sSP + '-counter');
-	else 
-		return $(oElement).parents('.' + this._sSP + ':first').find('.' + this._sSP + '-counter');
+BxDolVote.prototype._getCounter = function(oElement)
+{
+    if($(oElement).hasClass(this._sSP))
+        return $(oElement).find('.' + this._sSP + '-counter');
+    else 
+        return $(oElement).parents('.' + this._sSP + ':first').find('.' + this._sSP + '-counter');
 };
 
-BxDolVote.prototype._loadingInButton = function(e, bShow) {
-	if($(e).length)
-		bx_loading_btn($(e), bShow);
-	else
-		bx_loading($('body'), bShow);	
+BxDolVote.prototype._loadingInButton = function(e, bShow)
+{
+    if($(e).length)
+        bx_loading_btn($(e), bShow);
+    else
+        bx_loading($('body'), bShow);
 };
 
-BxDolVote.prototype._getDefaultParams = function() {
-	var oDate = new Date();
+BxDolVote.prototype._getDefaultParams = function() 
+{
+    var oDate = new Date();
     return {
         sys: this._sSystem,
         id: this._iObjId,

+ 36 - 0
inc/js/classes/BxDolVoteLikes.js

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+function BxDolVoteLikes(oOptions)
+{
+    BxDolVote.call(this, oOptions);
+}
+
+BxDolVoteLikes.prototype = Object.create(BxDolVote.prototype);
+BxDolVoteLikes.prototype.constructor = BxDolVoteLikes;
+
+BxDolVoteLikes.prototype.onVote = function (oLink, oData, onComplete)
+{
+    if(oData && oData.code != 0)
+        return;
+
+    if(oData && oData.label_icon)
+        $(oLink).find('.sys-icon').attr('class', 'sys-icon ' + oData.label_icon);
+
+    if(oData && oData.label_title) {
+        $(oLink).attr('title', oData.label_title);
+        $(oLink).find('span').html(oData.label_title);
+    }
+
+    if(oData && oData.disabled)
+        $(oLink).removeAttr('onclick').addClass($(oLink).hasClass('bx-btn') ? 'bx-btn-disabled' : 'bx-vote-disabled');
+
+    BxDolVote.prototype.onVote.call(this, oLink, oData, onComplete);
+};
+
+/** @} */

+ 112 - 0
inc/js/classes/BxDolVoteReactions.js

@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+function BxDolVoteReactions(oOptions)
+{
+    BxDolVote.call(this, oOptions);
+    
+    this._sClassDoButton = 'bx-vote-do-vote';
+
+}
+
+BxDolVoteReactions.prototype = Object.create(BxDolVote.prototype);
+BxDolVoteReactions.prototype.constructor = BxDolVoteReactions;
+
+BxDolVote.prototype.vote = function(oLink, iValue, sReaction, onComplete)
+{
+    var $this = this;
+    var oParams = this._getDefaultParams();
+    oParams['action'] = 'Vote';
+    oParams['value'] = iValue;
+    oParams['reaction'] = sReaction;
+
+    $.post(
+    	this._sActionsUrl,
+    	oParams,
+    	function(oData) {
+            if(oData && oData.message != undefined)
+                bx_alert(oData.message, function() {
+                    $this.onVote(oLink, oData, onComplete);
+                });
+            else
+                $this.onVote(oLink, oData, onComplete);
+        },
+        'json'
+    );
+};
+
+BxDolVoteReactions.prototype.onVote = function (oLink, oData, onComplete)
+{
+    if(oData && oData.code != 0)
+        return;
+    
+    $('#' + this._aHtmlIds['do_popup']).dolPopupHide({});
+
+    oLink = $(oLink);
+    if(!oLink.hasClass(this._sClassDoButton))
+        oLink = $('#' + this._aHtmlIds['main'] + ' .' + this._sClassDoButton);
+
+    //--- Update Do button.
+    if(oData && oData.label_icon)
+        oLink.find('.sys-icon').attr('class', 'sys-icon ' + oData.label_icon);
+
+    if(oData && oData.label_title) {
+        oLink.attr('title', oData.label_title);
+        oLink.find('span').html(oData.label_title);
+    }
+
+    if(oData && oData.label_click)
+        oLink.attr('onclick', 'javascript:' + oData.label_click)
+
+    if(oData && oData.disabled)
+        oLink.removeAttr('onclick').addClass($(oLink).hasClass('bx-btn') ? 'bx-btn-disabled' : 'bx-vote-disabled');
+
+    //--- Update Counter.
+    var oCounter = this._getCounter(oLink);
+    if(oCounter && oCounter.length > 0)
+        oCounter.filter('.' + oData.reaction).html(oData.countf).bx_anim(oData.count > 0 ? 'show' : 'hide');
+
+    if(typeof onComplete == 'function')
+        onComplete(oLink, oData);
+};
+
+BxDolVoteReactions.prototype.toggleDoPopup = function(oLink, iValue)
+{
+    var oParams = this._getDefaultParams();
+    oParams['action'] = 'GetDoVotePopup';
+
+    $(oLink).dolPopupAjax({
+        id: {value: this._aHtmlIds['do_popup'], force: true}, 
+        url: bx_append_url_params(this._sActionsUri, oParams),
+        value: iValue
+    });
+};
+
+BxDolVoteReactions.prototype.toggleByPopup = function(oLink, sReaction)
+{
+    var oParams = this._getDefaultParams();
+    oParams['action'] = 'GetVotedBy';
+    oParams['reaction'] = sReaction;
+
+    $(oLink).dolPopupAjax({
+        id: this._aHtmlIds['by_popup'], 
+        url: bx_append_url_params(this._sActionsUri, oParams),
+        removeOnClose: true
+    });
+};
+
+BxDolVoteReactions.prototype._getCounter = function(oElement)
+{
+    var oCounter = BxDolVote.prototype._getCounter.call(this, oElement);
+    if(oCounter && oCounter.length > 0)
+        return oCounter;
+
+    return $('#' + this._aHtmlIds['counter']).find('.' + this._sSP + '-counter');
+};
+
+/** @} */

+ 110 - 0
inc/js/classes/BxDolVoteStars.js

@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+function BxDolVoteStars(oOptions)
+{
+    BxDolVote.call(this, oOptions);
+
+    this._iSaveWidth = -1;
+
+    var $this = this;
+    $(document).ready(function() {
+    	$this.init();
+    });
+}
+
+BxDolVoteStars.prototype = Object.create(BxDolVote.prototype);
+BxDolVoteStars.prototype.constructor = BxDolVoteStars;
+
+BxDolVoteStars.prototype.init = function ()
+{
+    var $this = this;
+
+    $('.' + this._sSP + '.' + this._sSP + '-stars').each(function() {
+        var oDoVote = $(this);
+        var fRate = oDoVote.attr('bx_vote_data_rate');
+        var iStarWidth = $this._getStarWidthDo(oDoVote);
+
+        $this._getSliderDo(oDoVote).width(Math.round(fRate * iStarWidth));
+    });
+
+    $('.' + this._sSP + '-legend.' + this._sSP + '-legend-stars').each(function() {
+        var oLegend = $(this);
+        var iStarWidth = $this._getStarWidthLegend(oLegend);
+
+        oLegend.find('.' + $this._sSP + '-legend-item').each(function() {
+            var oItem = $(this);
+
+            oItem.find('.' + $this._sSP + '-slider').width(parseInt(oItem.attr('bx_vote_item_value')) * iStarWidth); 
+        });
+    });
+};
+
+BxDolVoteStars.prototype.onVote = function (oLink, oData, onComplete)
+{
+    if(oData && oData.code != 0)
+        return;
+
+    this._iSaveWidth = Math.round(oData.rate * this._getStarWidthDo(oLink));
+
+    BxDolVote.prototype.onVote.call(this, oLink, oData, onComplete);
+};
+
+BxDolVoteStars.prototype.over = function (oLink)
+{
+    var oSlider = this._getSliderDo(oLink);
+    var iIndex = this._getButtons(oLink).index(oLink);
+
+    this._iSaveWidth = parseInt(oSlider.width());
+    oSlider.width((iIndex + 1) * this._getStarWidthDo(oLink));
+};
+
+BxDolVoteStars.prototype.out = function (oLink)
+{
+	var oSlider = this._getSliderDo(oLink);
+
+	oSlider.width(this._iSaveWidth);
+};
+
+BxDolVoteStars.prototype._getButtons = function(oElement)
+{
+    if($(oElement).hasClass(this._sSP))
+        return $(oElement).find('.' + this._sSP + '-button');
+    else
+        return $(oElement).parents('.' + this._sSP + ':first').find('.' + this._sSP + '-button');
+};
+
+BxDolVoteStars.prototype._getSliderDo = function(oElement)
+{
+    return this._getSlider(oElement, '.' + this._sSP + '-do');
+};
+
+BxDolVoteStars.prototype._getSliderLegend = function(oElement) 
+{
+    return this._getSlider(oElement, '.' + this._sSP + '-legend');
+};
+
+BxDolVoteStars.prototype._getSlider = function(oElement, sParent) {
+    var sSlider = (sParent.length > 0 ? sParent + ' ' : '') + '.' + this._sSP + '-slider';
+    if($(oElement).hasClass(this._sSP))
+        return $(oElement).find(sSlider);
+    else
+        return $(oElement).parents('.' + this._sSP + ':first').find(sSlider);
+};
+
+BxDolVoteStars.prototype._getStarWidthDo = function(oElement)
+{
+    return this._getSliderDo(oElement).find('.sys-icon').width();
+};
+
+BxDolVoteStars.prototype._getStarWidthLegend = function(oElement)
+{
+    return this._getSliderLegend(oElement).find('.sys-icon').width();
+};
+
+/** @} */

+ 13 - 0
install/sql/system.sql

@@ -3093,6 +3093,7 @@ INSERT INTO `sys_form_pre_lists`(`key`, `title`, `module`, `use_for_sets`, `exte
 ('Sex', '_adm_form_txt_pre_lists_sex', 'system', '1', '1'),
 ('Language', '_adm_form_txt_pre_lists_language', 'system', '0', '1'),
 ('sys_report_types', '_sys_pre_lists_report_types', 'system', '0', '0'),
+('sys_vote_reactions', '_sys_pre_lists_vote_reactions', 'system', '0', '0'),
 ('sys_relations', '_sys_pre_lists_relations', 'system', '0', '1');
 
 
@@ -3423,6 +3424,14 @@ INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`) VALU
 ('sys_report_types', 'nude', 4, '_sys_pre_lists_report_types_nude', ''),
 ('sys_report_types', 'other', 5, '_sys_pre_lists_report_types_other', '');
 
+INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`, `Data`) VALUES
+('sys_vote_reactions', 'like', 1, '_sys_pre_lists_vote_reactions_like', '', 'a:2:{s:4:"icon";s:9:"thumbs-up";s:6:"weight";s:1:"1";}'),
+('sys_vote_reactions', 'love', 2, '_sys_pre_lists_vote_reactions_love', '', 'a:2:{s:4:"icon";s:5:"heart";s:6:"weight";s:1:"1";}'),
+('sys_vote_reactions', 'joy', 3, '_sys_pre_lists_vote_reactions_joy', '', 'a:2:{s:4:"icon";s:11:"grin-squint";s:6:"weight";s:1:"1";}'),
+('sys_vote_reactions', 'surprise', 4, '_sys_pre_lists_vote_reactions_surprise', '', 'a:2:{s:4:"icon";s:8:"surprise";s:6:"weight";s:1:"1";}'),
+('sys_vote_reactions', 'sadness', 5, '_sys_pre_lists_vote_reactions_sadness', '', 'a:2:{s:4:"icon";s:8:"sad-tear";s:6:"weight";s:1:"1";}'),
+('sys_vote_reactions', 'anger', 6, '_sys_pre_lists_vote_reactions_anger', '', 'a:2:{s:4:"icon";s:5:"angry";s:6:"weight";s:1:"1";}');
+
 INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`, `Data`) VALUES
 ('sys_relations', '1', 1, '_sys_pre_lists_relations_husband', '', 'a:1:{i:0;s:1:"2";}'),
 ('sys_relations', '2', 2, '_sys_pre_lists_relations_wife', '', 'a:1:{i:0;s:1:"1";}'),
@@ -3433,6 +3442,7 @@ INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`, `Dat
 ('sys_relations', '7', 7, '_sys_pre_lists_relations_brother', '', 'a:2:{i:0;s:1:"7";i:1;s:1:"8";}'),
 ('sys_relations', '8', 8, '_sys_pre_lists_relations_sister', '', 'a:2:{i:0;s:1:"7";i:1;s:1:"8";}');
 
+sys_reactions
 -- --------------------------------------------------------
 
 CREATE TABLE IF NOT EXISTS `sys_menu_templates` (
@@ -4526,6 +4536,9 @@ INSERT INTO `sys_preloader`(`module`, `type`, `content`, `active`, `order`) VALU
 ('system', 'js_system', 'BxDolScore.js', 1, 34),
 ('system', 'js_system', 'BxDolView.js', 1, 35),
 ('system', 'js_system', 'BxDolVote.js', 1, 36),
+('system', 'js_system', 'BxDolVoteLikes.js', 1, 37),
+('system', 'js_system', 'BxDolVoteReactions.js', 1, 38),
+('system', 'js_system', 'BxDolVoteStars.js', 1, 39),
 
 ('system', 'js_translation', '_Are_you_sure', 1, 1),
 ('system', 'js_translation', '_error occured', 1, 2),

+ 27 - 0
modules/base/general/classes/BxBaseModGeneralMenuViewActions.php

@@ -185,6 +185,33 @@ class BxBaseModGeneralMenuViewActions extends BxTemplMenuCustom
     	return array($sResult, $this->_sClassMiSa);
     }
 
+    protected function _getMenuItemReaction($aItem, $aParams = array())
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sObject = !empty($aParams['object']) ? $aParams['object'] : '';
+        if(empty($sObject) && !empty($CNF['OBJECT_REACTIONS']))
+            $sObject = $CNF['OBJECT_REACTIONS'];
+
+        $iId = !empty($aParams['id']) ? (int)$aParams['id'] : '';
+        if(empty($iId))
+            $iId = $this->_iContentId;
+
+        $oObject = !empty($sObject) ? BxDolVote::getObjectInstance($sObject, $iId) : false;
+        if(!$oObject || !$oObject->isEnabled())
+            return '';
+
+        $sResult = $oObject->getElementBlock(array(
+            'dynamic_mode' => $this->_bDynamicMode,
+            'show_do_vote_as_button' => $this->_bShowAsButton,
+            'show_do_vote_label' => $this->_bShowTitle
+        ));
+        if(empty($sResult))
+            return '';
+
+    	return array($sResult, $this->_sClassMiSa);
+    }
+
     protected function _getMenuItemScore($aItem, $aParams = array())
     {
         $CNF = &$this->_oModule->_oConfig->CNF;

+ 1 - 1
modules/base/profile/classes/BxBaseModProfileVote.php

@@ -9,7 +9,7 @@
  * @{
  */
 
-class BxBaseModProfileVote extends BxTemplVote
+class BxBaseModProfileVote extends BxTemplVoteLikes
 {
     protected $_sModule;
     protected $_oModule;

+ 7 - 10
modules/base/text/classes/BxBaseModTextVotePollAnswers.php

@@ -9,7 +9,7 @@
  * @{
  */
 
-class BxBaseModTextVotePollAnswers extends BxTemplVote
+class BxBaseModTextVotePollAnswers extends BxTemplVoteLikes
 {
     protected $_sModule;
     protected $_oModule;
@@ -31,7 +31,7 @@ class BxBaseModTextVotePollAnswers extends BxTemplVote
 
         $CNF = &$this->_oModule->_oConfig->CNF;
 
-        $this->_aElementDefaults['likes'] = array_merge($this->_aElementDefaults['likes'], array(
+        $this->_aElementDefaults = array_merge($this->_aElementDefaults, array(
             'show_do_vote_label' => true,
             'show_counter' => false
         ));
@@ -46,17 +46,14 @@ class BxBaseModTextVotePollAnswers extends BxTemplVote
         $this->_sTmplNameCounterText = 'poll_answer_vc_text.html';
     }
 
-    public function getJsClick()
+    public function getJsClick($iValue = 0)
     {
         $CNF = &$this->_oModule->_oConfig->CNF;
 
-        if(!$this->isLikeMode())
-            return false;
-
         $sJsObjectVote = $this->getJsObjectName();
         $sJsObjectPoll = $this->_oModule->_oConfig->getJsObject('poll');
 
-        return $sJsObjectVote . '.vote(this, ' . $this->getMaxValue() . ', function(oLink, oData) {' . $sJsObjectPoll . '.onPollAnswerVote(oLink, oData, ' . $this->_aPollInfo[$CNF['FIELD_POLL_ID']] . ');})';
+        return $sJsObjectVote . '.vote(this, ' . $this->getValue() . ', function(oLink, oData) {' . $sJsObjectPoll . '.onPollAnswerVote(oLink, oData, ' . $this->_aPollInfo[$CNF['FIELD_POLL_ID']] . ');})';
     }
 
     public function getCounter($aParams = array())
@@ -128,14 +125,14 @@ class BxBaseModTextVotePollAnswers extends BxTemplVote
     	return bx_process_output($this->_aObjectInfo['title']);
     }
 
-    protected function _getTitleDoBy()
+    protected function _getTitleDoBy($aParams = array())
     {
         $CNF = &$this->_oModule->_oConfig->CNF;
 
-    	return $CNF['T']['txt_poll_answer_vote_do_by'];
+    	return _t($CNF['T']['txt_poll_answer_vote_do_by']);
     }
 
-    protected function _getLabelCounter($iCount)
+    protected function _getLabelCounter($iCount, $aParams = array())
     {
         $CNF = &$this->_oModule->_oConfig->CNF;
 

+ 1 - 1
modules/base/text/template/poll_answer_ve_block.html

@@ -1,4 +1,4 @@
-<div id="__html_id__" class="__style_prefix__ __class__" bx_vote_data_rate="__rate__" bx_vote_data_count="__count__">
+<div id="__html_id__" class="__style_prefix__ __class__"<bx_if:show_vote_data> bx_vote_data_rate="__rate__" bx_vote_data_count="__count__"</bx_if:show_vote_data>>
 	<div class="__style_prefix__-do bx-clearfix">
 		<bx_if:show_do_vote>
 			<div class="__style_prefix__-element-holder">__do_vote__</div>

+ 21 - 0
modules/boonex/decorous/data/template/system/scripts/BxTemplVoteLikes.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteLikes extends BxBaseVoteLikes
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/decorous/data/template/system/scripts/BxTemplVoteReactions.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteReactions extends BxBaseVoteReactions
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/decorous/data/template/system/scripts/BxTemplVoteStars.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteStars extends BxBaseVoteStars
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 9 - 0
modules/boonex/english/data/langs/system/en.xml

@@ -2281,6 +2281,7 @@ If it is not enabled then please consider implement this optimization, since it
         <string name="_sys_menu_item_title_system_va_score"><![CDATA[Vote (for/against)]]></string>
         <string name="_sys_menu_item_title_system_va_favorite"><![CDATA[Favorite]]></string>
         <string name="_sys_menu_item_title_system_va_feature"><![CDATA[Feature]]></string>
+        <string name="_sys_menu_item_title_system_va_reaction"><![CDATA[Reaction]]></string>
         <string name="_sys_menu_item_title_system_va_repost"><![CDATA[Repost]]></string>
         <string name="_sys_menu_item_title_system_va_report"><![CDATA[Report]]></string>
         <string name="_sys_menu_item_title_system_va_more_auto"><![CDATA[More]]></string>
@@ -2507,6 +2508,13 @@ If it is not enabled then please consider implement this optimization, since it
 	<string name="_sys_pre_lists_report_types_fraud"><![CDATA[fraud]]></string>
 	<string name="_sys_pre_lists_report_types_nude"><![CDATA[nude]]></string>
 	<string name="_sys_pre_lists_report_types_other"><![CDATA[other]]></string>
+        <string name="_sys_pre_lists_vote_reactions"><![CDATA[Reactions]]></string>
+        <string name="_sys_pre_lists_vote_reactions_like"><![CDATA[Like]]></string>
+        <string name="_sys_pre_lists_vote_reactions_love"><![CDATA[Love]]></string>
+        <string name="_sys_pre_lists_vote_reactions_joy"><![CDATA[Haha]]></string>
+        <string name="_sys_pre_lists_vote_reactions_surprise"><![CDATA[Wow]]></string>
+        <string name="_sys_pre_lists_vote_reactions_sadness"><![CDATA[Sad]]></string>
+        <string name="_sys_pre_lists_vote_reactions_anger"><![CDATA[Angry]]></string>
 	<string name="_sys_profile_sample_single"><![CDATA[profile]]></string>
 	<string name="_sys_profile_friendship_added"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> befriended <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
 	<string name="_sys_profile_subscription_added"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> followed <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
@@ -2776,6 +2784,7 @@ If it is not enabled then please consider implement this optimization, since it
 	<string name="_vote_do_like"><![CDATA[Thank you!]]></string>
 	<string name="_vote_do_unlike"><![CDATA[No thank you]]></string>
 	<string name="_vote_do_by"><![CDATA[Thanked by]]></string>
+        <string name="_vote_do_by_reactions"><![CDATA["{0}" by]]></string>
 	<string name="_vote_err_not_enabled"><![CDATA[The feature is not available right now. Please, try again later.]]></string>
 	<string name="_vote_err_duplicate_vote"><![CDATA[Duplicate vote]]></string>
         <string name="_vote_n_votes"><![CDATA[{0} votes]]></string>

+ 21 - 0
modules/boonex/lucid/data/template/system/scripts/BxTemplVoteLikes.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteLikes extends BxBaseVoteLikes
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/lucid/data/template/system/scripts/BxTemplVoteReactions.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteReactions extends BxBaseVoteReactions
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/lucid/data/template/system/scripts/BxTemplVoteStars.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteStars extends BxBaseVoteStars
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 0 - 0
modules/boonex/market/classes/BxMarketVote.php → modules/boonex/market/classes/BxMarketVoteStars.php


+ 1 - 1
modules/boonex/market/install/sql/install.sql

@@ -509,7 +509,7 @@ INSERT INTO `sys_objects_cmts` (`Name`, `Module`, `Table`, `CharsPostMin`, `Char
 
 -- VOTES
 INSERT INTO `sys_objects_vote` (`Name`, `TableMain`, `TableTrack`, `PostTimeout`, `MinValue`, `MaxValue`, `IsUndo`, `IsOn`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldRate`, `TriggerFieldRateCount`, `ClassName`, `ClassFile`) VALUES 
-('bx_market', 'bx_market_votes', 'bx_market_votes_track', '604800', '1', '5', '0', '1', 'bx_market_products', 'id', 'author', 'rate', 'votes', 'BxMarketVote', 'modules/boonex/market/classes/BxMarketVote.php');
+('bx_market', 'bx_market_votes', 'bx_market_votes_track', '604800', '1', '5', '0', '1', 'bx_market_products', 'id', 'author', 'rate', 'votes', 'BxMarketVoteStars', 'modules/boonex/market/classes/BxMarketVoteStars.php');
 
 -- SCORES
 INSERT INTO `sys_objects_score` (`name`, `module`, `table_main`, `table_track`, `post_timeout`, `is_on`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_score`, `trigger_field_cup`, `trigger_field_cdown`, `class_name`, `class_file`) VALUES 

+ 21 - 0
modules/boonex/ocean/data/template/system/scripts/BxTemplVoteLikes.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteLikes extends BxBaseVoteLikes
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/ocean/data/template/system/scripts/BxTemplVoteReactions.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteReactions extends BxBaseVoteReactions
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/ocean/data/template/system/scripts/BxTemplVoteStars.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteStars extends BxBaseVoteStars
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/photos/classes/BxPhotosVoteStars.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Photos Photos
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxPhotosVoteStars extends BxTemplVoteStars
+{
+    function __construct($sSystem, $iId, $iInit = 1)
+    {
+        parent::__construct($sSystem, $iId, $iInit);        
+    }
+}
+
+/** @} */

+ 1 - 1
modules/boonex/photos/install/sql/install.sql

@@ -297,7 +297,7 @@ INSERT INTO `sys_objects_cmts` (`Name`, `Module`, `Table`, `CharsPostMin`, `Char
 -- VOTES
 INSERT INTO `sys_objects_vote` (`Name`, `TableMain`, `TableTrack`, `PostTimeout`, `MinValue`, `MaxValue`, `IsUndo`, `IsOn`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldRate`, `TriggerFieldRateCount`, `ClassName`, `ClassFile`) VALUES 
 ('bx_photos', 'bx_photos_votes', 'bx_photos_votes_track', '604800', '1', '1', '0', '1', 'bx_photos_entries', 'id', 'author', 'rate', 'votes', '', ''),
-('bx_photos_stars', 'bx_photos_svotes', 'bx_photos_svotes_track', '604800', '1', '5', '0', '1', 'bx_photos_entries', 'id', 'author', 'srate', 'svotes', '', '');
+('bx_photos_stars', 'bx_photos_svotes', 'bx_photos_svotes_track', '604800', '1', '5', '0', '1', 'bx_photos_entries', 'id', 'author', 'srate', 'svotes', 'BxPhotosVoteStars', 'modules/boonex/photos/classes/BxPhotosVoteStars.php');
 
 
 -- SCORES

+ 7 - 10
modules/boonex/polls/classes/BxPollsVoteSubentries.php

@@ -9,7 +9,7 @@
  * @{
  */
 
-class BxPollsVoteSubentries extends BxTemplVote
+class BxPollsVoteSubentries extends BxTemplVoteLikes
 {
     protected $_sModule;
     protected $_oModule;
@@ -29,7 +29,7 @@ class BxPollsVoteSubentries extends BxTemplVote
 
         $CNF = $this->_oModule->_oConfig->CNF;
 
-        $this->_aElementDefaults['likes'] = array_merge($this->_aElementDefaults['likes'], array(
+        $this->_aElementDefaults = array_merge($this->_aElementDefaults, array(
             'show_do_vote_label' => true,
             'show_counter' => false
         ));
@@ -41,17 +41,14 @@ class BxPollsVoteSubentries extends BxTemplVote
         $this->_sTmplNameCounterText = 'subentries_vc_text.html';
     }
 
-    public function getJsClick()
+    public function getJsClick($iValue = 0)
     {
         $CNF = $this->_oModule->_oConfig->CNF;
 
-        if(!$this->isLikeMode())
-            return false;
-
         $sJsObjectVote = $this->getJsObjectName();
         $sJsObjectEntry = $this->_oModule->_oConfig->getJsObject('entry');
 
-        return $sJsObjectVote . '.vote(this, ' . $this->getMaxValue() . ', function(oLink, oData) {' . $sJsObjectEntry . '.onVote(oLink, oData, ' . $this->_aContentInfo[$CNF['FIELD_ID']] . ');})';
+        return $sJsObjectVote . '.vote(this, ' . $this->getValue() . ', function(oLink, oData) {' . $sJsObjectEntry . '.onVote(oLink, oData, ' . $this->_aContentInfo[$CNF['FIELD_ID']] . ');})';
     }
 
     public function getCounter($aParams = array())
@@ -110,12 +107,12 @@ class BxPollsVoteSubentries extends BxTemplVote
     	return bx_process_output($this->_aObjectInfo['title']);
     }
 
-    protected function _getTitleDoBy()
+    protected function _getTitleDoBy($aParams = array())
     {
-    	return '_bx_polls_txt_subentry_vote_do_by';
+    	return _t('_bx_polls_txt_subentry_vote_do_by');
     }
 
-    protected function _getLabelCounter($iCount)
+    protected function _getLabelCounter($iCount, $aParams = array())
     {
         return _t('_bx_polls_txt_subentry_vote_counter', $iCount);
     }

+ 1 - 1
modules/boonex/polls/template/subentries_ve_block.html

@@ -1,4 +1,4 @@
-<div id="__html_id__" class="__style_prefix__ __class__" bx_vote_data_rate="__rate__" bx_vote_data_count="__count__">
+<div id="__html_id__" class="__style_prefix__ __class__"<bx_if:show_vote_data> bx_vote_data_rate="__rate__" bx_vote_data_count="__count__"</bx_if:show_vote_data>>
 	<div class="__style_prefix__-do bx-clearfix">
 		<bx_if:show_do_vote>
 			<div class="__style_prefix__-element-holder">__do_vote__</div>

+ 1 - 0
modules/boonex/posts/classes/BxPostsConfig.php

@@ -101,6 +101,7 @@ class BxPostsConfig extends BxBaseModTextConfig
             'OBJECT_REPORTS' => 'bx_posts',
             'OBJECT_VIEWS' => 'bx_posts',
             'OBJECT_VOTES' => 'bx_posts',
+            'OBJECT_REACTIONS' => 'bx_posts_reactions',
             'OBJECT_SCORES' => 'bx_posts',
             'OBJECT_FAVORITES' => 'bx_posts',
             'OBJECT_FEATURED' => 'bx_posts',

+ 1 - 0
modules/boonex/posts/install/sql/enable.sql

@@ -238,6 +238,7 @@ INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `titl
 ('bx_posts_view_actions', 'bx_posts', 'comment', '_sys_menu_item_title_system_va_comment', '', '', '', '', '', '', '', 0, 2147483647, 0, 0, 200),
 ('bx_posts_view_actions', 'bx_posts', 'view', '_sys_menu_item_title_system_va_view', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 210),
 ('bx_posts_view_actions', 'bx_posts', 'vote', '_sys_menu_item_title_system_va_vote', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 220),
+('bx_posts_view_actions', 'bx_posts', 'reaction', '_sys_menu_item_title_system_va_reaction', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 225),
 ('bx_posts_view_actions', 'bx_posts', 'score', '_sys_menu_item_title_system_va_score', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 230),
 ('bx_posts_view_actions', 'bx_posts', 'favorite', '_sys_menu_item_title_system_va_favorite', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 240),
 ('bx_posts_view_actions', 'bx_posts', 'feature', '_sys_menu_item_title_system_va_feature', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 250),

+ 23 - 0
modules/boonex/posts/install/sql/install.sql

@@ -12,6 +12,8 @@ CREATE TABLE IF NOT EXISTS `bx_posts_posts` (
   `views` int(11) NOT NULL default '0',
   `rate` float NOT NULL default '0',
   `votes` int(11) NOT NULL default '0',
+  `rrate` float NOT NULL default '0',
+  `rvotes` int(11) NOT NULL default '0',
   `score` int(11) NOT NULL default '0',
   `sc_up` int(11) NOT NULL default '0',
   `sc_down` int(11) NOT NULL default '0',
@@ -161,6 +163,26 @@ CREATE TABLE IF NOT EXISTS `bx_posts_votes_track` (
   KEY `vote` (`object_id`, `author_nip`)
 );
 
+CREATE TABLE IF NOT EXISTS `bx_posts_reactions` (
+  `object_id` int(11) NOT NULL default '0',
+  `reaction` varchar(32) NOT NULL default '',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  UNIQUE KEY `reaction` (`object_id`, `reaction`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_posts_reactions_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `reaction` varchar(32) NOT NULL default '',
+  `value` tinyint(4) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
 -- TABLE: views
 CREATE TABLE `bx_posts_views_track` (
   `object_id` int(11) NOT NULL default '0',
@@ -472,6 +494,7 @@ INSERT INTO `sys_objects_cmts` (`Name`, `Module`, `Table`, `CharsPostMin`, `Char
 -- VOTES
 INSERT INTO `sys_objects_vote` (`Name`, `TableMain`, `TableTrack`, `PostTimeout`, `MinValue`, `MaxValue`, `IsUndo`, `IsOn`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldRate`, `TriggerFieldRateCount`, `ClassName`, `ClassFile`) VALUES 
 ('bx_posts', 'bx_posts_votes', 'bx_posts_votes_track', '604800', '1', '1', '0', '1', 'bx_posts_posts', 'id', 'author', 'rate', 'votes', '', ''),
+('bx_posts_reactions', 'bx_posts_reactions', 'bx_posts_reactions_track', '604800', '1', '1', '1', '1', 'bx_posts_posts', 'id', 'author', 'rrate', 'rvotes', 'BxTemplVoteReactions', ''),
 ('bx_posts_poll_answers', 'bx_posts_polls_answers_votes', 'bx_posts_polls_answers_votes_track', '604800', '1', '1', '0', '1', 'bx_posts_polls_answers', 'id', 'author_id', 'rate', 'votes', 'BxPostsVotePollAnswers', 'modules/boonex/posts/classes/BxPostsVotePollAnswers.php');
 
 -- SCORES

+ 2 - 2
modules/boonex/posts/install/sql/uninstall.sql

@@ -1,6 +1,6 @@
 
 -- TABLES
-DROP TABLE IF EXISTS `bx_posts_posts`, `bx_posts_covers`, `bx_posts_files`, `bx_posts_photos`, `bx_posts_photos_resized`, `bx_posts_videos`, `bx_posts_videos_resized`, `bx_posts_cmts`, `bx_posts_votes`, `bx_posts_votes_track`, `bx_posts_views_track`, `bx_posts_meta_keywords`, `bx_posts_meta_locations`, `bx_posts_meta_mentions`, `bx_posts_reports`, `bx_posts_reports_track`, `bx_posts_favorites_track`, `bx_posts_scores`, `bx_posts_scores_track`, `bx_posts_polls`, `bx_posts_polls_answers`, `bx_posts_polls_answers_votes`, `bx_posts_polls_answers_votes_track`;
+DROP TABLE IF EXISTS `bx_posts_posts`, `bx_posts_covers`, `bx_posts_files`, `bx_posts_photos`, `bx_posts_photos_resized`, `bx_posts_videos`, `bx_posts_videos_resized`, `bx_posts_cmts`, `bx_posts_votes`, `bx_posts_votes_track`, `bx_posts_reactions`, `bx_posts_reactions_track`, `bx_posts_views_track`, `bx_posts_meta_keywords`, `bx_posts_meta_locations`, `bx_posts_meta_mentions`, `bx_posts_reports`, `bx_posts_reports_track`, `bx_posts_favorites_track`, `bx_posts_scores`, `bx_posts_scores_track`, `bx_posts_polls`, `bx_posts_polls_answers`, `bx_posts_polls_answers_votes`, `bx_posts_polls_answers_votes_track`;
 
 -- STORAGES & TRANSCODERS
 DELETE FROM `sys_objects_storage` WHERE `object` LIKE 'bx_posts_%';
@@ -25,7 +25,7 @@ DELETE FROM `sys_form_pre_values` WHERE `Key` IN('bx_posts_cats');
 DELETE FROM `sys_objects_cmts` WHERE `Name` = 'bx_posts';
 
 -- VOTES
-DELETE FROM `sys_objects_vote` WHERE `Name` IN ('bx_posts', 'bx_posts_poll_answers');
+DELETE FROM `sys_objects_vote` WHERE `Name` IN ('bx_posts', 'bx_posts_reactions', 'bx_posts_poll_answers');
 
 -- SCORES
 DELETE FROM `sys_objects_score` WHERE `name` = 'bx_posts';

+ 21 - 0
modules/boonex/protean/data/template/system/scripts/BxTemplVoteLikes.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteLikes extends BxBaseVoteLikes
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/protean/data/template/system/scripts/BxTemplVoteReactions.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteReactions extends BxBaseVoteReactions
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 21 - 0
modules/boonex/protean/data/template/system/scripts/BxTemplVoteStars.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaTemplate UNA Template Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxTemplVoteStars extends BxBaseVoteStars
+{
+    function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+    }
+}
+
+/** @} */

+ 9 - 0
modules/boonex/russian/data/langs/system/ru.xml

@@ -2279,6 +2279,7 @@
         <string name="_sys_menu_item_title_system_va_score"><![CDATA[Проголосовать (за/против)]]></string>
         <string name="_sys_menu_item_title_system_va_favorite"><![CDATA[В избранное]]></string>
         <string name="_sys_menu_item_title_system_va_feature"><![CDATA[В продвинутые]]></string>
+        <string name="_sys_menu_item_title_system_va_reaction"><![CDATA[Отреагировать]]></string>
         <string name="_sys_menu_item_title_system_va_repost"><![CDATA[Сделать репост]]></string>
         <string name="_sys_menu_item_title_system_va_report"><![CDATA[Пожаловаться]]></string>
         <string name="_sys_menu_item_title_system_va_more_auto"><![CDATA[Еще]]></string>
@@ -2504,6 +2505,13 @@
 	<string name="_sys_pre_lists_report_types_fraud"><![CDATA[обман]]></string>
 	<string name="_sys_pre_lists_report_types_nude"><![CDATA[обнаженность]]></string>
 	<string name="_sys_pre_lists_report_types_other"><![CDATA[другое]]></string>
+        <string name="_sys_pre_lists_vote_reactions"><![CDATA[Реакции]]></string>
+        <string name="_sys_pre_lists_vote_reactions_like"><![CDATA[Нравится]]></string>
+        <string name="_sys_pre_lists_vote_reactions_love"><![CDATA[Люблю]]></string>
+        <string name="_sys_pre_lists_vote_reactions_joy"><![CDATA[Ха-ха]]></string>
+        <string name="_sys_pre_lists_vote_reactions_surprise"><![CDATA[Вау!]]></string>
+        <string name="_sys_pre_lists_vote_reactions_sadness"><![CDATA[Грустно]]></string>
+        <string name="_sys_pre_lists_vote_reactions_anger"><![CDATA[Злюсь]]></string>
 	<string name="_sys_profile_sample_single"><![CDATA[профиль]]></string>
 	<string name="_sys_profile_friendship_added"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> предложил дружбу <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
 	<string name="_sys_profile_subscription_added"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> следит за <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
@@ -2771,6 +2779,7 @@
 	<string name="_vote_do_like"><![CDATA[Спасибо!]]></string>
 	<string name="_vote_do_unlike"><![CDATA[Нет, спасибо]]></string>
 	<string name="_vote_do_by"><![CDATA[Поблагодарили]]></string>
+        <string name="_vote_do_by_reactions"><![CDATA["{0}" от]]></string>
 	<string name="_vote_err_not_enabled"><![CDATA[Ваш голос не может быть принят в данный момент. Пожалуйста, попробуйте позднее.]]></string>
 	<string name="_vote_err_duplicate_vote"><![CDATA[Повторный голос]]></string>
         <string name="_vote_n_votes"><![CDATA[{0} голосов]]></string>

+ 15 - 7
modules/boonex/timeline/classes/BxTimelineModule.php

@@ -163,18 +163,26 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         $iId = bx_process_input(bx_get('id'), BX_DATA_INT);
         $aEvent = $this->_oDb->getEvents(array('browse' => 'id', 'value' => $iId));
 
-        $mixedAllowed = $this->{'isAllowed' . ((int)$aEvent['promoted'] == 0 ? 'Promote' : 'Unpromote')}($aEvent, true);
+        $sAction = (int)$aEvent['promoted'] == 0 ? 'promote' : 'unpromote';
+
+        $mixedAllowed = $this->{'isAllowed' . ucfirst($sAction)}($aEvent, true);
         if($mixedAllowed !== true)
             return echoJson(array('code' => 1, 'message' => strip_tags($mixedAllowed)));
 
-		$aEvent['promoted'] = (int)$aEvent['promoted'] == 0 ? time() : 0;
+        $aEvent['promoted'] = (int)$aEvent['promoted'] == 0 ? time() : 0;
         if(!$this->_oDb->updateEvent(array('promoted' => $aEvent['promoted']), array('id' => $iId)))
-        	return echoJson(array('code' => 2));
+            return echoJson(array('code' => 2));
+
+        bx_alert($this->_oConfig->getObject('alert'), $sAction . 'd', $iId, (int)$this->getUserId(), array(
+            'owner_id' => $aEvent['owner_id'],
+            'object_id' => $aEvent['object_id'],
+            'object_author_id' => $this->_oConfig->isSystem($aEvent['type'], $aEvent['action']) ? $aEvent['owner_id'] : $aEvent['object_id']
+        ));
 
-		echoJson(array(
-			'code' => 0, 
-			'message' => _t('_bx_timeline_txt_msg_performed_action')
-		));
+        echoJson(array(
+            'code' => 0, 
+            'message' => _t('_bx_timeline_txt_msg_performed_action')
+        ));
     }
 
     function actionDelete()

+ 0 - 0
modules/boonex/timeline/classes/BxTimelineVote.php → modules/boonex/timeline/classes/BxTimelineVoteLikes.php


+ 1 - 1
modules/boonex/timeline/install/sql/install.sql

@@ -429,7 +429,7 @@ INSERT INTO `sys_objects_view` (`name`, `table_track`, `period`, `is_on`, `trigg
 
 -- VOTES
 INSERT INTO `sys_objects_vote`(`Name`, `TableMain`, `TableTrack`, `PostTimeout`, `MinValue`, `MaxValue`, `IsUndo`, `IsOn`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldRate`, `TriggerFieldRateCount`, `ClassName`, `ClassFile`) VALUES 
-('bx_timeline', 'bx_timeline_votes', 'bx_timeline_votes_track', '604800', '1', '1', '0', '1', 'bx_timeline_events', 'id', 'object_id', 'rate', 'votes', 'BxTimelineVote', 'modules/boonex/timeline/classes/BxTimelineVote.php');
+('bx_timeline', 'bx_timeline_votes', 'bx_timeline_votes_track', '604800', '1', '1', '0', '1', 'bx_timeline_events', 'id', 'object_id', 'rate', 'votes', 'BxTimelineVoteLikes', 'modules/boonex/timeline/classes/BxTimelineVoteLikes.php');
 
 
 -- SCORES

+ 44 - 0
template/css/vote.css

@@ -53,6 +53,50 @@ div.bx-vote div.bx-vote-counter-holder {
     border-width: 0px;
 }
 
+/*--- Reactions based Element Styles ---*/
+.bx-vote-reactions .bx-vote-element-holder,
+.bx-vote-reactions .bx-vote-counter-holder {
+	line-height: 20px;
+	line-height: 1.25rem;
+}
+
+.bx-vote-reactions-button .bx-vote-element-holder,
+.bx-vote-reactions-button .bx-vote-counter-holder {
+	line-height: 36px;
+	line-height: 2.2rem;
+}
+
+.bx-vote-reactions-button-small .bx-vote-element-holder,
+.bx-vote-reactions-button-small .bx-vote-counter-holder {
+	line-height: 24px;
+	line-height: 1.6rem;
+}
+
+.bx-vote-reactions .bx-vote-element-holder .bx-vote-do-vote.bx-vote-disabled,
+.bx-vote-reactions .bx-vote-element-holder .bx-vote-do-vote.bx-vote-disabled .sys-icon {
+	color: #ddd;
+}
+
+.bx-vote-reactions-button .bx-vote-counter-holder,
+.bx-vote-reactions-button-small .bx-vote-counter-holder {
+	font-size: 1.5rem;
+}
+
+.bx-vote-reactions .bx-vote-counter-holder.bx-btn-border {
+    border-width: 0px;
+}
+
+.bx-vote-counter-wrapper-reactions .bx-vote-counter {
+    display: inline-block;
+}
+.bx-vote-counter-wrapper-reactions .bx-vote-counter.bx-vc-hidden {
+    display: none;
+}
+.bx-vote-counter-wrapper-reactions .bx-vote-counter i,
+.bx-vote-counter-wrapper-reactions .bx-vote-counter span {
+    vertical-align: middle;
+}
+
 /*--- Stars based Element Styles ---*/
 .bx-vote-stars,
 .bx-vote-stars .bx-vote-do,

+ 106 - 236
template/scripts/BxBaseVote.php

@@ -14,61 +14,41 @@ class BxBaseVote extends BxDolVote
 {
     protected static $_sTmplContentElementBlock;
     protected static $_sTmplContentElementInline;
-    protected static $_sTmplContentDoVoteLikesLabel;
     protected static $_sTmplContentCounter;
 
+    protected $_aElementDefaults;
+
     protected $_bCssJsAdded;
 
+    protected $_sJsClsName;
     protected $_sJsObjName;
     protected $_sStylePrefix;
 
     protected $_aHtmlIds;
 
-    protected $_aElementDefaults;
-
     protected $_sTmplNameLegend;
     protected $_sTmplNameByList;
-    protected $_sTmplNameDoVoteStars;
 
     public function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
     {
         parent::__construct($sSystem, $iId, $iInit, $oTemplate);
 
+        $this->_aElementDefaults = array();
+
         $this->_bCssJsAdded = false;
+        $this->_sStylePrefix = 'bx-vote';
 
+        $this->_sJsClsName = 'BxDolVote';
         $this->_sJsObjName = 'oVote' . bx_gen_method_name($sSystem, array('_' , '-')) . $iId;
-        $this->_sStylePrefix = 'bx-vote';
 
         $sHtmlId = str_replace(array('_' , ' '), array('-', '-'), $sSystem) . '-' . $iId;
         $this->_aHtmlIds = array(
-            'main_stars' => 'bx-vote-stars-' . $sHtmlId,
-            'main_likes' => 'bx-vote-likes-' . $sHtmlId,
             'counter' => 'bx-vote-counter-' . $sHtmlId,
-            'by_popup' => 'bx-vote-by-popup-' . $sHtmlId,
-            'legend_stars' => 'bx-vote-stars-legend-' . $sHtmlId,
-        );
-
-        $this->_aElementDefaults = array(
-            'stars' => array(
-                'show_do_vote_legend' => false,
-                'show_counter' => true,
-                'show_counter_empty' => false,
-                'show_legend' => false
-            ),
-            'likes' => array(
-                'show_do_vote_as_button' => false,
-                'show_do_vote_as_button_small' => false,
-                'show_do_vote_icon' => true,
-                'show_do_vote_label' => false,
-                'show_counter' => true,
-                'show_counter_empty' => false,
-                'show_legend' => false
-            )
+            'by_popup' => 'bx-vote-by-popup-' . $sHtmlId
         );
 
         $this->_sTmplNameLegend = 'vote_legend.html';
         $this->_sTmplNameByList = 'vote_by_list.html';
-        $this->_sTmplNameDoVoteStars = 'vote_do_vote_stars.html';
 
         if(empty(self::$_sTmplContentElementBlock))
             self::$_sTmplContentElementBlock = $this->_oTemplate->getHtml('vote_element_block.html');
@@ -76,13 +56,15 @@ class BxBaseVote extends BxDolVote
         if(empty(self::$_sTmplContentElementInline))
             self::$_sTmplContentElementInline = $this->_oTemplate->getHtml('vote_element_inline.html');
 
-        if(empty(self::$_sTmplContentDoVoteLikesLabel))
-            self::$_sTmplContentDoVoteLikesLabel = $this->_oTemplate->getHtml('vote_do_vote_likes_label.html');
-
         if(empty(self::$_sTmplContentCounter))
             self::$_sTmplContentCounter = $this->_oTemplate->getHtml('vote_counter.html');
     }
 
+    public function getJsClassName()
+    {
+        return $this->_sJsClsName;
+    }
+
     public function getJsObjectName()
     {
         return $this->_sJsObjName;
@@ -95,97 +77,54 @@ class BxBaseVote extends BxDolVote
             'sSystem' => $this->getSystemName(),
             'iAuthorId' => $this->_getAuthorId(),
             'iObjId' => $this->getId(),
-            'iLikeMode' => $this->isLikeMode() ? 1 : 0,
             'sRootUrl' => BX_DOL_URL_ROOT,
             'sStylePrefix' => $this->_sStylePrefix,
             'aHtmlIds' => $this->_aHtmlIds
         );
-        $sCode = "var " . $this->_sJsObjName . " = new BxDolVote(" . json_encode($aParams) . ");";
+        $sCode = "var " . $this->getJsObjectName() . " = new " . $this->getJsClassName() . "(" . json_encode($aParams) . ");";
 
         return $this->_oTemplate->_wrapInTagJsCode($sCode);
     }
 
-    public function getJsClick()
+    public function getJsClick($iValue = 0)
     {
-        if(!$this->isLikeMode())
-            return false;
-
-        return $this->getJsObjectName() . '.vote(this, ' . $this->getMaxValue() . ')';
+        return $this->getJsObjectName() . '.vote(this, ' . $iValue . ')';
     }
 
-    public function getJsClickCounter()
+    public function getJsClickCounter($aParams = array())
     {
         return $this->getJsObjectName() . '.toggleByPopup(this)';
     }
 
     public function getCounter($aParams = array())
     {
-        $sJsObject = $this->getJsObjectName();
-
         $bShowEmpty = isset($aParams['show_counter_empty']) && $aParams['show_counter_empty'] == true;
-        $bShowDoVoteAsButtonSmall = $this->_bLike && isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true;
-        $bShowDoVoteAsButton = $this->_bLike && !$bShowDoVoteAsButtonSmall && isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true;
 
-        $aVote = $this->_oQuery->getVote($this->getId());
         $sClass = $this->_sStylePrefix . '-counter';
-        if($bShowDoVoteAsButtonSmall)
-            $sClass .= ' bx-btn-small-height';
-        if($bShowDoVoteAsButton)
-            $sClass .= ' bx-btn-height';
+        if(!empty($aParams['class_counter']))
+            $sClass .= $aParams['class_counter'];
+
+        $aTmplVarsAttrs = array(
+            array('key' => 'class', 'value' => $sClass),
+            array('key' => 'href', 'value' => 'javascript:void(0)'),
+            array('key' => 'title', 'value' => bx_html_attribute($this->_getTitleDoBy($aParams))),
+            array('key' => 'onclick', 'value' => 'javascript:' . $this->getJsClickCounter($aParams))
+        );
+
+        $sHtmlId = isset($aParams['id_counter']) ? $aParams['id_counter'] : $this->_aHtmlIds['counter'];
+        if(!empty($sHtmlId))
+            $aTmplVarsAttrs[] = array('key' => 'id', 'value' => $sHtmlId);
 
+        $aVote = !empty($aParams['vote']) && is_array($aParams['vote']) ? $aParams['vote'] : $this->_getVote();
         return $this->_oTemplate->parseHtmlByContent($this->_getTmplContentCounter(), array(
-            'bx_repeat:attrs' => array(
-                array('key' => 'href', 'value' => 'javascript:void(0)'),
-                array('key' => 'id', 'value' => $this->_aHtmlIds['counter']),
-                array('key' => 'class', 'value' => $sClass),
-                array('key' => 'title', 'value' => _t($this->_getTitleDoBy())),
-                array('key' => 'onclick', 'value' => 'javascript:' . $this->getJsClickCounter())
-            ),
-            'content' => $bShowEmpty || (int)$aVote['count'] > 0 ? $this->_getLabelCounter($aVote['count']) : ''
+            'bx_repeat:attrs' => $aTmplVarsAttrs,
+            'content' => $bShowEmpty || (int)$aVote['count'] > 0 ? $this->_getLabelCounter($aVote['count'], $aParams) : ''
         ));
     }
 
-    /**
-     * Note. The Legend is available in Start based mode only.  
-     */
     public function getLegend($aParams = array())
     {
-        $sJsObject = $this->getJsObjectName();
-        $iMinValue = $this->getMinValue();
-        $iMaxValue = $this->getMaxValue();
-
-        $aLegend = $this->_oQuery->getLegend($this->_iId);
-
-        $aTmplVarsItems = array();
-        for($i = $iMaxValue; $i >= $iMinValue; $i--) {
-        	$aTmplVarsStars = $aTmplVarsSlider = array();
-
-	        for($j = $iMinValue; $j <= $iMaxValue; $j++) {
-	        	$aTmplVarsStars[] = array(
-	                'style_prefix' => $this->_sStylePrefix,
-	                'value' => $i
-	            );
-	
-	            $aTmplVarsSlider[] = array(
-	                'style_prefix' => $this->_sStylePrefix
-	            );
-	        }
-
-	        $aTmplVarsItems[] = array(
-	        	'style_prefix' => $this->_sStylePrefix,
-	        	'value' => $i,
-	        	'bx_repeat:stars' => $aTmplVarsStars,
-	        	'bx_repeat:slider' => $aTmplVarsSlider,
-	        	'label' => isset($aLegend[$i]['count']) ? (int)$aLegend[$i]['count'] : 0
-	        );
-        }
-
-        return $this->_oTemplate->parseHtmlByName($this->_sTmplNameLegend, array(
-        	'style_prefix'  => $this->_sStylePrefix,
-        	'html_id' => $this->_aHtmlIds['legend_stars'],
-        	'type' => $this->_sType,
-        	'bx_repeat:items' => $aTmplVarsItems
-        ));
+        return '';
     }
 
     public function getElementBlock($aParams = array())
@@ -204,174 +143,109 @@ class BxBaseVote extends BxDolVote
 
     public function getElement($aParams = array())
     {
-    	$aParams = array_merge($this->_aElementDefaults[$this->_sType], $aParams);
-    	$bDynamicMode = isset($aParams['dynamic_mode']) && $aParams['dynamic_mode'] === true;
-
-        $bShowCounterEmpty = isset($aParams['show_counter_empty']) && $aParams['show_counter_empty'] == true;
-        $bShowDoVoteAsButtonSmall = $this->_bLike && isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true;
-        $bShowDoVoteAsButton = $this->_bLike && !$bShowDoVoteAsButtonSmall && isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true;
+        $sMethod = '_getTmplContentElement' . bx_gen_method_name(!empty($aParams['usage']) ? $aParams['usage'] : BX_DOL_VOTE_USAGE_DEFAULT);
+        if(!method_exists($this, $sMethod))
+            return '';
 
-        $sMethodDoVote = '_getDoVote' . ucfirst($this->_sType);
-        if(!method_exists($this, $sMethodDoVote))
+        $aTmplVars = $this->_getTmplVarsElement($aParams);
+        if(empty($aTmplVars) || !is_array($aTmplVars))
             return '';
 
-		$iObjectId = $this->getId();
-		$iAuthorId = $this->_getAuthorId();
-		$aVote = $this->_oQuery->getVote($iObjectId);
-        $bCount = (int)$aVote['count'] != 0;
+        return $this->_oTemplate->parseHtmlByContent($this->$sMethod(), $aTmplVars);
+    }
 
+    /**
+     * Internal methods.
+     * 
+     * This method should be overwritten by subclass.
+     */
+    protected function _getTmplVarsElement($aParams = array())
+    {
+    	$aParams = array_merge($this->_aElementDefaults, $aParams);
+
+    	$bDynamicMode = isset($aParams['dynamic_mode']) && $aParams['dynamic_mode'] === true;
+        $bShowCounterEmpty = isset($aParams['show_counter_empty']) && $aParams['show_counter_empty'] == true;
+
+        $iObjectId = $this->getId();
+        $iAuthorId = $this->_getAuthorId();
+
+        $bCount = $this->_isCount();
         $isAllowedVote = $this->isAllowedVote();
-        $aParams['is_voted'] = $this->isPerformed($iObjectId, $iAuthorId) ? true : false;
+        $aParams['is_voted'] = $this->isPerformed($iObjectId, $iAuthorId);
+        $aParams['track'] = $aParams['is_voted'] ? $this->_getTrack($iObjectId, $iAuthorId) : array();
 
         //--- Do Vote
         $bTmplVarsDoVote = $this->_isShowDoVote($aParams, $isAllowedVote, $bCount);
         $aTmplVarsDoVote = array();
         if($bTmplVarsDoVote)
-        	$aTmplVarsDoVote = array(
-				'style_prefix' => $this->_sStylePrefix,
-				'do_vote' => $this->$sMethodDoVote($aParams, $isAllowedVote),
-			);
+            $aTmplVarsDoVote = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'do_vote' => $this->_getDoVote($aParams, $isAllowedVote),
+            );
 
         //--- Counter
         $bTmplVarsCounter = $this->_isShowCounter($aParams, $isAllowedVote, $bCount);
         $aTmplVarsCounter = array();
         if($bTmplVarsCounter)
-        	$aTmplVarsCounter = array(
-				'style_prefix' => $this->_sStylePrefix,
-				'bx_if:show_hidden' => array(
-					'condition' => !$bCount && !$bShowCounterEmpty,
-					'content' => array()
-				),
-				'counter' => $this->getCounter($aParams)
-        	);
-
-		//--- Legend
-		$bTmplVarsLegend = $this->_isShowLegend($aParams, $isAllowedVote, $bCount);
-		$aTmplVarsLegend = array();
-		if($bTmplVarsLegend)
-			$aTmplVarsLegend = array(
-				'legend' => $this->getLegend($aParams)
-			);
-
-		if(!$bTmplVarsDoVote && !$bTmplVarsCounter && !$bTmplVarsLegend)
-			return '';
-
-        $sMethod = '_getTmplContentElement' . bx_gen_method_name(!empty($aParams['usage']) ? $aParams['usage'] : BX_DOL_VOTE_USAGE_DEFAULT);
-        return $this->_oTemplate->parseHtmlByContent($this->$sMethod(), array(
-            'style_prefix' => $this->_sStylePrefix,
-            'html_id' => $this->_aHtmlIds['main_' . $this->_sType],
-            'class' => $this->_sStylePrefix . '-' . $this->_sType . ($bShowDoVoteAsButton ? '-button' : '') . ($bShowDoVoteAsButtonSmall ? '-button-small' : ''),
-            'rate' => $aVote['rate'],
-            'count' => $aVote['count'],
-        	'bx_if:show_do_vote' => array(
-        		'condition' => $bTmplVarsDoVote,
-        		'content' => $aTmplVarsDoVote
-        	),
-        	'bx_if:show_counter' => array(
-				'condition' => $bTmplVarsCounter,
-				'content' => $aTmplVarsCounter
-			),
-            'bx_if:show_legend' => array(
-            	'condition' => $bTmplVarsLegend,
-            	'content' => $aTmplVarsLegend
-            ),
-            'script' => $this->getJsScript($bDynamicMode)
-        ));
-    }
-
-    protected function _getDoVoteStars($aParams = array(), $isAllowedVote = true)
-    {
-        $sJsObject = $this->getJsObjectName();
-        $iMinValue = $this->getMinValue();
-        $iMaxValue = $this->getMaxValue();
-
-        $aTmplVarsStars = $aTmplVarsLegend = $aTmplVarsSlider = $aTmplVarsButtons = array();
-        for($i = $iMinValue; $i <= $iMaxValue; $i++) {
-            $aTmplVarsStars[] = array(
+            $aTmplVarsCounter = array(
                 'style_prefix' => $this->_sStylePrefix,
-                'value' => $i
+                'bx_if:show_hidden' => array(
+                    'condition' => !$bCount && !$bShowCounterEmpty,
+                    'content' => array()
+                ),
+                'counter' => $this->getCounter($aParams)
             );
 
-            $aTmplVarsLegend[] = array(
-                'style_prefix' => $this->_sStylePrefix,
-                'value' => $i
+        //--- Legend
+        $bTmplVarsLegend = $this->_isShowLegend($aParams, $isAllowedVote, $bCount);
+        $aTmplVarsLegend = array();
+        if($bTmplVarsLegend)
+            $aTmplVarsLegend = array(
+                'legend' => $this->getLegend($aParams)
             );
 
-            $aTmplVarsSlider[] = array(
-                'style_prefix' => $this->_sStylePrefix
-            );
+        if(!$bTmplVarsDoVote && !$bTmplVarsCounter && !$bTmplVarsLegend)
+            return array();
 
-            if($isAllowedVote)
-	            $aTmplVarsButtons[] = array(
-	                'style_prefix' => $this->_sStylePrefix,
-	                'js_object' => $sJsObject,
-	                'value' => $i
-	            );
-        }
+        $sClass = $this->_sStylePrefix . '-' . $this->_sType;
+        if(!empty($aParams['class_element']))
+            $sClass .= $aParams['class_element'];
 
-        return $this->_oTemplate->parseHtmlByName($this->_sTmplNameDoVoteStars, array(
+        return array(
             'style_prefix' => $this->_sStylePrefix,
-            'bx_repeat:stars' => $aTmplVarsStars,
+            'html_id' => $this->_aHtmlIds['main'],
+            'class' => $sClass,
+            'bx_if:show_vote_data' => array(
+                'condition' => false,
+                'content' => array()
+            ),
+            'bx_if:show_do_vote' => array(
+                'condition' => $bTmplVarsDoVote,
+                'content' => $aTmplVarsDoVote
+            ),
+            'bx_if:show_counter' => array(
+                'condition' => $bTmplVarsCounter,
+                'content' => $aTmplVarsCounter
+            ),
             'bx_if:show_legend' => array(
-                'condition' => isset($aParams['show_do_vote_legend']) && $aParams['show_do_vote_legend'] === true,
-                'content' => array(
-                    'style_prefix' => $this->_sStylePrefix,
-                    'bx_repeat:legend' => $aTmplVarsLegend,
-                )
+            	'condition' => $bTmplVarsLegend,
+            	'content' => $aTmplVarsLegend
             ),
-            'bx_repeat:slider' => $aTmplVarsSlider,
-            'bx_repeat:buttons' => $aTmplVarsButtons,
-        ));
+            'script' => $this->getJsScript($bDynamicMode)
+        );
     }
 
-    protected function _getDoVoteLikes($aParams = array(), $isAllowedVote = true)
+    protected function _getDoVote($aParams = array(), $isAllowedVote = true)
     {
-    	$bVoted = isset($aParams['is_voted']) && $aParams['is_voted'] === true;
-        $bShowDoVoteAsButtonSmall = isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true;
-        $bShowDoVoteAsButton = !$bShowDoVoteAsButtonSmall && isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true;
-		$bDisabled = !$isAllowedVote || ($bVoted && !$this->isUndo());
-
-        $sClass = '';
-		if($bShowDoVoteAsButton)
-			$sClass = 'bx-btn';
-		else if ($bShowDoVoteAsButtonSmall)
-			$sClass = 'bx-btn bx-btn-small';
-
-		if($bDisabled)
-			$sClass .= $bShowDoVoteAsButton || $bShowDoVoteAsButtonSmall ? ' bx-btn-disabled' : 'bx-vote-disabled';
-
-        return $this->_oTemplate->parseLink('javascript:void(0)', $this->_getLabelDoLike($aParams), array(
-            'class' => $this->_sStylePrefix . '-do-vote ' . $sClass,
-            'title' => _t($this->_getTitleDoLike($bVoted)),
-            'onclick' => !$bDisabled ? $this->getJsClick() : ''
-        ));
+        return '';
     }
 
-    protected function _getLabelCounter($iCount)
+    protected function _getLabelCounter($iCount, $aParams = array())
     {
         return _t('_vote_counter', $iCount);
     }
 
-    protected function _getLabelDoLike($aParams = array())
-    {
-    	$bVoted = isset($aParams['is_voted']) && $aParams['is_voted'] === true;
-        return $this->_oTemplate->parseHtmlByContent(self::$_sTmplContentDoVoteLikesLabel, array(
-        	'bx_if:show_icon' => array(
-        		'condition' => isset($aParams['show_do_vote_icon']) && $aParams['show_do_vote_icon'] == true,
-        		'content' => array(
-        			'name' => $this->_getIconDoLike($bVoted)
-        		)
-        	),
-        	'bx_if:show_text' => array(
-        		'condition' => isset($aParams['show_do_vote_label']) && $aParams['show_do_vote_label'] == true,
-        		'content' => array(
-        			'text' => _t($this->_getTitleDoLike($bVoted))
-        		)
-        	)
-        ));
-    }
-
-    protected function _getVotedBy()
+    protected function _getVotedBy($aParams = array())
     {
         $aTmplUsers = array();
 
@@ -395,21 +269,17 @@ class BxBaseVote extends BxDolVote
 
     protected function _isShowDoVote($aParams, $isAllowedVote, $bCount)
     {
-        $bShowDoVote = !isset($aParams['show_do_vote']) || $aParams['show_do_vote'] == true;
-
-        return $bShowDoVote && (!$this->_bLike || $isAllowedVote || $bCount);
+        return !isset($aParams['show_do_vote']) || $aParams['show_do_vote'] == true;
     }
 
     protected function _isShowCounter($aParams, $isAllowedVote, $bCount)
     {
-        $bShowCounter = isset($aParams['show_counter']) && $aParams['show_counter'] === true;
-
-        return $bShowCounter && ($isAllowedVote || $bCount);
+        return isset($aParams['show_counter']) && $aParams['show_counter'] === true && ($isAllowedVote || $bCount);
     }
 
     protected function _isShowLegend($aParams, $isAllowedVote, $bCount)
     {
-        return !$this->_bLike && isset($aParams['show_legend']) && $aParams['show_legend'] === true;
+        return false;
     }
 
     protected function _getTmplContentElementBlock()

+ 138 - 0
template/scripts/BxBaseVoteLikes.php

@@ -0,0 +1,138 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaBaseView UNA Base Representation Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote, BxDolVoteLikes
+ */
+class BxBaseVoteLikes extends BxDolVoteLikes
+{
+    protected static $_sTmplContentDoVoteLabelLikes;
+
+    public function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+
+        $this->_sJsClsName .= 'Likes';
+
+        $sHtmlId = str_replace(array('_' , ' '), array('-', '-'), $sSystem) . '-' . $iId;
+        $this->_aHtmlIds = array_merge($this->_aHtmlIds, array(
+            'main' => 'bx-vote-likes-' . $sHtmlId
+        ));
+
+        $this->_aElementDefaults = array(
+            'show_do_vote_as_button' => false,
+            'show_do_vote_as_button_small' => false,
+            'show_do_vote_icon' => true,
+            'show_do_vote_label' => false,
+            'show_counter' => true,
+            'show_counter_empty' => false,
+            'show_legend' => false
+        );
+
+        if(empty(self::$_sTmplContentDoVoteLabelLikes))
+            self::$_sTmplContentDoVoteLabelLikes = $this->_oTemplate->getHtml('vote_do_vote_likes_label.html');
+    }
+
+    public function getJsClick($iValue = 0)
+    {
+        if(empty($iValue))
+            $iValue = $this->getValue();
+
+        return parent::getJsClick($iValue);
+    }
+
+    public function getCounter($aParams = array())
+    {
+        $sClass = '';
+        if(isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true)
+            $sClass = 'bx-btn-small-height';
+        else if(isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true)
+            $sClass = 'bx-btn-height';
+
+        $aParams['class_counter'] = isset($aParams['class_counter']) ? $aParams['class_counter'] : '';
+        $aParams['class_counter'] .= ' ' . $sClass;
+
+        return parent::getCounter($aParams);
+    }
+
+    public function getElement($aParams = array())
+    {
+        $sClass = $this->_sStylePrefix . '-' . $this->_sType;
+        if(isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true)
+            $sClass .=  '-button-small';
+        else if(isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true)
+            $sClass .= '-button';
+
+        $aParams['class_element'] = isset($aParams['class_element']) ? $aParams['class_element'] : '';
+        $aParams['class_element'] .= ' ' . $sClass;
+
+        return parent::getElement($aParams);
+    }
+
+    /**
+     * Internal methods.
+     */
+    protected function _getDoVote($aParams = array(), $isAllowedVote = true)
+    {
+    	$bVoted = isset($aParams['is_voted']) && $aParams['is_voted'] === true;
+        $bShowDoVoteAsButtonSmall = isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true;
+        $bShowDoVoteAsButton = !$bShowDoVoteAsButtonSmall && isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true;
+        $bDisabled = !$isAllowedVote || ($bVoted && !$this->isUndo());
+
+        $sClass = '';
+        if($bShowDoVoteAsButton)
+            $sClass = 'bx-btn';
+        else if ($bShowDoVoteAsButtonSmall)
+            $sClass = 'bx-btn bx-btn-small';
+
+        if($bDisabled)
+            $sClass .= $bShowDoVoteAsButton || $bShowDoVoteAsButtonSmall ? ' bx-btn-disabled' : 'bx-vote-disabled';
+
+        return $this->_oTemplate->parseLink('javascript:void(0)', $this->_getDoVoteLabel($aParams), array(
+            'class' => $this->_sStylePrefix . '-do-vote ' . $sClass,
+            'title' => _t($this->_getTitleDo($bVoted)),
+            'onclick' => !$bDisabled ? $this->getJsClick() : ''
+        ));
+    }
+
+    protected function _getDoVoteLabel($aParams = array())
+    {
+    	$bVoted = isset($aParams['is_voted']) && $aParams['is_voted'] === true;
+        return $this->_oTemplate->parseHtmlByContent($this->_getTmplContentDoVoteLabel(), array(
+            'bx_if:show_icon' => array(
+                'condition' => isset($aParams['show_do_vote_icon']) && $aParams['show_do_vote_icon'] == true,
+                'content' => array(
+                    'name' => $this->_getIconDo($bVoted)
+                )
+            ),
+            'bx_if:show_text' => array(
+                'condition' => isset($aParams['show_do_vote_label']) && $aParams['show_do_vote_label'] == true,
+                'content' => array(
+                    'text' => _t($this->_getTitleDo($bVoted))
+                )
+            )
+        ));
+    }
+
+    protected function _isShowDoVote($aParams, $isAllowedVote, $bCount)
+    {
+        $bResult = parent::_isShowDoVote($aParams, $isAllowedVote, $bCount);
+        if(!$bResult)
+            return $bResult;
+
+        return $isAllowedVote || $bCount;
+    }
+
+    protected function _getTmplContentDoVoteLabel()
+    {
+        return self::$_sTmplContentDoVoteLabelLikes;
+    }
+}
+
+/** @} */

+ 258 - 0
template/scripts/BxBaseVoteReactions.php

@@ -0,0 +1,258 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaBaseView UNA Base Representation Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote, BxDolVoteLikes
+ */
+class BxBaseVoteReactions extends BxDolVoteReactions
+{
+    protected static $_sTmplContentDoVoteLabelReactions;
+    protected static $_sTmplContentCounterWrapper;
+    protected static $_sTmplContentCounterLabel;
+
+    public function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+
+        $this->_sJsClsName .= 'Reactions';
+
+        $sHtmlId = str_replace(array('_' , ' '), array('-', '-'), $sSystem) . '-' . $iId;
+        $this->_aHtmlIds = array_merge($this->_aHtmlIds, array(
+            'main' => 'bx-vote-reactions-' . $sHtmlId,
+            'do_menu' => 'bx-vote-do-menu-' . $sHtmlId,
+            'do_popup' => 'bx-vote-do-popup-' . $sHtmlId
+        ));
+
+        $this->_aElementDefaults = array(
+            'show_do_vote_as_button' => false,
+            'show_do_vote_as_button_small' => false,
+            'show_do_vote_icon' => true,
+            'show_do_vote_label' => false,
+            'show_counter' => true,
+            'show_counter_empty' => true,
+            'show_legend' => false
+        );
+
+        if(empty(self::$_sTmplContentDoVoteLabelReactions))
+            self::$_sTmplContentDoVoteLabelReactions = $this->_oTemplate->getHtml('vote_do_vote_reactions_label.html');
+
+        if(empty(self::$_sTmplContentCounterWrapper))
+            self::$_sTmplContentCounterWrapper = $this->_oTemplate->getHtml('vote_counter_wrapper_reactions.html');
+
+        if(empty(self::$_sTmplContentCounterLabel))
+            self::$_sTmplContentCounterLabel = $this->_oTemplate->getHtml('vote_counter_label_reactions.html');
+    }
+
+    public function getJsClick($iValue = 0)
+    {
+        if(empty($iValue))
+            $iValue = $this->getValue();
+
+        return $this->getJsObjectName() . '.toggleDoPopup(this, ' . $iValue . ')';
+    }
+
+    public function getJsClickDo($sReaction, $iValue = 0)
+    {
+        if(empty($iValue))
+            $iValue = $this->getValue();
+
+        return $this->getJsObjectName() . '.vote(this, ' . $iValue . ', \'' . $sReaction . '\')';
+    }
+
+    public function getJsClickCounter($aParams = array())
+    {
+        $sReaction = !empty($aParams['reaction']) ? $aParams['reaction'] : $this->_sDefault;
+
+        return $this->getJsObjectName() . '.toggleByPopup(this, \'' . $sReaction . '\')';
+    }
+
+    public function getCounter($aParams = array())
+    {
+        $aVote = $this->_getVote();
+        $aReactions = $this->getReactions();
+
+        $sClass = isset($aParams['class_counter']) ? $aParams['class_counter'] : '';
+        if(isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true)
+            $sClass .= ' bx-btn-small-height';
+        else if(isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true)
+            $sClass .= ' bx-btn-height';
+        $sClass .= ' bx-def-margin-sec-left-auto';
+
+        $aParams['id_counter'] = '';
+
+        $sResult = '';
+        foreach($aReactions as $sName) {
+            $aParams['class_counter'] = ((int)$aVote['count_' . $sName] == 0 ? ' bx-vc-hidden' : '') . ' ' . $sName . ' ' . $sClass;
+            $aParams['reaction'] = $sName;
+            $aParams['vote'] = array(
+                'count' => $aVote['count_' . $sName],
+                'sum' => $aVote['sum_' . $sName],
+                'rate' => $aVote['rate_' . $sName],
+            );
+
+            $sResult .= parent::getCounter($aParams);
+        }
+
+        return $this->_oTemplate->parseHtmlByContent($this->_getTmplContentCounterWrapper(), array(
+            'html_id' => $this->_aHtmlIds['counter'],
+            'style_prefix' => $this->_sStylePrefix,
+            'type' => $this->_sType,
+            'counter' => $sResult
+        ));
+    }
+
+    public function getElement($aParams = array())
+    {
+        $sClass = $this->_sStylePrefix . '-' . $this->_sType;
+        if(isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true)
+            $sClass .=  '-button-small';
+        else if(isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true)
+            $sClass .= '-button';
+
+        $aParams['class_element'] = isset($aParams['class_element']) ? $aParams['class_element'] : '';
+        $aParams['class_element'] .= ' ' . $sClass;
+
+        return parent::getElement($aParams);
+    }
+
+    /**
+     * Internal methods.
+     */
+    protected function _isShowDoVote($aParams, $isAllowedVote, $bCount)
+    {
+        $bResult = parent::_isShowDoVote($aParams, $isAllowedVote, $bCount);
+        if(!$bResult)
+            return $bResult;
+
+        return $isAllowedVote || $bCount;
+    }
+
+    protected function _getDoVote($aParams = array(), $isAllowedVote = true)
+    {
+        $bUndo = $this->isUndo();
+    	$bVoted = isset($aParams['is_voted']) && $aParams['is_voted'] === true;
+        $bShowDoVoteAsButtonSmall = isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true;
+        $bShowDoVoteAsButton = !$bShowDoVoteAsButtonSmall && isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true;
+        $bDisabled = !$isAllowedVote || ($bVoted && !$bUndo);
+
+        $sClass = '';
+        if($bShowDoVoteAsButton)
+            $sClass = 'bx-btn';
+        else if ($bShowDoVoteAsButtonSmall)
+            $sClass = 'bx-btn bx-btn-small';
+
+        $sJsClick = '';
+        if($bDisabled)
+            $sClass .= $bShowDoVoteAsButton || $bShowDoVoteAsButtonSmall ? ' bx-btn-disabled' : 'bx-vote-disabled';
+        else
+            $sJsClick = $bVoted && $bUndo ? $this->getJsClickDo($aParams['track']['reaction']) : $this->getJsClick();
+
+        return $this->_oTemplate->parseLink('javascript:void(0)', $this->_getDoVoteLabel($aParams), array(
+            'class' => $this->_sStylePrefix . '-do-vote ' . $sClass,
+            'title' => _t($this->_getTitleDoWithTrack($bVoted, $aParams['track'])),
+            'onclick' => $sJsClick
+        ));
+    }
+
+    protected function _getDoVoteLabel($aParams = array())
+    {
+    	$bVoted = isset($aParams['is_voted']) && $aParams['is_voted'] === true;
+
+        return $this->_oTemplate->parseHtmlByContent($this->_getTmplContentDoVoteLabel(), array(
+            'bx_if:show_icon' => array(
+                'condition' => isset($aParams['show_do_vote_icon']) && $aParams['show_do_vote_icon'] == true,
+                'content' => array(
+                    'name' => $this->_getIconDoWithTrack($bVoted, $aParams['track'])
+                )
+            ),
+            'bx_if:show_text' => array(
+                'condition' => isset($aParams['show_do_vote_label']) && $aParams['show_do_vote_label'] == true,
+                'content' => array(
+                    'text' => _t($this->_getTitleDoWithTrack($bVoted, $aParams['track']))
+                )
+            )
+        ));
+    }
+
+    public function _getDoVotePopup($iValue = 0)
+    {
+        if(empty($iValue))
+            $iValue = $this->getValue();
+
+        $sJsObject = $this->getJsObjectName();
+        $aReactions = $this->getReactions(true);
+
+        $aMenu = array();        
+        foreach($aReactions as $sName => $aReaction) {
+            $aMenu[] = array(
+                'id' => $sName, 
+                'name' => $sName, 
+                'class' => '', 
+                'link' => 'javascript:void(0)', 
+                'onclick' => 'javascript:' . $this->getJsClickDo($sName, $iValue), 
+                'target' => '_self', 
+                'title' => _t($aReaction['title']), 
+                'icon' => $aReaction['icon'], 
+                'active' => 1
+            );
+        }
+
+        $oMenu = new BxTemplMenu(array('template' => 'menu_buttons_icon_hor.html', 'menu_id'=> $this->_aHtmlIds['do_menu'], 'menu_items' => $aMenu));
+        return $oMenu->getCode();
+    }
+
+    protected function _getLabelCounter($iCount, $aParams = array())
+    {
+        $sReaction = !empty($aParams['reaction']) ? $aParams['reaction'] : $this->_sDefault;
+
+        return $this->_oTemplate->parseHtmlByContent($this->_getTmplContentCounterLabel(), array(
+            'name' => $this->_aDataList[$sReaction]['icon'],
+            'text' => parent::_getLabelCounter($iCount)
+        ));
+    }
+
+    protected function _getVotedBy($aParams = array())
+    {
+        $aTmplUsers = array();
+
+        $aUserIds = $this->_oQuery->getPerformed(array('type' => 'by', 'object_id' => $this->getId(), 'reaction' => $aParams['reaction']));
+        foreach($aUserIds as $iUserId) {
+            list($sUserName, $sUserUrl, $sUserIcon, $sUserUnit) = $this->_getAuthorInfo($iUserId);
+            $aTmplUsers[] = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'user_unit' => $sUserUnit
+            );
+        }
+
+        if(empty($aTmplUsers))
+            $aTmplUsers = MsgBox(_t('_Empty'));
+
+        return $this->_oTemplate->parseHtmlByName($this->_sTmplNameByList, array(
+            'style_prefix' => $this->_sStylePrefix,
+            'bx_repeat:list' => $aTmplUsers
+        ));
+    }
+
+    protected function _getTmplContentDoVoteLabel()
+    {
+        return self::$_sTmplContentDoVoteLabelReactions;
+    }
+
+    protected function _getTmplContentCounterWrapper()
+    {
+        return self::$_sTmplContentCounterWrapper;
+    }
+
+    protected function _getTmplContentCounterLabel()
+    {
+        return self::$_sTmplContentCounterLabel;
+    }
+}
+
+/** @} */

+ 149 - 0
template/scripts/BxBaseVoteStars.php

@@ -0,0 +1,149 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaBaseView UNA Base Representation Classes
+ * @{
+ */
+
+/**
+ * @see BxDolVote
+ */
+class BxBaseVoteStars extends BxDolVoteStars
+{
+    protected $_sTmplNameDoVoteStars;
+
+    public function __construct($sSystem, $iId, $iInit = true, $oTemplate = false)
+    {
+        parent::__construct($sSystem, $iId, $iInit, $oTemplate);
+
+        $this->_sJsClsName .= 'Stars';
+
+        $sHtmlId = str_replace(array('_' , ' '), array('-', '-'), $sSystem) . '-' . $iId;
+        $this->_aHtmlIds = array_merge($this->_aHtmlIds, array(
+            'main' => 'bx-vote-stars-' . $sHtmlId,
+            'legend_stars' => 'bx-vote-stars-legend-' . $sHtmlId,
+        ));
+
+        $this->_aElementDefaults = array(
+            'show_do_vote_legend' => false,
+            'show_counter' => true,
+            'show_counter_empty' => false,
+            'show_legend' => false
+        );
+
+        $this->_sTmplNameDoVoteStars = 'vote_do_vote_stars.html';
+    }
+
+    public function getLegend($aParams = array())
+    {
+        $sJsObject = $this->getJsObjectName();
+        $iMinValue = $this->getMinValue();
+        $iMaxValue = $this->getMaxValue();
+
+        $aLegend = $this->_oQuery->getLegend($this->_iId);
+
+        $aTmplVarsItems = array();
+        for($i = $iMaxValue; $i >= $iMinValue; $i--) {
+            $aTmplVarsStars = $aTmplVarsSlider = array();
+
+            for($j = $iMinValue; $j <= $iMaxValue; $j++) {
+                $aTmplVarsStars[] = array(
+                    'style_prefix' => $this->_sStylePrefix,
+                    'value' => $i
+                );
+
+                $aTmplVarsSlider[] = array(
+                    'style_prefix' => $this->_sStylePrefix
+                );
+            }
+
+            $aTmplVarsItems[] = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'value' => $i,
+                'bx_repeat:stars' => $aTmplVarsStars,
+                'bx_repeat:slider' => $aTmplVarsSlider,
+                'label' => isset($aLegend[$i]['count']) ? (int)$aLegend[$i]['count'] : 0
+            );
+        }
+
+        return $this->_oTemplate->parseHtmlByName($this->_sTmplNameLegend, array(
+            'style_prefix'  => $this->_sStylePrefix,
+            'html_id' => $this->_aHtmlIds['legend_stars'],
+            'type' => $this->_sType,
+            'bx_repeat:items' => $aTmplVarsItems
+        ));
+    }
+
+    /**
+     * Internal methods.
+     */
+    protected function _getTmplVarsElement($aParams = array())
+    {
+        $aTmplVars = parent::_getTmplVarsElement($aParams);
+
+        $aVote = $this->_getVote();
+        $aTmplVars['bx_if:show_vote_data'] = array(
+            'condition' => true,
+            'content' => array(
+                'rate' => $aVote['rate'],
+                'count' => $aVote['count']
+            )
+        );
+
+        return $aTmplVars;
+    }
+
+    protected function _getDoVote($aParams = array(), $isAllowedVote = true)
+    {
+        $sJsObject = $this->getJsObjectName();
+        $iMinValue = $this->getMinValue();
+        $iMaxValue = $this->getMaxValue();
+
+        $aTmplVarsStars = $aTmplVarsLegend = $aTmplVarsSlider = $aTmplVarsButtons = array();
+        for($i = $iMinValue; $i <= $iMaxValue; $i++) {
+            $aTmplVarsStars[] = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'value' => $i
+            );
+
+            $aTmplVarsLegend[] = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'value' => $i
+            );
+
+            $aTmplVarsSlider[] = array(
+                'style_prefix' => $this->_sStylePrefix
+            );
+
+            if($isAllowedVote)
+                $aTmplVarsButtons[] = array(
+                    'style_prefix' => $this->_sStylePrefix,
+                    'js_object' => $sJsObject,
+                    'value' => $i
+                );
+        }
+
+        return $this->_oTemplate->parseHtmlByName($this->_sTmplNameDoVoteStars, array(
+            'style_prefix' => $this->_sStylePrefix,
+            'bx_repeat:stars' => $aTmplVarsStars,
+            'bx_if:show_legend' => array(
+                'condition' => isset($aParams['show_do_vote_legend']) && $aParams['show_do_vote_legend'] === true,
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+                    'bx_repeat:legend' => $aTmplVarsLegend,
+                )
+            ),
+            'bx_repeat:slider' => $aTmplVarsSlider,
+            'bx_repeat:buttons' => $aTmplVarsButtons,
+        ));
+    }
+
+    protected function _isShowLegend($aParams, $isAllowedVote, $bCount)
+    {
+        return isset($aParams['show_legend']) && $aParams['show_legend'] === true;
+    }
+}
+
+/** @} */

+ 1 - 0
template/vote_counter_label_reactions.html

@@ -0,0 +1 @@
+<i class="sys-icon __name__"></i><span class="bx-def-margin-sec-left-auto">__text__</span>

+ 1 - 0
template/vote_counter_wrapper_reactions.html

@@ -0,0 +1 @@
+<div id="__html_id__" class="__style_prefix__-counter-wrapper __style_prefix__-counter-wrapper-__type__">__counter__</div>

+ 1 - 0
template/vote_do_vote_reactions_label.html

@@ -0,0 +1 @@
+<bx_if:show_icon><i class="sys-icon __name__"></i></bx_if:show_icon><bx_if:show_text><span class="bx-def-margin-sec-left-auto">__text__</span></bx_if:show_text>

+ 3 - 3
template/vote_element_block.html

@@ -1,10 +1,10 @@
-<div id="__html_id__" class="__style_prefix__ __class__ bx-def-margin-right-auto bx-def-margin-thd-topbottom" bx_vote_data_rate="__rate__" bx_vote_data_count="__count__">
+<div id="__html_id__" class="__style_prefix__ __class__ bx-def-margin-right-auto bx-def-margin-thd-topbottom"<bx_if:show_vote_data> bx_vote_data_rate="__rate__" bx_vote_data_count="__count__"</bx_if:show_vote_data>>
     <div class="__style_prefix__-do bx-clearfix">
         <bx_if:show_do_vote>
-                <div class="__style_prefix__-element-holder">__do_vote__</div>
+            <div class="__style_prefix__-element-holder">__do_vote__</div>
         </bx_if:show_do_vote>
         <bx_if:show_counter>
-                <div class="__style_prefix__-counter-holder bx-def-margin-sec-left-auto bx-btn-border"<bx_if:show_hidden> style="display:none"</bx_if:show_hidden>>__counter__</div>
+            <div class="__style_prefix__-counter-holder bx-def-margin-sec-left-auto bx-btn-border"<bx_if:show_hidden> style="display:none"</bx_if:show_hidden>>__counter__</div>
         </bx_if:show_counter>
     </div>
     <bx_if:show_legend>__legend__</bx_if:show_legend>

+ 2 - 2
template/vote_element_inline.html

@@ -1,3 +1,3 @@
-<span id="__html_id__" class="__style_prefix__ __class__ bx-def-margin-right-auto bx-def-margin-thd-topbottom" bx_vote_data_rate="__rate__" bx_vote_data_count="__count__">
-	<span class="__style_prefix__-do"><bx_if:show_do_vote><span class="__style_prefix__-element-holder">__do_vote__</span></bx_if:show_do_vote><bx_if:show_counter><span class="__style_prefix__-counter-holder bx-def-margin-thd-left-auto bx-btn-border"<bx_if:show_hidden> style="display:none"</bx_if:show_hidden>>__counter__</span></bx_if:show_counter></span>__script__
+<span id="__html_id__" class="__style_prefix__ __class__ bx-def-margin-right-auto bx-def-margin-thd-topbottom"<bx_if:show_vote_data> bx_vote_data_rate="__rate__" bx_vote_data_count="__count__"</bx_if:show_vote_data>>
+    <span class="__style_prefix__-do"><bx_if:show_do_vote><span class="__style_prefix__-element-holder">__do_vote__</span></bx_if:show_do_vote><bx_if:show_counter><span class="__style_prefix__-counter-holder bx-def-margin-thd-left-auto bx-btn-border"<bx_if:show_hidden> style="display:none"</bx_if:show_hidden>>__counter__</span></bx_if:show_counter></span>__script__
 </span>