Selaa lähdekoodia

Merge remote-tracking branch 'origin/master'

Alexander 5 vuotta sitten
vanhempi
sitoutus
f8b8488a12
50 muutettua tiedostoa jossa 512 lisäystä ja 164 poistoa
  1. 8 2
      inc/classes/BxDolStorageS3.php
  2. 9 1
      inc/js/classes/BxDolCmts.js
  3. 8 0
      inc/js/functions.js
  4. 6 1
      modules/base/groups/classes/BxBaseModGroupsConfig.php
  5. 6 6
      modules/base/groups/classes/BxBaseModGroupsDb.php
  6. 118 0
      modules/base/groups/classes/BxBaseModGroupsFormPrice.php
  7. 2 2
      modules/base/groups/classes/BxBaseModGroupsGridConnections.php
  8. 4 6
      modules/base/groups/classes/BxBaseModGroupsGridPricesManage.php
  9. 32 1
      modules/base/groups/classes/BxBaseModGroupsGridPricesView.php
  10. 62 9
      modules/base/groups/classes/BxBaseModGroupsModule.php
  11. 26 1
      modules/base/payment/classes/BxBaseModPaymentOrders.php
  12. 1 2
      modules/base/profile/classes/BxBaseModProfileMenuSnippetMeta.php
  13. 2 2
      modules/boonex/acl/install/langs/en.xml
  14. 1 0
      modules/boonex/ads/install/langs/en.xml
  15. 1 1
      modules/boonex/decorous/data/template/system/_header_js_custom.html
  16. 4 10
      modules/boonex/decorous/data/template/system/_header_js_toolbar.html
  17. 3 17
      modules/boonex/decorous/data/template/system/css/general.css
  18. 4 0
      modules/boonex/groups/classes/BxGroupsConfig.php
  19. 22 0
      modules/boonex/groups/classes/BxGroupsFormPrice.php
  20. 0 10
      modules/boonex/groups/classes/BxGroupsModule.php
  21. 13 8
      modules/boonex/groups/install/langs/en.xml
  22. 10 0
      modules/boonex/groups/install/sql/enable.sql
  23. 16 13
      modules/boonex/groups/install/sql/install.sql
  24. 26 0
      modules/boonex/groups/js/prices.js
  25. 1 6
      modules/boonex/lucid/data/template/system/_header_js_toolbar.html
  26. 0 15
      modules/boonex/lucid/data/template/system/css/general.css
  27. 5 5
      modules/boonex/market/install/langs/en.xml
  28. 18 0
      modules/boonex/notifications/classes/BxNtfsConfig.php
  29. 28 2
      modules/boonex/notifications/classes/BxNtfsModule.php
  30. 3 21
      modules/boonex/notifications/classes/BxNtfsTemplate.php
  31. 1 1
      modules/boonex/organizations/install/sql/enable.sql
  32. 2 1
      modules/boonex/payment/classes/BxPaymentModule.php
  33. 8 5
      modules/boonex/payment/classes/BxPaymentOrders.php
  34. 11 2
      modules/boonex/payment/classes/BxPaymentProviderChargebeeV3.php
  35. 1 0
      modules/boonex/payment/install/langs/en.xml
  36. 2 2
      modules/boonex/russian/data/langs/bx_acl/ru.xml
  37. 2 1
      modules/boonex/russian/data/langs/bx_ads/ru.xml
  38. 7 2
      modules/boonex/russian/data/langs/bx_groups/ru.xml
  39. 5 5
      modules/boonex/russian/data/langs/bx_market/ru.xml
  40. 1 0
      modules/boonex/russian/data/langs/bx_payment/ru.xml
  41. 1 1
      modules/boonex/shopify/classes/BxShopifyConfig.php
  42. 5 0
      modules/boonex/shopify/js/shop.js
  43. 2 0
      modules/boonex/timeline/classes/BxTimelineModule.php
  44. 1 1
      storage_uploader.php
  45. 1 1
      studio/template/scripts/BxBaseStudioBadgesGrid.php
  46. 1 1
      template/comments_block.html
  47. 6 0
      template/css/media-desktop.css
  48. 6 0
      template/css/media-phone.css
  49. 6 0
      template/css/media-tablet.css
  50. 4 0
      template/scripts/BxBaseCmts.php

+ 8 - 2
inc/classes/BxDolStorageS3.php

@@ -246,8 +246,14 @@ class BxDolStorageS3 extends BxDolStorage
 
     protected function getObjectBaseUrl ($isPrivate = false)
     {
-        $sProto = $this->_bSSL ? 'https://' : 'http://';
-        return $sProto . $this->_sBucket . '.' . ($this->_sEndpoint ? $this->_sEndpoint : 's3.amazonaws.com'). '/' . $this->getObjectBaseDir($isPrivate);
+        $sPath = $this->_bSSL ? 'https://' : 'http://';
+        if ($this->_sDomain)
+            $sPath .= $this->_sDomain;
+        elseif ($this->_sEndpoint)
+            $sPath .= $this->_sEndpoint . '/' . $this->_sBucket;
+        else
+            $sPath .= $this->_sBucket . '.s3.amazonaws.com';
+        return $sPath . '/' . $this->getObjectBaseDir($isPrivate);
     }
 }
 

+ 9 - 1
inc/js/classes/BxDolCmts.js

@@ -217,6 +217,14 @@ BxDolCmts.prototype.cmtPin = function(oLink, iCmtId, iWay, bHideMenu) {
 
                         $this._sDisplayType = $this._sDisplayType;
                         $this._cmtsReplaceContent($(sListId), sContent);
+
+                        var oDivider = $(sListId).siblings('.cmts-divider');
+                        if(oDivider.length > 0) {
+                            if(oDivider.is(':hidden') && sContent.length != 0)
+                                oDivider.show();
+                            else if(!oDivider.is(':hidden') && sContent.length == 0)
+                                oDivider.hide();
+                        }
                     });
             	}
             };
@@ -658,7 +666,7 @@ BxDolCmts.prototype._getCmt = function (e, iCmtId)
         function (oData) {
             $this._loadingInBlock (e, false);
 
-            var sListId = $this._sRootId + ' #cmt' + oData.vparent_id + ' > ul';
+            var sListId = $this._sRootId + ' #cmt' + oData.vparent_id + ' > ul.cmts-all';
             var sReplyFormId = $this._sRootId + ' #cmt' + oData.parent_id + ' > .cmt-reply';
 
             //--- Hide reply form ---//

+ 8 - 0
inc/js/functions.js

@@ -1181,6 +1181,14 @@ function bx_prompt(sMessage, sValue, fOnClickOk, fOnClickCancel, oParams)
     });
 }
 
