AntonLV 1 год назад
Родитель
Сommit
2202c41ce9
22 измененных файлов с 1778 добавлено и 0 удалено
  1. 68 0
      modules/boonex/reputation/classes/BxReputationAlertsResponse.php
  2. 68 0
      modules/boonex/reputation/classes/BxReputationConfig.php
  3. 107 0
      modules/boonex/reputation/classes/BxReputationDb.php
  4. 239 0
      modules/boonex/reputation/classes/BxReputationGridManage.php
  5. 695 0
      modules/boonex/reputation/classes/BxReputationGridhandlers.php
  6. 35 0
      modules/boonex/reputation/classes/BxReputationModule.php
  7. 38 0
      modules/boonex/reputation/classes/BxReputationStudioPage.php
  8. 36 0
      modules/boonex/reputation/classes/BxReputationTemplate.php
  9. 94 0
      modules/boonex/reputation/install/config.php
  10. 20 0
      modules/boonex/reputation/install/installer.php
  11. 51 0
      modules/boonex/reputation/install/langs/en.xml
  12. 22 0
      modules/boonex/reputation/install/sql/disable.sql
  13. 55 0
      modules/boonex/reputation/install/sql/enable.sql
  14. 58 0
      modules/boonex/reputation/install/sql/install.sql
  15. 17 0
      modules/boonex/reputation/install/sql/uninstall.sql
  16. 42 0
      modules/boonex/reputation/js/manage.js
  17. 18 0
      modules/boonex/reputation/request.php
  18. 5 0
      modules/boonex/reputation/template/block_summary.html
  19. 15 0
      modules/boonex/reputation/template/css/manage.css
  20. 25 0
      modules/boonex/reputation/template/images/icons/std-icon.svg
  21. 19 0
      modules/boonex/reputation/template/popup_handler.html
  22. 51 0
      modules/boonex/russian/data/langs/bx_reputation/ru.xml

+ 68 - 0
modules/boonex/reputation/classes/BxReputationAlertsResponse.php

@@ -0,0 +1,68 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defdroup    Reputation Reputation
+ * @indroup     UnaModules
+ *
+ * @{
+ */
+
+class BxReputationAlertsResponse extends BxBaseModNotificationsResponse
+{
+    public function __construct()
+    {
+        $this->_sModule = 'bx_reputation';
+
+        parent::__construct();
+    }
+
+    public function response($oAlert)
+    {
+        parent::response($oAlert);
+
+        $sMethod = '_process' . bx_gen_method_name($oAlert->sUnit . '_' . $oAlert->sAction);           	
+        if(method_exists($this, $sMethod))
+            return $this->$sMethod($oAlert);
+
+        /**
+         * @hooks
+         * @hookdef hook-bx_reputation-before_register_alert 'bx_reputation', 'before_register_alert' - hook to override alert (hook) before processing
+         * - $unit_name - equals `bx_reputation`
+         * - $action - equals `before_register_alert`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `unit` - [string] alert (hook) unit
+         *      - `action` - [string] alert (hook) action
+         *      - `alert` - [array] by ref, an instance of alert (hook), @see BxDolAlerts, can be overridden in hook processing        
+         * @hook @ref hook-bx_reputation-before_register_alert
+         */
+        bx_alert($this->_oModule->getName(), 'before_register_alert', 0, 0, [
+            'unit' => $oAlert->sUnit,
+            'action' => $oAlert->sAction,
+            'alert' => &$oAlert,
+        ]);
+
+        $aHandler = $this->_oModule->_oConfig->getHandlers($oAlert->sUnit . '_' . $oAlert->sAction);
+        if(empty($aHandler) || !is_array($aHandler))
+            return;
+
+        $iOwnerId = $oAlert->iSender;
+        $iObjectOwnerId = $this->_getObjectOwnerId($oAlert->aExtras);
+
+        if(($iPoints = (int)$aHandler['points_active']) != 0)
+            $this->_oModule->_oDb->insertProfile($iOwnerId, $iPoints);
+
+        if(($iPoints = (int)$aHandler['points_passive']) != 0 && $iOwnerId != $iObjectOwnerId)
+            $this->_oModule->_oDb->insertProfile($iObjectOwnerId, $iPoints);
+    }
+
+    protected function _processProfileDelete($oAlert)
+    {
+        $this->_oModule->_oDb->deleteProfile($oAlert->iObject);
+    }
+}
+
+/** @} */

+ 68 - 0
modules/boonex/reputation/classes/BxReputationConfig.php

@@ -0,0 +1,68 @@
+<?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 BxReputationConfig extends BxBaseModNotificationsConfig
+{
+    protected $_oDb;
+
+    public function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->CNF = [
+            // database tables
+            'TABLE_PROFILES' => $aModule['db_prefix'] . 'profiles',
+
+            // database fields
+            'FIELD_' => '',
+
+            // some params
+            'PARAM_' => 'bx_reputation_',
+
+            // objects
+            'OBJECT_FORM_HANDLER' => 'bx_reputation_handler',
+            'OBJECT_FORM_HANDLER_DISPLAY_EDIT' => 'bx_reputation_handler_edit',
+            'OBJECT_GRID_MANAGE' => 'bx_reputation_manage',
+
+            // 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',
+                'err_cannot_perform' => '_bx_reputation_err_cannot_perform',
+            ]
+        ];
+
+        $this->_aJsClasses = [
+            'manage' => 'BxReputationManage',
+        ];
+
+        $this->_aJsObjects = [
+            'manage' => 'oBxReputationManage',
+        ];
+
+        $sHtmlPrefix = str_replace('_', '-', $this->_sName);
+        $this->_aHtmlIds = [
+            'handler_popup' => $sHtmlPrefix . '-handler-popup',
+        ];
+
+        $this->_aHandlerFields = ['group', 'type', 'alert_unit', 'alert_action', 'points_active', 'points_passive'];
+        $this->_aHandlerDescriptor = [];
+        $this->_sHandlersMethod = 'get_reputation_data';
+    }
+
+    public function init(&$oDb)
+    {
+        parent::init($oDb);
+    }
+}
+
+/** @} */

+ 107 - 0
modules/boonex/reputation/classes/BxReputationDb.php

@@ -0,0 +1,107 @@
+<?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 BxReputationDb extends BxBaseModNotificationsDb
+{
+    protected $_oConfig;
+
+    public function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+
+        $this->_oConfig = $oConfig;
+    }
+
+    public function insertEvent($aParamsSet)
+    {
+        return 0;
+    }
+
+    public function updateEvent($aParamsSet, $aParamsWhere)
+    {
+        return false;
+    }
+
+    public function deleteEvent($aParams, $sWhereAddon = "")
+    {
+        return false;
+    }
+
+    public function getEvents($aParams)
+    {
+        return [];
+    }
+
+    public function insertProfile($iId, $iPoints)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return $this->query("INSERT INTO `" . $CNF['TABLE_PROFILES'] . "` (`id`, `points`) VALUES (:id, :points) ON DUPLICATE KEY UPDATE `points`=`points`+:points", [
+            'id' => $iId,
+            'points' => $iPoints
+        ]);
+    }
+
+    public function deleteProfile($iId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return $this->query("DELETE FROM `" . $CNF['TABLE_PROFILES'] . "` WHERE `id`=:id", ['id' => $iId]);
+    }
+    
+    public function getProfile($aParams = [])
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+        $sSelectClause = '*';
+        $sWhereClause = '';
+        
+        switch($aParams['sample']) {
+            case 'id':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'id' => $aParams['id']
+                ];
+
+                $sWhereClause = "AND `id` = :id";
+                break;
+        }
+        
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $CNF['TABLE_PROFILES'] . "` WHERE 1 " . $sWhereClause;
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+    
+    public function getHandlers($aParams = []) 
+    {
+        $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+        $sSelectClause = '*';
+        $sWhereClause = '';
+
+        if(!empty($aParams))
+            switch($aParams['type']) {
+                case 'alert_units_list':
+                    $aMethod['name'] = 'getColumn';
+                    $aMethod['params'][1] = 'alert_unit';
+
+                    $sSelectClause = 'DISTINCT `alert_unit`';
+                    break;
+
+                default:
+                    return parent::getHandlers($aParams);
+            }
+
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `{$this->_sTableHandlers}` WHERE 1 " . $sWhereClause;
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+}
+
+/** @} */

