AntonLV 1 год назад
Родитель
Сommit
32f9c7f20a

+ 2 - 2
modules/boonex/reputation/classes/BxReputationAlertsResponse.php

@@ -68,7 +68,7 @@ class BxReputationAlertsResponse extends BxBaseModNotificationsResponse
                 'points' => $iPoints
             ]));
 
-            $this->_oModule->_oDb->insertProfile($iOwnerId, $iPoints);
+            $this->_oModule->assignPoints($iOwnerId, $iPoints);
         }
 
         if(($iPoints = (int)$aHandler['points_passive']) != 0 && $iOwnerId != $iObjectOwnerId) {
@@ -77,7 +77,7 @@ class BxReputationAlertsResponse extends BxBaseModNotificationsResponse
                 'points' => $iPoints
             ]));
 
-            $this->_oModule->_oDb->insertProfile($iObjectOwnerId, $iPoints);
+            $this->_oModule->assignPoints($iObjectOwnerId, $iPoints);
         }
     }
 

+ 28 - 5
modules/boonex/reputation/classes/BxReputationConfig.php

@@ -20,39 +20,52 @@ class BxReputationConfig extends BxBaseModNotificationsConfig
         $this->CNF = [
             // database tables
             'TABLE_EVENTS' => $aModule['db_prefix'] . 'events',
+            'TABLE_LEVELS' => $aModule['db_prefix'] . 'levels',
             'TABLE_PROFILES' => $aModule['db_prefix'] . 'profiles',
+            'TABLE_PROFILES_LEVELS' => $aModule['db_prefix'] . 'profiles_levels',
 
             // database fields
-            'FIELD_' => '',
+            'FIELD_LEVEL_NAME' => 'name',
+            'FIELD_LEVEL_ICON' => 'icon',
 
             // some params
+            'PARAM_MULTILEVEL' => 'bx_reputation_enable_multilevel',
             'PARAM_LEADERBOARD_LIMIT' => 'bx_reputation_leaderboard_limit',
 
             // objects
             'OBJECT_FORM_HANDLER' => 'bx_reputation_handler',
             'OBJECT_FORM_HANDLER_DISPLAY_EDIT' => 'bx_reputation_handler_edit',
-            'OBJECT_GRID_MANAGE' => 'bx_reputation_manage',
+            'OBJECT_FORM_LEVEL' => 'bx_reputation_level',
+            'OBJECT_FORM_LEVEL_DISPLAY_ADD' => 'bx_reputation_level_add',
+            'OBJECT_FORM_LEVEL_DISPLAY_EDIT' => 'bx_reputation_level_edit',
+            'OBJECT_GRID_HANDLERS' => 'bx_reputation_handlers',
+            'OBJECT_GRID_LEVELS' => 'bx_reputation_levels',
 
             // some language keys
             'T' => [
                 'grid_action_err_perform' => '_bx_reputation_grid_action_err_perform',
                 'popup_title_handler_edit' => '_bx_reputation_popup_title_handler_edit',
-                'filter_item_select_one_filter1' => '_bx_reputation_grid_filter_item_title_select_one_filter1',
+                'popup_title_level_add' => '_bx_reputation_popup_title_level_add',
+                'popup_title_level_edit' => '_bx_reputation_popup_title_level_edit',
+                'filter_item_select_one_filter1' => '_bx_reputation_grid_filter_item_title_hdr_select_one_filter1',
                 'err_cannot_perform' => '_bx_reputation_err_cannot_perform',
             ]
         ];
 
         $this->_aJsClasses = [
-            'manage' => 'BxReputationManage',
+            'handlers' => 'BxReputationHandlers',
+            'levels' => 'BxReputationLevels',
         ];
 
         $this->_aJsObjects = [
-            'manage' => 'oBxReputationManage',
+            'handlers' => 'oBxReputationHandlers',
+            'levels' => 'oBxReputationLevels',
         ];
 
         $sHtmlPrefix = str_replace('_', '-', $this->_sName);
         $this->_aHtmlIds = [
             'handler_popup' => $sHtmlPrefix . '-handler-popup',
+            'level_popup' => $sHtmlPrefix . '-level-popup',
         ];
 
         $this->_aHandlerFields = ['group', 'type', 'alert_unit', 'alert_action', 'points_active', 'points_passive'];
@@ -64,6 +77,16 @@ class BxReputationConfig extends BxBaseModNotificationsConfig
     {
         parent::init($oDb);
     }
+
+    public function isMultilevel()
+    {
+        return getParam($this->CNF['PARAM_MULTILEVEL']) == 'on';
+    }
+
+    public function getLevelName($sName)
+    {
+        return uriGenerate($sName, $this->CNF['TABLE_LEVELS'], $this->CNF['FIELD_LEVEL_NAME'], ['lowercase' => false]);
+    }
 }
 
 /** @} */

+ 111 - 6
modules/boonex/reputation/classes/BxReputationDb.php

@@ -69,6 +69,51 @@ class BxReputationDb extends BxBaseModNotificationsDb
         return call_user_func_array([$this, $aMethod['name']], $aMethod['params']);
     }
 
+    public function getLevels($aParams = []) 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+        $sSelectClause = '`trl`.*';
+        $sJoinClause = $sWhereClause = '';
+
+        if(!empty($aParams))
+            switch($aParams['sample']) {
+                case 'id':
+                    $aMethod['name'] = 'getRow';
+                    $aMethod['params'][1] = [
+                        'id' => $aParams['id']
+                    ];
+
+                    $sWhereClause = "AND `trl`.`id` = :id";
+                    break;
+
+                case 'profile_id':
+                    $aMethod['params'][1] = [
+                        'profile_id' => $aParams['profile_id']
+                    ];
+
+                    $sSelectClause .= ", `trpl`.`date` AS `date_assign`";
+                    $sJoinClause = " INNER JOIN `" . $CNF['TABLE_PROFILES_LEVELS'] . "` AS `trpl` ON `trl`.`id`=`trpl`.`level_id` AND `trpl`.`profile_id`=:profile_id";
+                    break;
+
+                case 'points':
+                    $aMethod['params'][1] = [
+                        'points' => $aParams['points']
+                    ];
+
+                    $sWhereClause = "AND `trl`.`points_in` <= :points AND IF(`trl`.`points_out` <> 0, `trl`.`points_out` > :points, 1)";
+                    break;
+            }
+
+        $aMethod['params'][0] = "SELECT 
+                " . $sSelectClause . " 
+            FROM `" . $CNF['TABLE_LEVELS'] . "` AS `trl` " . $sJoinClause . " 
+            WHERE 1 " . $sWhereClause;
+
+        return call_user_func_array([$this, $aMethod['name']], $aMethod['params']);
+    }
+
     public function insertProfile($iId, $iPoints)
     {
         $CNF = &$this->_oConfig->CNF;
@@ -91,8 +136,8 @@ class BxReputationDb extends BxBaseModNotificationsDb
         $CNF = &$this->_oConfig->CNF;
 
         $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
-        $sSelectClause = '*';
-        $sWhereClause = $sOrderClause = $sLimitClause = '';
+        $sSelectClause = '`trp`.*';
+        $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = '';
         
         switch($aParams['sample']) {
             case 'id':
@@ -101,7 +146,7 @@ class BxReputationDb extends BxBaseModNotificationsDb
                     'id' => $aParams['id']
                 ];
 
-                $sWhereClause = "AND `id` = :id";
+                $sWhereClause = "AND `trp`.`id` = :id";
                 break;
 
             case 'stats':
@@ -110,7 +155,7 @@ class BxReputationDb extends BxBaseModNotificationsDb
                 $aMethod['params'][2] = 'points';
                 $aMethod['params'][3] = [];
 
-                $sOrderClause = '`points` DESC';
+                $sOrderClause = '`trp`.`points` DESC';
                 $sLimitClause = '0, ' . (int)$aParams['limit'];
                 break;
         }
@@ -121,10 +166,70 @@ class BxReputationDb extends BxBaseModNotificationsDb
         if($sLimitClause)
             $sLimitClause = "LIMIT " . $sLimitClause;
 