+/**
+ * Check if mobile or desktop device.
+ */
+function bx_check_mq()
+{
+    return window.getComputedStyle(document.querySelector('body'), '::before').getPropertyValue('content').replace(/'/g, "").replace(/"/g, "");
+}
+
 /**
  * This function loads the passed list of js files and executes callback function when the all files are loaded.
  * In case when this function is called for dynamic loading of the same file but many times (like Plyr video init)

+ 6 - 1
modules/base/groups/classes/BxBaseModGroupsConfig.php

@@ -56,7 +56,12 @@ class BxBaseModGroupsConfig extends BxBaseModProfileConfig
 
     public function isPaidJoin()
     {
-        return !empty($this->CNF['PARAM_MMODE']) && getParam($this->CNF['PARAM_MMODE']) == BX_BASE_MOD_GROUPS_MMODE_PAID_JOIN;
+        return isset($this->CNF['PARAM_PAID_JOIN_ENABLED']) && $this->CNF['PARAM_PAID_JOIN_ENABLED'] === true;
+    }
+
+    public function getPriceName($sName)
+    {
+        return uriGenerate($sName, $this->CNF['TABLE_PRICES'], $this->CNF['FIELD_PRICE_NAME']);
     }
 }
 

+ 6 - 6
modules/base/groups/classes/BxBaseModGroupsDb.php

@@ -97,12 +97,12 @@ class BxBaseModGroupsDb extends BxBaseModProfileDb
         if(!isset($CNF['TABLE_ADMINS']))
             return false;
 
-        $aBindings = array('group_profile_id' => $iGroupProfileId, 'fan_id' => $iFanId);
-        $sWhereClause = " AND `group_profile_id` = :group_profile_id AND `fan_id` = :fan_id";
-        if($this->_oConfig->isPaidJoin()) {
-            $aBindings['role'] = 'BX_BASE_MOD_GROUPS_ROLE_COMMON';
-            $sWhereClause .= " AND `role` <> :role";
-        }
+        $aBindings = array(
+            'group_profile_id' => $iGroupProfileId, 
+            'fan_id' => $iFanId, 
+            'role' => BX_BASE_MOD_GROUPS_ROLE_COMMON
+        );
+        $sWhereClause = " AND `group_profile_id` = :group_profile_id AND `fan_id` = :fan_id AND `role` <> :role";
 
         return $this->getOne("SELECT `id` FROM `" . $CNF['TABLE_ADMINS'] . "` WHERE 1" . $sWhereClause, $aBindings) ? true : false;
     }

+ 118 - 0
modules/base/groups/classes/BxBaseModGroupsFormPrice.php

@@ -0,0 +1,118 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    BaseGroups Base classes for groups modules
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxBaseModGroupsFormPrice extends BxTemplFormView
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        parent::__construct($aInfo, $oTemplate);        
+        
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(isset($this->aInputs[$CNF['FIELD_PRICE_NAME']])) {
+            $sJsObject = $this->_oModule->_oConfig->getJsObject('prices');
+
+            $iId = $this->getId();
+            $aMask = array('mask' => "javascript:%s.checkName('%s');", $sJsObject, $CNF['FIELD_PRICE_NAME']);
+            if(!empty($iId) && $this->aParams['display'] == $CNF['OBJECT_FORM_PRICE_DISPLAY_EDIT']) {
+                $aMask['mask'] = "javascript:%s.checkName('%s', %d);";
+                $aMask[] = $iId;
+            }
+
+            $sOnBlur = call_user_func_array('sprintf', array_values($aMask)); 
+            $this->aInputs[$CNF['FIELD_PRICE_NAME']]['attrs']['onblur'] = $sOnBlur;
+        }
+    }
+
+    public function setAction($sAction)
+    {
+        $this->aFormAttrs['action'] = $sAction;
+    }
+
+    public function setRoleId($iRoleId)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->aInputs[$CNF['FIELD_PRICE_ROLE_ID']]['value'] = (int)$iRoleId;
+        if(empty($this->aInputs[$CNF['FIELD_PRICE_NAME']]['value'])) {
+            $aRoles = $this->_oModule->_oConfig->getRoles();
+            $this->aInputs[$CNF['FIELD_PRICE_NAME']]['value'] = $this->_oModule->_oConfig->getPriceName(_t($aRoles[$iRoleId]));
+        }
+    }
+
+    public function getCode($bDynamicMode = false)
+    {
+    	$sJs = $this->_oModule->_oTemplate->addJs(array('prices.js'), $bDynamicMode);
+
+        $sCode = '';
+        if($bDynamicMode)
+            $sCode .= $sJs;
+
+        $sCode .= $this->_oModule->_oTemplate->getJsCode('prices');
+        $sCode .= parent::getCode($bDynamicMode);
+
+        return $sCode;
+    }
+
+    public function insert ($aValsToAdd = array(), $isIgnore = false)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(isset($this->aInputs[$CNF['FIELD_PRICE_NAME']])) {
+            $sName = BxDolForm::getSubmittedValue($CNF['FIELD_PRICE_NAME'], $this->aFormAttrs['method'], $this->_aSpecificValues);
+            $sName = $this->_oModule->_oConfig->getPriceName($sName);
+            BxDolForm::setSubmittedValue($CNF['FIELD_PRICE_NAME'], $sName, $this->aFormAttrs['method'], $this->_aSpecificValues);
+        }
+
+        return parent::insert ($aValsToAdd, $isIgnore);
+    }
+
+    function update ($iContentId, $aValsToAdd = array(), &$aTrackTextFieldsChanges = null)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sNameKey = $CNF['FIELD_PRICE_NAME'];
+        if(isset($this->aInputs[$sNameKey])) {
+            $sName = $this->getCleanValue($sNameKey);
+            $aContentInfo = $this->_oModule->_oDb->getPrices(array('type' => 'by_id', 'value' => $iContentId));
+
+            if($aContentInfo[$sNameKey] != $sName) {
+                $sName = $this->_oModule->_oConfig->getPriceName($sName);
+                BxDolForm::setSubmittedValue($sNameKey, $sName, $this->aFormAttrs['method'], $this->_aSpecificValues);
+            }
+        }
+
+        return parent::update ($iContentId, $aValsToAdd, $aTrackTextFieldsChanges);
+    }
+
+    public function getId()
+    {
+        $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;
+    }
+}
+
+/** @} */

+ 2 - 2
modules/base/groups/classes/BxBaseModGroupsGridConnections.php

@@ -178,7 +178,7 @@ class BxBaseModGroupsGridConnections extends BxDolGridConnections
         if($this->_bManageMembers || $iProfileId == bx_get_logged_profile_id()) {
             $aRole = $this->_oModule->_oDb->getRoles(array('type' => 'by_gf_id', 'group_profile_id' => $this->_iGroupProfileId, 'fan_id' => $iProfileId));
             if(!empty($aRole) && is_array($aRole))
-                $mixedValue = bx_time_js($aRole['added'], BX_FORMAT_DATE, true);
+                $mixedValue = !empty($aRole['added']) ? bx_time_js($aRole['added'], BX_FORMAT_DATE, true) : '';
         }
 
         return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
@@ -203,7 +203,7 @@ class BxBaseModGroupsGridConnections extends BxDolGridConnections
         if($this->_bManageMembers || $iProfileId == bx_get_logged_profile_id()) {
             $aRole = $this->_oModule->_oDb->getRoles(array('type' => 'by_gf_id', 'group_profile_id' => $this->_iGroupProfileId, 'fan_id' => $iProfileId));
             if(!empty($aRole) && is_array($aRole))
-                $mixedValue = bx_time_js($aRole['expired'], BX_FORMAT_DATE, true);
+                $mixedValue = !empty($aRole['expired']) ? bx_time_js($aRole['expired'], BX_FORMAT_DATE, true) : '';
         }
 
         return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);

+ 4 - 6
modules/base/groups/classes/BxBaseModGroupsGridPricesManage.php

@@ -60,8 +60,8 @@ class BxBaseModGroupsGridPricesManage extends BxBaseModGroupsGridPrices
             $this->_iRoleId = (int)$iRoleId;
 
     	$oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_PRICE'], $CNF['OBJECT_FORM_PRICE_DISPLAY_ADD']);