+ 239 - 0
modules/boonex/reputation/classes/BxReputationGridManage.php

@@ -0,0 +1,239 @@
+<?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 BxReputationGridManage extends BxTemplGrid
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    protected $_sFilter;
+    protected $_sFilter1Name;
+    protected $_sFilter1Value;
+    protected $_aFilter1Values;
+
+    protected $_sParamsDivider;
+    
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_reputation';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_aQueryReset = array($this->_aOptions['filter_get'], $this->_aOptions['paginate_get_start'], $this->_aOptions['paginate_get_per_page']);
+
+        $this->_sParamsDivider = '#-#';
+
+        $this->_sFilter = '';
+        if(($sFilter = $this->_getFilterValue()))
+            $this->_sFilter = $sFilter;
+
+        $this->_sFilter1Name = 'filter1';
+        $this->_aFilter1Values = [];
+
+        $aUnits = $this->_oModule->_oDb->getHandlers(['type' => 'alert_units_list']);
+        if(!empty($aUnits) && is_array($aUnits))
+            foreach($aUnits as $sUnit)
+                $this->_aFilter1Values[$sUnit] = _t('_' . $sUnit);
+
+    	$sFilter1 = bx_get($this->_sFilter1Name);
+        if(!empty($sFilter1)) {
+            $this->_sFilter1Value = bx_process_input($sFilter1);
+            $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;
+    }   
+
+    public function performActionEdit()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sAction = 'edit';
+
+        $aIds = $this->_getIds();
+        if($aIds === false)
+            return $this->_getActionResult([]);
+
+        $iHandler = array_shift($aIds);
+        $aHandler = $this->_oModule->_oDb->getHandlers(['type' => 'by_id', 'value' => $iHandler]);
+        if(!is_array($aHandler) || empty($aHandler))
+            return $this->_getActionResult([]);
+
+        $sForm = $CNF['OBJECT_FORM_HANDLER_DISPLAY_EDIT'];
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_HANDLER'], $CNF['OBJECT_FORM_HANDLER_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,
+            $this->_aOptions['filter_get'] => $this->_sFilter,
+            'id' => $iHandler
+        ]));
+
+        $oForm->initChecker($aHandler);
+        if($oForm->isSubmittedAndValid()) {
+            if(!$oForm->update($iHandler))
+                return $this->_getActionResult(['msg' => _t($CNF['T']['err_cannot_perform'])]);
+
+            return $this->_bIsApi ? [] : echoJson(['grid' => $this->getCode(false), 'blink' => $iHandler]);    
+        }
+
+        if($this->_bIsApi)
+            return $this->getFormBlockAPI($oForm, $sAction, $iHandler);
+
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->_oModule->_oConfig->getHtmlIds('handler_popup'), _t($CNF['T']['popup_title_handler_edit']), $this->_oModule->_oTemplate->parseHtmlByName('popup_handler.html', [
+            'form_id' => $oForm->getId(),
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        return echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+    }
+
+    public function performActionActivate()
+    {
+    	$this->_performActionEnable(true);
+    }
+
+    public function performActionDeactivate()
+    {
+    	$this->_performActionEnable(false);
+    }
+
+    protected function _isCheckboxDisabled($aRow)
+    {
+        return false;
+    }
+
+    protected function _getCellAlertUnit($mixedValue, $sKey, $aField, $aRow)
+    {
+        return self::_getCellDefault(_t('_' . $mixedValue), $sKey, $aField, $aRow);
+    }
+    
+    protected function _getCellAlertAction($mixedValue, $sKey, $aField, $aRow)
+    {
+        return self::_getCellDefault(_t('_bx_reputation_grid_column_value_aa_' . $mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getFilterControls()
+    {
+        parent::_getFilterControls();
+
+        $sContent = $this->_getFilterSelectOne($this->_sFilter1Name, $this->_sFilter1Value, $this->_aFilter1Values);
+        $sContent .= $this->_getSearchInput();
+        return $sContent;
+    }
+
+    protected function _getFilterSelectOne($sFilterName, $sFilterValue, $aFilterValues, $bAddSelectOne = true)
+    {
+        if(empty($sFilterName) || empty($aFilterValues))
+            return '';
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        $sJsObject = $this->_oModule->_oConfig->getJsObject('manage');
+
+        $aInputValues = [];
+        if($bAddSelectOne)
+            $aInputValues[''] = _t($CNF['T']['filter_item_select_one_' . $sFilterName]);
+
+        foreach($aFilterValues as $sKey => $sValue)
+            $aInputValues[$sKey] = _t($sValue);
+
+        $aInputModules = [
+            'type' => 'select',
+            'name' => $sFilterName,
+            'attrs' => [
+                'id' => 'bx-grid-' . $sFilterName . '-' . $this->_sObject,
+                'onChange' => 'javascript:' . $sJsObject . '.onChangeFilter(this)'
+            ],
+            'value' => $sFilterValue,
+            'values' => $aInputValues
+        ];
+
+        $oForm = new BxTemplFormView([]);
+        return $oForm->genRow($aInputModules);
+    }
+
+    protected function _getSearchInput()
+    {
+        $sJsObject = $this->_oModule->_oConfig->getJsObject('manage_tools');
+
+        $aInputSearch = [
+            'type' => 'text',
+            'name' => 'search',
+            'attrs' => [
+                'id' => 'bx-grid-search-' . $this->_sObject,
+                'onKeyup' => 'javascript:$(this).off(\'keyup focusout\'); ' . $sJsObject . '.onChangeFilter(this)',
+                'onBlur' => 'javascript:' . $sJsObject . '.onChangeFilter(this)',
+            ]
+        ];
+
+        $oForm = new BxTemplFormView([]);
+        return $oForm->genRow($aInputSearch);
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        if(strpos($sFilter, $this->_sParamsDivider) !== false)
+            list($this->_sFilter1Value, $sFilter) = explode($this->_sParamsDivider, $sFilter);
+
+    	if(!empty($this->_sFilter1Value))
+            $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString(" AND `alert_unit`=?", $this->_sFilter1Value);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+
+    protected function _performActionEnable($isChecked)
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	$aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds))
+            return $this->_getActionResult([]);
+
+        $iAffected = 0;
+        $aIdsAffected = array();
+        foreach($aIds as $iId)
+            if($this->_enable($iId, $isChecked)) {
+                $aIdsAffected[] = $iId;
+                $iAffected++;
+            }
+
+        if(!$iAffected)
+            return $this->_getActionResult(['msg' => _t($CNF['T']['grid_action_err_perform'])]);
+
+        return $this->_bIsApi ? [] : echoJson(['grid' => $this->getCode(false), 'blink' => $aIdsAffected]);
+    }
+
+    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;
+    }
+}
+
+/** @} */

+ 695 - 0
modules/boonex/reputation/classes/BxReputationGridhandlers.php