-        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $CNF['TABLE_PROFILES'] . "` WHERE 1 " . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+        $aMethod['params'][0] = "SELECT 
+                " . $sSelectClause . " 
+            FROM `" . $CNF['TABLE_PROFILES'] . "` AS `trp` " . $sJoinClause . " 
+            WHERE 1 " . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+
         return call_user_func_array([$this, $aMethod['name']], $aMethod['params']);
     }
-    
+
+    public function getProfilePoints($iProfileId)
+    {
+        $aProfile = $this->getProfiles([
+            'sample' => 'id', 
+            'id' => $iProfileId
+        ]);
+
+        return $aProfile && isset($aProfile['points']) ? (int)$aProfile['points'] : 0;
+    }
+
+    public function insertProfilesLevels($aSet)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!isset($aSet['date']))
+            $aSet['date'] = time();
+
+        return $this->query("INSERT INTO `" . $CNF['TABLE_PROFILES_LEVELS'] . "` SET " . $this->arrayToSQL($aSet));
+    }
+
+    public function deleteProfilesLevels($aParams)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aBindings = [];
+        $sWhereClause = "";
+
+        switch($aParams['sample']) {
+            case 'profile_id':
+                $aBindings = [
+                    'profile_id' => $aParams['profile_id']
+                ];
+
+                $sWhereClause = "`profile_id`=:profile_id";
+                break;
+        }
+
+        if(!$sWhereClause)
+            return false;
+
+        return $this->query("DELETE FROM `" . $CNF['TABLE_PROFILES_LEVELS'] . "` WHERE " . $sWhereClause, $aBindings);
+    }
+
+    public function deleteProfilesLevelsByPoints($iProfileId, $iPoints)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return $this->query("DELETE FROM `trpl` 
+                USING `" . $CNF['TABLE_PROFILES_LEVELS'] . "` AS `trpl` 
+                LEFT JOIN `" . $CNF['TABLE_LEVELS'] . "` AS `trl` ON `trpl`.`level_id`=`trl`.`id` 
+                WHERE `trpl`.`profile_id` = :profile_id AND (`trl`.`points_in` > :points OR IF(`trl`.`points_out` <> 0, `trl`.`points_out` <= :points, 0))", [
+            'profile_id' => $iProfileId,
+            'points' => $iPoints
+        ]);
+    }
+
     public function getHandlers($aParams = []) 
     {
         $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];

+ 92 - 0
modules/boonex/reputation/classes/BxReputationFormLevel.php

@@ -0,0 +1,92 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Reputation Reputation
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxReputationFormLevel extends BxTemplStudioFormView
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_reputation';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        parent::__construct($aInfo, $oTemplate);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(isset($this->aInputs[$CNF['FIELD_LEVEL_NAME']])) {
+            $sJsObject = $this->_oModule->_oConfig->getJsObject('levels');
+
+            $iId = $this->getLevelId();
+            $aMask = array('mask' => "javascript:%s.checkName('%s');", $sJsObject, $CNF['FIELD_LEVEL_NAME']);
+            if(!empty($iId) && $this->aParams['display'] == $CNF['OBJECT_FORM_LEVEL_DISPLAY_EDIT']) {
+                $aMask['mask'] = "javascript:%s.checkName('%s', %d);";
+                $aMask[] = $iId;
+            }
+
+            $sOnBlur = call_user_func_array('sprintf', array_values($aMask)); 
+            $this->aInputs[$CNF['FIELD_LEVEL_NAME']]['attrs']['onblur'] = $sOnBlur;
+        }
+        
+        if(isset($this->aInputs[$CNF['FIELD_LEVEL_ICON']]))
+            $this->aInputs[$CNF['FIELD_LEVEL_ICON']]['code'] = 1;
+    }
+
+    public function insert ($aValsToAdd = [], $isIgnore = false)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(isset($this->aInputs[$CNF['FIELD_LEVEL_NAME']])) {
+            $sName = BxDolForm::getSubmittedValue($CNF['FIELD_LEVEL_NAME'], $this->aFormAttrs['method'], $this->_aSpecificValues);
+            $sName = $this->_oModule->_oConfig->getLevelName($sName);
+            BxDolForm::setSubmittedValue($CNF['FIELD_LEVEL_NAME'], $sName, $this->aFormAttrs['method'], $this->_aSpecificValues);
+        }
+
+        return parent::insert ($aValsToAdd, $isIgnore);
+    }
+
+    function update ($iContentId, $aValsToAdd = [], &$aTrackTextFieldsChanges = null)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sNameKey = $CNF['FIELD_LEVEL_NAME'];
+        if(isset($this->aInputs[$sNameKey])) {
+            $sName = $this->getCleanValue($sNameKey);
+            $aContentInfo = $this->_oModule->_oDb->getLevels(['sample' => 'id', 'id' => $iContentId]);
+
+            if($aContentInfo[$sNameKey] != $sName) {
+                $sName = $this->_oModule->_oConfig->getLevelName($sName);
+                BxDolForm::setSubmittedValue($sNameKey, $sName, $this->aFormAttrs['method'], $this->_aSpecificValues);
+            }
+        }
+
+        return parent::update ($iContentId, $aValsToAdd, $aTrackTextFieldsChanges);
+    }
+
+    public function getLevelId()
+    {
+        $iResult = 0;
+
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $sId = bx_get('id');
+            if(!empty($sId))
+                $iResult = $sId;
+        }
+        else
+            $iResult = array_shift($aIds);
+
+        return (int)$iResult;
+    }
+}
+
+/** @} */

+ 6 - 5
modules/boonex/reputation/classes/BxReputationGridManage.php → modules/boonex/reputation/classes/BxReputationGridHandlers.php

@@ -9,7 +9,7 @@
  * @{
  */
 
-class BxReputationGridManage extends BxTemplGrid
+class BxReputationGridHandlers extends BxTemplGrid
 {
     protected $_sModule;
     protected $_oModule;
@@ -50,13 +50,14 @@ class BxReputationGridManage extends BxTemplGrid
             $this->_aQueryAppend['filter1'] = $this->_sFilter1Value;
         }
     }
+
     public function getCode($isDisplayHeader = true)
     {
         $mixedResult = parent::getCode($isDisplayHeader);
         if(!$mixedResult)
             return $mixedResult;
 
-        return $this->_oModule->_oTemplate->getJsCode('manage', ['sObjNameGrid' => $this->_sObject]) . $mixedResult;
+        return $this->_oModule->_oTemplate->getJsCode('handlers', ['sObjNameGrid' => $this->_sObject]) . $mixedResult;
     }   
 
     public function performActionEdit()
@@ -128,7 +129,7 @@ class BxReputationGridManage extends BxTemplGrid
     
     protected function _getCellAlertAction($mixedValue, $sKey, $aField, $aRow)
     {
-        return self::_getCellDefault(_t('_bx_reputation_grid_column_value_aa_' . $mixedValue), $sKey, $aField, $aRow);
+        return self::_getCellDefault(_t('_bx_reputation_grid_column_value_hdr_aa_' . $mixedValue), $sKey, $aField, $aRow);
     }
 
     protected function _getFilterControls()
@@ -146,7 +147,7 @@ class BxReputationGridManage extends BxTemplGrid
             return '';
 
         $CNF = &$this->_oModule->_oConfig->CNF;
-        $sJsObject = $this->_oModule->_oConfig->getJsObject('manage');
+        $sJsObject = $this->_oModule->_oConfig->getJsObject('handlers');
 
         $aInputValues = [];
         if($bAddSelectOne)