-    	$oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . bx_append_url_params('grid.php', array('o' => $this->_sObject, 'a' => $sAction, 'profile_id' => $this->_iGroupProfileId, 'role_id' => $this->_iRoleId));
-    	$oForm->aInputs['role_id']['value'] = $this->_iRoleId;
+    	$oForm->setAction(BX_DOL_URL_ROOT . bx_append_url_params('grid.php', array('o' => $this->_sObject, 'a' => $sAction, 'profile_id' => $this->_iGroupProfileId, 'role_id' => $this->_iRoleId)));
+        $oForm->setRoleId($this->_iRoleId);
 
         $oForm->initChecker();
         if($oForm->isSubmittedAndValid()) {
@@ -75,9 +75,7 @@ class BxBaseModGroupsGridPricesManage extends BxBaseModGroupsGridPrices
             if(!empty($aPrice) && is_array($aPrice))
                 return echoJson(array('msg' => _t('_bx_groups_err_price_duplicate')));
 
-            $sName = uriGenerate(strtolower(strmaxtextlen($this->_aGroupContentInfo[$CNF['FIELD_TITLE']], 8, '')) . ' ' . strtolower($this->_aRoles[$this->_iRoleId]) . ' ' . $iPeriod . ' ' . $sPeriodUnit, $CNF['TABLE_PRICES'], 'name');
-
-            $iId = (int)$oForm->insert(array('name' => $sName, 'profile_id' => $this->_iGroupProfileId, 'order' => $this->_oModule->_oDb->getPriceOrderMax($this->_iRoleId) + 1));
+            $iId = (int)$oForm->insert(array('profile_id' => $this->_iGroupProfileId, 'order' => $this->_oModule->_oDb->getPriceOrderMax($this->_iRoleId) + 1));
             if($iId != 0)
                 $aRes = array('grid' => $this->getCode(false), 'blink' => $iId);
             else
@@ -113,7 +111,7 @@ class BxBaseModGroupsGridPricesManage extends BxBaseModGroupsGridPrices
             return echoJson(array());
 
         $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_PRICE'], $CNF['OBJECT_FORM_PRICE_DISPLAY_EDIT']);
-    	$oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . bx_append_url_params('grid.php', array('o' => $this->_sObject, 'a' => $sAction, 'profile_id' => $this->_iGroupProfileId, 'role_id' => $this->_iRoleId));
+    	$oForm->setAction(BX_DOL_URL_ROOT . bx_append_url_params('grid.php', array('o' => $this->_sObject, 'a' => $sAction, 'profile_id' => $this->_iGroupProfileId, 'role_id' => $this->_iRoleId)));
 
         $oForm->initChecker($aItem);
         if($oForm->isSubmittedAndValid()) {

+ 32 - 1
modules/base/groups/classes/BxBaseModGroupsGridPricesView.php

@@ -13,6 +13,12 @@ require_once('BxBaseModGroupsGridPrices.php');
 
 class BxBaseModGroupsGridPricesView extends BxBaseModGroupsGridPrices
 {
+    /**
+     * Array with check_sum => JS_code pairs of all JS codes 
+     * which should be added to the page.
+     */
+    protected $_aJsCodes;
+
     protected $_oPayment;
     protected $_bTypeSingle;
     protected $_bTypeRecurring;
@@ -27,6 +33,8 @@ class BxBaseModGroupsGridPricesView extends BxBaseModGroupsGridPrices
 
         $CNF = &$this->_oModule->_oConfig->CNF;
 
+        $this->_aJsCodes = array();
+
         $this->_oPayment = BxDolPayments::getInstance();
 
         $this->_iSeller = $this->_aGroupContentInfo[$CNF['FIELD_AUTHOR']];
@@ -60,7 +68,15 @@ class BxBaseModGroupsGridPricesView extends BxBaseModGroupsGridPrices
 
     public function getCode($isDisplayHeader = true)
     {
-    	return $this->_oPayment->getCartJs() . parent::getCode($isDisplayHeader);
+    	return parent::getCode($isDisplayHeader) . $this->getJsCode();
+    }
+
+    public function getJsCode()
+    {
+        if(empty($this->_aJsCodes) || !is_array($this->_aJsCodes))
+            return '';
+
+        return implode('', $this->_aJsCodes);
     }
 
     protected function _getCellRoleId($mixedValue, $sKey, $aField, $aRow)
@@ -87,6 +103,10 @@ class BxBaseModGroupsGridPricesView extends BxBaseModGroupsGridPrices
         if(!empty($aJs) && is_array($aJs)) {
             list($sJsCode, $sJsMethod) = $aJs;
 
+            $sJsCodeCheckSum = md5($sJsCode);
+            if(!isset($this->_aJsCodes[$sJsCodeCheckSum]))
+                $this->_aJsCodes[$sJsCodeCheckSum] = $sJsCode;
+
             $a['attr'] = array(
                 'title' => bx_html_attribute(_t($CNF['T']['txt_buy_title'])),
                 'onclick' => $sJsMethod
@@ -107,6 +127,10 @@ class BxBaseModGroupsGridPricesView extends BxBaseModGroupsGridPrices
         if(!empty($aJs) && is_array($aJs)) {
             list($sJsCode, $sJsMethod) = $aJs;
 
+            $sJsCodeCheckSum = md5($sJsCode);
+            if(!isset($this->_aJsCodes[$sJsCodeCheckSum]))
+                $this->_aJsCodes[$sJsCodeCheckSum] = $sJsCode;
+
             $a['attr'] = array(
                 'title' => bx_html_attribute(_t($CNF['T']['txt_subscribe_title'])),
                 'onclick' => $sJsMethod
@@ -115,6 +139,13 @@ class BxBaseModGroupsGridPricesView extends BxBaseModGroupsGridPrices
 
         return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
     }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString("AND `profile_id`=? ", $this->_iGroupProfileId);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);;
+    }
 }
 
 /** @} */

+ 62 - 9
modules/base/groups/classes/BxBaseModGroupsModule.php

@@ -10,7 +10,6 @@
  */
 
 define('BX_BASE_MOD_GROUPS_MMODE_MULTI_ROLES', 'multi_roles');
-define('BX_BASE_MOD_GROUPS_MMODE_PAID_JOIN', 'paid_join');
 
 define('BX_BASE_MOD_GROUPS_ROLE_COMMON', 0);
 define('BX_BASE_MOD_GROUPS_ROLE_ADMINISTRATOR', 1);
@@ -76,6 +75,28 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
         }   
     }
 
+    public function actionCheckName()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$sName = bx_process_input(bx_get('name'));
+    	if(empty($sName))
+            return echoJson(array());
+
+        $sResult = '';
+
+        $iId = (int)bx_get('id');
+        if(!empty($iId)) {
+            $aPrice = $this->_oDb->getPrices(array('type' => 'by_id', 'value' => $iId)); 
+            if(strcmp($sName, $aPrice[$CNF['FIELD_PRICE_NAME']]) == 0) 
+                $sResult = $sName;
+        }
+
+    	echoJson(array(
+            'name' => !empty($sResult) ? $sResult : $this->_oConfig->getPriceName($sName)
+    	));
+    }
+
     public function serviceGetOptionsMembersMode()
     {
         $CNF = &$this->_oConfig->CNF;
@@ -199,20 +220,26 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
     /**
      * Reset group's author for particular group
      * @param $iContentId group id 
+     * @parem $iAuthorId new author profile ID
      * @return false of error, or number of updated records on success
      */
-    public function serviceReassignEntityAuthor ($iContentId)
+    public function serviceReassignEntityAuthor ($iContentId, $iAuthorId = 0)
     {
         $aContentInfo = $this->_oDb->getContentInfoById((int)$iContentId);
         if (!$aContentInfo)
             return false;
 
-        if (!($oGroupProfile = BxDolProfile::getInstanceByContentAndType($iContentId, $this->getName())))
-            return false;
+        if (empty($iAuthorId)) {
+            $oGroupProfile = BxDolProfile::getInstanceByContentAndType($iContentId, $this->getName());
+            if (!$oGroupProfile)
+                return false;
 
-        $aAdmins = $this->_oDb->getAdmins($oGroupProfile->id());
+            $aAdmins = $this->_oDb->getAdmins($oGroupProfile->id());
+            if($aAdmins)
+                $iAuthorId = array_pop($aAdmins);
+        }
 
-        return $this->_oDb->updateAuthorById($iContentId, $aAdmins ? array_pop($aAdmins) : 0);
+        return $this->_oDb->updateAuthorById($iContentId, $iAuthorId);
     }
 
     /**
@@ -244,10 +271,11 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
     
     /**
      * Reset group's author when author profile is deleted
-     * @param $iProfileId profile id 
+     * @param $iProfileId author profile id 
+     * @param $iAuthorId new author profile id 
      * @return number of changed items
      */
-    public function serviceReassignEntitiesByAuthor ($iProfileId)
+    public function serviceReassignEntitiesByAuthor ($iProfileId, $iAuthorId = 0)
     {
         $a = $this->_oDb->getEntriesByAuthor((int)$iProfileId);
         if (!$a)
@@ -255,7 +283,7 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
 
         $iCount = 0;
         foreach ($a as $aContentInfo)
-            $iCount += ('' == $this->serviceReassignEntityAuthor($aContentInfo[$this->_oConfig->CNF['FIELD_ID']]) ? 1 : 0);
+            $iCount += ('' == $this->serviceReassignEntityAuthor($aContentInfo[$this->_oConfig->CNF['FIELD_ID']], $iAuthorId) ? 1 : 0);
 
         return $iCount;
     }
@@ -534,6 +562,9 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
         if(!$this->_oConfig->isPaidJoin())
             return '';
 
+        if($this->checkAllowedUsePaidJoin() !== CHECK_ACTION_RESULT_ALLOWED)
+            return MsgBox(_t('_Access denied'));
+
         if($this->checkAllowedManageAdmins($iProfileId) !== CHECK_ACTION_RESULT_ALLOWED)
             return MsgBox(_t('_Access denied'));
 
@@ -601,6 +632,9 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
         if(!$this->_oConfig->isPaidJoin())
             return false;
 
+        if($this->checkAllowedUsePaidJoin() !== CHECK_ACTION_RESULT_ALLOWED)
+            return false;        
+
         if($this->checkAllowedManageAdmins($iProfileId) !== CHECK_ACTION_RESULT_ALLOWED)
             return false;
 
@@ -635,6 +669,12 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
         return !$this->isPaidJoinByProfile($oProfile->id());
     }
 