@@ -0,0 +1,695 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Forum Forum
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+define('BX_FORUM_FILTER_STATUS_RESOLVED', 'yes');
+define('BX_FORUM_FILTER_STATUS_UNRESOLVED', 'no');
+
+define('BX_FORUM_FILTER_ORDER_RECENT', 'latest');
+define('BX_FORUM_FILTER_ORDER_NEW', 'new');
+define('BX_FORUM_FILTER_ORDER_TOP', 'top');
+define('BX_FORUM_FILTER_ORDER_UPDATED', 'updated');
+define('BX_FORUM_FILTER_ORDER_POPULAR', 'popular');
+define('BX_FORUM_FILTER_ORDER_FEATURED', 'featured');
+define('BX_FORUM_FILTER_ORDER_FAVORITE', 'favorite');
+define('BX_FORUM_FILTER_ORDER_PARTAKEN', 'partaken');
+
+class BxForumGrid extends BxTemplGrid
+{
+    protected $_oModule;
+    protected $_sDefaultSource; 
+    
+    protected $_sFilter1Name;
+    protected $_sFilter1Value;
+    protected $_aFilter1Values;
+    
+    protected $_sFilter2Name;
+    protected $_sFilter2Value;
+    protected $_aFilter2Values;
+    
+    protected $_sFilter3Name;
+    protected $_sFilter3Value;
+    protected $_aFilter3Values;
+    
+    protected $_sParamsDivider;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        $this->_oModule = BxDolModule::getInstance('bx_forum');
+
+        parent::__construct ($aOptions, $oTemplate);
+        
+        $this->_sFilter1Name = 'filter1';
+        $this->_aFilter1Values = [
+            '' => _t('_bx_forum_grid_filter_resolved_all'),
+            BX_FORUM_FILTER_STATUS_RESOLVED => _t('_bx_forum_grid_filter_resolved_resolved'),
+            BX_FORUM_FILTER_STATUS_UNRESOLVED => _t('_bx_forum_grid_filter_resolved_unresolved'),
+        ];
+
+        if(($sFilter1 = bx_get($this->_sFilter1Name))) {
+            $this->_sFilter1Value = bx_process_input($sFilter1);
+            $this->_aQueryAppend['filter1'] = $this->_sFilter1Value;
+        }
+
+        $this->_sFilter2Name = 'filter2';
+        $this->_aFilter2Values = [];
+
+        $aBadges = BxDolBadges::getInstance()->getData(['type' => 'by_module', 'module' => $this->_oModule->_aModule['name']]);
+        if(is_array($aBadges) && count($aBadges) > 0) {
+            $this->_aFilter2Values[''] = _t('_bx_forum_grid_filter_badges_all');
+
+            foreach($aBadges as $aBadge)
+                $this->_aFilter2Values[$aBadge['id']] = $aBadge['text'];
+        }
+
+        if(($sFilter2 = bx_get($this->_sFilter2Name))) {
+            $this->_sFilter2Value = bx_process_input($sFilter2);
+            $this->_aQueryAppend['filter2'] = $this->_sFilter2Value;
+        }
+        
+        $this->_sFilter3Name = 'filter3';
+        $this->_aFilter3Values = [
+            BX_FORUM_FILTER_ORDER_RECENT => _t('_bx_forum_grid_filter_order_updated'),
+            BX_FORUM_FILTER_ORDER_NEW => _t('_bx_forum_grid_filter_order_new'),
+            BX_FORUM_FILTER_ORDER_TOP => _t('_bx_forum_grid_filter_order_top'),
+            BX_FORUM_FILTER_ORDER_POPULAR => _t('_bx_forum_grid_filter_order_popular'),
+            BX_FORUM_FILTER_ORDER_FEATURED => _t('_bx_forum_grid_filter_order_featured'),
+            BX_FORUM_FILTER_ORDER_FAVORITE => _t('_bx_forum_grid_filter_order_favorite'),
+            BX_FORUM_FILTER_ORDER_PARTAKEN => _t('_bx_forum_grid_filter_order_partaken'),
+        ];
+        
+        if(($sFilter3 = bx_get($this->_sFilter3Name))) {
+            $this->_sFilter1Value = bx_process_input($sFilter3);
+            $this->_aQueryAppend['filter3'] = $this->_sFilter1Value;
+        }
+
+        $this->_sParamsDivider = '#-#';
+
+        $this->_sDefaultSortingOrder = 'DESC';
+        $this->_sDefaultSource = $this->_aOptions['source'];
+    }
+
+    public function setBrowseParams($aParams)
+    {
+        if(isset($aParams['ajax_paginate'])) {
+            if(!$aParams['ajax_paginate']) {
+                list($sPageLink, $aPageParams) = bx_get_base_url_inline();
+
+                $aPageParams[$this->_aOptions['paginate_get_start']] = '{start}';
+                $this->_aOptions['paginate_url'] = BxDolPermalinks::getInstance()->permalink(bx_append_url_params($sPageLink, $aPageParams, true, ['{start}']));
+            }
+
+            unset($aParams['ajax_paginate']);
+        }
+        
+        if ($aParams['type'])
+            $this->_sFilter3Value = $aParams['type'];
+
+        parent::setBrowseParams($aParams);
+
+    	$sField = 'added';
+    	if(!empty($this->_aBrowseParams['type']))
+            switch($this->_aBrowseParams['type']) {
+                case 'new':
+                case 'index':
+                case 'author':
+                case 'favorite':
+                case 'category':
+                    $sField = 'added';
+                    break;
+
+                case 'featured':
+                    $sField = 'featured';
+                    break;
+
+                case 'latest':
+                    $sField = 'lr_timestamp';
+                    break;
+
+                case 'popular':
+                    $sField = 'views';
+                    break;
+            }
+
+        $this->_aOptions['field_order'] = $sField;
+        $this->_aOptions['paginate_per_page'] = !empty($this->_aBrowseParams['per_page']) ? (int)$this->_aBrowseParams['per_page'] : (int)$this->_oModule->_oDb->getParam('bx_forum_per_page_browse');
+    }
+
+    public function getCode ($isDisplayHeader = true)
+    {
+        $sCode = parent::getCode($isDisplayHeader);
+        if(empty($sCode))
+            return '';
+
+    	return $this->_oModule->_oTemplate->parseHtmlByName('units.html', array(
+            'code' => $sCode,
+            'js_code' => $this->_oModule->_oTemplate->getJsCode('main')
+        ));
+    }
+
+    protected function _getActionAdd ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if($this->_oModule->checkAllowedAdd() !== CHECK_ACTION_RESULT_ALLOWED)
+            return '';
+
+        $sUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $this->_oModule->_oConfig->CNF['URI_ADD_ENTRY']));
+
+        unset($a['attr']['bx_grid_action_independent']);
+        $a['attr'] = array_merge($a['attr'], array(
+    		"onclick" => "window.open('" . $sUrl . "','_self');"
+    	));
+
+        return parent::_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+
+    protected function _getRowHead ()
+    {
+    	return array();
+    }
+
+    protected function _getCellDefault ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $aField['attr_cell']['class'] = 'bx-grid-table-cell-' . $sKey;
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellAuthor($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$mixedValue = $this->_oModule->_oTemplate->getEntryAuthor($aRow);
+
+        return self::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+    
+    protected function _getCellCategory($mixedValue, $sKey, $aField, $aRow)
+    {
+        $sIcon = $this->_oModule->_oTemplate->parseHtmlByName('default_category.html', []);
+        $o = BxDolCategory::getObjectInstance('bx_forum_cats');
+        
+        $aCategoryData = $this->_oModule->_oDb->getCategories(array('type' => 'by_category', 'category' => $aRow['cat']));
+
+        if(isset($aCategoryData['icon']))
+            $sIcon = $this->_oTemplate->getImage($aCategoryData['icon'], array('class' => 'sys-icon'));
+        
+        $mixedValue = $this->_oModule->_oTemplate->parseHtmlByName('thumb.html', ['icon' => $sIcon, 'title' => $o->getCategoryTitle($aRow['cat'])]);
+        return self::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+    
+    protected function _getCellRating($mixedValue, $sKey, $aField, $aRow)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $oObject = isset($CNF['OBJECT_SCORES']) ? BxDolScore::getObjectInstance($CNF['OBJECT_SCORES'], $aRow[$CNF['FIELD_ID']], true, $this->_oModule->_oTemplate) : null;
+        $mixedValue = $oObject ? $oObject->getElementInline() : '';
+        
+        return self::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+    
+    protected function _getCellParticipants($mixedValue, $sKey, $aField, $aRow)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+    	$oObject = isset($CNF['OBJECT_COMMENTS']) ? BxDolCmts::getObjectInstance($CNF['OBJECT_COMMENTS'], $aRow[$CNF['FIELD_ID']]) : null;
+      
+        $mixedValue = $oObject ? $oObject->getCounter(['show_counter_empty' => true, 'show_counter' => false, 'show_counter_style' => 'simple', 'dynamic_mode' => true, 'show_icon' => false, 'caption_empty' => '']) : '';
+
+        return self::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellText($mixedValue, $sKey, $aField, $aRow)
+    {
+        $mixedValue = $this->_oModule->_oTemplate->getEntryPreview($aRow, ['show_meta_counters' => false, 'show_meta_reply' => false]);
+
+        return self::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+
+        $this->_oModule->_oTemplate->addJs(array('main.js'));
+        $this->_oModule->_oTemplate->addCss(array('main.css', 'main-media-phone.css', 'main-media-tablet.css', 'main-media-desktop.css'));
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        
+        if(strpos($sFilter, $this->_sParamsDivider) !== false)
+            list($this->_sFilter1Value, $this->_sFilter2Value, $this->_sFilter3Value, $sFilter) = explode($this->_sParamsDivider, $sFilter);
+
+        $bAuthor = false;
+        $iAuthorId = bx_get_logged_profile_id();
+    	if(!empty($this->_aBrowseParams['author']) && ($oProfileAuthor = BxDolProfile::getInstance((int)$this->_aBrowseParams['author'])) !== false) {
+            $bAuthor = true;
+            $iAuthorId = $oProfileAuthor->id();
+    	}
+
+        // featured
+        if ($this->_sFilter3Value == BX_FORUM_FILTER_ORDER_FEATURED) {
+            $this->_aBrowseParams['where'] = ['fld' => 'featured', 'val' => 0, 'opr' => '<>'];
+        }
+        
+        // partaken
+        if ($this->_sFilter3Value == BX_FORUM_FILTER_ORDER_PARTAKEN) {
+            $this->_aBrowseParams['select'] = [
+                'tbla' => 'tco', 
+                'fld' => 'cmt_author_id',
+            ];
+
+            $this->_aBrowseParams['join'] = [
+                'tp' => 'LEFT',
+                'tbl1' => 'bx_forum_cmts',
+                'tbl1a' => 'tco',
+                'fld1' => 'cmt_object_id',
+                'tbl2' => 'bx_forum_discussions',
+                'fld2' => 'id'
+            ];
+
+            $this->_aBrowseParams['where'] = ['grp' => true, 'opr' => 'OR', 'cnds' => [[
+                    'tbl' => 'bx_forum_discussions',
+                    'fld' => 'author',
+                    'val' => $iAuthorId, 
+                    'opr' => '='
+                ], [
+                    'tbl' => 'tco', 
+                    'fld' => 'cmt_author_id', 
+                    'val' => $iAuthorId, 
+                    'opr' => '='
+                ]
+            ]];
+
+            $this->_aBrowseParams['group_by'] = [
+                'tbl' => 'bx_forum_discussions', 
+                'fld' => 'id',
+            ];
+        }
+
+        //FAVORITE
+        if ($this->_sFilter3Value == BX_FORUM_FILTER_ORDER_FAVORITE){
+            $oProfile = BxDolProfile:: getInstance(bx_process_input(bx_get('profile_id'), BX_DATA_INT));
+            if(!$oProfile)
+                $oProfile = BxDolProfile::getInstance();
+            if(!$oProfile)
+                return '';
+
+            $iProfileAuthor = $oProfile->id();
+            $oFavorite = $this->_oModule->getObjectFavorite();
+            if(!$oFavorite->isPublic() && $iProfileAuthor != bx_get_logged_profile_id())
+                return '';
+            
+            $aConditions = $oFavorite->getConditionsTrack($CNF['TABLE_ENTRIES'], 'id', $iProfileAuthor);
+            if(empty($aConditions) || !is_array($aConditions)) 
+                return '';
+
+            $aJoinGroup = array('grp' => true, 'cnds' => array());
+            if(!empty($aConditions['join']))
+                foreach($aConditions['join'] as $aCondition)
+                    $aJoinGroup['cnds'][] = array(
+                        'tp' => $aCondition['type'],
+                        'tbl1' => $aCondition['table'],
+                        'fld1' => $aCondition['onField'],
+                        'tbl2' => $aCondition['mainTable'],
+                        'fld2' => $aCondition['mainField']
+                    );
+
+            $aWhereGroup = array('grp' => true, 'opr' => 'AND', 'cnds' => array());
+            if(!empty($aConditions['restriction']))
+                foreach($aConditions['restriction'] as $aCondition)
+                    $aWhereGroup['cnds'][] = array(
+                        'tbl' => (!empty($aCondition['table']) ? $aCondition['table'] : ''), 
+                        'fld' => $aCondition['field'], 
+                        'val' => $aCondition['value'], 
+                        'opr' => $aCondition['operator']
+                    );
+            
+            $this->_aBrowseParams['author'] = $iProfileAuthor; 
+            $this->_aBrowseParams['join'] = $aJoinGroup;
+            $this->_aBrowseParams['where'] = $aWhereGroup; 
+        }
+        
+    	$sSelectClause = $sJoinClause = $sWhereClause = $sGroupByClause = '';
+
+    	//--- Check status
+    	$sWhereClause .= " AND `{$CNF['TABLE_ENTRIES']}`.`" . $CNF['FIELD_STATUS'] . "`='active'";
+    	$sWhereClause .= " AND `{$CNF['TABLE_ENTRIES']}`.`" . $CNF['FIELD_STATUS_ADMIN'] . "`='active'";
+
+    	//--- Check privacy
+        $bIncludeContexts = isset($this->_aBrowseParams['include_contexts']) && $this->_aBrowseParams['include_contexts'];
+
+        $sPrivacy = $CNF['OBJECT_PRIVACY_VIEW'];
+        $oPrivacy = BxDolPrivacy::getObjectInstance($sPrivacy);
+        $aCondition = $oPrivacy ? $oPrivacy->{'getContentPublic' . ($bIncludeContexts ? 'AndInContext' : '') . 'AsSQLPart'}($bAuthor ? $iAuthorId : 0) : [];
+        if(isset($aCondition['join']))
+            $sJoinClause .= $aCondition['join'];
+        if(isset($aCondition['where']))
+            $sWhereClause .= $aCondition['where'];
+
+        //--- Check browse params
+        if(!empty($this->_aBrowseParams['select']) && is_array($this->_aBrowseParams['select'])) {
+            $sSelectClauseBrowse = $this->_getSqlSelectFromGroup($this->_aBrowseParams['select']);
+            if(!empty($sSelectClauseBrowse))
+                $sSelectClause .= ", " . $sSelectClauseBrowse;
+        }
+
+        if(!empty($this->_aBrowseParams['join']) && is_array($this->_aBrowseParams['join'])) {
+            $sJoinClauseBrowse = $this->_getSqlJoinFromGroup($this->_aBrowseParams['join']);
+            if(!empty($sJoinClauseBrowse))
+                $sJoinClause .= " " . $sJoinClauseBrowse;
+        }
+
+        if(!empty($this->_aBrowseParams['where']) && is_array($this->_aBrowseParams['where'])) {
+            $sWhereClauseBrowse = $this->_getSqlWhereFromGroup($this->_aBrowseParams['where']);
+            if(!empty($sWhereClauseBrowse))
+                $sWhereClause .= " AND " . $sWhereClauseBrowse;
+        }
+
+        if(!empty($this->_aBrowseParams['group_by']) && is_array($this->_aBrowseParams['group_by'])) {
+            $sGroupByClauseBrowse = $this->_getSqlGroupByFromGroup($this->_aBrowseParams['group_by']);
+            if(!empty($sGroupByClauseBrowse))
+                $sGroupByClause .= " GROUP BY " . $sGroupByClauseBrowse;
+        }
+        
+        $sFilterSql = "";
+
+        // filter by resolved status
+        if(!empty($this->_sFilter1Value))
+            $sWhereClause .= " AND `" . $CNF['TABLE_ENTRIES'] . "`.`" . $CNF['FIELD_RESOLVABLE'] . "` = 1 AND `" . $CNF['TABLE_ENTRIES'] . "`.`" . $CNF['FIELD_RESOLVE'] . "` = " . ($this->_sFilter1Value == BX_FORUM_FILTER_STATUS_RESOLVED ? 1 : 0);
+
+        // filter out posts of suspended members
+        $sJoinClause .= " INNER JOIN `sys_profiles` AS `p` ON (`p`.`id` = `{$CNF['TABLE_ENTRIES']}`.`{$CNF['FIELD_AUTHOR']}` AND `p`.`status` = 'active') ";
+
+        // filter out posts by content filter
+        $oCf = BxDolContentFilter::getInstance();
+        if($oCf->isEnabled())
+            $sWhereClause .= $oCf->getSQLParts ($CNF['TABLE_ENTRIES'], $CNF['FIELD_CF']);
+
+        // filter by badges
+        if(isset($this->_sFilter2Value) && $this->_sFilter2Value != '') {
+            $aObjects = BxDolBadges::getInstance()->getData(['type' => 'by_module&badge', 'badge_id' => $this->_sFilter2Value, 'module' => $this->_oModule->_aModule['name']]);
+            $sWhereClause .= " AND `" . $CNF['TABLE_ENTRIES'] . "`.`" . $CNF['FIELD_ID'] . "` IN (" . implode(',', $aObjects ) . ")";
+        }
+
+        $this->_aOptions['source'] = sprintf($this->_sDefaultSource, $sSelectClause, $sJoinClause, $sWhereClause, $sGroupByClause);
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+
+    protected function _getDataSqlOrderClause($sOrderByFilter, $sOrderField, $sOrderDir, $bFieldsOnly = false)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(isset($this->_sFilter3Value) && $this->_sFilter3Value != ''){
+            $sOrderField = $this->_aBrowseParams['type'] = $this->_sFilter3Value;
+            if (!empty($this->_aBrowseParams['type']) && $this->_aBrowseParams['type'] != 'partaken'){
+                $this->_aBrowseParams['type'] = $this->_aBrowseParams['type'] = $this->_sFilter3Value;
+            }
+        }
+        
+    	$sOrder = parent::_getDataSqlOrderClause($sOrderByFilter, $sOrderField, $sOrderDir, true);
+                
+        if(!empty($this->_aBrowseParams['type']))
+            switch($this->_aBrowseParams['type']) {
+                case 'top':
+                    $aPartsUp = $aPartsDown = array(0);
+                    if(($oVote = BxDolVote::getObjectInstance($CNF['OBJECT_VOTES'], 0, false)) !== false && $oVote->isEnabled()) {
+                        $aVote = $oVote->getSystemInfo();
+                        if(!empty($aVote['trigger_table']) && !empty($aVote['trigger_field_count']))
+                            $aPartsUp[] = '`' . $aVote['trigger_table'] . '`.`' . $aVote['trigger_field_count'] . '`';
+                    }
+
+                    if(($oReaction = BxDolVote::getObjectInstance($CNF['OBJECT_REACTIONS'], 0, false)) !== false && $oReaction->isEnabled()) {
+                        $aReaction = $oReaction->getSystemInfo();
+                        if(!empty($aReaction['trigger_table']) && !empty($aReaction['trigger_field_count']))
+                            $aPartsUp[] = '`' . $aReaction['trigger_table'] . '`.`' . $aReaction['trigger_field_count'] . '`';
+                    }
+
+                    if(($oScore = BxDolScore::getObjectInstance($CNF['OBJECT_SCORES'], 0, false)) !== false && $oScore->isEnabled()) {
+                        $aScore = $oScore->getSystemInfo();
+                        if(!empty($aScore['trigger_table']) && !empty($aScore['trigger_field_cup']) && !empty($aScore['trigger_field_cdown'])) {
+                            $aPartsUp[] = '`' . $aScore['trigger_table'] . '`.`' . $aScore['trigger_field_cup'] . '`';
+                            $aPartsDown[] = '`' . $aScore['trigger_table'] . '`.`' . $aScore['trigger_field_cdown'] . '`';
+                        }
+                    }
+
+                    $sOrder = pow(10, 8) . ' * ((' . implode(' + ', $aPartsUp) . ') - (' . implode(' + ', $aPartsDown) . ')) / (UNIX_TIMESTAMP() - `' . $this->_aOptions['table'] . '`.`' . $CNF['FIELD_ADDED'] . '`) ' . $this->_sDefaultSortingOrder;
+                    break;
+
+                case 'partaken':
+                    $sOrder = 'MAX(IF(`tco`.`cmt_time`, `tco`.`cmt_time`, `' . $CNF['TABLE_ENTRIES'] . '`.`' . $CNF['FIELD_ADDED'] . '`)) ' . $this->_sDefaultSortingOrder;
+                    break;
+            }           
+
+    	return " ORDER BY `" . $CNF['TABLE_ENTRIES'] . "`.`" . $CNF['FIELD_STICK'] . "` DESC, " . $sOrder;
+    }
+
+    protected function _getSqlSelectFromGroup($aGrp)
+    {
+        if(!isset($aGrp['grp']) || (bool)$aGrp['grp'] !== true)
+            return $this->_getSqlSelectFromCondition($aGrp);
+
+        $sResult = "";
+        if(empty($aGrp['cnds']) || !is_array($aGrp['cnds']))
+            return $sResult;
+
+        $sOprGrp = ", ";
+        foreach($aGrp['cnds'] as $aCnd) {
+            $sResultCnd = $this->_getSqlSelectFromCondition($aCnd);
+            if(!empty($sResultCnd))
+                $sResult .= $sOprGrp . $sResultCnd;
+        }
+
+        return trim($sResult, $sOprGrp);
+    }
+
+    protected function _getSqlSelectFromCondition($aCnd)
+    {
+    	$sResult = "";
+    	if(!isset($aCnd['fld']))
+            return $sResult;
+
+        $sResult = "`" . $aCnd['fld'] . "`";
+        if(!empty($aCnd['tbla']))
+            $sResult = "`" . $aCnd['tbla'] . "`." . $sResult;
+        else if(!empty($aCnd['tbl']))
+            $sResult = "`" . $aCnd['tbl'] . "`." . $sResult;
+
+        return $sResult;
+    }
+
+    protected function _getSqlJoinFromGroup($aGrp)
+    {
+        if(!isset($aGrp['grp']) || (bool)$aGrp['grp'] !== true)
+            return $this->_getSqlJoinFromCondition($aGrp);
+
+        $sResult = "";
+        if(empty($aGrp['cnds']) || !is_array($aGrp['cnds']))
+            return $sResult;
+
+        $sOprGrp = " ";
+        foreach($aGrp['cnds'] as $aCnd) {
+            $sResultCnd = $this->_getSqlJoinFromCondition($aCnd);
+            if(!empty($sResultCnd))
+                $sResult .= $sOprGrp . $sResultCnd;
+        }
+
+        return trim($sResult, $sOprGrp);
+    }
+
+    protected function _getSqlJoinFromCondition($aCnd)
+    {
+    	$sResult = "";
+    	if(!isset($aCnd['tp'], $aCnd['tbl1'], $aCnd['fld1'], $aCnd['fld2']))
+            return $sResult;
+
+        $sTbl1A = $aCnd['tbl1'];
+        if(!empty($aCnd['tbl1a']))
+            $sTbl1A = $aCnd['tbl1a'];
+
+        $sField1 = "`" . $sTbl1A . "`.`" . $aCnd['fld1'] . "`";
+
+        $sField2 = "`" . $aCnd['fld2'] . "`";
+        if(!empty($aCnd['tbl2a']))
+            $sField2 = "`" . $aCnd['tbl2a'] . "`." . $sField2;
+        else if(!empty($aCnd['tbl2']))
+            $sField2 = "`" . $aCnd['tbl2'] . "`." . $sField2;
+
+        return $aCnd['tp'] . " JOIN `" . $aCnd['tbl1'] . "` AS `" . $sTbl1A . "` ON (" . $sField1 . " = " . $sField2 . ")";
+    }
+
+    protected function _getSqlWhereFromGroup($aGrp)
+    {
+        if(!isset($aGrp['grp']) || (bool)$aGrp['grp'] !== true)
+            return $this->_getSqlWhereFromCondition($aGrp);
+
+        $sResult = "";
+        if(!isset($aGrp['opr'], $aGrp['cnds']) || empty($aGrp['cnds']) || !is_array($aGrp['cnds']))
+            return $sResult;
+
+        $sOprGrp = " " . $aGrp['opr'] . " ";
+        foreach($aGrp['cnds'] as $aCnd) {
+            $sMethod = '_getSqlWhereFrom' . (isset($aGrp['grp']) && (bool)$aGrp['grp'] === true ? 'Group' : 'Condition');
+            $sResultCnd = $this->$sMethod($aCnd);
+            if(!empty($sResultCnd))
+                $sResult .= $sOprGrp . $sResultCnd;
+        }
+
+        $sResult = trim($sResult, $sOprGrp);
+        if(!empty($sResult))
+            $sResult = "(" . $sResult . ")";
+
+    	return $sResult;
+    }
+
+    protected function _getSqlWhereFromCondition($aCnd)
+    {
+        $sResult = "";
+        if(!isset($aCnd['fld'], $aCnd['val'], $aCnd['opr']))
+            return $sResult;
+
+        $sMethod = '_getSqlWhereFromCondition' . bx_gen_method_name($aCnd['fld']);
+        if(method_exists($this, $sMethod)) {
+            $mixedResult = $this->$sMethod($aCnd);
+            if(is_array($mixedResult) && isset($mixedResult['fld'], $mixedResult['val'], $mixedResult['opr']))
+                $aCnd = $mixedResult;
+            else
+                return $mixedResult;
+        }
+        
+        $sField = "`" . $aCnd['fld'] . "`";
+        if(!empty($aCnd['tbl']))
+            $sField = "`" . $aCnd['tbl'] . "`." . $sField;
+
+        switch($aCnd['opr']) {
+            case 'IN':
+                if(empty($aCnd['val']) || !is_array($aCnd['val']))
+                    break;
+
+                $sResult .= $sField . " IN (" . $this->_oModule->_oDb->implode_escape($aCnd['val']) . ")";
+                break;
+
+            case 'LIKE':
+                $sResult .= $sField . " LIKE " . $this->_oModule->_oDb->escape('%' . $aCnd['val'] . '%');
+                break;
+
+            default:
+                $sResult .= $sField . " " . $aCnd['opr'] . " " . $this->_oModule->_oDb->escape($aCnd['val']);
+        }
+
+        return $sResult;
+    }
+
+    protected function _getSqlGroupByFromGroup($aGrp)
+    {
+        if(!isset($aGrp['grp']) || (bool)$aGrp['grp'] !== true)
+            return $this->_getSqlGroupByFromCondition($aGrp);
+
+        $sResult = "";
+        if(empty($aGrp['cnds']) || !is_array($aGrp['cnds']))
+            return $sResult;
+
+        $sOprGrp = ", ";
+        foreach($aGrp['cnds'] as $aCnd) {
+            $sResultCnd = $this->_getSqlGroupByFromCondition($aCnd);
+            if(!empty($sResultCnd))
+                $sResult .= $sOprGrp . $sResultCnd;
+        }
+
+        return trim($sResult, $sOprGrp);
+    }
+
+    protected function _getSqlGroupByFromCondition($aCnd)
+    {
+    	$sResult = "";
+    	if(!isset($aCnd['fld']))
+            return $sResult;
+
+        $sResult = "`" . $aCnd['fld'] . "`";
+        if(!empty($aCnd['tbla']))
+            $sResult = "`" . $aCnd['tbla'] . "`." . $sResult;
+        else if(!empty($aCnd['tbl']))
+            $sResult = "`" . $aCnd['tbl'] . "`." . $sResult;
+
+        return $sResult;
+    }
+
+    protected function _getSqlWhereFromConditionAuthorComment($aCnd)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $aEntriesIds = $this->_oModule->_oDb->getComments(array('type' => 'entries_author_search', 'author' => $aCnd['val']));
+        if(!empty($aEntriesIds) && is_array($aEntriesIds))
+            return array('tbl' => $CNF['TABLE_ENTRIES'], 'fld' => 'id', 'val' => $aEntriesIds, 'opr' => 'IN');
+
+        return '';
+    }
+
+    protected function _getSqlWhereFromConditionKeywordComment($aCnd)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $aEntriesIds = $this->_oModule->_oDb->getComments(array('type' => 'entries_keyword_search', 'keyword' => $aCnd['val']));
+        if(!empty($aEntriesIds) && is_array($aEntriesIds))
+            return array('tbl' => $CNF['TABLE_ENTRIES'], 'fld' => 'id', 'val' => $aEntriesIds, 'opr' => 'IN');
+
+        return '';
+    }
+    
+    protected function _getFilterControls ()
+    {
+        parent::_getFilterControls();
+        return  $this->_getFilterSelectOne($this->_sFilter3Name, $this->_sFilter3Value, $this->_aFilter3Values) . $this->_getFilterSelectOne($this->_sFilter1Name, $this->_sFilter1Value, $this->_aFilter1Values) . (count($this->_aFilter2Values) > 0 ? $this->_getFilterSelectOne($this->_sFilter2Name, $this->_sFilter2Value, $this->_aFilter2Values) : '') . $this->_getSearchInput();
+    }
+    
+    protected function _getSearchInput()
+    {
+        $sJsObject = $this->_oModule->_oConfig->getJsObject('main');
+        $aInputSearch = array(
+            'type' => 'text',
+            'name' => 'search',
+            'attrs' => array(
+                'id' => 'bx-grid-search-' . $this->_sObject,
+                'onKeyup' => 'javascript:$(this).off(\'keyup focusout\'); ' . $sJsObject . '.onChangeFilter(this)',
+                'onBlur' => 'javascript:' . $sJsObject . '.onChangeFilter(this)',
+            )
+        );
+
+        $oForm = new BxTemplFormView(array());
+        return $oForm->genRow($aInputSearch);
+    }
+    
+    protected function _getFilterSelectOne($sFilterName, $sFilterValue, $aFilterValues)
+    {
+        if(empty($sFilterName) || empty($aFilterValues))
+            return '';
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        $sJsObject = $this->_oModule->_oConfig->getJsObject('main');
+
+        foreach($aFilterValues as $sKey => $sValue)
+            $aFilterValues[$sKey] = _t($sValue);
+
+        $aInputModules = array(
+            'type' => 'select',
+            'name' => $sFilterName,
+            'attrs' => array(
+                'id' => 'bx-grid-' . $sFilterName . '-' . $this->_sObject,
+                'onChange' => 'javascript:' . $sJsObject . '.onChangeFilter(this)'
+            ),
+            'value' => $sFilterValue,
+            'values' => $aFilterValues
+        );
+
+        $oForm = new BxTemplFormView(array());
+        return $oForm->genRow($aInputModules);
+    }
+}
+
+/** @} */

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

