فهرست منبع

Ticket #1694 - Core changes.

AntonLV 7 سال پیش
والد
کامیت
cac1f8125f

+ 5 - 1
inc/classes/BxDolPrivacy.php

@@ -63,6 +63,8 @@ class BxDolPrivacy extends BxDolFactory implements iBxDolFactoryObject
     protected $_sObject;
     protected $_aObject;
 
+    protected $_aGroupsExclude;
+
     /**
      * Constructor
      * @param $aObject array of grid options
@@ -76,6 +78,8 @@ class BxDolPrivacy extends BxDolFactory implements iBxDolFactoryObject
 
         $this->_oDb = new BxDolPrivacyQuery();
         $this->_oDb->init($this->_aObject);
+
+        $this->_aGroupsExclude = array();
     }
 
     /**
@@ -423,7 +427,7 @@ class BxDolPrivacy extends BxDolFactory implements iBxDolFactoryObject
         $aValues = array();
         $aGroups = $this->_oDb->getGroupsBy(array('type' => 'active'));
         foreach($aGroups as $aGroup) {
-            if((int)$aGroup['active'] == 0)
+            if((int)$aGroup['active'] == 0 || in_array($aGroup['id'], $this->_aGroupsExclude))
                continue;
 
             $aValues[] = array('key' => $aGroup['id'], 'value' => _t($aGroup['title']));

+ 28 - 28
modules/base/general/classes/BxBaseModGeneralModule.php

@@ -1086,36 +1086,9 @@ class BxBaseModGeneralModule extends BxDolModule
 
         return $iProfileId;
     }
-    
-    // ====== PERMISSION METHODS
-
-    public function checkAllowedSetThumb ($iContentId = 0)
-    {
-        return CHECK_ACTION_RESULT_ALLOWED;
-    }
-    
-    /**
-     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make "true === " checking.
-     */
-    public function checkAllowedBrowse ()
-    {
-        // check alert to allow custom checks
-        $mixedResult = null;
-        bx_alert('system', 'check_allowed_browse', 0, 0, array('module' => $this->getName(), 'profile_id' => $this->_iProfileId, 'override_result' => &$mixedResult));
-        if($mixedResult !== null)
-            return $mixedResult;
-
-        return CHECK_ACTION_RESULT_ALLOWED;
-    }
 
-    /**
-     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
-     */
-    public function checkAllowedView ($aDataEntry, $isPerformAction = false)
-    {
-        return $this->serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction);
-    }
 
+    // ====== PERMISSION METHODS
     public function serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction = false, $iProfileId = false)
     {
         if (!$iProfileId)
@@ -1148,6 +1121,33 @@ class BxBaseModGeneralModule extends BxDolModule
         return CHECK_ACTION_RESULT_ALLOWED;
     }
 
+    public function checkAllowedSetThumb ($iContentId = 0)
+    {
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+    
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make "true === " checking.
+     */
+    public function checkAllowedBrowse ()
+    {
+        // check alert to allow custom checks
+        $mixedResult = null;
+        bx_alert('system', 'check_allowed_browse', 0, 0, array('module' => $this->getName(), 'profile_id' => $this->_iProfileId, 'override_result' => &$mixedResult));
+        if($mixedResult !== null)
+            return $mixedResult;
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
+     */
+    public function checkAllowedView ($aDataEntry, $isPerformAction = false)
+    {
+        return $this->serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction);
+    }
+
     /**
      * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
      */

+ 2 - 10
modules/base/groups/classes/BxBaseModGroupsModule.php

@@ -530,17 +530,8 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
         return $a;
     }
 
-    /**
-     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
-     */
-    public function checkAllowedPost ($aDataEntry, $isPerformAction = false)
-    {
-        if (isLogged() && ($this->isFan($aDataEntry[$this->_oConfig->CNF['FIELD_ID']]) || ($aDataEntry[$this->_oConfig->CNF['FIELD_ID']] == BxDolProfile::getInstance()->getContentId() && BxDolProfile::getInstance()->getModule() == $this->getName())))
-            return CHECK_ACTION_RESULT_ALLOWED;
-
-        return _t('_sys_txt_access_denied');
-    }
 