+    /**
+     * Is Paid Join enabled as is and whether a group has pricing plans added.
+     * 
+     * @param type $iProfileId - Group profile ID.
+     * @return boolean
+     */
     public function isPaidJoinByProfile($iProfileId)
     {
         if(!$this->_oConfig->isPaidJoin())
@@ -1017,6 +1057,19 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
 
 
     // ====== PERMISSION METHODS
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
+     */
+    public function checkAllowedUsePaidJoin($isPerformAction = false)
+    {
+        // check ACL
+        $aCheck = checkActionModule($this->_iProfileId, 'use paid join', $this->getName(), $isPerformAction);
+        if($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+            return $aCheck[CHECK_ACTION_MESSAGE];
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
     /**
      * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
      */

+ 26 - 1
modules/base/payment/classes/BxBaseModPaymentOrders.php

@@ -38,7 +38,7 @@ class BxBaseModPaymentOrders extends BxDol
     /** 
      * @ref bx_base_payment-get_orders_url "get_orders_url"
      */
-	public function serviceGetOrdersUrl()
+    public function serviceGetOrdersUrl()
     {
     	if(!$this->_oModule->isLogged())
             return '';
@@ -46,6 +46,31 @@ class BxBaseModPaymentOrders extends BxDol
     	return $this->_oModule->_oConfig->getUrl('URL_ORDERS');
     }
 
+    /**
+     * @page service Service Calls
+     * @section bx_base_payment Base Payment
+     * @subsection bx_base_payment-integration Integration
+     * @subsubsection bx_base_payment-get_pending_orders_url get_pending_orders_url
+     * 
+     * @code bx_srv('bx_payment', 'get_pending_orders_url', [...], 'Orders'); @endcode
+     * 
+     * Get pending orders page URL.
+     *
+     * @return string with pending orders page URL.
+     * 
+     * @see BxBaseModPaymentOrders::serviceGetPendingOrdersUrl
+     */
+    /** 
+     * @ref bx_base_payment-get_pending_orders_url "get_pending_orders_url"
+     */
+    public function serviceGetPendingOrdersUrl()
+    {
+    	if(!$this->_oModule->isLogged())
+            return '';
+
+    	return $this->_oModule->_oConfig->getUrl('URL_ORDERS', array('type' => 'pending'));
+    }
+
     /**
      * @page service Service Calls
      * @section bx_base_payment Base Payment

+ 1 - 2
modules/base/profile/classes/BxBaseModProfileMenuSnippetMeta.php

@@ -1,5 +1,4 @@
-<?php use Symfony\Component\Finder\Tests\FakeAdapter\DummyAdapter;
-defined('BX_DOL') or die('hack attempt');
+<?php defined('BX_DOL') or die('hack attempt');
 /**
  * Copyright (c) UNA, Inc - https://una.io
  * MIT License - https://opensource.org/licenses/MIT

+ 2 - 2
modules/boonex/acl/install/langs/en.xml

@@ -69,8 +69,8 @@
 	<string name="_bx_acl_grid_action_delete"><![CDATA[Delete]]></string>
 	<string name="_bx_acl_grid_action_buy"><![CDATA[Buy]]></string>
 	<string name="_bx_acl_grid_action_buy_title"><![CDATA[One-time payment]]></string>
-	<string name="_bx_acl_grid_action_subscribe"><![CDATA[Rent]]></string>
-	<string name="_bx_acl_grid_action_subscribe_title"><![CDATA[Subscribe on recurring payments]]></string>
+	<string name="_bx_acl_grid_action_subscribe"><![CDATA[Subscribe]]></string>
+	<string name="_bx_acl_grid_action_subscribe_title"><![CDATA[Create a subscription with recurring billing]]></string>
 	<string name="_bx_acl_grid_action_choose"><![CDATA[Choose]]></string>
     <string name="_bx_acl_grid_action_choose_title"><![CDATA[Switch to the level for free]]></string>
 

+ 1 - 0
modules/boonex/ads/install/langs/en.xml

@@ -524,5 +524,6 @@
     <string name="_bx_ads_txt_poll_answer_vote_counter"><![CDATA[{0} votes]]></string>
     <string name="_bx_ads_txt_poll_answer_vote_percent"><![CDATA[({0}%)]]></string>
 
+    <string name="_bx_ads_alert_action_doInterest_personal"><![CDATA[Someone interested in your ad]]></string>
     <string name="_bx_ads_txt_ntfs_subobject_interested"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> {subentry_sample} <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
 </resources>

+ 1 - 1
modules/boonex/decorous/data/template/system/_header_js_custom.html

@@ -9,7 +9,7 @@
             sidebar = $('.cd-side-nav'),
             sidebarTrigger = $('.cd-nav-trigger');
 
-        var mq = checkMQ();            
+        var mq = bx_check_mq();            
         if (mq != 'mobile') {
             $(window).scroll(function () {
                 $.each($('.content-wrapper-wrp, .cd-side-nav'), function (index, val) {

+ 4 - 10
modules/boonex/decorous/data/template/system/_header_js_toolbar.html

@@ -1,14 +1,9 @@
 <script type="text/javascript" language="javascript">
-    function checkMQ() {
-        //check if mobile or desktop device
-        return window.getComputedStyle(document.querySelector('.cd-main-content'), '::before').getPropertyValue('content').replace(/'/g, "").replace(/"/g, "");
-    }
-
     $(document).ready(function () {
-		var mq = checkMQ();
-		if (mq != 'mobile')
-			$("#bx-logo-container").prependTo("#bx-sliding-menu-sys_site .bx-sliding-menu-main-cnt");
-		
+        var mq = bx_check_mq();
+        if (mq != 'mobile')
+            $("#bx-logo-container").prependTo("#bx-sliding-menu-sys_site .bx-sliding-menu-main-cnt");
+	
         if(mq == 'mobile') {
             // hide toolbar on scrolldown, show - on scroll up
             new Headroom($('#bx-toolbar').get(0), {
@@ -28,7 +23,6 @@
                     var sidebar = $('.cd-side-nav');
                     if(sidebar.hasClass('nav-is-visible'))
                         sidebar.toggleClass('nav-is-visible');
-
                 }
             }).init();
         }

+ 3 - 17
modules/boonex/decorous/data/template/system/css/general.css

@@ -27,24 +27,10 @@
     min-height: 100vh;
     right: 100%;
 }
-.cd-main-content::before {
-    /* never visible - used to check MQ in jQuery */
-    display: none;
-    content: 'mobile';
-}
-@media only screen and (min-width: 720px) {
-    .cd-main-content::before {
-        content: 'tablet';
-    }
-}
-
 @media only screen and (min-width: 1025px) {
-	#bx-sliding-menu-search{
-		padding-left: 200px;
-		width:calc(100% - 200px);
-	}
-    .cd-main-content::before {
-        content: 'desktop';
+    #bx-sliding-menu-search{
+        padding-left: 200px;
+        width:calc(100% - 200px);
     }
 }
 

+ 4 - 0
modules/boonex/groups/classes/BxGroupsConfig.php

@@ -56,6 +56,9 @@ class BxGroupsConfig extends BxBaseModGroupsConfig
             'FIELD_LABELS' => 'labels',
             'FIELDS_WITH_KEYWORDS' => 'auto', // can be 'auto', array of fields or comma separated string of field names, works only when OBJECT_METATAGS is specified
 
+            'FIELD_PRICE_ROLE_ID' => 'role_id',
+            'FIELD_PRICE_NAME' => 'name',
+
             // page URIs
             'URI_VIEW_ENTRY' => 'view-group-profile',
             'URI_EDIT_ENTRY' => 'edit-group-profile',
@@ -79,6 +82,7 @@ class BxGroupsConfig extends BxBaseModGroupsConfig
             'PARAM_PER_PAGE_BROWSE_RECOMMENDED' => 'bx_groups_per_page_browse_recommended',
 
             'PARAM_MMODE' => 'bx_groups_members_mode',
+            'PARAM_PAID_JOIN_ENABLED' => true,
             'PARAM_RECURRING_RESERVE' => 3, // 3 days for recurring payment to be registered
             'PARAM_PER_PAGE_FOR_FAVORITES_LISTS' => 'bx_groups_per_page_for_favorites_lists',
             'PARAM_USE_IN' => 'bx_groups_internal_notifications',

+ 22 - 0
modules/boonex/groups/classes/BxGroupsFormPrice.php

@@ -0,0 +1,22 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Groups Groups
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxGroupsFormPrice extends BxBaseModGroupsFormPrice
+{
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_groups';
+
+        parent::__construct($aInfo, $oTemplate);
+    }
+}
+
+/** @} */

+ 0 - 10
modules/boonex/groups/classes/BxGroupsModule.php

@@ -20,16 +20,6 @@ class BxGroupsModule extends BxBaseModGroupsModule
 
         $this->_aSearchableNamesExcept[] = $this->_oConfig->CNF['FIELD_JOIN_CONFIRMATION'];
     }
-
-    public function serviceGetOptionsMembersMode()
-    {
-        $CNF = &$this->_oConfig->CNF;
-
-        $aOptions = parent::serviceGetOptionsMembersMode();
-        $aOptions[] = array('key' => BX_BASE_MOD_GROUPS_MMODE_PAID_JOIN, 'value' => _t($CNF['T']['option_members_mode_' . BX_BASE_MOD_GROUPS_MMODE_PAID_JOIN]));
-
-        return $aOptions;
-    }
 }
 
 /** @} */

+ 13 - 8
modules/boonex/groups/install/langs/en.xml

@@ -56,6 +56,10 @@
     <string name="_bx_groups_form_price_display_edit"><![CDATA[Edit]]></string>
     <string name="_bx_groups_form_price_input_sys_id"><![CDATA[ID]]></string>
     <string name="_bx_groups_form_price_input_sys_role_id"><![CDATA[Role ID]]></string>
+    <string name="_bx_groups_form_price_input_sys_name"><![CDATA[Name]]></string>
+    <string name="_bx_groups_form_price_input_name"><![CDATA[Name]]></string>
+    <string name="_bx_groups_form_price_input_inf_name"><![CDATA[Unique value which will be used in back end and API calls]]></string>
+    <string name="_bx_groups_form_price_input_err_name"><![CDATA[You need to enter a unique value here]]></string>
     <string name="_bx_groups_form_price_input_sys_period"><![CDATA[Period]]></string>
     <string name="_bx_groups_form_price_input_period"><![CDATA[Period]]></string>
     <string name="_bx_groups_form_price_input_inf_period"><![CDATA[Use 0 for lifetime]]></string>
@@ -71,12 +75,13 @@
     <string name="_bx_groups_form_price_input_sys_do_cancel"><![CDATA[Cancel]]></string>
     <string name="_bx_groups_form_price_input_do_cancel"><![CDATA[Cancel]]></string>
 
-	<string name="_bx_groups_acl_action_create_profile"><![CDATA[Create Group]]></string>
-	<string name="_bx_groups_acl_action_delete_profile"><![CDATA[Delete Group]]></string>
-	<string name="_bx_groups_acl_action_view_profile"><![CDATA[View Group]]></string>
-	<string name="_bx_groups_acl_action_edit_any_profile"><![CDATA[Edit Any Group]]></string>
-	<string name="_bx_groups_acl_action_delete_any_profile"><![CDATA[Delete Any Group]]></string>
-  <string name="_bx_groups_acl_action_delete_invites"><![CDATA[Delete Invites]]></string>
+    <string name="_bx_groups_acl_action_create_profile"><![CDATA[Create Group]]></string>
+    <string name="_bx_groups_acl_action_use_paid_join"><![CDATA[Use paid join]]></string>
+    <string name="_bx_groups_acl_action_delete_profile"><![CDATA[Delete Group]]></string>
+    <string name="_bx_groups_acl_action_view_profile"><![CDATA[View Group]]></string>
+    <string name="_bx_groups_acl_action_edit_any_profile"><![CDATA[Edit Any Group]]></string>
+    <string name="_bx_groups_acl_action_delete_any_profile"><![CDATA[Delete Any Group]]></string>
+    <string name="_bx_groups_acl_action_delete_invites"><![CDATA[Delete Invites]]></string>
 
         <string name="_bx_groups_page_title_sys_entries_in_context"><![CDATA[Groups in context]]></string>
         <string name="_bx_groups_page_title_entries_in_context"><![CDATA[Groups in {display_name}]]></string>
@@ -325,8 +330,8 @@
     <string name="_bx_groups_grid_action_title_delete"><![CDATA[Delete]]></string>
     <string name="_bx_groups_grid_action_title_buy"><![CDATA[Buy]]></string>
     <string name="_bx_groups_grid_action_title_buy_title"><![CDATA[One-time payment]]></string>
-    <string name="_bx_groups_grid_action_title_subscribe"><![CDATA[Rent]]></string>
-    <string name="_bx_groups_grid_action_title_subscribe_title"><![CDATA[Subscribe on recurring payments]]></string>
+    <string name="_bx_groups_grid_action_title_subscribe"><![CDATA[Subscribe]]></string>
+    <string name="_bx_groups_grid_action_title_subscribe_title"><![CDATA[Create a subscription with recurring billing]]></string>
     <string name="_bx_groups_grid_action_title_choose"><![CDATA[Choose]]></string>
     <string name="_bx_groups_grid_action_title_choose_title"><![CDATA[Switch to the level for free]]></string>
     <string name="_bx_groups_popup_title_price_add"><![CDATA[Add Pricing Plan]]></string>

+ 10 - 0
modules/boonex/groups/install/sql/enable.sql