@@ -0,0 +1,35 @@
+<?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 BxReputationModule extends BxBaseModNotificationsModule
+{
+    public function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_oConfig->init($this->_oDb);
+    }
+
+    /**
+     * SERVICE METHODS
+     */
+    public function serviceGetBlockSummary($iProfileId = 0)
+    {
+        if(!$iProfileId && ($iLoggedId = bx_get_logged_profile_id()))
+            $iProfileId = $iLoggedId;
+        if(!$iProfileId)
+            return false;
+
+        return $this->_oTemplate->getBlockSummary($iProfileId);
+    }
+}
+
+/** @} */

+ 38 - 0
modules/boonex/reputation/classes/BxReputationStudioPage.php

@@ -0,0 +1,38 @@
+<?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 BxReputationStudioPage extends BxBaseModNotificationsStudioPage
+{
+    public function __construct($sModule, $mixedPageName, $sPage = "")
+    {
+    	$this->_sModule = 'bx_reputation';
+
+        parent::__construct($sModule, $mixedPageName, $sPage);
+
+        $this->aMenuItems['manage'] = ['name' => 'manage', 'icon' => 'cogs', 'title' => '_bx_reviews_menu_item_title_manage'];
+    }
+
+    protected function getManage()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $oGrid = BxDolGrid::getObjectInstance($CNF['OBJECT_GRID_MANAGE'], BxDolStudioTemplate::getInstance());
+        if(!$oGrid)
+            return '';
+
+        $this->_oModule->_oTemplate->addStudioCss(['manage.css']);
+        $this->_oModule->_oTemplate->addStudioJs(['manage.js']);
+        $this->_oModule->_oTemplate->addStudioJsTranslation(['_sys_grid_search']);
+        return $oGrid->getCode();
+    }
+}
+
+/** @} */

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