+    // ====== PERMISSION METHODS
     /**
      * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
      */
@@ -553,6 +544,7 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
     {
         if ($this->isFan($aDataEntry[$this->_oConfig->CNF['FIELD_ID']], $iProfileId))
             return CHECK_ACTION_RESULT_ALLOWED;
+
         return parent::serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction, $iProfileId);
     }
 

+ 15 - 25
modules/base/groups/classes/BxBaseModGroupsPrivacy.php

@@ -9,16 +9,20 @@
  * @{
  */
 
+define('BX_BASE_MOD_GROUPS_PG_CLOSED', 'c');
+define('BX_BASE_MOD_GROUPS_PG_SECRET', 's');
+
 class BxBaseModGroupsPrivacy extends BxBaseModProfilePrivacy
-{    
+{
     public function __construct($aOptions, $oTemplate = false)
     {
         parent::__construct($aOptions, $oTemplate);
 
-        $this->_aPrivacyParticallyVisible = array_merge($this->_aPrivacyParticallyVisible, array('c', BX_DOL_PG_FRIENDS));
+        $this->_aGroupsExclude = array(BX_DOL_PG_FRIENDS);
+        $this->_aPrivacyParticallyVisible = array('c');
     }
 
-	/**
+    /**
      * Check whethere viewer is a member of dynamic group.
      *
      * @param  mixed   $mixedGroupId   dynamic group ID.
@@ -28,10 +32,10 @@ class BxBaseModGroupsPrivacy extends BxBaseModProfilePrivacy
      */
     public function isDynamicGroupMember($mixedGroupId, $iObjectOwnerId, $iViewerId, $iObjectId)
     {
-        if ('c' == $mixedGroupId)
+        if ($mixedGroupId == BX_BASE_MOD_GROUPS_PG_CLOSED)
             return $this->isClosedGroupAccess($iObjectOwnerId, $iViewerId, $iObjectId);
 
-        if ('s' == $mixedGroupId)
+        if ($mixedGroupId == BX_BASE_MOD_GROUPS_PG_SECRET)
             return $this->isSecretGroupAccess($iObjectOwnerId, $iViewerId, $iObjectId);
 
         return false;
@@ -54,31 +58,17 @@ class BxBaseModGroupsPrivacy extends BxBaseModProfilePrivacy
     static function getGroupChooser ($sObject, $iOwnerId = 0, $aParams = array())
     {
         if (!$iOwnerId)
-    		$iOwnerId = bx_get_logged_profile_id();
+            $iOwnerId = bx_get_logged_profile_id();
 
-		$aParams['dynamic_groups'] = array(
-			array ('key' => '', 'value' => '----'),
-			array ('key' => 'c', 'value' => _t('_sys_ps_group_title_closed')),
-			array ('key' => 's', 'value' => _t('_sys_ps_group_title_secret')),
-		);
+        $aParams['dynamic_groups'] = array(
+            array ('key' => '', 'value' => '----'),
+            array ('key' => BX_BASE_MOD_GROUPS_PG_CLOSED, 'value' => _t('_sys_ps_group_title_closed')),
+            array ('key' => BX_BASE_MOD_GROUPS_PG_SECRET, 'value' => _t('_sys_ps_group_title_secret')),
+        );
 
         return parent::getGroupChooser($sObject, $iOwnerId, $aParams);
     }
 
-    protected function getGroups() 
-    {
-        $a = parent::getGroups();
-    
-        foreach ($a as $i => $r) {
-            if (BX_DOL_PG_FRIENDS == $r['key']) {
-                unset($a[$i]);
-                break;
-            }
-        }
-
-        return $a;
-    }
-
     protected function getObjectInfo($sAction, $iObjectId)
     {
         return BxDolPrivacy::getObjectInfo($sAction, $iObjectId);

+ 89 - 0
modules/base/groups/classes/BxBaseModGroupsPrivacyPost.php

@@ -0,0 +1,89 @@
+<?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
+ *
+ * @{
+ */
+
+define('BX_BASE_MOD_GROUPS_PG_PARTICIPANTS', 'p');
+define('BX_BASE_MOD_GROUPS_PG_FOLLOWERS', 'f');
+
+class BxBaseModGroupsPrivacyPost extends BxBaseModProfilePrivacyPost
+{
+    public function __construct($aOptions, $oTemplate = false)
+    {
+        parent::__construct($aOptions, $oTemplate);
+
+        $this->_aGroupsExclude = array(BX_DOL_PG_FRIENDS);
+    }
+
+    /**
+     * Check whethere viewer is a member of dynamic group.
+     *
+     * @param  mixed   $mixedGroupId   dynamic group ID.
+     * @param  integer $iObjectOwnerId object owner ID.
+     * @param  integer $iViewerId      viewer ID.
+     * @return boolean result of operation.
+     */
+    public function isDynamicGroupMember($mixedGroupId, $iObjectOwnerId, $iViewerId, $iObjectId)
+    {
+        if($mixedGroupId == BX_BASE_MOD_GROUPS_PG_PARTICIPANTS)
+            return $this->isParticipantAccess($iObjectOwnerId, $iViewerId, $iObjectId);
+
+        if($mixedGroupId == BX_BASE_MOD_GROUPS_PG_FOLLOWERS)
+            return $this->isFollowersAccess($iObjectOwnerId, $iViewerId, $iObjectId);
+
+        return false;
+    }
+
+    public function isParticipantAccess ($iObjectOwnerId, $iViewerId, $iObjectId)
+    {
+        $oConnection = BxDolConnection::getObjectInstance($this->_oModule->_oConfig->CNF['OBJECT_CONNECTIONS']);
+        if(!$oConnection)
+            return false;
+
+        $oGroupProfile = BxDolProfile::getInstanceByContentAndType($iObjectId, $this->_sModule);
+        return $oConnection->isConnected($iViewerId, $oGroupProfile->id(), true);
+    }
+
+    public function isFollowersAccess ($iObjectOwnerId, $iViewerId, $iObjectId)
+    {
+        $oConnection = BxDolConnection::getObjectInstance('sys_profiles_subscriptions');
+        if(!$oConnection)
+            return false;
+
+        $oGroupProfile = BxDolProfile::getInstanceByContentAndType($iObjectId, $this->_sModule);
+        return $oConnection->isConnected($iViewerId, $oGroupProfile->id());
+    }
+
+    static function getGroupChooser ($sObject, $iOwnerId = 0, $aParams = array())
+    {
+        $oPrivacy = BxDolPrivacy::getObjectInstance($sObject);
+        if(empty($oPrivacy))
+            return array();
+
+        $CNF = &$oPrivacy->_oModule->_oConfig->CNF;
+
+        if (!$iOwnerId)
+            $iOwnerId = bx_get_logged_profile_id();
+
+        $aParams['dynamic_groups'] = array(
+            array ('key' => '', 'value' => '----'),
+            array ('key' => BX_BASE_MOD_GROUPS_PG_PARTICIPANTS, 'value' => _t(!empty($CNF['T']['txt_group_participants']) ? $CNF['T']['txt_group_participants'] : '_sys_ps_group_title_participants')),
+            array ('key' => BX_BASE_MOD_GROUPS_PG_FOLLOWERS, 'value' => _t(!empty($CNF['T']['txt_group_followers']) ? $CNF['T']['txt_group_followers'] : '_sys_ps_group_title_followers'))
+        );
+
+        return parent::getGroupChooser($sObject, $iOwnerId, $aParams);
+    }
+
+    protected function getObjectInfo($sAction, $iObjectId)
+    {
+        return BxDolPrivacy::getObjectInfo($sAction, $iObjectId);
+    }
+}
+
+/** @} */

+ 13 - 0
modules/base/profile/classes/BxBaseModProfileFormEntry.php

@@ -23,6 +23,19 @@ class BxBaseModProfileFormEntry extends BxBaseModGeneralFormEntry
 
         $CNF = &$this->_oModule->_oConfig->CNF;
 
+        if (isset($CNF['FIELD_ALLOW_POST_TO']) && isset($this->aInputs[$CNF['FIELD_ALLOW_POST_TO']]) && isset($CNF['OBJECT_PRIVACY_POST']) && ($oPrivacy = BxDolPrivacy::getObjectInstance($CNF['OBJECT_PRIVACY_POST'])) !== false) {
+
+            $aSave = array('db' => array('pass' => 'Xss'));
+            array_walk($this->aInputs[$CNF['FIELD_ALLOW_POST_TO']], function ($a, $k, $aSave) {
+                if (in_array($k, array('info', 'caption', 'value')))
+                    $aSave[0][$k] = $a;
+            }, array(&$aSave));
+
+            $aGroupChooser = $oPrivacy->getGroupChooser($CNF['OBJECT_PRIVACY_POST']);
+
+            $this->aInputs[$CNF['FIELD_ALLOW_POST_TO']] = array_merge($this->aInputs[$CNF['FIELD_ALLOW_POST_TO']], $aGroupChooser, $aSave);
+        }
+
         if (!empty($CNF['FIELD_PICTURE']) && isset($this->aInputs[$CNF['FIELD_PICTURE']])) {
             $this->_aImageFields[$CNF['FIELD_PICTURE']] = array (
                 'storage_object' => $CNF['OBJECT_STORAGE'],

+ 96 - 39
modules/base/profile/classes/BxBaseModProfileModule.php

@@ -376,33 +376,6 @@ class BxBaseModProfileModule extends BxBaseModGeneralModule implements iBxDolCon
 
         return $aRet;
     }
-    
-    /**
-     * @see iBxDolProfileService::serviceCheckSpacePrivacy
-     */ 
-    public function serviceCheckSpacePrivacy($iContentId)
-    {
-        return $this->serviceCheckAllowedProfileView($iContentId);
-    }
-    
-    /**
-     * @see iBxDolProfileService::serviceCheckAllowedProfileView
-     */ 
-    public function serviceCheckAllowedProfileView($iContentId)
-    {        
-        return $this->serviceCheckAllowedWithContent('View', $iContentId);
-    }
-
-    /**
-     * @see iBxDolProfileService::serviceCheckAllowedPostInProfile
-     */ 
-    public function serviceCheckAllowedPostInProfile($iContentId, $iProfileIdPoster = false)
-    {
-        if (!$iContentId || !($aContentInfo = $this->_oDb->getContentInfoById($iContentId)))
-            return _t('_sys_request_page_not_found_cpt');
-        
-        return $this->serviceCheckAllowedViewForProfile($aContentInfo, false, $iProfileIdPoster);
-    }
 
     /**
      * Prepare fields from some universal set of fields to fields in particular profile module. 
@@ -966,8 +939,102 @@ class BxBaseModProfileModule extends BxBaseModGeneralModule implements iBxDolCon
             );
         }
     }
-    
+
+
     // ====== PERMISSION METHODS
+    /**
+     * @see iBxDolProfileService::serviceCheckAllowedProfileView
+     */ 
+    public function serviceCheckAllowedProfileView($iContentId)
+    {        
+        return $this->serviceCheckAllowedWithContent('View', $iContentId);
+    }
+
+    /**
+     * @see iBxDolProfileService::serviceCheckAllowedPostInProfile
+     */
+    public function serviceCheckAllowedPostInProfile($iContentId)
+    {
+        return $this->serviceCheckAllowedWithContent('Post', $iContentId);
+    }
+
+    /**
+     * @see iBxDolProfileService::serviceCheckSpacePrivacy
+     */ 
+    public function serviceCheckSpacePrivacy($iContentId)
+    {
+        return $this->serviceCheckAllowedProfileView($iContentId);
+    }
+
+    /**
+     * Check if the profile can be viewed.
+     * 
+     * NOTE. This service should be used if it's needed to pass some specific values in 
+     * $isPerformAction and $iProfileId parameters, otherwise it's recommended to use 
+     * BxBaseModProfileModule::serviceCheckAllowedProfileView service method or 
+     * BxDolProfile::checkAllowedProfileView method.
+     * 
+     * @param type $aDataEntry - entry which the action will be performed for
+     * @param type $isPerformAction - perform or just check the action
+     * @param type $iProfileId - performer's profile ID
+     * @return integer - one of CHECK_ACTION_RESULT_XXX constants.
+     */
+    public function serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction = false, $iProfileId = false)
+    {
+        if (!$iProfileId)
+            $iProfileId = $this->_iProfileId;
+
+        $oProfile = BxDolProfile::getInstanceByContentAndType($aDataEntry[$this->_oConfig->CNF['FIELD_ID']], $this->getName());
+        if ($oProfile && $oProfile->id() == $iProfileId)
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        return parent::serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction, $iProfileId);
+    }
+
+    /**
+     * Check if posting (comment, post in Timeline) is available.
+     * 
+     * NOTE. This service should be used if it's needed to pass some specific values in 
+     * $isPerformAction and $iProfileId parameters, otherwise it's recommended to use 
+     * BxBaseModProfileModule::serviceCheckAllowedPostInProfile service method or 
+     * BxDolProfile::checkAllowedPostInProfile method.
+     * 
+     * @param type $aDataEntry - entry which the action will be performed for
+     * @param type $isPerformAction - perform or just check the action
+     * @param type $iProfileId - performer's profile ID
+     * @return integer - one of CHECK_ACTION_RESULT_XXX constants.
+     */
+    public function serviceCheckAllowedPostForProfile ($aDataEntry, $isPerformAction = false, $iProfileId = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$iProfileId)
+            $iProfileId = $this->_iProfileId;
+
+        // check is view allowed
+        if(($mixedResult = $this->serviceCheckAllowedViewForProfile($aDataEntry, $isPerformAction, $iProfileId)) !== CHECK_ACTION_RESULT_ALLOWED)
+            return $mixedResult;
+
+        // moderator and owner always have access
+        $oProfile = BxDolProfile::getInstanceByContentAndType($aDataEntry[$CNF['FIELD_ID']], $this->getName());
+        if(($oProfile && $oProfile->id() == $iProfileId) || $this->_isModerator($isPerformAction))
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        // check privacy
+        if(!empty($CNF['OBJECT_PRIVACY_POST'])) {
+            $oPrivacy = BxDolPrivacy::getObjectInstance($CNF['OBJECT_PRIVACY_POST']);
+            if($oPrivacy && !$oPrivacy->check($aDataEntry[$CNF['FIELD_ID']], $iProfileId))
+                return _t('_sys_access_denied_to_private_content');
+        }
+
+        // check alert to allow custom checks
+        $mixedResult = null;
+        bx_alert('system', 'check_allowed_post', 0, 0, array('module' => $this->getName(), 'content_info' => $aDataEntry, 'profile_id' => $iProfileId, 'override_result' => &$mixedResult));
+        if($mixedResult !== null)
+            return $mixedResult;
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
 
     /**
      * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
@@ -987,16 +1054,6 @@ class BxBaseModProfileModule extends BxBaseModGeneralModule implements iBxDolCon
     {
         return $this->serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction);
     }
-
-    public function serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction = false, $iProfileId = false)
-    {
-        if (!$iProfileId)
-            $iProfileId = $this->_iProfileId;
-        $oProfile = BxDolProfile::getInstanceByContentAndType($aDataEntry[$this->_oConfig->CNF['FIELD_ID']], $this->getName());
-        if ($oProfile && $oProfile->id() == $iProfileId)
-            return CHECK_ACTION_RESULT_ALLOWED;
-        return parent::serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction, $iProfileId);
-    }
     
     /**
      * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
@@ -1029,7 +1086,7 @@ class BxBaseModProfileModule extends BxBaseModGeneralModule implements iBxDolCon
      */
     public function checkAllowedPost ($aDataEntry, $isPerformAction = false)
     {
-        return $this->checkAllowedView ($aDataEntry, $isPerformAction);
+        return $this->serviceCheckAllowedPostForProfile ($aDataEntry, $isPerformAction);
     }
 
     /**

+ 5 - 4
modules/base/profile/classes/BxBaseModProfilePrivacy.php

@@ -14,7 +14,7 @@ class BxBaseModProfilePrivacy extends BxTemplPrivacy
     protected $MODULE;
     protected $_oModule;
 
-    protected $_aPrivacyParticallyVisible = array ();
+    protected $_aPrivacyParticallyVisible;
 
     public function __construct($aOptions, $oTemplate = false)
     {
@@ -37,12 +37,13 @@ class BxBaseModProfilePrivacy extends BxTemplPrivacy
 
     protected function getObjectInfo($sAction, $iObjectId)
     {
-        $a = $this->_oDb->getObjectInfo($sAction, $iObjectId);
         $oProfile = BxDolProfile::getInstanceByContentAndType($iObjectId, $this->MODULE);
-        if (!$oProfile)
+        if(!$oProfile)
             return false;
 
-        $a['owner_id'] = $oProfile->id();        
+        $a = $this->_oDb->getObjectInfo($sAction, $iObjectId);
+        $a['owner_id'] = $oProfile->id();
+
         return $a;
     }
 }

+ 37 - 0
modules/base/profile/classes/BxBaseModProfilePrivacyPost.php

@@ -0,0 +1,37 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    BaseProfile Base classes for profile modules
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxBaseModProfilePrivacyPost extends BxTemplPrivacy
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    public function __construct($aOptions, $oTemplate = false)
+    {
+        parent::__construct($aOptions, $oTemplate);
+
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+    }
+
+    protected function getObjectInfo($sAction, $iObjectId)
+    {
+        $oProfile = BxDolProfile::getInstanceByContentAndType($iObjectId, $this->_sModule);
+        if(!$oProfile)
+            return false;
+
+        $a = $this->_oDb->getObjectInfo($sAction, $iObjectId);
+        $a['owner_id'] = $oProfile->id();
+
+        return $a;
+    }
+}
+
+/** @} */

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

@@ -2527,10 +2527,12 @@ If it is not enabled then please consider implement this optimization, since it
             <li><b>{0}</b> total</li>
             <li><b>{1}</b> deleted</li>
         </ul>]]></string>
+        <string name="_sys_ps_group_title_followers"><![CDATA[Followers]]></string>
 	<string name="_sys_ps_group_title_friends"><![CDATA[Friends]]></string>
 	<string name="_sys_ps_group_title_me_only"><![CDATA[Me only]]></string>
 	<string name="_sys_ps_group_title_members"><![CDATA[Members]]></string>
 	<string name="_sys_ps_group_title_public"><![CDATA[Public]]></string>
+        <string name="_sys_ps_group_title_participants"><![CDATA[Participants]]></string>
 	<string name="_sys_ps_group_title_closed"><![CDATA[Closed]]></string>
 	<string name="_sys_ps_group_title_secret"><![CDATA[Secret]]></string>
     <string name="_sys_ps_ferr_incorrect_select"><![CDATA[You need to select some value]]></string>

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

@@ -2524,10 +2524,12 @@
             <li><b>{0}</b> всего</li>
             <li><b>{1}</b> удалено</li>
         </ul>]]></string>
+        <string name="_sys_ps_group_title_followers"><![CDATA[Подписчики]]></string>
 	<string name="_sys_ps_group_title_friends"><![CDATA[Друзья]]></string>
 	<string name="_sys_ps_group_title_me_only"><![CDATA[Только я]]></string>
 	<string name="_sys_ps_group_title_members"><![CDATA[Пользователей]]></string>
 	<string name="_sys_ps_group_title_public"><![CDATA[Публичные]]></string>
+        <string name="_sys_ps_group_title_participants"><![CDATA[Участники]]></string>
 	<string name="_sys_ps_group_title_closed"><![CDATA[Закрытая]]></string>
     <string name="_sys_ps_group_title_secret"><![CDATA[Секретная]]></string>
     <string name="_sys_ps_ferr_incorrect_select"><![CDATA[Вам нужно выбрать значение]]></string>