@@ -411,6 +411,10 @@ INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`,
 ('bx_groups', 'create entry', NULL, '_bx_groups_acl_action_create_profile', '', 1, 1);
 SET @iIdActionProfileCreate = LAST_INSERT_ID();
 
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_groups', 'use paid join', NULL, '_bx_groups_acl_action_use_paid_join', '', 1, 1);
+SET @iIdActionUsePaidJoin = LAST_INSERT_ID();
+
 INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
 ('bx_groups', 'delete entry', NULL, '_bx_groups_acl_action_delete_profile', '', 1, 1);
 SET @iIdActionProfileDelete = LAST_INSERT_ID();
@@ -452,6 +456,12 @@ INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
 (@iAdministrator, @iIdActionProfileCreate),
 (@iPremium, @iIdActionProfileCreate),
 
+-- use paid join
+(@iStandard, @iIdActionUsePaidJoin),
+(@iModerator, @iIdActionUsePaidJoin),
+(@iAdministrator, @iIdActionUsePaidJoin),
+(@iPremium, @iIdActionUsePaidJoin),
+
 -- profile delete
 (@iAccount, @iIdActionProfileDelete),
 (@iStandard, @iIdActionProfileDelete),

+ 16 - 13
modules/boonex/groups/install/sql/install.sql

@@ -359,7 +359,7 @@ INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for
 
 -- FORMS: Price
 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_groups_price', 'bx_groups', '_bx_groups_form_price', '', '', 'do_submit', 'bx_groups_prices', 'id', '', '', '', 0, 1, '', '');
+('bx_groups_price', 'bx_groups', '_bx_groups_form_price', '', '', 'do_submit', 'bx_groups_prices', 'id', '', '', '', 0, 1, 'BxGroupsFormPrice', 'modules/boonex/groups/classes/BxGroupsFormPrice.php');
 
 INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
 ('bx_groups_price_add', 'bx_groups', 'bx_groups_price', '_bx_groups_form_price_display_add', 0),
@@ -368,6 +368,7 @@ INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `v
 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_groups_price', 'bx_groups', 'id', '', '', 0, 'hidden', '_bx_groups_form_price_input_sys_id', '', '', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
 ('bx_groups_price', 'bx_groups', 'role_id', '', '', 0, 'hidden', '_bx_groups_form_price_input_sys_role_id', '', '', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_groups_price', 'bx_groups', 'name', '', '', 0, 'text', '_bx_groups_form_price_input_sys_name', '_bx_groups_form_price_input_name', '_bx_groups_form_price_input_inf_name', 1, 0, 0, '', '', '', 'Avail', '', '_bx_groups_form_price_input_err_name', 'Xss', '', 1, 0),
 ('bx_groups_price', 'bx_groups', 'period', '', '', 0, 'text', '_bx_groups_form_price_input_sys_period', '_bx_groups_form_price_input_period', '_bx_groups_form_price_input_inf_period', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
 ('bx_groups_price', 'bx_groups', 'period_unit', '', '#!bx_groups_period_units', 0, 'select', '_bx_groups_form_price_input_sys_period_unit', '_bx_groups_form_price_input_period_unit', '_bx_groups_form_price_input_inf_period_unit', 1, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
 ('bx_groups_price', 'bx_groups', 'price', '', '', 0, 'text', '_bx_groups_form_price_input_sys_price', '_bx_groups_form_price_input_price', '_bx_groups_form_price_input_inf_price', 1, 0, 0, '', '', '', '', '', '', 'Float', '', 1, 0),
@@ -378,21 +379,23 @@ INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `c
 INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
 ('bx_groups_price_add', 'id', 2147483647, 0, 1),
 ('bx_groups_price_add', 'role_id', 2147483647, 1, 2),
-('bx_groups_price_add', 'price', 2147483647, 1, 3),
-('bx_groups_price_add', 'period', 2147483647, 1, 4),
-('bx_groups_price_add', 'period_unit', 2147483647, 1, 5),
-('bx_groups_price_add', 'controls', 2147483647, 1, 6),
-('bx_groups_price_add', 'do_submit', 2147483647, 1, 7),
-('bx_groups_price_add', 'do_cancel', 2147483647, 1, 8),
+('bx_groups_price_add', 'name', 2147483647, 1, 3),
+('bx_groups_price_add', 'price', 2147483647, 1, 4),
+('bx_groups_price_add', 'period', 2147483647, 1, 5),
+('bx_groups_price_add', 'period_unit', 2147483647, 1, 6),
+('bx_groups_price_add', 'controls', 2147483647, 1, 7),
+('bx_groups_price_add', 'do_submit', 2147483647, 1, 8),
+('bx_groups_price_add', 'do_cancel', 2147483647, 1, 9),
 
 ('bx_groups_price_edit', 'id', 2147483647, 1, 1),
 ('bx_groups_price_edit', 'role_id', 2147483647, 1, 2),
-('bx_groups_price_edit', 'price', 2147483647, 1, 3),
-('bx_groups_price_edit', 'period', 2147483647, 1, 4),
-('bx_groups_price_edit', 'period_unit', 2147483647, 1, 5),
-('bx_groups_price_edit', 'controls', 2147483647, 1, 6),
-('bx_groups_price_edit', 'do_submit', 2147483647, 1, 7),
-('bx_groups_price_edit', 'do_cancel', 2147483647, 1, 8);
+('bx_groups_price_edit', 'name', 2147483647, 1, 3),
+('bx_groups_price_edit', 'price', 2147483647, 1, 4),
+('bx_groups_price_edit', 'period', 2147483647, 1, 5),
+('bx_groups_price_edit', 'period_unit', 2147483647, 1, 6),
+('bx_groups_price_edit', 'controls', 2147483647, 1, 7),
+('bx_groups_price_edit', 'do_submit', 2147483647, 1, 8),
+('bx_groups_price_edit', 'do_cancel', 2147483647, 1, 9);
 
 -- PRE-VALUES
 INSERT INTO `sys_form_pre_lists`(`key`, `title`, `module`, `use_for_sets`) VALUES

+ 26 - 0
modules/boonex/groups/js/prices.js

@@ -18,6 +18,32 @@ function BxGroupsPrices(oOptions) {
     this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
 }
 
+BxGroupsPrices.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'
+    );
+};
+
 BxGroupsPrices.prototype.onChangeRole = function() {
 	this.reloadGrid($('#bx-grid-level-' + this._sObjNameGrid).val());
 };

+ 1 - 6
modules/boonex/lucid/data/template/system/_header_js_toolbar.html

@@ -1,11 +1,6 @@
 <script language="javascript">
     var iToolbarSubmenuTopOffset = -2;
 
-    function checkMQ() {
-        //check if mobile or desktop device
-        return window.getComputedStyle(document.querySelector('.cd-main-content'), '::before').getPropertyValue('content').replace(/'/g, "").replace(/"/g, "");
-    }
-
     $(document).ready(function () {
 
         // hide toolbar on scrolldown, show - on scroll up
@@ -25,7 +20,7 @@
             }
         }).init();
 
-        var mq = checkMQ();
+        var mq = bx_check_mq();
         if(mq == 'mobile') {
             //process submenu in top menu
             $('.bx-menu-floating-blocks-big:visible > li:has(.bx-sliding-smenu-submenu) > a').bind('click', function() {

+ 0 - 15
modules/boonex/lucid/data/template/system/css/general.css

@@ -52,21 +52,6 @@
     min-height: 100%;
     min-height: 100vh;
 }
-.cd-main-content::before {
-  /* never visible - used to check MQ in jQuery */
-  display: none;
-  content: 'mobile';
-}
-@media only screen and (min-width: 720px) {
-  .cd-main-content::before {
-    content: 'tablet';
-  }
-}
-@media only screen and (min-width: 1024px) {
-  .cd-main-content::before {
-    content: 'desktop';
-  }
-}
 
 /*--- Header ---*/
 .cd-main-header {

+ 5 - 5
modules/boonex/market/install/langs/en.xml

@@ -40,8 +40,8 @@
 	<string name="_bx_market_form_entry_input_price_single"><![CDATA[Price]]></string>
     <string name="_bx_market_form_entry_input_price_single_inf"><![CDATA[Leave empty to make the product free.]]></string>
     <string name="_bx_market_form_entry_input_sys_price_recurring"><![CDATA[Recurring price]]></string>
-	<string name="_bx_market_form_entry_input_price_recurring"><![CDATA[Rent]]></string>
-    <string name="_bx_market_form_entry_input_price_recurring_inf"><![CDATA[Select 'None' to make the product free.]]></string>
+	<string name="_bx_market_form_entry_input_price_recurring"><![CDATA[Price]]></string>
+    <string name="_bx_market_form_entry_input_price_recurring_inf"><![CDATA[Leave empty to make the product free.]]></string>
     <string name="_bx_market_form_entry_input_sys_duration_recurring"><![CDATA[Billing period]]></string>
     <string name="_bx_market_form_entry_input_duration_recurring"><![CDATA[Billing period]]></string>
     <string name="_bx_market_form_entry_input_sys_trial_recurring"><![CDATA[Recurring trial]]></string>
@@ -264,8 +264,8 @@
     <string name="_bx_market_menu_item_title_delete_entry"><![CDATA[Delete]]></string>
     <string name="_bx_market_menu_item_title_system_add_to_cart"><![CDATA[Add to Cart]]></string>
     <string name="_bx_market_menu_item_title_add_to_cart"><![CDATA[Buy for {0}{1}]]></string>
-    <string name="_bx_market_menu_item_title_system_subscribe"><![CDATA[Rent]]></string>
-    <string name="_bx_market_menu_item_title_subscribe"><![CDATA[Rent for {0}{1}/{2}]]></string>
+    <string name="_bx_market_menu_item_title_system_subscribe"><![CDATA[Subscribe]]></string>
+    <string name="_bx_market_menu_item_title_subscribe"><![CDATA[Subscribe for {0}{1}/{2}]]></string>
     <string name="_bx_market_menu_item_title_system_download"><![CDATA[Download Product]]></string>
     <string name="_bx_market_menu_item_title_download"><![CDATA[Download]]></string>
     <string name="_bx_market_menu_item_title_system_product_more"><![CDATA[More Actions]]></string>
@@ -446,7 +446,7 @@
     <string name="_bx_market_txt_last_active"><![CDATA[Last Active:]]></string>
     <string name="_bx_market_txt_price_single"><![CDATA[Buy for {0}{1}]]></string>
     <string name="_bx_market_txt_price_single_short"><![CDATA[{0}{1}]]></string>
-    <string name="_bx_market_txt_price_recurring"><![CDATA[Rent for {0}{1}/{2}]]></string>
+    <string name="_bx_market_txt_price_recurring"><![CDATA[Subscribe for {0}{1}/{2}]]></string>
     <string name="_bx_market_txt_price_recurring_short"><![CDATA[{0}{1}/{2}]]></string>
     <string name="_bx_market_txt_price_free"><![CDATA[Free]]></string>
     <string name="_bx_market_txt_per_hour"><![CDATA[hour]]></string>

+ 18 - 0
modules/boonex/notifications/classes/BxNtfsConfig.php

@@ -191,6 +191,24 @@ class BxNtfsConfig extends BxBaseModNotificationsConfig
         return BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=notifications-view');
     }
 
+    public function getContentModule(&$aEvent)
+    {
+        $sModule = $aEvent['type'];
+        if($this->_oDb->isEnabledByName($sModule))
+            return $sModule;
+
+        $sModule = str_replace(array('_media', '_reactions'), array('', ''), $sModule);
+        if($this->_oDb->isEnabledByName($sModule))
+            return $sModule;
+
+        return '';
+    }
+
+    public function getContentObjectId(&$aEvent)
+    {
+        return !empty($aEvent['content']['object_id']) ? (int)$aEvent['content']['object_id'] : (int)$aEvent['object_id'];
+    }
+
     public function getProfileBasedModules() 
     {
         if ($this->_aModulesProfiles !== false && $this->_aModulesContexts !== false)

+ 28 - 2
modules/boonex/notifications/classes/BxNtfsModule.php

@@ -304,10 +304,36 @@ class BxNtfsModule extends BxBaseModNotificationsModule
         $aParams = $this->_prepareParams(BX_NTFS_TYPE_DEFAULT, $iOwnerId);
         $aParams = array_merge($aParams, array(
             'new' => 1,
-            'count_only' => 1
         ));
 
-        return (int)$this->_oDb->getEvents($aParams);
+        $sParamCheck = 'perform_privacy_check';
+        $sParamCheckFor = 'perform_privacy_check_for';
+
+        $iEvents = 0;
+        $aEvents = $this->_oDb->getEvents($aParams);
+        foreach($aEvents as $aEvent) {
+            if(isset($aBrowseParams[$sParamCheck]) && $aBrowseParams[$sParamCheck] !== true) 
+                continue;
+
+            $iViewerId = !empty($aBrowseParams[$sParamCheckFor]) ? (int)$aBrowseParams[$sParamCheckFor] : 0;
+
+            $oPrivacyInt = BxDolPrivacy::getObjectInstance($this->_oConfig->getObject('privacy_view'));
+            if(!$oPrivacyInt->check($aEvent['id'], $iViewerId))
+                continue;
+
+            $oPrivacyExt = $this->_oConfig->getPrivacyObject($aEvent['type'] . '_' . $aEvent['action']);
+            if($oPrivacyExt !== false && !$oPrivacyExt->check($aEvent['id'], $iViewerId))
+                continue;
+
+            $sSrvModule = $this->_oConfig->getContentModule($aEvent);
+            $sSrvMethod = 'check_allowed_with_content_for_profile';
+            if($sSrvModule && BxDolRequest::serviceExists($sSrvModule, $sSrvMethod) && BxDolService::call($sSrvModule, $sSrvMethod, array('view', $this->_oConfig->getContentObjectId($aEvent), $iViewerId)) !== CHECK_ACTION_RESULT_ALLOWED)
+                continue;
+
+            $iEvents++;
+        }
+
+        return $iEvents;
     }
 
     /**

+ 3 - 21
modules/boonex/notifications/classes/BxNtfsTemplate.php

@@ -138,13 +138,13 @@ class BxNtfsTemplate extends BxBaseModNotificationsTemplate
             if(!$oPrivacyInt->check($aEvent['id'], $iViewerId))
                 return '';
 
-            $oPrivacyExt = $oModule->_oConfig->getPrivacyObject($aEvent['type'] . '_' . $aEvent['action']);
+            $oPrivacyExt = $this->_oConfig->getPrivacyObject($aEvent['type'] . '_' . $aEvent['action']);
             if($oPrivacyExt !== false && !$oPrivacyExt->check($aEvent['id'], $iViewerId))
                 return '';
 
-            $sSrvModule = $this->_getContentModule($aEvent);
+            $sSrvModule = $this->_oConfig->getContentModule($aEvent);
             $sSrvMethod = 'check_allowed_with_content_for_profile';
-            if($sSrvModule && BxDolRequest::serviceExists($sSrvModule, $sSrvMethod) && BxDolService::call($sSrvModule, $sSrvMethod, array('view', $this->_getContentObjectId($aEvent), $iViewerId)) !== CHECK_ACTION_RESULT_ALLOWED)
+            if($sSrvModule && BxDolRequest::serviceExists($sSrvModule, $sSrvMethod) && BxDolService::call($sSrvModule, $sSrvMethod, array('view', $this->_oConfig->getContentObjectId($aEvent), $iViewerId)) !== CHECK_ACTION_RESULT_ALLOWED)
                 return '';
         }
 
@@ -349,24 +349,6 @@ class BxNtfsTemplate extends BxBaseModNotificationsTemplate
         return $this->$sMethod($aEvent);
     }
 
-    protected function _getContentModule(&$aEvent)
-    {
-        $sModule = $aEvent['type'];
-        if($this->_oDb->isEnabledByName($sModule))
-            return $sModule;
-
-        $sModule = str_replace(array('_media', '_reactions'), array('', ''), $sModule);
-        if($this->_oDb->isEnabledByName($sModule))
-            return $sModule;
-
-        return '';
-    }
-
-    protected function _getContentObjectId(&$aEvent)
-    {
-        return !empty($aEvent['content']['object_id']) ? (int)$aEvent['content']['object_id'] : (int)$aEvent['object_id'];
-    }
-
     protected function _getContentLangKey(&$aEvent)
     {
         if(!empty($aEvent['subobject_id']))

+ 1 - 1
modules/boonex/organizations/install/sql/enable.sql

@@ -420,7 +420,7 @@ INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `tit
 ('sys_profile_stats', 'bx_organizations', 'profile-stats-manage-organizations', '_bx_orgs_menu_item_title_system_manage_my_organizations', '_bx_orgs_menu_item_title_manage_my_organizations', 'page.php?i=organizations-manage', '', '_self', 'briefcase col-red2', 'a:2:{s:6:"module";s:16:"bx_organizations";s:6:"method";s:41:"get_menu_addon_manage_tools_profile_stats";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 2),
 ('sys_profile_stats', 'bx_organizations', 'profile-stats-favorite-organizations', '_bx_orgs_menu_item_title_system_favorites', '_bx_orgs_menu_item_title_favorites', 'page.php?i=organization-profile-favorites&profile_id={member_id}', '', '', 'star col-red2', 'a:2:{s:6:"module";s:16:"bx_organizations";s:6:"method";s:38:"get_menu_addon_favorites_profile_stats";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 3),
 ('sys_profile_stats', 'bx_organizations', 'profile-stats-subscriptions', '_bx_orgs_menu_item_title_system_subscriptions', '_bx_orgs_menu_item_title_subscriptions', 'page.php?i=organization-profile-subscriptions&profile_id={member_id}#subscriptions', '', '_self', 'rss col-red2', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"get_connected_content_num";s:6:"params";a:1:{i:0;s:26:"sys_profiles_subscriptions";}s:5:"class";s:23:"TemplServiceConnections";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 4),
-('sys_profile_stats', 'bx_organizations', 'profile-stats-subscribed-me', '_bx_orgs_menu_item_title_system_subscribed_me', '_bx_orgs_menu_item_title_subscribed_me', 'page.php?i=organization-profile-subscribed-me&profile_id={member_id}#subscribers', '', '_self', 'rss col-red2', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:28:"get_connected_initiators_num";s:6:"params";a:1:{i:0;s:26:"sys_profiles_subscriptions";}s:5:"class";s:23:"TemplServiceConnections";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 5),
+('sys_profile_stats', 'bx_organizations', 'profile-stats-subscribed-me', '_bx_orgs_menu_item_title_system_subscribed_me', '_bx_orgs_menu_item_title_subscribed_me', 'page.php?i=organization-profile-subscriptions&profile_id={member_id}#subscribers', '', '_self', 'rss col-red2', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:28:"get_connected_initiators_num";s:6:"params";a:1:{i:0;s:26:"sys_profiles_subscriptions";}s:5:"class";s:23:"TemplServiceConnections";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 5),
 ('sys_profile_stats', 'bx_organizations', 'profile-stats-relations', '_bx_orgs_menu_item_title_system_relations', '_bx_orgs_menu_item_title_relations', 'page.php?i=organization-profile-relations&profile_id={member_id}#relations', '', '_self', 'sync col-blue3', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"get_connected_content_num";s:6:"params";a:3:{i:0;s:22:"sys_profiles_relations";i:1;i:0;i:2;i:1;}s:5:"class";s:23:"TemplServiceConnections";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 6),
 ('sys_profile_stats', 'bx_organizations', 'profile-stats-related-me', '_bx_orgs_menu_item_title_system_related_me', '_bx_orgs_menu_item_title_related_me', 'page.php?i=organization-profile-relations&profile_id={member_id}#related-me', '', '_self', 'sync col-blue3', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:28:"get_connected_initiators_num";s:6:"params";a:3:{i:0;s:22:"sys_profiles_relations";i:1;i:0;i:2;i:1;}s:5:"class";s:23:"TemplServiceConnections";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 7);
 

+ 2 - 1
modules/boonex/payment/classes/BxPaymentModule.php

@@ -474,7 +474,8 @@ class BxPaymentModule extends BxBaseModPaymentModule
             'profile_link' => $oBuyer->getUrl(),
             'items' => $sItems,
             'amount' => $aData['currency']['sign'] . sprintf("%.2f", (float)($aData['amount'])),
-            'date' => bx_time_js(time(), BX_FORMAT_DATE, true)
+            'date' => bx_time_js(time(), BX_FORMAT_DATE, true),
+            'pending_orders_link' => $this->getObjectOrders()->serviceGetPendingOrdersUrl()
         ), 0, $iSeller);
 
         $sEmail = '';

+ 8 - 5
modules/boonex/payment/classes/BxPaymentOrders.php

@@ -44,12 +44,15 @@ class BxPaymentOrders extends BxBaseModPaymentOrders
     /** 
      * @ref bx_payment-get_block_orders "get_block_orders"
      */
-	public function serviceGetBlockOrders($sType = '', $iUserId = BX_PAYMENT_EMPTY_ID)
+    public function serviceGetBlockOrders($sType = '', $iUserId = BX_PAYMENT_EMPTY_ID)
     {
         $CNF = &$this->_oModule->_oConfig->CNF;
 
-    	if(empty($sType))
-    		$sType = bx_get('type') !== false ? bx_process_input(bx_get('type')) : BX_PAYMENT_ORDERS_TYPE_PROCESSED;
+    	if(empty($sType) && bx_get('type') !== false)
+            $sType = bx_process_input(bx_get('type'));
+        
+        if(empty($sType) || !in_array($sType, array(BX_PAYMENT_ORDERS_TYPE_PENDING, BX_PAYMENT_ORDERS_TYPE_PROCESSED)))
+            $sType = BX_PAYMENT_ORDERS_TYPE_PROCESSED;
 
     	if(!$this->_oModule->isLogged())
             return array(
@@ -68,8 +71,8 @@ class BxPaymentOrders extends BxBaseModPaymentOrders
             $oBlockSubmenu->setSelected($this->MODULE, 'orders-' . $sType);
 
         return array(
-        	'title' => _t($this->_sLangsPrefix . 'page_block_title_orders_' . $sType),
-        	'content' => $this->_oModule->_oTemplate->displayBlockOrders($sType, $iUserId),
+            'title' => _t($this->_sLangsPrefix . 'page_block_title_orders_' . $sType),
+            'content' => $this->_oModule->_oTemplate->displayBlockOrders($sType, $iUserId),
             'menu' => $oBlockSubmenu
         );
     }

+ 11 - 2
modules/boonex/payment/classes/BxPaymentProviderChargebeeV3.php

@@ -93,6 +93,15 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
         $this->_oModule->_oTemplate->addCss($this->_aIncludeCss);
     }
 
+    public function getJsObject($aParams = array())
+    {
+        $sJsObject = $this->_oModule->_oConfig->getJsObject($this->_sName);
+        if(isset($aParams['iModuleId'], $aParams['iSellerId'], $aParams['iItemId']))
+            $sJsObject .= '_' . md5($aParams['iModuleId'] . '-' . $aParams['iSellerId'] . '-' . $aParams['iItemId']);
+        
+        return $sJsObject;
+    }
+
     public function initializeCheckout($iPendingId, $aCartInfo, $sRedirect = '')
     {
         $sPageId = bx_process_input(bx_get('page_id'));
@@ -255,8 +264,6 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
     
     protected function _getButtonJs($sType, $iClientId, $iVendorId, $aParams = array())
     {
-        $sJsObject = $this->_oModule->_oConfig->getJsObject($this->_sName);
-
         $sSite = '';
         bx_alert($this->_oModule->_oConfig->getName(), $this->_sName . '_get_button', 0, $iClientId, array(
             'type' => &$sType, 
@@ -265,6 +272,7 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
         ));
 
         $sJsMethod = '';
+        $sJsObject = $this->getJsObject($aParams);
         switch($sType) {
             case BX_PAYMENT_TYPE_SINGLE:
                 /**
@@ -278,6 +286,7 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
         }
 
         return array($this->_oModule->_oTemplate->getJsCode($this->_sName, array_merge(array(
+            'js_object' => $sJsObject,
             'sProvider' => $this->_sName,
             'sSite' => !empty($sSite) ? $sSite : $this->_getSite(),
             'iClientId' => $iClientId

+ 1 - 0
modules/boonex/payment/install/langs/en.xml

@@ -328,6 +328,7 @@
 <p>A site member <a href="{profile_link}">{profile_name}</a> wanted to pay you with cash for:</p>
 <p>{items}</p>
 <p>Amount: {amount}<br />Date: {date}</p>
+<p>Go to the <a href="{pending_orders_link}">pending orders page</a> to process the order once the payment is received.</p>
 {email_footer}]]></string>
 
 	<!-- MESSAGES, WARNINGS and ERRORS -->

+ 2 - 2
modules/boonex/russian/data/langs/bx_acl/ru.xml

@@ -69,8 +69,8 @@
 	<string name="_bx_acl_grid_action_delete"><![CDATA[Удалить]]></string>
 	<string name="_bx_acl_grid_action_buy"><![CDATA[Купить]]></string>
 	<string name="_bx_acl_grid_action_buy_title"><![CDATA[Единовременный платеж]]></string>
-	<string name="_bx_acl_grid_action_subscribe"><![CDATA[Арендовать]]></string>
-	<string name="_bx_acl_grid_action_subscribe_title"><![CDATA[Подписка на периодические платежи]]></string>
+	<string name="_bx_acl_grid_action_subscribe"><![CDATA[Подписаться]]></string>
+	<string name="_bx_acl_grid_action_subscribe_title"><![CDATA[Создать подписку с регулярным выставлением счетов]]></string>
 	<string name="_bx_acl_grid_action_choose"><![CDATA[Выбирать]]></string>
     <string name="_bx_acl_grid_action_choose_title"><![CDATA[Бесплатно переключиться на уровень]]></string>
 

+ 2 - 1
modules/boonex/russian/data/langs/bx_ads/ru.xml

@@ -523,6 +523,7 @@
     <string name="_bx_ads_txt_poll_answer_vote_do_by"><![CDATA[Проголосовали за]]></string>
     <string name="_bx_ads_txt_poll_answer_vote_counter"><![CDATA[{0} голосов]]></string>
     <string name="_bx_ads_txt_poll_answer_vote_percent"><![CDATA[({0}%)]]></string>
-    
+
+    <string name="_bx_ads_alert_action_doInterest_personal"><![CDATA[Кто-то заинтересовался вашим объявлением]]></string>
     <string name="_bx_ads_txt_ntfs_subobject_interested"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> {subentry_sample} <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
 </resources>

+ 7 - 2
modules/boonex/russian/data/langs/bx_groups/ru.xml

@@ -56,6 +56,10 @@
     <string name="_bx_groups_form_price_display_edit"><![CDATA[Редактирование]]></string>
     <string name="_bx_groups_form_price_input_sys_id"><![CDATA[ID]]></string>
     <string name="_bx_groups_form_price_input_sys_role_id"><![CDATA[ID роли]]></string>
+    <string name="_bx_groups_form_price_input_sys_name"><![CDATA[Имя]]></string>
+    <string name="_bx_groups_form_price_input_name"><![CDATA[Имя]]></string>
+    <string name="_bx_groups_form_price_input_inf_name"><![CDATA[Уникальное значение, которое будет использоваться в серверной части и API запросах]]></string>
+    <string name="_bx_groups_form_price_input_err_name"><![CDATA[Введите уникальное значение]]></string>
     <string name="_bx_groups_form_price_input_sys_period"><![CDATA[Период]]></string>
     <string name="_bx_groups_form_price_input_period"><![CDATA[Период]]></string>
     <string name="_bx_groups_form_price_input_inf_period"><![CDATA[Используйте 0 для пожизненного]]></string>
@@ -72,6 +76,7 @@
     <string name="_bx_groups_form_price_input_do_cancel"><![CDATA[Отменить]]></string>
 
 	<string name="_bx_groups_acl_action_create_profile"><![CDATA[Создать группу]]></string>
+        <string name="_bx_groups_acl_action_use_paid_join"><![CDATA[Использовать платный вход в группу]]></string>
 	<string name="_bx_groups_acl_action_delete_profile"><![CDATA[Удалить группу]]></string>
 	<string name="_bx_groups_acl_action_view_profile"><![CDATA[Просмотреть группу]]></string>
 	<string name="_bx_groups_acl_action_edit_any_profile"><![CDATA[Редактировать любую группу]]></string>
@@ -321,8 +326,8 @@
     <string name="_bx_groups_grid_action_title_delete"><![CDATA[Удалить]]></string>
     <string name="_bx_groups_grid_action_title_buy"><![CDATA[Купить]]></string>
     <string name="_bx_groups_grid_action_title_buy_title"><![CDATA[Единовременный платеж]]></string>
-    <string name="_bx_groups_grid_action_title_subscribe"><![CDATA[Арендовать]]></string>
-    <string name="_bx_groups_grid_action_title_subscribe_title"><![CDATA[Подписка на периодические платежи]]></string>
+    <string name="_bx_groups_grid_action_title_subscribe"><![CDATA[Подписаться]]></string>
+    <string name="_bx_groups_grid_action_title_subscribe_title"><![CDATA[Создать подписку с регулярным выставлением счетов]]></string>
     <string name="_bx_groups_grid_action_title_choose"><![CDATA[Выбирать]]></string>
     <string name="_bx_groups_grid_action_title_choose_title"><![CDATA[Бесплатно переключиться на уровень]]></string>
     <string name="_bx_groups_popup_title_price_add"><![CDATA[Добавить ценовой план]]></string>

+ 5 - 5
modules/boonex/russian/data/langs/bx_market/ru.xml

@@ -40,8 +40,8 @@
 	<string name="_bx_market_form_entry_input_price_single"><![CDATA[Цена]]></string>
     <string name="_bx_market_form_entry_input_price_single_inf"><![CDATA[Оставьте пустым, чтобы сделать продукт бесплатным.]]></string>
 	<string name="_bx_market_form_entry_input_sys_price_recurring"><![CDATA[Периодические выплаты]]></string>
-	<string name="_bx_market_form_entry_input_price_recurring"><![CDATA[Рента]]></string>
-    <string name="_bx_market_form_entry_input_price_recurring_inf"><![CDATA[Выберите 'Нет', чтобы сделать продукт бесплатным.]]></string>
+	<string name="_bx_market_form_entry_input_price_recurring"><![CDATA[Цена]]></string>
+    <string name="_bx_market_form_entry_input_price_recurring_inf"><![CDATA[Оставьте пустым, чтобы сделать продукт бесплатным.]]></string>
 	<string name="_bx_market_form_entry_input_sys_duration_recurring"><![CDATA[Период оплаты]]></string>
     <string name="_bx_market_form_entry_input_duration_recurring"><![CDATA[Период оплаты]]></string>
     <string name="_bx_market_form_entry_input_sys_trial_recurring"><![CDATA[Пробный период]]></string>
@@ -260,8 +260,8 @@
     <string name="_bx_market_menu_item_title_delete_entry"><![CDATA[Удалить продукт]]></string>
 	<string name="_bx_market_menu_item_title_system_add_to_cart"><![CDATA[Купить]]></string>
     <string name="_bx_market_menu_item_title_add_to_cart"><![CDATA[Купить за {0}{1}]]></string>
-    <string name="_bx_market_menu_item_title_system_subscribe"><![CDATA[Арендовать]]></string>
-    <string name="_bx_market_menu_item_title_subscribe"><![CDATA[Арендовать за {0}{1}/{2}]]></string>
+    <string name="_bx_market_menu_item_title_system_subscribe"><![CDATA[Подписаться]]></string>
+    <string name="_bx_market_menu_item_title_subscribe"><![CDATA[Подписаться за {0}{1}/{2}]]></string>
     <string name="_bx_market_menu_item_title_system_download"><![CDATA[Скачать]]></string>
     <string name="_bx_market_menu_item_title_download"><![CDATA[Скачать]]></string>
     <string name="_bx_market_menu_item_title_system_product_more"><![CDATA[Ещё действия]]></string>
@@ -441,7 +441,7 @@
     <string name="_bx_market_txt_last_active"><![CDATA[Последняя активность:]]></string>
     <string name="_bx_market_txt_price_single"><![CDATA[Купить за {0}{1}]]></string>
     <string name="_bx_market_txt_price_single_short"><![CDATA[{0}{1}]]></string>
-    <string name="_bx_market_txt_price_recurring"><![CDATA[Арендовать за {0}{1}/{2}]]></string>
+    <string name="_bx_market_txt_price_recurring"><![CDATA[Подписаться за {0}{1}/{2}]]></string>
     <string name="_bx_market_txt_price_recurring_short"><![CDATA[{0}{1}/{2}]]></string>
     <string name="_bx_market_txt_price_free"><![CDATA[Бесплатно]]></string>
     <string name="_bx_market_txt_per_hour"><![CDATA[час]]></string>

+ 1 - 0
modules/boonex/russian/data/langs/bx_payment/ru.xml

@@ -329,6 +329,7 @@
 <p>Пользователь сайта <a href="{profile_link}">{profile_name}</a> хотел заплатить Вам наличными за:</p>
 <p>{items}</p>
 <p>Сумма: {amount}<br />Дата: {date}</p>
+<p>Перейдите на <a href="{pending_orders_link}">страницу неоплаченных заказов</a> для обработки заказа, когда оплата будет получена.</p>
 {email_footer}]]></string>
 
 

+ 1 - 1
modules/boonex/shopify/classes/BxShopifyConfig.php

@@ -39,7 +39,7 @@ class BxShopifyConfig extends BxBaseModTextConfig
             'FIELD_CHANGED' => 'changed',
             'FIELD_CODE' => 'code',
             'FIELD_TITLE' => 'title',
-            'FIELD_TEXT' => 'text',
+            'FIELD_TEXT' => '',
             'FIELD_TEXT_ID' => 'shopify-entry-text',
             'FIELD_CATEGORY' => 'cat',
             'FIELD_ALLOW_VIEW_TO' => 'allow_view_to',

+ 5 - 0
modules/boonex/shopify/js/shop.js

@@ -135,8 +135,13 @@ BxShopifyShop.prototype.onGetEntry = function(oProduct) {
     //--- Fetch price.
     if(oProduct.variants.length > 0) {
         var oBuy = $('#' + this._aHtmlIds['entry_buy']);
+
         var sBuyContent = oBuy.html();
         oBuy.html(sBuyContent.replace('{price}', oProduct.variants[0].price)).show();
+
+        var oItem = oBuy.parents('.bx-menu-item:first');
+        if(oItem.attr('bx-mma-width'))
+            oItem.attr('bx-mma-width', oItem.outerWidth());
     }
 
     bx_loading($('.' + this._sClassCover), false);

+ 2 - 0
modules/boonex/timeline/classes/BxTimelineModule.php

@@ -4002,6 +4002,8 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
 
         $this->_iOwnerId = $aParams['owner_id'];
         $oProfileOwner = BxDolProfile::getInstance($this->_iOwnerId);
+        if(!$oProfileOwner)
+            return array();
 
         $mixedResult = $oProfileOwner->checkAllowedProfileView();
         if($mixedResult !== CHECK_ACTION_RESULT_ALLOWED)

+ 1 - 1
storage_uploader.php

@@ -50,7 +50,7 @@ switch ($sAction) {
         break;
 
     case 'restore_ghosts':
-        header('Content-type: text/html; charset=utf-8');
+        header('Content-Type: application/json; charset=utf-8');
         $sImagesTranscoder = bx_process_input(bx_get('img_trans'));
         echo $oUploader->getGhosts((int)bx_get_logged_profile_id(), $sFormat, $sImagesTranscoder, $iContentId);
         break;

+ 1 - 1
studio/template/scripts/BxBaseStudioBadgesGrid.php

@@ -194,7 +194,7 @@ class BxBaseStudioBadgesGrid extends BxDolStudioBadgesGrid
     
     protected function _getCellModule ($mixedValue, $sKey, $aField, $aRow)
     {
-        $oModule = bxDolModule::getInstance($aRow['module']);
+        $oModule = BxDolModule::getInstance($aRow['module']);
         if($oModule && $oModule instanceof iBxDolContentInfoService){
             $mixedValue = $oModule->_aModule['title'];
         }

+ 1 - 1
template/comments_block.html

@@ -4,7 +4,7 @@
         __content_before__
         <a id="__list_anchor__" class="bx-anchor-invisible"></a>
         <ul class="cmts cmts-pinned">__comments_pinned__</ul>
-        <div class="bx-def-hr"></div>
+        <div class="cmts-divider bx-def-hr" style="<bx_if:show_divider_hidden>display:none</bx_if:show_divider_hidden>"></div>
         <ul class="cmts cmts-all">__comments__</ul>
         __content_after__
     </div>

+ 6 - 0
template/css/media-desktop.css

@@ -3,6 +3,12 @@ html.bx-media-desktop {
     font-size:100%;
 }
 
+/* never visible - used to check MQ */
+.bx-media-desktop body::before {
+    display: none;
+    content: 'desktop';
+}
+
 .bx-media-desktop .bx-def-media-desktop-hide {
     display:none !important;
 }

+ 6 - 0
template/css/media-phone.css

@@ -3,6 +3,12 @@ html.bx-media-phone {
     font-size:85%;
 }
 
+/* never visible - used to check MQ */
+.bx-media-phone body::before {
+  display: none;
+  content: 'mobile';
+}
+
 .bx-media-phone .bx-def-media-phone-hide {
     display:none !important;
 }

+ 6 - 0
template/css/media-tablet.css

@@ -3,6 +3,12 @@ html.bx-media-tablet {
     font-size:100%;
 }
 
+/* never visible - used to check MQ */
+.bx-media-tablet body::before {
+    display: none;
+    content: 'tablet';
+}
+
 .bx-media-tablet .bx-def-media-tablet-hide {
     display:none !important;
 }

+ 4 - 0
template/scripts/BxBaseCmts.php

@@ -175,6 +175,10 @@ class BxBaseCmts extends BxDolCmts
             'content_before' => $sContentBefore,
             'comments' => $sComments,
             'comments_pinned' => $sCommentsPinned,
+            'bx_if:show_divider_hidden' => array(
+                'condition' => empty($sCommentsPinned),
+                'content' => array()
+            ),
             'content_after' => $sContentAfter,
             'post_form_top' => $sPostFormTop,
             'post_form_bottom'  => $sPostFormBottom,