@@ -0,0 +1,36 @@
+<?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 BxReputationTemplate extends BxBaseModNotificationsTemplate
+{
+    public function __construct(&$oConfig, &$oDb)
+    {
+        parent::__construct($oConfig, $oDb);
+    }
+
+    public function getBlockSummary($iProfileId)
+    {
+        $oProfile = BxDolProfile::getInstance($iProfileId);
+        if(!$oProfile)
+            return false;
+
+        $aProfileInfo = $this->_oDb->getProfile(['sample' => 'id', 'id' => $iProfileId]);
+        $bProfileInfo = !empty($aProfileInfo) && is_array($aProfileInfo);
+
+        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,
+        ]);
+    }
+}
+
+/** @} */

+ 94 - 0
modules/boonex/reputation/install/config.php

@@ -0,0 +1,94 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Reputation Reputation
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = [
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_reputation',
+    'title' => 'Reputation',
+    'note' => 'Reputation module.',
+    'version' => '14.0.0',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => [
+        '14.0.x'
+    ],
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/reputation/',
+    'home_uri' => 'reputation',
+
+    'db_prefix' => 'bx_reputation_',
+    'class_prefix' => 'BxReputation',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Reputation',
+    
+    /**
+     * List of page triggers.
+     */
+    'page_triggers' => [
+        'trigger_page_profile_view_entry'
+    ],
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => [
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ],
+    'uninstall' => [
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ],
+    'enable' => [
+        'execute_sql' => 1,
+        'update_relations_for_all' => 1,
+        'clear_db_cache' => 1,
+    ],
+    'enable_success' => [
+    	'process_page_triggers' => 1,
+    	'clear_db_cache' => 1,
+    ],
+    'disable' => [
+        'execute_sql' => 1,
+        'update_relations_for_all' => 1,
+        'clear_db_cache' => 1,
+    ],
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => [],
+
+    /**
+     * Relations Section
+     */
+    'relation_handlers' => [
+    	'on_install' => '',
+    	'on_uninstall' => 'delete_module_events',
+        'on_enable' => 'add_handlers',
+        'on_disable' => 'delete_handlers',
+    ],
+    'relation_handlers_method' => 'get_reputation_data',
+];
+
+/** @} */