@@ -172,7 +173,7 @@ class BxReputationGridManage extends BxTemplGrid
 
     protected function _getSearchInput()
     {
-        $sJsObject = $this->_oModule->_oConfig->getJsObject('manage_tools');
+        $sJsObject = $this->_oModule->_oConfig->getJsObject('handlers');
 
         $aInputSearch = [
             'type' => 'text',

+ 154 - 0
modules/boonex/reputation/classes/BxReputationGridLevels.php

@@ -0,0 +1,154 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Reputation Reputation
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxReputationGridLevels extends BxTemplGrid
+{
+    protected $_sModule;
+    protected $_oModule;
+    
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_reputation';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        parent::__construct ($aOptions, $oTemplate);
+    }
+
+    public function getCode($isDisplayHeader = true)
+    {
+        $mixedResult = parent::getCode($isDisplayHeader);
+        if(!$mixedResult)
+            return $mixedResult;
+
+        return $this->_oModule->_oTemplate->getJsCode('levels', ['sObjNameGrid' => $this->_sObject]) . $mixedResult;
+    } 
+
+    public function performActionAdd()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sAction = 'add';
+
+        $sForm = $CNF['OBJECT_FORM_LEVEL_DISPLAY_ADD'];
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_LEVEL'], $CNF['OBJECT_FORM_LEVEL_DISPLAY_ADD']);
+        $oForm->setId($sForm);
+        $oForm->setName($sForm);
+    	$oForm->setAction(BX_DOL_URL_ROOT . bx_append_url_params('grid.php', [
+            'o' => $this->_sObject, 
+            'a' => $sAction
+        ]));
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+            $aValsToAdd = ['date' => time()];
+
+            $iLevel = $oForm->insert($aValsToAdd);
+            if(!$iLevel)
+                return $this->_getActionResult(['msg' => _t($CNF['T']['err_cannot_perform'])]);
+
+            return $this->_bIsApi ? [] : echoJson(['grid' => $this->getCode(false), 'blink' => $iLevel]);    
+        }
+
+        if($this->_bIsApi)
+            return $this->getFormBlockAPI($oForm, $sAction);
+
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->_oModule->_oConfig->getHtmlIds('level_popup'), _t($CNF['T']['popup_title_level_add']), $this->_oModule->_oTemplate->parseHtmlByName('popup_level.html', [
+            'form_id' => $oForm->getId(),
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        return echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+    }
+
+    public function performActionEdit()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sAction = 'edit';
+
+        $aIds = $this->_getIds();
+        if($aIds === false)
+            return $this->_getActionResult([]);
+
+        $iLevel = array_shift($aIds);
+        $aLevel = $this->_oModule->_oDb->getLevels(['sample' => 'id', 'id' => $iLevel]);
+        if(!is_array($aLevel) || empty($aLevel))
+            return $this->_getActionResult([]);
+
+        $sForm = $CNF['OBJECT_FORM_LEVEL_DISPLAY_EDIT'];
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_LEVEL'], $CNF['OBJECT_FORM_LEVEL_DISPLAY_EDIT']);
+        $oForm->setId($sForm);
+        $oForm->setName($sForm);
+    	$oForm->setAction(BX_DOL_URL_ROOT . bx_append_url_params('grid.php', [
+            'o' => $this->_sObject, 
+            'a' => $sAction,
+            'id' => $iLevel
+        ]));
+
+        $oForm->initChecker($aLevel);
+        if($oForm->isSubmittedAndValid()) {
+            if(!$oForm->update($iLevel))
+                return $this->_getActionResult(['msg' => _t($CNF['T']['err_cannot_perform'])]);
+
+            return $this->_bIsApi ? [] : echoJson(['grid' => $this->getCode(false), 'blink' => $iLevel]);    
+        }
+
+        if($this->_bIsApi)
+            return $this->getFormBlockAPI($oForm, $sAction, $iLevel);
+
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->_oModule->_oConfig->getHtmlIds('level_popup'), _t($CNF['T']['popup_title_level_edit']), $this->_oModule->_oTemplate->parseHtmlByName('popup_level.html', [
+            'form_id' => $oForm->getId(),
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        return echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+    }
+
+    protected function _getCellIcon($mixedValue, $sKey, $aField, $aRow)
+    {
+        $mixedValue = $this->_oTemplate->getIcon($mixedValue, ['class' => 'bx-reputation-level-icon']);
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellPointsOut($mixedValue, $sKey, $aField, $aRow)
+    {
+        if((int)$mixedValue == 0)
+            $mixedValue = _t('_lifetime');
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellDate($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(bx_time_js($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getIds()
+    {
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $iId = (int)bx_get('id');
+            if(!$iId) 
+                return false;
+
+            $aIds = [$iId];
+        }
+
+        return $aIds;
+    }
+}
+
+/** @} */

+ 83 - 0
modules/boonex/reputation/classes/BxReputationModule.php

@@ -18,9 +18,45 @@ class BxReputationModule extends BxBaseModNotificationsModule
         $this->_oConfig->init($this->_oDb);
     }
 
+    /**
+     * ACTION METHODS
+     */
+    public function actionCheckName()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$sName = bx_process_input(bx_get('name'));
+    	if(empty($sName))
+            return echoJson([]);
+
+        $sResult = '';
+        if(($iId = (int)bx_get('id')) != 0) {
+            $aLevel = $this->_oDb->getLevels(['sample' => 'id', 'id' => $iId]);
+            if(strcmp($sName, $aLevel[$CNF['FIELD_LEVEL_NAME']]) == 0) 
+                $sResult = $sName;
+        }
+
+    	echoJson([
+            'name' => !empty($sResult) ? $sResult : $this->_oConfig->getLevelName($sName)
+    	]);
+    }
+
     /**
      * SERVICE METHODS
      */
+    public function serviceAssignPoints($iProfileId, $iPoints)
+    {
+        return $this->assignPoints($iProfileId, $iPoints);
+    }
+
+    public function serviceGetLevels($iProfileId)
+    {
+        return $this->_oDb->getLevels([
+            'sample' => 'profile_id', 
+            'profile_id' => $iProfileId
+        ]);
+    }
+
     public function serviceGetBlockSummary($iProfileId = 0)
     {
         if(!$iProfileId && ($iLoggedId = bx_get_logged_profile_id()))
@@ -35,6 +71,53 @@ class BxReputationModule extends BxBaseModNotificationsModule
     {
         return $this->_oTemplate->getBlockLeaderboard($iDays);
     }
+
+    /**
+     * COMMON METHODS
+     */
+    public function assignPoints($iProfileId, $iPoints)
+    {
+        if(!$this->_oDb->insertProfile($iProfileId, $iPoints))
+            return false; 
+            
+        $iProfilePoints = $this->_oDb->getProfilePoints($iProfileId);
+
+        $aLevels = $this->_oDb->getLevels([
+            'sample' => 'points', 
+            'points' => $iProfilePoints
+        ]);
+
+        $bMultilevel = $this->_oConfig->isMultilevel();
+        if($bMultilevel)
+            $this->_oDb->deleteProfilesLevelsByPoints($iProfileId, $iProfilePoints);
+
+        foreach($aLevels as $aLevel) {
+            $iLevelId = (int)$aLevel['id'];
+            if($this->hasLevel($iProfileId, $iLevelId))
+                continue;
+
+            if(!$bMultilevel)
+                $this->_oDb->deleteProfilesLevels(['sample' => 'profile_id', 'profile_id' => $iProfileId]);
+
+            $this->_oDb->insertProfilesLevels(['profile_id' => $iProfileId, 'level_id' => $iLevelId]);
+        }
+
+        return true; 
+    }
+
+    public function hasLevel($iProfileId, $iLevelId)
+    {
+        $aProfileLevels = $this->_oDb->getLevels([
+            'sample' => 'profile_id', 
+            'profile_id' => $iProfileId
+        ]);
+
+        foreach($aProfileLevels as $aProfileLevel)
+            if($aProfileLevel['id'] == $iLevelId)
+                return true;
+
+        return false;
+    }
 }
 
 /** @} */

+ 21 - 5
modules/boonex/reputation/classes/BxReputationStudioPage.php

@@ -17,22 +17,38 @@ class BxReputationStudioPage extends BxBaseModNotificationsStudioPage
 
         parent::__construct($sModule, $mixedPageName, $sPage);
 
-        $this->aMenuItems['manage'] = ['name' => 'manage', 'icon' => 'cogs', 'title' => '_bx_reviews_menu_item_title_manage'];
+        $this->aMenuItems = array_merge($this->aMenuItems, [
+            'handlers' => ['name' => 'manage', 'icon' => 'cogs', 'title' => '_bx_reviews_menu_item_title_handlers'],
+            'levels' => ['name' => 'levels', 'icon' => 'cogs', 'title' => '_bx_reviews_menu_item_title_levels'],
+        ]);
     }
 
-    protected function getManage()
+    protected function getHandlers()
     {
         $CNF = &$this->_oModule->_oConfig->CNF;
 
-        $oGrid = BxDolGrid::getObjectInstance($CNF['OBJECT_GRID_MANAGE'], BxDolStudioTemplate::getInstance());
+        $oGrid = BxDolGrid::getObjectInstance($CNF['OBJECT_GRID_HANDLERS'], BxDolStudioTemplate::getInstance());
         if(!$oGrid)
             return '';
 
-        $this->_oModule->_oTemplate->addStudioCss(['manage.css']);
-        $this->_oModule->_oTemplate->addStudioJs(['manage.js']);
+        $this->_oModule->_oTemplate->addStudioCss(['handlers.css']);
+        $this->_oModule->_oTemplate->addStudioJs(['handlers.js']);
         $this->_oModule->_oTemplate->addStudioJsTranslation(['_sys_grid_search']);
         return $oGrid->getCode();
     }
+
+    protected function getLevels()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $oGrid = BxDolGrid::getObjectInstance($CNF['OBJECT_GRID_LEVELS'], BxDolStudioTemplate::getInstance());
+        if(!$oGrid)
+            return '';
+
+        $this->_oModule->_oTemplate->addStudioCss(['levels.css']);
+        $this->_oModule->_oTemplate->addStudioJs(['levels.js']);
+        return $oGrid->getCode();
+    }
 }
 
 /** @} */

+ 35 - 0
modules/boonex/reputation/classes/BxReputationTemplate.php

@@ -24,11 +24,46 @@ class BxReputationTemplate extends BxBaseModNotificationsTemplate
 
         $aProfileInfo = $this->_oDb->getProfiles(['sample' => 'id', 'id' => $iProfileId]);
         $bProfileInfo = !empty($aProfileInfo) && is_array($aProfileInfo);
+        $aProfileLevels = $this->_oDb->getLevels([
+            'sample' => 'profile_id', 
+            'profile_id' => $iProfileId
+        ]);
+
+        $oFunctions = BxTemplFunctions::getInstance();
+
+        $aTmplVarsLevels = [];
+        foreach($aProfileLevels as $aProfileLevel) {
+            list($sIconFont, $sIconUrl, $sIconA, $sIconHtml) = $oFunctions->getIcon($aProfileLevel['icon']);
+            $bIconFont = !empty($sIconFont);
+            $bIconHtml = !empty($sIconHtml);
+
+            $aTmplVarsLevels[] = [
+                'bx_if:icon' => [
+                    'condition' => $bIconFont || $bIconHtml,
+                    'content' => [
+                        'bx_if:icon_font' => [
+                            'condition' => $bIconFont,
+                            'content' => [
+                                'icon' => $sIconFont
+                            ]
+                        ],
+                        'bx_if:icon_html' => [
+                            'condition' => $bIconHtml,
+                            'content' => [
+                                'icon' => $sIconHtml
+                            ]
+                        ],
+                    ]
+                ],
+                'level_title' => _t($aProfileLevel['title'])
+            ];
+        }
 
         return $this->parseHtmlByName('block_summary.html', [
             'profile_image' => $oProfile->getUnit($iProfileId, ['template' => ['name' => 'unit_wo_info', 'size' => 'ava']]),
             'profile_name' => $oProfile->getDisplayName(),
             'points' => $bProfileInfo ? $aProfileInfo['points'] : 0,
+            'bx_repeat:levels' => $aTmplVarsLevels
         ]);
     }
 