+ 20 - 0
modules/boonex/reputation/install/installer.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    Reputation Reputation
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxReputationInstaller extends BxDolStudioInstaller
+{
+    public function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}
+
+/** @} */

+ 51 - 0
modules/boonex/reputation/install/langs/en.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string name="_bx_reputation"><![CDATA[Reputation]]></string>
+
+    <string name="_bx_reputation_option_"><![CDATA[]]></string>
+
+    <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>
+    <string name="_bx_reputation_form_handler_input_points_active"><![CDATA[Points (active)]]></string>
+    <string name="_bx_reputation_form_handler_input_points_active_info"><![CDATA[Points received by a user when he does something (create a post, comment, vote, etc).]]></string>
+    <string name="_bx_reputation_form_handler_input_sys_points_passive"><![CDATA[Points (passive)]]></string>
+    <string name="_bx_reputation_form_handler_input_points_passive"><![CDATA[Points (passive)]]></string>
+    <string name="_bx_reputation_form_handler_input_points_passive_info"><![CDATA[Points received by a user when an action (comment, vote, etc) was done with his content by another user.]]></string>
+    <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_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_reputation_popup_title_handler_edit"><![CDATA[Edit Handler]]></string>
+
+    <string name="_bx_reputation_page_block_title_system_summary"><![CDATA[Summary]]></string>
+    <string name="_bx_reputation_page_block_title_summary"><![CDATA[Summary]]></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_deleted"><![CDATA[Remove]]></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_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>
+
+    <string name="_bx_reputation_err_cannot_perform"><![CDATA[An error appeared. Please report.]]></string>
+
+    <string name="_bx_reputation_txt_points"><![CDATA[points]]></string>
+</resources>

+ 22 - 0
modules/boonex/reputation/install/sql/disable.sql

@@ -0,0 +1,22 @@
+SET @sName = 'bx_reputation';
+
+
+-- SETTINGS
+DELETE FROM `tot`, `toc`, `to` USING `sys_options_types` AS `tot` LEFT JOIN `sys_options_categories` AS `toc` ON `tot`.`id`=`toc`.`type_id` LEFT JOIN `sys_options` AS `to` ON `toc`.`id`=`to`.`category_id` WHERE `tot`.`name`=@sName;
+
+
+-- PAGES & BLOCKS
+DELETE FROM `sys_objects_page` WHERE `module` = @sName;
+DELETE FROM `sys_pages_blocks` WHERE `module` = @sName; -- OR `object` IN('bx_reputation_home');
+
+
+-- GRIDS
+DELETE FROM `sys_objects_grid` WHERE `object` LIKE 'bx_reputation_%';
+DELETE FROM `sys_grid_fields` WHERE `object` LIKE 'bx_reputation_%';
+DELETE FROM `sys_grid_actions` WHERE `object` LIKE 'bx_reputation_%';
+
+
+-- ALERTS
+SET @iHandler := (SELECT `id` FROM `sys_alerts_handlers` WHERE `name` = @sName LIMIT 1);
+DELETE FROM `sys_alerts` WHERE `handler_id` = @iHandler;
+DELETE FROM `sys_alerts_handlers` WHERE `id` = @iHandler;

+ 55 - 0
modules/boonex/reputation/install/sql/enable.sql