+ 71 - 26
modules/boonex/reputation/install/langs/en.xml

@@ -2,8 +2,11 @@
 <resources name="en" flag="gb" title="English">
     <string name="_bx_reputation"><![CDATA[Reputation]]></string>
 
+    <!-- Options -->
+    <string name="_bx_reputation_option_enable_multilevel"><![CDATA[Enable the ability to have multiple levels at the same time]]></string>
     <string name="_bx_reputation_option_leaderboard_limit"><![CDATA[Number of items in leaderboard]]></string>
 
+    <!-- Forms -->
     <string name="_bx_reputation_form_handler"><![CDATA[Handler]]></string>
     <string name="_bx_reputation_form_handler_display_edit"><![CDATA[Edit Handler]]></string>
     <string name="_bx_reputation_form_handler_input_sys_points_active"><![CDATA[Points (active)]]></string>
@@ -15,10 +18,37 @@
     <string name="_bx_reputation_form_handler_input_sys_controls"><![CDATA[Controls]]></string>
     <string name="_bx_reputation_form_handler_input_sys_do_submit"><![CDATA[Submit]]></string>
     <string name="_bx_reputation_form_handler_input_do_submit"><![CDATA[Submit]]></string>
-    <string name="_bx_reputation_form_handler_input_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_reputation_form_handler_input_sys_do_cancel"><![CDATA[Cancel]]></string>
     <string name="_bx_reputation_form_handler_input_do_cancel"><![CDATA[Cancel]]></string>
     <string name="_bx_reputation_popup_title_handler_edit"><![CDATA[Edit Handler]]></string>
 
+    <string name="_bx_reputation_form_level"><![CDATA[Level]]></string>
+    <string name="_bx_reputation_form_level_display_add"><![CDATA[Add Level]]></string>
+    <string name="_bx_reputation_form_level_display_edit"><![CDATA[Edit Level]]></string>
+    <string name="_bx_reputation_form_level_input_sys_name"><![CDATA[Name]]></string>
+    <string name="_bx_reputation_form_level_input_name"><![CDATA[Name]]></string>
+    <string name="_bx_reputation_form_level_input_sys_title"><![CDATA[Title]]></string>
+    <string name="_bx_reputation_form_level_input_title"><![CDATA[Title]]></string>
+    <string name="_bx_reputation_form_level_input_sys_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_reputation_form_level_input_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_reputation_form_level_input_icon_info"><![CDATA[Font icon, SVG or Emoji code can be used here.]]></string>
+    <string name="_bx_reputation_form_level_input_sys_points_in"><![CDATA[Points In]]></string>
+    <string name="_bx_reputation_form_level_input_points_in"><![CDATA[Points In]]></string>
+    <string name="_bx_reputation_form_level_input_points_in_info"><![CDATA[Number of points to receive the level.]]></string>
+    <string name="_bx_reputation_form_level_input_sys_points_out"><![CDATA[Points Out]]></string>
+    <string name="_bx_reputation_form_level_input_points_out"><![CDATA[Points Out]]></string>
+    <string name="_bx_reputation_form_level_input_points_out_info"><![CDATA[Number of points to leave this level. Leave empty for lifetime, which can be useful in multi-level mode.]]></string>
+    <string name="_bx_reputation_form_level_input_sys_controls"><![CDATA[Controls]]></string>
+    <string name="_bx_reputation_form_level_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_reputation_form_level_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_reputation_form_level_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_reputation_form_level_input_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_reputation_popup_title_level_add"><![CDATA[Add Level]]></string>
+    <string name="_bx_reputation_popup_title_level_edit"><![CDATA[Edit {0} Level]]></string>
+
+    <string name="_bx_reputation_form_input_err"><![CDATA[The field is essential. Please, fill it in.]]></string>
+
+    <!-- Pages -->
     <string name="_bx_reputation_page_title_sys_leaderboard"><![CDATA[Leaderboard]]></string>
     <string name="_bx_reputation_page_title_leaderboard"><![CDATA[Leaderboard]]></string>
     <string name="_bx_reputation_page_block_title_leaderboard_week"><![CDATA[Leaderboard (7-days)]]></string>
@@ -27,36 +57,51 @@
     <string name="_bx_reputation_page_block_title_system_summary"><![CDATA[Summary]]></string>
     <string name="_bx_reputation_page_block_title_summary"><![CDATA[Summary]]></string>
 
+    <!-- Menus -->
     <string name="_bx_reputation_menu_item_title_system_leaderboard"><![CDATA[Leaderboard]]></string>
     <string name="_bx_reputation_menu_item_title_leaderboard"><![CDATA[Leaderboard]]></string>