@@ -0,0 +1,55 @@
+SET @sName = 'bx_reputation';
+
+
+-- SETTINGS
+-- SET @iTypeOrder = (SELECT MAX(`order`) FROM `sys_options_types` WHERE `group` = 'modules');
+-- INSERT INTO `sys_options_types`(`group`, `name`, `caption`, `icon`, `order`) VALUES 
+-- ('modules', @sName, '_bx_reputation', 'bx_reputation@modules/boonex/ewts/|std-icon.svg', IF(ISNULL(@iTypeOrder), 1, @iTypeOrder + 1));
+-- SET @iTypeId = LAST_INSERT_ID();
+
+-- INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+-- 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_badge_persons', '', @iCategId, '_bx_reputation_option_badge_persons', 'select', 'a:3:{s:6:"module";s:13:"bx_reputation";s:6:"method";s:17:"get_options_badge";s:6:"params";a:1:{i:0;s:13:"bx_reputation";}}', '', '', 10);
+
+
+-- PAGES: add page block on home
+SET @iPBCellHome = 2;
+SET @iPBOrderHome = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_pages_blocks` WHERE `object` = 'sys_home' AND `cell_id` = @iPBCellHome ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `tabs`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('sys_home', @iPBCellHome, @sName, '_bx_reputation_page_block_title_system_summary', '_bx_reputation_page_block_title_summary', 13, 0, 2147483644, 'service', 'a:2:{s:6:"module";s:13:"bx_reputation";s:6:"method";s:17:"get_block_summary";}', 0, 1, 1, @iPBOrderHome + 1);
+
+-- PAGES: add page block to profiles modules (trigger* page objects are processed separately upon modules enable/disable)
+SET @iPBCellProfile = 3;
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES
+('trigger_page_profile_view_entry', @iPBCellProfile, @sName, '_bx_reputation_page_block_title_system_summary', '_bx_reputation_page_block_title_summary', 13, 2147483647, 'service', 'a:3:{s:6:"module";s:13:"bx_reputation";s:6:"method";s:17:"get_block_summary";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 0);
+
+
+-- 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');
+
+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);
+
+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);
+
+
+-- ALERTS
+INSERT INTO `sys_alerts_handlers` (`name`, `class`, `file`, `service_call`) VALUES 
+(@sName, 'BxReputationAlertsResponse', 'modules/boonex/reputation/classes/BxReputationAlertsResponse.php', '');
+SET @iHandler := LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('profile', 'delete', @iHandler);

+ 58 - 0
modules/boonex/reputation/install/sql/install.sql

@@ -0,0 +1,58 @@
+SET @sName = 'bx_reputation';
+
+
+-- TABLE: handlers
+CREATE TABLE IF NOT EXISTS `bx_reputation_handlers` (
+  `id` int(11) NOT NULL auto_increment,
+  `group` varchar(64) NOT NULL default '',
+  `type` enum('insert','update','delete') NOT NULL DEFAULT 'insert',
+  `alert_unit` varchar(64) NOT NULL default '',
+  `alert_action` varchar(64) NOT NULL default '',
+  `points_active` int(11) NOT NULL default '0',
+  `points_passive` int(11) NOT NULL default '0',
+  `active` tinyint(4) NOT NULL default '1',
+  PRIMARY KEY (`id`),
+  UNIQUE `alert` (`alert_unit`, `alert_action`)
+);
+
+-- TABLE: profiles
+CREATE TABLE IF NOT EXISTS `bx_reputation_profiles` (
+  `id` int(11) NOT NULL DEFAULT '0',
+  `points` 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
+('bx_reputation_handler', @sName, '_bx_reputation_form_handler', '', '', 'do_submit', 'bx_reputation_handlers', 'id', '', '', '', 0, 1, '', '');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_reputation_handler_edit', @sName, 'bx_reputation_handler', '_bx_reputation_form_handler_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_handler', @sName, 'points_active', '', '', 0, 'text', '_bx_reputation_form_handler_input_sys_points_active', '_bx_reputation_form_handler_input_points_active', '_bx_reputation_form_handler_input_points_active_info', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('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);
+
+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),
+('bx_reputation_handler_edit', 'points_passive', 2147483647, 1, 2),
+('bx_reputation_handler_edit', 'controls', 2147483647, 1, 3),
+('bx_reputation_handler_edit', 'do_submit', 2147483647, 1, 4),
+('bx_reputation_handler_edit', 'do_cancel', 2147483647, 1, 5);
+
+
+-- STUDIO PAGE & WIDGET
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, @sName, '_bx_reputation', '_bx_reputation', 'bx_reputation@modules/boonex/reputation/|std-icon.svg');
+SET @iPageId = LAST_INSERT_ID();
+
+SET @iParentPageId = (SELECT `id` FROM `sys_std_pages` WHERE `name` = 'home');
+SET @iParentPageOrder = (SELECT MAX(`order`) FROM `sys_std_pages_widgets` WHERE `page_id` = @iParentPageId);
+INSERT INTO `sys_std_widgets` (`page_id`, `module`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, @sName, '{url_studio}module.php?name=bx_reputation', '', 'bx_reputation@modules/boonex/reputation/|std-icon.svg', '_bx_reputation', '', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_actions";s:6:"params";a:0:{}s:5:"class";s:18:"TemplStudioModules";}');
+INSERT INTO `sys_std_pages_widgets` (`page_id`, `widget_id`, `order`) VALUES
+(@iParentPageId, LAST_INSERT_ID(), IF(ISNULL(@iParentPageOrder), 1, @iParentPageOrder + 1));

+ 17 - 0
modules/boonex/reputation/install/sql/uninstall.sql

@@ -0,0 +1,17 @@
+SET @sName = 'bx_reputation';
+
+
+DROP TABLE IF EXISTS `bx_reputation_handlers`, `bx_reputation_profiles`;
+
+
+-- FORMS
+DELETE FROM `sys_objects_form` WHERE `module` = @sName;
+DELETE FROM `sys_form_displays` WHERE `module` = @sName;
+DELETE FROM `sys_form_inputs` WHERE `module` = @sName;
+DELETE FROM `sys_form_display_inputs` WHERE `display_name` LIKE 'bx_reputation_%';
+
+
+-- STUDIO PAGE & WIDGET
+DELETE FROM `tp`, `tw`, `tpw`
+USING `sys_std_pages` AS `tp`, `sys_std_widgets` AS `tw`, `sys_std_pages_widgets` AS `tpw`
+WHERE `tp`.`id` = `tw`.`page_id` AND `tw`.`id` = `tpw`.`widget_id` AND `tp`.`name` = @sName;

+ 42 - 0
modules/boonex/reputation/js/manage.js

@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Reputation Reputation
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxReputationManage(oOptions)
+{
+    this._iSearchTimeoutId = false;
+    this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjNameGrid = oOptions.sObjNameGrid;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxReputationManage' : oOptions.sObjName;
+
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._sParamsDivider = oOptions.sParamsDivider == undefined ? '#-#' : oOptions.sParamsDivider;
+
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+}
+
+BxReputationManage.prototype.onChangeFilter = function(oFilter) {
+    var $this = this;
+    var oFilter1 = $('#bx-grid-filter1-' + this._sObjNameGrid);
+    var sValueFilter1 = oFilter1.length > 0 ? oFilter1.val() : '';
+
+    var oSearch = $('#bx-grid-search-' + this._sObjNameGrid);
+    var sValueSearch = oSearch.length > 0 ? oSearch.val() : '';
+    if(sValueSearch == _t('_sys_grid_search'))
+        sValueSearch = '';
+
+    clearTimeout($this._iSearchTimeoutId);
+    $this._iSearchTimeoutId = setTimeout(function () {
+        glGrids[$this._sObjNameGrid].setFilter(sValueFilter1 + $this._sParamsDivider + sValueSearch, true);
+    }, 500);
+};
+
+/** @} */

+ 18 - 0
modules/boonex/reputation/request.php

@@ -0,0 +1,18 @@
+<?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
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+bx_import('BxDolLanguages');
+
+BxDolRequest::processAsAction($GLOBALS['aModule'], $GLOBALS['aRequest']);
+
+/** @} */

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

@@ -0,0 +1,5 @@
+<div class="bx-reputation-summary flex flex-col items-center">
+    <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>

+ 15 - 0
modules/boonex/reputation/template/css/manage.css

@@ -0,0 +1,15 @@
+div.bx-grid-header div.bx-form-element-wrapper {
+    position: relative;
+    float: left;
+    width: 165px;
+}
+div.bx-grid-header div.bx-form-element-wrapper {
+    margin-right: 0.5rem;
+}
+div.bx-grid-header div.bx-form-element-wrapper:last-child {
+    margin-right: 0;
+}
+div.bx-grid-header div#bx-form-element-filter1,
+div.bx-grid-header div#bx-form-element-search {
+    margin-top: 0px;
+}

+ 25 - 0
modules/boonex/reputation/template/images/icons/std-icon.svg

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#00B356;}
+	.st1{fill:#FFFFFF;}
+</style>
+<title>contact</title>
+<desc>Created with Sketch.</desc>
+<g id="Page-1">
+	<g id="Artboard" transform="translate(-826.000000, -430.000000)">
+		<g id="contact" transform="translate(826.000000, 430.000000)">
+			<g id="Path">
+				<path id="path-1_1_" class="st0" d="M32,64c14,0,21.8,0,26.9-5.1S64,46,64,32s0-21.8-5.1-26.9S46,0,32,0S10.2,0,5.1,5.1
+					S0,18,0,32s0,21.8,5.1,26.9S18,64,32,64z"/>
+			</g>
+		</g>
+	</g>
+</g>
+<path class="st1" d="M15.3,22h33.4c0.7,0,1.3-0.6,1.3-1.3v-3.2c0-0.7-0.6-1.3-1.3-1.3H15.3c-0.7,0-1.3,0.6-1.3,1.3v3.2
+	C14,21.5,14.6,22,15.3,22z M15.3,34.9h33.4c0.7,0,1.3-0.6,1.3-1.3v-3.2c0-0.7-0.6-1.3-1.3-1.3H15.3c-0.7,0-1.3,0.6-1.3,1.3v3.2
+	C14,34.3,14.6,34.9,15.3,34.9z M15.3,47.8h33.4c0.7,0,1.3-0.6,1.3-1.3v-3.2c0-0.7-0.6-1.3-1.3-1.3H15.3c-0.7,0-1.3,0.6-1.3,1.3v3.2
+	C14,47.2,14.6,47.8,15.3,47.8z"/>
+</svg>

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

@@ -0,0 +1,19 @@
+<div class="bx-reputation-handler-form">
+    <div class="bx-reputation-handler-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>

+ 51 - 0
modules/boonex/russian/data/langs/bx_reputation/ru.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="ru" flag="ru" title="Russian">
+    <string name="_bx_reputation"><![CDATA[Репутация]]></string>
+
+    <string name="_bx_reputation_option_"><![CDATA[]]></string>
+
+    <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>
+    <string name="_bx_reputation_form_handler_input_points_active"><![CDATA[Баллы (активные)]]></string>
+    <string name="_bx_reputation_form_handler_input_points_active_info"><![CDATA[Баллы, которые получает пользователь, когда он что-то делает (создает пост, комментирует, голосует и тд).]]></string>
+    <string name="_bx_reputation_form_handler_input_sys_points_passive"><![CDATA[Баллы (пассивные)]]></string>
+    <string name="_bx_reputation_form_handler_input_points_passive"><![CDATA[Баллы (пассивные)]]></string>
+    <string name="_bx_reputation_form_handler_input_points_passive_info"><![CDATA[Баллы, полученные пользователем, когда другой пользователь совершил действие (комментарий, голосование и тд) с его контентом.]]></string>
+    <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_do_cancel"><![CDATA[Отмена]]></string>
+    <string name="_bx_reputation_popup_title_handler_edit"><![CDATA[Редактировать обработчик]]></string>
+
+    <string name="_bx_reputation_page_block_title_system_summary"><![CDATA[Сводка]]></string>
+    <string name="_bx_reputation_page_block_title_summary"><![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_deleted"><![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_reputation_grid_action_err_perform"><![CDATA[Невозможно выполнить действие для выбранного(ых) элементов.]]></string>
+    <string name="_bx_reputation_grid_filter_item_title_select_one_filter1"><![CDATA[Все разделы]]></string>
+
+    <string name="_bx_reputation_err_cannot_perform"><![CDATA[Произошла ошибка. Пожалуйста, сообщите.]]></string>
+
+    <string name="_bx_reputation_txt_points"><![CDATA[баллов]]></string>
+</resources>