-    <string name="_bx_reviews_menu_item_title_manage"><![CDATA[Manage]]></string>
-
-    <string name="_bx_reputation_grid_column_title_active"><![CDATA[Active]]></string>
-    <string name="_bx_reputation_grid_column_title_alert_unit"><![CDATA[Section]]></string>
-    <string name="_bx_reputation_grid_column_title_alert_action"><![CDATA[Action]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_added"><![CDATA[Add]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_post_common"><![CDATA[Write post]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_repost"><![CDATA[Do repost]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_deleted"><![CDATA[Remove]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_delete"><![CDATA[Remove post]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_delete_repost"><![CDATA[Remove repost]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_commentPost"><![CDATA[Post comment]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_commentRemoved"><![CDATA[Remove comment]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_doVote"><![CDATA[Do vote]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_undoVote"><![CDATA[Undo vote]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_doVoteUp"><![CDATA[Do up vote]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_undoVoteUp"><![CDATA[Undo up vote]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_doVoteDown"><![CDATA[Do down vote]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_undoVoteDown"><![CDATA[Undo down vote]]></string>
-    <string name="_bx_reputation_grid_column_title_points_active"><![CDATA[Points (active)]]></string>
-    <string name="_bx_reputation_grid_column_title_points_passive"><![CDATA[Points (passive)]]></string>
-    <string name="_bx_reputation_grid_action_title_activate"><![CDATA[Activate]]></string>
-    <string name="_bx_reputation_grid_action_title_deactivate"><![CDATA[Deactivate]]></string>
-    <string name="_bx_reputation_grid_action_title_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_reviews_menu_item_title_handlers"><![CDATA[Handlers]]></string>
+    <string name="_bx_reviews_menu_item_title_levels"><![CDATA[Levels]]></string>
+
+    <!-- Grids -->
+    <string name="_bx_reputation_grid_column_title_hdr_active"><![CDATA[Active]]></string>
+    <string name="_bx_reputation_grid_column_title_hdr_alert_unit"><![CDATA[Section]]></string>
+    <string name="_bx_reputation_grid_column_title_hdr_alert_action"><![CDATA[Action]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_added"><![CDATA[Add]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_post_common"><![CDATA[Write post]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_repost"><![CDATA[Do repost]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_deleted"><![CDATA[Remove]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_delete"><![CDATA[Remove post]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_delete_repost"><![CDATA[Remove repost]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_commentPost"><![CDATA[Post comment]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_commentRemoved"><![CDATA[Remove comment]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_doVote"><![CDATA[Do vote]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_undoVote"><![CDATA[Undo vote]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_doVoteUp"><![CDATA[Do up vote]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_undoVoteUp"><![CDATA[Undo up vote]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_doVoteDown"><![CDATA[Do down vote]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_undoVoteDown"><![CDATA[Undo down vote]]></string>
+    <string name="_bx_reputation_grid_column_title_hdr_points_active"><![CDATA[Points (active)]]></string>
+    <string name="_bx_reputation_grid_column_title_hdr_points_passive"><![CDATA[Points (passive)]]></string>
+    <string name="_bx_reputation_grid_action_title_hdr_activate"><![CDATA[Activate]]></string>
+    <string name="_bx_reputation_grid_action_title_hdr_deactivate"><![CDATA[Deactivate]]></string>
+    <string name="_bx_reputation_grid_action_title_hdr_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_reputation_grid_filter_item_title_hdr_select_one_filter1"><![CDATA[All Sections]]></string>
+
+    <string name="_bx_reputation_grid_column_title_lvl_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_reputation_grid_column_title_lvl_title"><![CDATA[Title]]></string>
+    <string name="_bx_reputation_grid_column_title_lvl_points_in"><![CDATA[Points In]]></string>
+    <string name="_bx_reputation_grid_column_title_lvl_points_out"><![CDATA[Points Out]]></string>
+    <string name="_bx_reputation_grid_column_title_lvl_date"><![CDATA[Date]]></string>
+    <string name="_bx_reputation_grid_action_title_lvl_add"><![CDATA[Add level]]></string>
+    <string name="_bx_reputation_grid_action_title_lvl_edit"><![CDATA[Edit level]]></string>
+    <string name="_bx_reputation_grid_action_title_lvl_delete"><![CDATA[Delete level]]></string>
+
     <string name="_bx_reputation_grid_action_err_perform"><![CDATA[Cannot perform the action for selected items(s).]]></string>
-    <string name="_bx_reputation_grid_filter_item_title_select_one_filter1"><![CDATA[All Sections]]></string>
 
+    <!-- Errors, Warnings, Messages -->
     <string name="_bx_reputation_err_cannot_perform"><![CDATA[An error appeared. Please report.]]></string>
 
+    <!-- Other -->
     <string name="_bx_reputation_txt_points"><![CDATA[points]]></string>
 </resources>

+ 32 - 12
modules/boonex/reputation/install/sql/enable.sql

@@ -12,7 +12,8 @@ VALUES (@iTypeId, @sName, '_bx_reputation', 10);
 SET @iCategId = LAST_INSERT_ID();
 
 INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `extra`, `check`, `check_error`, `order`) VALUES
-('bx_reputation_leaderboard_limit', '10', @iCategId, '_bx_reputation_option_leaderboard_limit', 'digit', '', '', '', 1);
+('bx_reputation_enable_multilevel', '', @iCategId, '_bx_reputation_option_enable_multilevel', 'checkbox', '', '', '', 1),
+('bx_reputation_leaderboard_limit', '10', @iCategId, '_bx_reputation_option_leaderboard_limit', 'digit', '', '', '', 11);
 
 
 -- PAGE: leaderboard
@@ -45,21 +46,40 @@ INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `tit
 
 -- GRIDS: administration tools
 INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
-('bx_reputation_manage', 'Sql', 'SELECT * FROM `bx_reputation_handlers` WHERE 1 ', 'bx_reputation_handlers', 'id', '', 'active', '', 20, NULL, 'start', '', 'type,alert_unit,alert_action', '', 'like', 'reports', '', 192, 'BxReputationGridManage', 'modules/boonex/reputation/classes/BxReputationGridManage.php');
+('bx_reputation_handlers', 'Sql', 'SELECT * FROM `bx_reputation_handlers` WHERE 1 ', 'bx_reputation_handlers', 'id', '', 'active', '', 20, NULL, 'start', '', 'type,alert_unit,alert_action', '', 'like', 'reports', '', 192, 'BxReputationGridHandlers', 'modules/boonex/reputation/classes/BxReputationGridHandlers.php');
 
 INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
-('bx_reputation_manage', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
-('bx_reputation_manage', 'switcher', '_bx_reputation_grid_column_title_active', '8%', 0, 0, '', 2),
-('bx_reputation_manage', 'alert_unit', '_bx_reputation_grid_column_title_alert_unit', '25%', 0, 0, '', 3),
-('bx_reputation_manage', 'alert_action', '_bx_reputation_grid_column_title_alert_action', '21%', 0, 0, '', 4),
-('bx_reputation_manage', 'points_active', '_bx_reputation_grid_column_title_points_active', '12%', 0, 0, '', 5),
-('bx_reputation_manage', 'points_passive', '_bx_reputation_grid_column_title_points_passive', '12%', 0, 0, '', 6),
-('bx_reputation_manage', 'actions', '', '20%', 0, 0, '', 7);
+('bx_reputation_handlers', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_reputation_handlers', 'switcher', '_bx_reputation_grid_column_title_hdr_active', '8%', 0, 0, '', 2),
+('bx_reputation_handlers', 'alert_unit', '_bx_reputation_grid_column_title_hdr_alert_unit', '25%', 0, 0, '', 3),
+('bx_reputation_handlers', 'alert_action', '_bx_reputation_grid_column_title_hdr_alert_action', '21%', 0, 0, '', 4),
+('bx_reputation_handlers', 'points_active', '_bx_reputation_grid_column_title_hdr_points_active', '12%', 0, 0, '', 5),
+('bx_reputation_handlers', 'points_passive', '_bx_reputation_grid_column_title_hdr_points_passive', '12%', 0, 0, '', 6),
+('bx_reputation_handlers', 'actions', '', '20%', 0, 0, '', 7);
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
-('bx_reputation_manage', 'bulk', 'activate', '_bx_reputation_grid_action_title_activate', '', 0, 0, 1),
-('bx_reputation_manage', 'bulk', 'deactivate', '_bx_reputation_grid_action_title_deactivate', '', 0, 0, 2),
-('bx_reputation_manage', 'single', 'edit', '_bx_reputation_grid_action_title_edit', 'pencil-alt', 1, 0, 1);
+('bx_reputation_handlers', 'bulk', 'activate', '_bx_reputation_grid_action_title_hdr_activate', '', 0, 0, 1),
+('bx_reputation_handlers', 'bulk', 'deactivate', '_bx_reputation_grid_action_title_hdr_deactivate', '', 0, 0, 2),
+('bx_reputation_handlers', 'single', 'edit', '_bx_reputation_grid_action_title_hdr_edit', 'pencil-alt', 1, 0, 1);
+
+-- GRIDS: levels
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `override_class_name`, `override_class_file`) VALUES
+('bx_reputation_levels', 'Sql', 'SELECT * FROM `bx_reputation_levels` WHERE 1 ', 'bx_reputation_levels', 'id', 'order', 'active', '', 20, NULL, 'start', '', 'name', 'title', 'like', '', '', 'BxReputationGridLevels', 'modules/boonex/reputation/classes/BxReputationGridLevels.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_reputation_levels', 'order', '', '1%', 0, 0, '', 1),
+('bx_reputation_levels', 'switcher', '', '9%', 0, 0, '', 2),
+('bx_reputation_levels', 'icon', '_bx_reputation_grid_column_title_lvl_icon', '5%', 0, 0, '', 3),
+('bx_reputation_levels', 'title', '_bx_reputation_grid_column_title_lvl_title', '35%', 1, 16, '', 4),
+('bx_reputation_levels', 'points_in', '_bx_reputation_grid_column_title_lvl_points_in', '10%', 0, 0, '', 5),
+('bx_reputation_levels', 'points_out', '_bx_reputation_grid_column_title_lvl_points_out', '10%', 0, 0, '', 6),
+('bx_reputation_levels', 'date', '_bx_reputation_grid_column_title_lvl_date', '10%', 0, 0, '', 7),
+('bx_reputation_levels', 'actions', '', '20%', 0, 0, '', 8);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_reputation_levels', 'independent', 'add', '_bx_reputation_grid_action_title_lvl_add', '', 0, 0, 1),
+('bx_reputation_levels', 'single', 'edit', '_bx_reputation_grid_action_title_lvl_edit', 'pencil-alt', 1, 0, 1),
+('bx_reputation_levels', 'single', 'delete', '_bx_reputation_grid_action_title_lvl_delete', 'remove', 1, 1, 2);
 
 
 -- ALERTS

+ 61 - 1
modules/boonex/reputation/install/sql/install.sql

@@ -30,6 +30,21 @@ CREATE TABLE IF NOT EXISTS `bx_reputation_events` (
   KEY `object_id` (`object_id`)
 );
 
+-- TABLE: levels
+CREATE TABLE IF NOT EXISTS `bx_reputation_levels` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(32) NOT NULL default '',
+  `title` varchar(64) NOT NULL default '',
+  `icon` text NOT NULL,
+  `points_in` int(11) NOT NULL DEFAULT '0',
+  `points_out` int(11) NOT NULL DEFAULT '0',
+  `date` int(11) NOT NULL default '0',
+  `active` tinyint(4) NOT NULL DEFAULT '1',
+  `order` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`)
+);
+
 -- TABLE: profiles
 CREATE TABLE IF NOT EXISTS `bx_reputation_profiles` (
   `id` int(11) NOT NULL DEFAULT '0',
@@ -37,6 +52,15 @@ CREATE TABLE IF NOT EXISTS `bx_reputation_profiles` (
   PRIMARY KEY (`id`)
 );
 
+-- TABLE: profiles' levels
+CREATE TABLE IF NOT EXISTS `bx_reputation_profiles_levels` (
+  `id` int(11) NOT NULL auto_increment,
+  `profile_id` int(11) NOT NULL DEFAULT '0',
+  `level_id` int(11) NOT NULL DEFAULT '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`)
+);
+
 
 -- FORMS: handler
 INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
@@ -50,7 +74,7 @@ INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `c
 ('bx_reputation_handler', @sName, 'points_passive', '', '', 0, 'text', '_bx_reputation_form_handler_input_sys_points_passive', '_bx_reputation_form_handler_input_points_passive', '_bx_reputation_form_handler_input_points_passive_info', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
 ('bx_reputation_handler', @sName, 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '_bx_reputation_form_handler_input_sys_controls', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('bx_reputation_handler', @sName, 'do_submit', '_bx_reputation_form_handler_input_do_submit', '', 0, 'submit', '_bx_reputation_form_handler_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
-('bx_reputation_handler', @sName, 'do_cancel', '_bx_reputation_form_handler_input_do_cancel', '', 0, 'button', '_bx_reputation_form_handler_input_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+('bx_reputation_handler', @sName, 'do_cancel', '_bx_reputation_form_handler_input_do_cancel', '', 0, 'button', '_bx_reputation_form_handler_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
 
 INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
 ('bx_reputation_handler_edit', 'points_active', 2147483647, 1, 1),
@@ -59,6 +83,42 @@ INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_fo
 ('bx_reputation_handler_edit', 'do_submit', 2147483647, 1, 4),
 ('bx_reputation_handler_edit', 'do_cancel', 2147483647, 1, 5);
 
+-- FORMS: level
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_reputation_level', @sName, '_bx_reputation_form_level', '', '', 'do_submit', 'bx_reputation_levels', 'id', '', '', '', 0, 1, 'BxReputationFormLevel', 'modules/boonex/reputation/classes/BxReputationFormLevel.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_reputation_level_add', @sName, 'bx_reputation_level', '_bx_reputation_form_level_display_add', 0);
+('bx_reputation_level_edit', @sName, 'bx_reputation_level', '_bx_reputation_form_level_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_reputation_level', @sName, 'name', '', '', 0, 'text', '_bx_reputation_form_level_input_sys_name', '_bx_reputation_form_level_input_name', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_reputation_form_input_err', 'Xss', '', 1, 0),
+('bx_reputation_level', @sName, 'title', '', '', 0, 'text_translatable', '_bx_reputation_form_level_input_sys_title', '_bx_reputation_form_level_input_title', '', 1, 0, 0, '', '', '', 'AvailTranslatable', 'a:1:{i:0;s:5:"title";}', '_bx_reputation_form_input_err', 'Xss', '', 1, 0),
+('bx_reputation_level', @sName, 'icon', '', '', 0, 'textarea', '_bx_reputation_form_level_input_sys_icon', '_bx_reputation_form_level_input_icon', '_bx_reputation_form_level_input_icon_info', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_reputation_level', @sName, 'points_in', '', '', 0, 'text', '_bx_reputation_form_level_input_sys_points_in', '_bx_reputation_form_level_input_points_in', '_bx_reputation_form_level_input_points_in_info', 1, 0, 0, '', '', '', 'Avail', '', '_bx_reputation_form_input_err', 'Int', '', 1, 0),
+('bx_reputation_level', @sName, 'points_out', '', '', 0, 'text', '_bx_reputation_form_level_input_sys_points_out', '_bx_reputation_form_level_input_points_out', '_bx_reputation_form_level_input_points_out_info', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_reputation_level', @sName, 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '_bx_reputation_form_level_input_sys_controls', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_reputation_level', @sName, 'do_submit', '_bx_reputation_form_level_input_do_submit', '', 0, 'submit', '_bx_reputation_form_level_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_reputation_level', @sName, 'do_cancel', '_bx_reputation_form_level_input_do_cancel', '', 0, 'button', '_bx_reputation_form_level_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_reputation_level_add', 'name', 2147483647, 1, 1),
+('bx_reputation_level_add', 'title', 2147483647, 1, 2),
+('bx_reputation_level_add', 'icon', 2147483647, 1, 3),
+('bx_reputation_level_add', 'points_in', 2147483647, 1, 4),
+('bx_reputation_level_add', 'points_out', 2147483647, 1, 5),
+('bx_reputation_level_add', 'controls', 2147483647, 1, 6),
+('bx_reputation_level_add', 'do_submit', 2147483647, 1, 7),
+('bx_reputation_level_add', 'do_cancel', 2147483647, 1, 8),
+
+('bx_reputation_level_edit', 'title', 2147483647, 1, 1),
+('bx_reputation_level_edit', 'icon', 2147483647, 1, 2),
+('bx_reputation_level_edit', 'points_in', 2147483647, 1, 3),
+('bx_reputation_level_edit', 'points_out', 2147483647, 1, 4),
+('bx_reputation_level_edit', 'controls', 2147483647, 1, 5),
+('bx_reputation_level_edit', 'do_submit', 2147483647, 1, 6),
+('bx_reputation_level_edit', 'do_cancel', 2147483647, 1, 7);
+
 
 -- STUDIO PAGE & WIDGET
 INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES

+ 2 - 1
modules/boonex/reputation/install/sql/uninstall.sql

@@ -1,7 +1,8 @@
 SET @sName = 'bx_reputation';
 
 
-DROP TABLE IF EXISTS `bx_reputation_handlers`, `bx_reputation_events`, `bx_reputation_profiles`;
+-- TABLES
+DROP TABLE IF EXISTS `bx_reputation_handlers`, `bx_reputation_events`, `bx_reputation_levels`, `bx_reputation_profiles`, `bx_reputation_profiles_levels`;
 
 
 -- FORMS

+ 3 - 3
modules/boonex/reputation/js/manage.js → modules/boonex/reputation/js/handlers.js

@@ -8,12 +8,12 @@
  * @{
  */
 
-function BxReputationManage(oOptions)
+function BxReputationHandlers(oOptions)
 {
     this._iSearchTimeoutId = false;
     this._sActionsUrl = oOptions.sActionUrl;
     this._sObjNameGrid = oOptions.sObjNameGrid;
-    this._sObjName = oOptions.sObjName == undefined ? 'oBxReputationManage' : oOptions.sObjName;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxReputationHandlers' : oOptions.sObjName;
 
     this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
     this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
@@ -23,7 +23,7 @@ function BxReputationManage(oOptions)
     this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
 }
 
-BxReputationManage.prototype.onChangeFilter = function(oFilter) {
+BxReputationHandlers.prototype.onChangeFilter = function(oFilter) {
     var $this = this;
     var oFilter1 = $('#bx-grid-filter1-' + this._sObjNameGrid);
     var sValueFilter1 = oFilter1.length > 0 ? oFilter1.val() : '';

+ 50 - 0
modules/boonex/reputation/js/levels.js

@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Reputation Reputation
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxReputationLevels(oOptions)
+{
+    this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjNameGrid = oOptions.sObjNameGrid;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxReputationLevels' : oOptions.sObjName;
+
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'slide' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+}
+
+BxReputationLevels.prototype.checkName = function(sNameId, iId) {
+    var oDate = new Date();
+
+    var oName = jQuery("[name='" + sNameId + "']");
+    var sName = oName.val();
+
+    if(!sName.length)
+        return;
+
+    jQuery.get(
+        this._sActionsUrl + 'check_name',
+        {
+            name: sName,
+            id: iId && parseInt(iId) > 0 ? iId : 0,
+            _t: oDate.getTime()
+        },
+        function(oData) {
+            if(!oData || oData.name == undefined)
+                return;
+
+            oName.val(oData.name);
+        },
+        'json'
+    );
+};
+
+/** @} */

+ 13 - 0
modules/boonex/reputation/template/block_summary.html

@@ -2,4 +2,17 @@
     <div class="bx-reputation-s-profile-icon">__profile_image__</div>
     <div class="bx-reputation-s-profile-name mt-2 text-xl">__profile_name__</div>
     <div class="bx-reputation-s-points mt-2"><span class="mr-2 text-2xl font-bold">__points__</span><span><bx_text:_bx_reputation_txt_points /></span></div>
+    <div class="bx-reputation-s-levels flex mt-2 space-x-4">
+        <bx_repeat:levels>
+            <div class="bx-reputation-s-level flex items-center">
+                <bx_if:icon>
+                    <div class="bx-reputation-sl-icon mr-1">
+                        <bx_if:icon_font><i class="sys-icon __icon__"></i></bx_if:icon_font>
+                        <bx_if:icon_html>__icon__</bx_if:icon_html>
+                    </div>
+                </bx_if:icon>
+                <div class="bx-reputation-sl-title">__level_title__</div>
+            </div>
+        </bx_repeat:levels>
+    </div>
 </div>

+ 0 - 0
modules/boonex/reputation/template/css/manage.css → modules/boonex/reputation/template/css/handlers.css


+ 10 - 0
modules/boonex/reputation/template/css/levels.css

@@ -0,0 +1,10 @@
+.bx-reputation-level-icon {
+    display: inline-block;
+    width: 1.5rem;
+    height: 1.5rem;
+}
+i.bx-reputation-level-icon {
+    text-align: center;
+    line-height: 1.5rem;
+    font-size: 1.0rem;
+}

+ 19 - 0
modules/boonex/reputation/template/popup_level.html

@@ -0,0 +1,19 @@
+<div class="bx-reputation-levele-form">
+    <div class="bx-reputation-levele-form-cnt">
+        __form__
+        <script language="javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function (formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function (data) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        glGrids.__object__.processJson(data, '__action__');
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 68 - 26
modules/boonex/russian/data/langs/bx_reputation/ru.xml

@@ -2,8 +2,11 @@
 <resources name="ru" flag="ru" title="Russian">
     <string name="_bx_reputation"><![CDATA[Репутация]]></string>
 
+    <!-- Options -->
+    <string name="_bx_reputation_option_enable_multilevel"><![CDATA[Включить возможность иметь несколько уровней одновременно]]></string>
     <string name="_bx_reputation_option_leaderboard_limit"><![CDATA[Количество элементов в таблице лидеров]]></string>
 
+    <!-- Forms -->
     <string name="_bx_reputation_form_handler"><![CDATA[Обработчик]]></string>
     <string name="_bx_reputation_form_handler_display_edit"><![CDATA[Редактировать обработчик]]></string>
     <string name="_bx_reputation_form_handler_input_sys_points_active"><![CDATA[Баллы (активные)]]></string>
@@ -15,10 +18,34 @@
     <string name="_bx_reputation_form_handler_input_sys_controls"><![CDATA[Управление]]></string>
     <string name="_bx_reputation_form_handler_input_sys_do_submit"><![CDATA[Выполнить]]></string>
     <string name="_bx_reputation_form_handler_input_do_submit"><![CDATA[Выполнить]]></string>
-    <string name="_bx_reputation_form_handler_input_do_cancel"><![CDATA[Отмена]]></string>
+    <string name="_bx_reputation_form_handler_input_sys_do_cancel"><![CDATA[Отмена]]></string>
     <string name="_bx_reputation_form_handler_input_do_cancel"><![CDATA[Отмена]]></string>
     <string name="_bx_reputation_popup_title_handler_edit"><![CDATA[Редактировать обработчик]]></string>
 
+    <string name="_bx_reputation_form_level"><![CDATA[Уровень]]></string>
+    <string name="_bx_reputation_form_level_display_add"><![CDATA[Добавить уровень]]></string>
+    <string name="_bx_reputation_form_level_display_edit"><![CDATA[Редактировать уровень]]></string>
+    <string name="_bx_reputation_form_level_input_sys_name"><![CDATA[Имя]]></string>
+    <string name="_bx_reputation_form_level_input_name"><![CDATA[Имя]]></string>
+    <string name="_bx_reputation_form_level_input_sys_title"><![CDATA[Название]]></string>
+    <string name="_bx_reputation_form_level_input_title"><![CDATA[Название]]></string>
+    <string name="_bx_reputation_form_level_input_sys_icon"><![CDATA[Изображение]]></string>
+    <string name="_bx_reputation_form_level_input_icon"><![CDATA[Изображение]]></string>
+    <string name="_bx_reputation_form_level_input_icon_info"><![CDATA[Здесь можно использовать значок шрифта, код SVG или эмодзи.]]></string>
+    <string name="_bx_reputation_form_level_input_sys_points_in"><![CDATA[Баллы: Вкл]]></string>
+    <string name="_bx_reputation_form_level_input_points_in"><![CDATA[Баллы: Вкл]]></string>
+    <string name="_bx_reputation_form_level_input_points_in_info"><![CDATA[Количество баллов для получения этого уровня.]]></string>
+    <string name="_bx_reputation_form_level_input_sys_points_out"><![CDATA[Баллы: Выкл]]></string>
+    <string name="_bx_reputation_form_level_input_points_out"><![CDATA[Баллы: Выкл]]></string>
+    <string name="_bx_reputation_form_level_input_points_out_info"><![CDATA[Количество баллов для выхода с этого уровня. Оставьте пустым, если уровень пожизненный, что может быть полезно в многоуровневом режиме.]]></string>
+    <string name="_bx_reputation_form_level_input_sys_controls"><![CDATA[Управление]]></string>
+    <string name="_bx_reputation_form_level_input_sys_do_submit"><![CDATA[Выполнить]]></string>
+    <string name="_bx_reputation_form_level_input_do_submit"><![CDATA[Выполнить]]></string>
+    <string name="_bx_reputation_form_level_input_sys_do_cancel"><![CDATA[Отмена]]></string>
+    <string name="_bx_reputation_form_level_input_do_cancel"><![CDATA[Отмена]]></string>
+    <string name="_bx_reputation_form_input_err"><![CDATA[Поле обязательно. Пожалуйста, заполните его.]]></string>
+
+    <!-- Pages -->
     <string name="_bx_reputation_page_title_sys_leaderboard"><![CDATA[Таблица лидеров]]></string>
     <string name="_bx_reputation_page_title_leaderboard"><![CDATA[Таблица лидеров]]></string>
     <string name="_bx_reputation_page_block_title_leaderboard_week"><![CDATA[Таблица лидеров (7 дней)]]></string>
@@ -27,36 +54,51 @@
     <string name="_bx_reputation_page_block_title_system_summary"><![CDATA[Сводка]]></string>
     <string name="_bx_reputation_page_block_title_summary"><![CDATA[Сводка]]></string>
 
+    <!-- Menus -->
     <string name="_bx_reputation_menu_item_title_system_leaderboard"><![CDATA[Таблица лидеров]]></string>
     <string name="_bx_reputation_menu_item_title_leaderboard"><![CDATA[Таблица лидеров]]></string>
-    <string name="_bx_reviews_menu_item_title_manage"><![CDATA[Управление]]></string>
-
-    <string name="_bx_reputation_grid_column_title_active"><![CDATA[Активные]]></string>
-    <string name="_bx_reputation_grid_column_title_alert_unit"><![CDATA[Раздел]]></string>
-    <string name="_bx_reputation_grid_column_title_alert_action"><![CDATA[Действие]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_added"><![CDATA[Добавить]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_post_common"><![CDATA[Написать пост]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_repost"><![CDATA[Сдулать репост]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_deleted"><![CDATA[Удалить]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_delete"><![CDATA[Удалить пост]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_delete_repost"><![CDATA[Удалить репост]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_commentPost"><![CDATA[Оставить комментарий]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_commentRemoved"><![CDATA[Удалить комментарий]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_doVote"><![CDATA[Проголосовать]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_undoVote"><![CDATA[Отменить голосование]]></string>
-    <string name="_bx_reputation_grid_column_value_aa_doVoteUp"><![CDATA[Проголосовать 'за']]></string>
-    <string name="_bx_reputation_grid_column_value_aa_undoVoteUp"><![CDATA[Отменить голосование 'за']]></string>
-    <string name="_bx_reputation_grid_column_value_aa_doVoteDown"><![CDATA[Проголосовать 'против']]></string>
-    <string name="_bx_reputation_grid_column_value_aa_undoVoteDown"><![CDATA[Отменить голосование 'против']]></string>
-    <string name="_bx_reputation_grid_column_title_points_active"><![CDATA[Баллы (активные)]]></string>
-    <string name="_bx_reputation_grid_column_title_points_passive"><![CDATA[Баллы (пассивные)]]></string>
-    <string name="_bx_reputation_grid_action_title_activate"><![CDATA[Активировать]]></string>
-    <string name="_bx_reputation_grid_action_title_deactivate"><![CDATA[Деактивировать]]></string>
-    <string name="_bx_reputation_grid_action_title_edit"><![CDATA[Редактировать]]></string>
+    <string name="_bx_reviews_menu_item_title_handlers"><![CDATA[Обработчики]]></string>
+    <string name="_bx_reviews_menu_item_title_levels"><![CDATA[Уровни]]></string>
+
+    <!-- Grids -->
+    <string name="_bx_reputation_grid_column_title_hdr_active"><![CDATA[Активные]]></string>
+    <string name="_bx_reputation_grid_column_title_hdr_alert_unit"><![CDATA[Раздел]]></string>
+    <string name="_bx_reputation_grid_column_title_hdr_alert_action"><![CDATA[Действие]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_added"><![CDATA[Добавить]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_post_common"><![CDATA[Написать пост]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_repost"><![CDATA[Сдулать репост]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_deleted"><![CDATA[Удалить]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_delete"><![CDATA[Удалить пост]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_delete_repost"><![CDATA[Удалить репост]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_commentPost"><![CDATA[Оставить комментарий]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_commentRemoved"><![CDATA[Удалить комментарий]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_doVote"><![CDATA[Проголосовать]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_undoVote"><![CDATA[Отменить голосование]]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_doVoteUp"><![CDATA[Проголосовать 'за']]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_undoVoteUp"><![CDATA[Отменить голосование 'за']]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_doVoteDown"><![CDATA[Проголосовать 'против']]></string>
+    <string name="_bx_reputation_grid_column_value_hdr_aa_undoVoteDown"><![CDATA[Отменить голосование 'против']]></string>
+    <string name="_bx_reputation_grid_column_title_hdr_points_active"><![CDATA[Баллы (активные)]]></string>
+    <string name="_bx_reputation_grid_column_title_hdr_points_passive"><![CDATA[Баллы (пассивные)]]></string>
+    <string name="_bx_reputation_grid_action_title_hdr_activate"><![CDATA[Активировать]]></string>
+    <string name="_bx_reputation_grid_action_title_hdr_deactivate"><![CDATA[Деактивировать]]></string>
+    <string name="_bx_reputation_grid_action_title_hdr_edit"><![CDATA[Редактировать]]></string>
+    <string name="_bx_reputation_grid_filter_item_title_hdr_select_one_filter1"><![CDATA[Все разделы]]></string>
+
+    <string name="_bx_reputation_grid_column_title_lvl_icon"><![CDATA[Иконка]]></string>
+    <string name="_bx_reputation_grid_column_title_lvl_title"><![CDATA[Название]]></string>
+    <string name="_bx_reputation_grid_column_title_lvl_points_in"><![CDATA[Баллы: Вкл]]></string>
+    <string name="_bx_reputation_grid_column_title_lvl_points_out"><![CDATA[Баллы: Выкл]]></string>
+    <string name="_bx_reputation_grid_column_title_lvl_date"><![CDATA[Дата]]></string>
+    <string name="_bx_reputation_grid_action_title_lvl_add"><![CDATA[Добавить уровень]]></string>
+    <string name="_bx_reputation_grid_action_title_lvl_edit"><![CDATA[Редактировать уровень]]></string>
+    <string name="_bx_reputation_grid_action_title_lvl_delete"><![CDATA[Удалить уровень]]></string>
+
     <string name="_bx_reputation_grid_action_err_perform"><![CDATA[Невозможно выполнить действие для выбранного(ых) элементов.]]></string>
-    <string name="_bx_reputation_grid_filter_item_title_select_one_filter1"><![CDATA[Все разделы]]></string>
 
+    <!-- Errors, Warnings, Messages -->
     <string name="_bx_reputation_err_cannot_perform"><![CDATA[Произошла ошибка. Пожалуйста, сообщите.]]></string>
 
+    <!-- Other -->
     <string name="_bx_reputation_txt_points"><![CDATA[баллов]]></string>
 </resources>