Просмотр исходного кода

Ticket #1430 - Mature-audience content filter (PG, PG13, R)

AntonLV 4 лет назад
Родитель
Сommit
f4731fda5f
29 измененных файлов с 888 добавлено и 160 удалено
  1. 105 0
      inc/classes/BxDolCronProfile.php
  2. 10 1
      inc/classes/BxDolProfile.php
  3. 73 0
      inc/classes/BxDolProfileQuery.php
  4. 3 0
      inc/classes/BxDolSearch.php
  5. 19 0
      inc/utils.inc.php
  6. 27 5
      install/sql/system.sql
  7. 39 0
      modules/base/general/classes/BxBaseModGeneralDb.php
  8. 16 12
      modules/base/general/classes/BxBaseModGeneralGridAdministration.php
  9. 21 1
      modules/base/general/classes/BxBaseModGeneralPageEntry.php
  10. 40 0
      modules/base/general/classes/BxBaseModGeneralSearchResult.php
  11. 84 18
      modules/base/profile/classes/BxBaseModProfileGridAdministration.php
  12. 12 4
      modules/base/profile/classes/BxBaseModProfileMenuManageTools.php
  13. 33 4
      modules/base/text/classes/BxBaseModTextGridAdministration.php
  14. 21 0
      modules/boonex/artificer/data/template/system/scripts/BxTemplFormProfile.php
  15. 21 0
      modules/boonex/decorous/data/template/system/scripts/BxTemplFormProfile.php
  16. 43 22
      modules/boonex/english/data/langs/system/en.xml
  17. 21 0
      modules/boonex/lucid/data/template/system/scripts/BxTemplFormProfile.php
  18. 21 0
      modules/boonex/ocean/data/template/system/scripts/BxTemplFormProfile.php
  19. 9 5
      modules/boonex/persons/install/langs/en.xml
  20. 6 3
      modules/boonex/persons/install/sql/enable.sql
  21. 30 24
      modules/boonex/persons/js/manage_tools.js
  22. 21 0
      modules/boonex/protean/data/template/system/scripts/BxTemplFormProfile.php
  23. 5 4
      modules/boonex/russian/data/langs/bx_persons/ru.xml
  24. 51 31
      modules/boonex/russian/data/langs/system/ru.xml
  25. 20 0
      template/content_filter_manage_popup.html
  26. 40 0
      template/scripts/BxBaseFormProfile.php
  27. 19 18
      template/scripts/BxBaseFormView.php
  28. 72 2
      template/scripts/BxBaseServiceProfiles.php
  29. 6 6
      template/scripts/BxBaseServices.php

+ 105 - 0
inc/classes/BxDolCronProfile.php

@@ -0,0 +1,105 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+class BxDolCronProfile extends BxDolCron
+{
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    public function processing()
+    {
+        $this->_processContentFilters();
+    }
+
+    protected function _processContentFilters()
+    {
+        $aProfileModules = bx_srv('system', 'get_modules_by_type', ['profile']);
+        if(empty($aProfileModules) || !is_array($aProfileModules))
+            return;
+
+        $aProfileTypes = [];
+        foreach($aProfileModules as $aProfileModule) 
+            $aProfileTypes[] = $aProfileModule['name'];
+
+        $oProfileQuery = BxDolProfileQuery::getInstance();
+
+        $aProfiles = $oProfileQuery->getProfiles(['type' => 'active', 'types' => $aProfileTypes]);
+        if(empty($aProfiles) || !is_array($aProfiles))
+            return;
+
+        $aFilters = BxDolFormQuery::getDataItems('sys_content_filter', true, BX_DATA_VALUES_ALL);
+
+        foreach($aProfiles as $aProfile) {
+            if(!BxDolRequest::serviceExists($aProfile['type'], 'get_info'))
+                continue;
+
+            $aProfileInfo = bx_srv($aProfile['type'], 'get_info', [$aProfile['content_id'], false]);
+
+            $iCfwValue = (int)$aProfile['cfw_value'];
+            $bUpdateCfwValue = false;
+
+            $iCfwItems = $iCfuItems = 0;
+            $bUpdateCfwItems = $bUpdateCfuItems = false;
+            foreach($aFilters as $aFilter) {
+                $iFilter = (int)$aFilter['Value'];
+
+                if(!empty($aFilter['Data'])) {
+                    $aData = unserialize($aFilter['Data']);
+                    if(empty($aData) || !is_array($aData))
+                        continue;
+                }
+                else
+                    $aData = [
+                        'is_allowed_watch' => ['module' => 'system', 'method' => 'is_allowed_cfilter', 'params' => ['watch'], 'class' => 'BaseServiceProfiles'],
+                        'is_allowed_use' => ['module' => 'system', 'method' => 'is_allowed_cfilter', 'params' => ['use'], 'class' => 'BaseServiceProfiles'],
+                    ];
+
+                if(!empty($aData['is_allowed_watch'])) {
+                    $aData['is_allowed_watch']['params'] = array_merge($aData['is_allowed_watch']['params'], [$iFilter, $aProfileInfo]);
+                    
+                    $iWatch = call_user_func_array('bx_srv', $aData['is_allowed_watch']);
+                    if($iWatch === false)
+                        continue;
+
+                    if($iWatch == 0) {
+                        $iCfwValue &= ~ (1 << ($iFilter - 1));
+                        $bUpdateCfwValue = true;
+                    }
+
+                    $iCfwItems |= $iWatch;
+                    $bUpdateCfwItems = true;
+                }
+
+                if(!empty($aData['is_allowed_use']) && (int)$aProfile['cfu_locked'] == 0) {
+                    $aData['is_allowed_use']['params'] = array_merge($aData['is_allowed_use']['params'], [$aFilter['Value'], $aProfileInfo]);
+
+                    $iUse = call_user_func_array('bx_srv', $aData['is_allowed_use']);
+                    if($iUse === false)
+                        continue;
+
+                    $iCfuItems |= $iUse;
+                    $bUpdateCfuItems = true;
+                }
+            }
+
+            if($bUpdateCfwValue) 
+                $oProfileQuery->changeCfwValue($aProfile['id'], $iCfwValue);
+
+            if($bUpdateCfwItems)
+                $oProfileQuery->changeCfwItems($aProfile['id'], $iCfwItems);
+
+            if($bUpdateCfuItems)
+                $oProfileQuery->changeCfuItems($aProfile['id'], $iCfuItems);
+        }
+    }
+}
+
+/** @} */

+ 10 - 1
inc/classes/BxDolProfile.php

@@ -603,7 +603,16 @@ class BxDolProfile extends BxDolFactory implements iBxDolProfile
         }
     }
 
-	/**
+    public function checkContentFilter($iValue, $iProfileId = 0)
+    {
+        $aProfileInfo = $this->getInfo($iProfileId);
+        if(!$aProfileInfo)
+            return false;
+
+        return (1 << ($iValue - 1)) & $aProfileInfo['cfw_value'];
+    }
+            
+    /**
      * Add permament messages.
      */
     public function addInformerPermanentMessages ($oInformer)

+ 73 - 0
inc/classes/BxDolProfileQuery.php

@@ -57,6 +57,46 @@ class BxDolProfileQuery extends BxDolDb implements iBxDolSingleton
         return $this->getAllWithKey($sSql, 'id');
     }
 
+    /**
+     * Get profile(s) by params
+     * @param  array   $aParams browse params
+     * @return array with profile(s)
+     */
+    public function getProfiles($aParams)
+    {
+        $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+
+    	$sFieldsClause = "`tp`.*"; 
+    	$sJoinClause = $sWhereClause = $sGroupClause = "";
+        $sOrderClause = "`tp`.`id` ASC";
+
+    	switch($aParams['type']) {
+            case 'active':
+                $sWhereClause = " AND `tp`.`status`='active'";
+
+                if(isset($aParams['types'])) {
+                    if(!is_array($aParams['types']))
+                        $aParams['types'] = [$aParams['types']];
+
+                    $sWhereClause .= " AND `tp`.`type` IN (" . $this->implode_escape($aParams['types']) . ")";
+                }
+                break;
+
+            case 'all':
+                break;
+    	}
+
+    	$sGroupClause = $sGroupClause ? "GROUP BY " . $sGroupClause : "";
+        $sOrderClause = $sOrderClause ? "ORDER BY " . $sOrderClause : "";
+
+        $aMethod['params'][0] = "SELECT
+            " . $sFieldsClause . "
+            FROM `sys_profiles` AS `tp`" . $sJoinClause . "
+            WHERE 1" . $sWhereClause . " " . $sGroupClause . " " . $sOrderClause;
+
+        return call_user_func_array([$this, $aMethod['name']], $aMethod['params']);
+    }
+
     /**
      * Get profile by content id, type and account.
      * @param  string $iAccountId account id
@@ -148,6 +188,39 @@ class BxDolProfileQuery extends BxDolDb implements iBxDolSingleton
         return $this->_updateField ($iProfileId, 'status', $sStatus);
     }
 
+    /**
+     * Update profile's content filter value (watch)
+     * @param $iProfileId profile id
+     * @param $iValue bitmask of selected items 
+     * @return true on success or false otherwise
+     */
+    public function changeCfwValue ($iProfileId, $iValue)
+    {
+        return $this->_updateField ($iProfileId, 'cfw_value', $iValue);
+    }
+
+    /**
+     * Update profile's content filter items (watch)
+     * @param $iProfileId profile id
+     * @param $iValue bitmask of available items 
+     * @return true on success or false otherwise
+     */
+    public function changeCfwItems ($iProfileId, $iValue)
+    {
+        return $this->_updateField ($iProfileId, 'cfw_items', $iValue);
+    }
+
+    /**
+     * Update profile's content filter items (use)
+     * @param $iProfileId profile id
+     * @param $iValue bitmask of available items 
+     * @return true on success or false otherwise
+     */
+    public function changeCfuItems ($iProfileId, $iValue)
+    {
+        return $this->_updateField ($iProfileId, 'cfu_items', $iValue);
+    }
+
     /**
      * Get current account profile.
      * @param  string  $iAccountId account id

+ 3 - 0
inc/classes/BxDolSearch.php

@@ -876,6 +876,9 @@ class BxDolSearchResult implements iBxDolReplaceable
                             $sValuesString = $this->getMultiValues($aValue['value']);
                             $sqlCondition .= strtoupper($aValue['operator']) . '(' . $sValuesString . ')';
                             break;
+                       case 'in_set':
+                           $sqlCondition = "1 << (" . $sqlCondition . " - 1) & " . (int)$aValue['value'];
+                           break;
                        default:
                                $sqlCondition .= $aValue['operator'] . (isset($aValue['no_quote_value']) && $aValue['no_quote_value'] ?  $aValue['value'] : $oDb->escape($aValue['value']));
                        break;

+ 19 - 0
inc/utils.inc.php

@@ -2099,4 +2099,23 @@ function bx_log($sObject, $mixed)
         return false;
 }
 
+function bx_birthday2age($sBirthday)
+{
+    $iPosSpace = strpos($sBirthday, ' ');
+    if($iPosSpace !== false)
+        $sBirthday = trim(substr($sBirthday, 0, $iPosSpace));
+
+    $aDate = explode('-', $sBirthday);
+
+    $iCdYear = (int)date('Y');
+    $iCdMonth = (int)date('n');
+    $iCdDay = (int)date('j');
+
+    $iResult = $iCdYear - (int)$aDate[0];
+    if($iCdMonth < (int)$aDate[1] || ($iCdMonth == (int)$aDate[1] && $iCdDay < (int)$aDate[2]))
+        $iResult -= 1;
+
+    return $iResult;
+}
+
 /** @} */

+ 27 - 5
install/sql/system.sql

@@ -473,7 +473,8 @@ INSERT INTO `sys_options`(`category_id`, `name`, `caption`, `value`, `type`, `ex
 (@iCategoryId, 'sys_recaptcha_key_public', '_adm_stg_cpt_option_sys_recaptcha_key_public', '', 'digit', '', '', '', 20),
 (@iCategoryId, 'sys_recaptcha_key_private', '_adm_stg_cpt_option_sys_recaptcha_key_private', '', 'digit', '', '', '', 21),
 (@iCategoryId, 'sys_add_nofollow', '_adm_stg_cpt_option_sys_add_nofollow', 'on', 'checkbox', '', '', '', 30),
-(@iCategoryId, 'sys_security_block_content_after_n_reports', '_adm_stg_cpt_option_sys_security_block_content_after_n_reports', '0', 'digit', '', '', '', 35);
+(@iCategoryId, 'sys_security_block_content_after_n_reports', '_adm_stg_cpt_option_sys_security_block_content_after_n_reports', '0', 'digit', '', '', '', 35),
+(@iCategoryId, 'sys_cf_prohibited', '_adm_stg_cpt_option_sys_cf_prohibited', '', 'list', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"get_options_cf_prohibited";s:6:"params";a:0:{}s:5:"class";s:13:"TemplServices";}', '', '', 40);
 
 --
 -- CATEGORY: Site Settings
@@ -1168,6 +1169,7 @@ CREATE TABLE IF NOT EXISTS `sys_profiles` (
   `cfw_value` int(10) unsigned NOT NULL DEFAULT '0',
   `cfw_items` int(10) unsigned NOT NULL DEFAULT '0',
   `cfu_items` int(10) unsigned NOT NULL DEFAULT '0',
+  `cfu_locked` tinyint(4) NOT NULL DEFAULT '0',
   `status` enum('active','pending','suspended') NOT NULL DEFAULT 'active',
   PRIMARY KEY (`id`),
   UNIQUE KEY `account_type_content` (`account_id`,`type`,`content_id`),
@@ -2142,6 +2144,7 @@ INSERT INTO `sys_cron_jobs` (`name`, `time`, `class`, `file`, `service_call`) VA
 ('pruning', '0 0 * * *', 'BxDolCronPruning', 'inc/classes/BxDolCronPruning.php', ''),
 ('sys_acl', '0 0 * * *', 'BxDolCronAcl', 'inc/classes/BxDolCronAcl.php', ''),
 ('sys_account', '0 0 * * *', 'BxDolCronAccount', 'inc/classes/BxDolCronAccount.php', ''),
+('sys_profile', '0 0 * * *', 'BxDolCronProfile', 'inc/classes/BxDolCronProfile.php', ''),
 ('sys_upgrade', '0 3 * * *', 'BxDolCronUpgradeCheck', 'inc/classes/BxDolCronUpgradeCheck.php', ''),
 ('sys_upgrade_modules', '30 2 * * *', 'BxDolCronUpgradeModulesCheck', 'inc/classes/BxDolCronUpgradeModulesCheck.php', ''),
 ('sys_storage', '* * * * *', 'BxDolCronStorage', 'inc/classes/BxDolCronStorage.php', ''),
@@ -3319,6 +3322,7 @@ CREATE TABLE IF NOT EXISTS `sys_objects_form` (
 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
 ('sys_login', 'system', '_sys_form_login', 'member.php', 'a:3:{s:2:"id";s:14:"sys-form-login";s:6:"action";s:10:"member.php";s:8:"onsubmit";s:31:"return validateLoginForm(this);";}', 'a:3:{i:0;s:4:"role";i:1;s:10:"do_sendsms";i:2;s:12:"do_checkcode";}', '', '', '', '', 'a:1:{s:14:"checker_helper";s:24:"BxFormLoginCheckerHelper";}', 0, 1, 'BxTemplFormLogin', ''),
 ('sys_account', 'system', '_sys_form_account', '', '', 'do_submit', 'sys_accounts', 'id', '', '', 'a:1:{s:14:"checker_helper";s:26:"BxFormAccountCheckerHelper";}', 0, 1, 'BxTemplFormAccount', ''),
+('sys_profile', 'system', '_sys_form_profile', '', '', 'do_submit', 'sys_profiles', 'id', '', '', '', 0, 1, 'BxTemplFormProfile', ''),
 ('sys_forgot_password', 'system', '_sys_form_forgot_password', '', '', 'do_submit', '', '', '', '', 'a:1:{s:14:"checker_helper";s:33:"BxFormForgotPasswordCheckerHelper";}', 0, 1, 'BxTemplFormForgotPassword', ''),
 ('sys_confirm_email', 'system', '_sys_form_confirm_email', '', '', 'do_submit', '', '', '', '', 'a:1:{s:14:"checker_helper";s:31:"BxFormConfirmEmailCheckerHelper";}', 0, 1, 'BxTemplFormConfirmEmail', ''),
 ('sys_confirm_phone', 'system', '_sys_form_confirm_phone', '', '', 'a:2:{i:0;s:9:"do_submit";i:1;s:10:"do_sendsms";}', '', '', '', '', 'a:1:{s:14:"checker_helper";s:31:"BxFormConfirmPhoneCheckerHelper";}', 0, 1, 'BxTemplFormConfirmPhone', ''),
@@ -3353,6 +3357,8 @@ INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `v
 ('sys_account_settings_pwd', 'system', 'sys_account', '_sys_form_display_account_settings_password', 0),
 ('sys_account_settings_info', 'system', 'sys_account', '_sys_form_display_account_settings_info', 0),
 ('sys_account_settings_del_account', 'system', 'sys_account', '_sys_form_display_account_settings_delete', 0),
+('sys_profile_cf_set', 'system', 'sys_profile', '_sys_form_display_profile_cf_set', 0),
+('sys_profile_cf_manage', 'system', 'sys_profile', '_sys_form_display_profile_cf_manage', 0),
 ('sys_forgot_password', 'system', 'sys_forgot_password', '_sys_form_display_forgot_password', 0),
 ('sys_forgot_password_reset', 'system', 'sys_forgot_password', '_sys_form_display_forgot_password_reset', 0),
 ('sys_confirm_phone_set_phone', 'system', 'sys_confirm_phone', '_sys_form_display_confirm_phone_set_phone', 0),
@@ -3437,6 +3443,13 @@ INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `c
 ('sys_account', 'system', 'receive_news', '1', '', 1, 'switcher', '_sys_form_login_input_caption_system_receive_news', '_sys_form_account_input_receive_news', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
 ('sys_account', 'system', 'agreement', '', '', 0, 'custom', '_sys_form_login_input_caption_system_agreement', '_sys_form_account_input_agreement', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 
+('sys_profile', 'system', 'cfw_value', '', '#!sys_content_filter', 0, 'checkbox_set', '_sys_form_profile_input_sys_cfw_value', '_sys_form_profile_input_cfw_value', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 1, 0),
+('sys_profile', 'system', 'cfu_items', '', '#!sys_content_filter', 0, 'checkbox_set', '_sys_form_profile_input_sys_cfu_items', '_sys_form_profile_input_cfu_items', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 1, 0),
+('sys_profile', 'system', 'cfu_locked', '1', '', 0, 'switcher', '_sys_form_profile_input_sys_cfu_locked', '_sys_form_profile_input_cfu_locked', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('sys_profile', 'system', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('sys_profile', 'system', 'do_submit', '_sys_form_profile_input_do_submit', '', 0, 'submit', '_sys_form_profile_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('sys_profile', 'system', 'do_cancel', '_sys_form_profile_input_do_cancel', '', 0, 'button', '_sys_form_profile_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:5:"class";s:22:"bx-def-margin-sec-left";s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";}', '', '', '', '', '', '', '', 0, 0),
+
 ('sys_forgot_password', 'system', 'key', '', '', 0, 'hidden', '_sys_form_forgot_password_input_caption_system_key', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
 ('sys_forgot_password', 'system', 'password', '', '', 0, 'password', '_sys_form_forgot_password_input_caption_system_password', '_sys_form_forgot_password_input_caption_password', '', 1, 0, 0, '', '', '', 'Preg', 'a:1:{s:4:"preg";s:38:"~^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}~";}', '_sys_form_account_input_password_error', 'Xss', '', 1, 0),
 ('sys_forgot_password', 'system', 'email', '', '', 0, 'text', '_sys_form_forgot_password_input_caption_system_email', '_sys_form_forgot_password_input_email', '', 1, 0, 0, '', '', '', 'EmailExistOrEmpty', '', '_sys_form_account_input_email_error', 'Xss', '', 0, 0),
@@ -3613,6 +3626,15 @@ INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_fo
 ('sys_account_settings_info', 'name', 2147483647, 1, 1),
 ('sys_account_settings_info', 'do_submit', 2147483647, 1, 2),
 
+('sys_profile_cf_set', 'cfw_value', 2147483647, 1, 1),
+('sys_profile_cf_set', 'do_submit', 2147483647, 1, 2),
+
+('sys_profile_cf_manage', 'cfu_items', 2147483647, 1, 1),
+('sys_profile_cf_manage', 'cfu_locked', 2147483647, 1, 2),
+('sys_profile_cf_manage', 'controls', 2147483647, 1, 3),
+('sys_profile_cf_manage', 'do_submit', 2147483647, 1, 4),
+('sys_profile_cf_manage', 'do_cancel', 2147483647, 1, 5),
+
 ('sys_forgot_password', 'email', 2147483647, 1, 1),
 ('sys_forgot_password', 'phone', 2147483647, 1, 2),
 ('sys_forgot_password', 'captcha', 2147483647, 1, 3),
@@ -4168,10 +4190,10 @@ INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`, `Dat
 
 INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`, `Data`) VALUES
 ('sys_content_filter', 1, 1, '_sys_pre_lists_content_filter_g', '', ''),
-('sys_content_filter', 2, 2, '_sys_pre_lists_content_filter_pg', '', 'a:2:{s:16:"is_allowed_watch";a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"is_allowed_content_filter";s:6:"params";a:2:{i:0;s:5:"watch";i:1;s:7:"{value}";}s:5:"class";s:12:"BaseServices";}s:14:"is_allowed_use";a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"is_allowed_content_filter";s:6:"params";a:2:{i:0;s:3:"use";i:1;s:7:"{value}";}s:5:"class";s:12:"BaseServices";}}'),
-('sys_content_filter', 3, 3, '_sys_pre_lists_content_filter_pg13', '', 'a:2:{s:16:"is_allowed_watch";a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"is_allowed_content_filter";s:6:"params";a:2:{i:0;s:5:"watch";i:1;s:7:"{value}";}s:5:"class";s:12:"BaseServices";}s:14:"is_allowed_use";a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"is_allowed_content_filter";s:6:"params";a:2:{i:0;s:3:"use";i:1;s:7:"{value}";}s:5:"class";s:12:"BaseServices";}}'),
-('sys_content_filter', 4, 4, '_sys_pre_lists_content_filter_r', '', 'a:2:{s:16:"is_allowed_watch";a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"is_allowed_content_filter";s:6:"params";a:2:{i:0;s:5:"watch";i:1;s:7:"{value}";}s:5:"class";s:12:"BaseServices";}s:14:"is_allowed_use";a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"is_allowed_content_filter";s:6:"params";a:2:{i:0;s:3:"use";i:1;s:7:"{value}";}s:5:"class";s:12:"BaseServices";}}'),
-('sys_content_filter', 5, 5, '_sys_pre_lists_content_filter_x', '', 'a:2:{s:16:"is_allowed_watch";a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"is_allowed_content_filter";s:6:"params";a:2:{i:0;s:5:"watch";i:1;s:7:"{value}";}s:5:"class";s:12:"BaseServices";}s:14:"is_allowed_use";a:4:{s:6:"module";s:6:"system";s:6:"method";s:25:"is_allowed_content_filter";s:6:"params";a:2:{i:0;s:3:"use";i:1;s:7:"{value}";}s:5:"class";s:12:"BaseServices";}}');
+('sys_content_filter', 2, 2, '_sys_pre_lists_content_filter_pg', '', ''),
+('sys_content_filter', 3, 3, '_sys_pre_lists_content_filter_pg13', '', ''),
+('sys_content_filter', 4, 4, '_sys_pre_lists_content_filter_r', '', ''),
+('sys_content_filter', 5, 5, '_sys_pre_lists_content_filter_x', '', '');
 
 INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`, `Data`) VALUES
 ('sys_studio_widget_types', '', 1, '_sys_pre_lists_studio_widget_types_library', '', 'a:1:{s:4:"icon";s:15:"lmi-library.svg";}'),

+ 39 - 0
modules/base/general/classes/BxBaseModGeneralDb.php

@@ -358,6 +358,14 @@ class BxBaseModGeneralDb extends BxDolModuleDb
     }
 
     protected function _addCustomConditions($aParams, &$aMethod, &$sSelectClause, &$sJoinClause, &$sWhereClause, &$sOrderClause, &$sLimitClause)
+    {
+        $this->_addConditionsForAuthorStatus($aParams, $aMethod, $sSelectClause, $sJoinClause, $sWhereClause, $sOrderClause, $sLimitClause);
+        
+        $this->_addConditionsForProhibitedCf($aParams, $aMethod, $sSelectClause, $sJoinClause, $sWhereClause, $sOrderClause, $sLimitClause);
+        $this->_addConditionsForViewerCf($aParams, $aMethod, $sSelectClause, $sJoinClause, $sWhereClause, $sOrderClause, $sLimitClause);
+    }
+    
+    protected function _addConditionsForAuthorStatus($aParams, &$aMethod, &$sSelectClause, &$sJoinClause, &$sWhereClause, &$sOrderClause, &$sLimitClause)
     {
         $CNF = &$this->_oConfig->CNF;
         if (empty($CNF['FIELD_AUTHOR']))
@@ -369,6 +377,37 @@ class BxBaseModGeneralDb extends BxDolModuleDb
         $sJoinClause .= " INNER JOIN `sys_profiles` as `p` ON (`p`.`id` = `{$CNF['TABLE_ENTRIES']}`.`{$CNF['FIELD_AUTHOR']}` AND `p`.`status` = 'active') ";
     }
 
+    protected function _addConditionsForProhibitedCf($aParams, &$aMethod, &$sSelectClause, &$sJoinClause, &$sWhereClause, &$sOrderClause, &$sLimitClause)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        if(empty($CNF['FIELD_CF']))
+            return;
+
+        $sValues = getParam('sys_cf_prohibited');
+        if(!$sValues)
+            return;
+
+        $aValues = explode(',', $sValues);
+        if(!$aValues)
+            return;
+
+        $sWhereClause .= " AND `" . $CNF['TABLE_ENTRIES'] . "`.`" . $CNF['FIELD_CF'] . "` NOT IN (" . $this->implode_escape($aValues) . ")";
+    }
+
+    protected function _addConditionsForViewerCf($aParams, &$aMethod, &$sSelectClause, &$sJoinClause, &$sWhereClause, &$sOrderClause, &$sLimitClause)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        if(empty($CNF['FIELD_CF']))
+            return;
+
+        $iViewer = bx_get_logged_profile_id();
+        $aViewerInfo = BxDolProfileQuery::getInstance()->getInfoById($iViewer);
+        if(empty($aViewerInfo) || !is_array($aViewerInfo))
+            return;
+
+        $sWhereClause .= " AND 1 << (`" . $CNF['TABLE_ENTRIES'] . "`.`" . $CNF['FIELD_CF'] . "` - 1) & " . $aViewerInfo['cfw_value'];
+    }
+
     protected function _getEntriesBySearchIdsOrder($aParams, &$sOrderClause)
     {
         $CNF = &$this->_oConfig->CNF;

+ 16 - 12
modules/base/general/classes/BxBaseModGeneralGridAdministration.php

@@ -150,33 +150,37 @@ class BxBaseModGeneralGridAdministration extends BxTemplGrid
     	return $this->_getActionDefault($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
     }
 
-    protected function _getFilterSelectOne($sFilterName, $sFilterValue, $aFilterValues)
+    protected function _getFilterSelectOne($sFilterName, $sFilterValue, $aFilterValues, $bAddSelectOne = true)
     {
         if(empty($sFilterName) || empty($aFilterValues))
             return '';
 
-		$CNF = &$this->_oModule->_oConfig->CNF;
-		$sJsObject = $this->_oModule->_oConfig->getJsObject('manage_tools');
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        $sJsObject = $this->_oModule->_oConfig->getJsObject('manage_tools');
+
+        $aInputValues = [];
+        if($bAddSelectOne)
+            $aInputValues[''] = _t($CNF['T']['filter_item_select_one_' . $sFilterName]);
 
-		foreach($aFilterValues as $sKey => $sValue)
-			$aFilterValues[$sKey] = _t($sValue);
+        foreach($aFilterValues as $sKey => $sValue)
+            $aInputValues[$sKey] = _t($sValue);
 
-        $aInputModules = array(
+        $aInputModules = [
             'type' => 'select',
             'name' => $sFilterName,
-            'attrs' => array(
+            'attrs' => [
                 'id' => 'bx-grid-' . $sFilterName . '-' . $this->_sObject,
                 'onChange' => 'javascript:' . $sJsObject . '.onChangeFilter(this)'
-            ),
+            ],
             'value' => $sFilterValue,
-            'values' => array_merge(array('' => _t($CNF['T']['filter_item_select_one_' . $sFilterName])), $aFilterValues)
-        );
+            'values' => $aInputValues
+        ];
 
-        $oForm = new BxTemplFormView(array());
+        $oForm = new BxTemplFormView([]);
         return $oForm->genRow($aInputModules);
     }
 
-	protected function _getSearchInput()
+    protected function _getSearchInput()
     {
         $sJsObject = $this->_oModule->_oConfig->getJsObject('manage_tools');
 

+ 21 - 1
modules/base/general/classes/BxBaseModGeneralPageEntry.php

@@ -48,12 +48,33 @@ class BxBaseModGeneralPageEntry extends BxTemplPage
     
     public function getCode ()
     {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
         // check if content exists
         if (!$this->_aContentInfo || !$this->isActive()) { // if entry is not found - display standard "404 page not found" page
             $this->_oTemplate->displayPageNotFound();
             exit;
         }
 
+        if(!empty($CNF['FIELD_CF'])) {
+            // check global content filter
+            $sValues = getParam('sys_cf_prohibited');
+            if(!empty($sValues)) {
+                $aValues = explode(',', $sValues);
+                if(!empty($aValues) && is_array($aValues) && in_array($this->_aContentInfo[$CNF['FIELD_CF']], $aValues)) {
+                    $this->_oTemplate->displayPageNotFound();
+                    exit;
+                }
+            }
+
+            // check Viewer's content filter
+            $oViewer = BxDolProfile::getInstance();
+            if($oViewer && !$oViewer->checkContentFilter($this->_aContentInfo[$CNF['FIELD_CF']])) {
+                $this->_oTemplate->displayPageNotFound();
+                exit;
+            }
+        }
+
         $oCover = BxDolCover::getInstance($this->_oModule->_oTemplate);        
         if ($oCover)
             $oCover->setCoverClass($this->_sCoverClass);
@@ -62,7 +83,6 @@ class BxBaseModGeneralPageEntry extends BxTemplPage
         $this->_processPermissionsCheck ();
 
         // count views
-        $CNF = &$this->_oModule->_oConfig->CNF;
         if(!empty($CNF['OBJECT_VIEWS'])) {
             $oView = BxDolView::getObjectInstance($CNF['OBJECT_VIEWS'], $this->_aContentInfo[$CNF['FIELD_ID']]);
             if($oView && $oView->isEnabled())

+ 40 - 0
modules/base/general/classes/BxBaseModGeneralSearchResult.php

@@ -58,6 +58,9 @@ class BxBaseModGeneralSearchResult extends BxTemplSearchResult
     protected function addCustomConditions($CNF, $oProfile, $sMode, $aParams)
     {
         $this->addConditionsForAuthorStatus($CNF);
+
+        $this->addConditionsForProhibitedCf($CNF);
+        $this->addConditionsForViewerCf($CNF);
     }
 
     protected function addConditionsForAuthorStatus($CNF)
@@ -81,6 +84,43 @@ class BxBaseModGeneralSearchResult extends BxTemplSearchResult
         ];
     }
 
+    protected function addConditionsForProhibitedCf($CNF)
+    {
+        if(empty($CNF['FIELD_CF']))
+            return;
+
+        $sValues = getParam('sys_cf_prohibited');
+        if(!$sValues)
+            return;
+
+        $aValues = explode(',', $sValues);
+        if(!$aValues)
+            return;
+
+        $this->aCurrent['restriction']['prohibited_cf'] = [
+            'value' => $aValues,
+            'field' => $CNF['FIELD_CF'],
+            'operator' => 'not in',
+        ];
+    }
+
+    protected function addConditionsForViewerCf($CNF)
+    {
+        if(empty($CNF['FIELD_CF']) || !isLogged())
+            return;
+
+        $iViewer = bx_get_logged_profile_id();
+        $aViewerInfo = BxDolProfileQuery::getInstance()->getInfoById($iViewer);
+        if(empty($aViewerInfo) || !is_array($aViewerInfo))
+            return;
+
+        $this->aCurrent['restriction']['viewer_cf'] = [
+            'value' => $aViewerInfo['cfw_value'],
+            'field' => $CNF['FIELD_CF'],
+            'operator' => 'in_set',
+        ];
+    }
+
     /**
      * Add conditions for private content
      */

+ 84 - 18
modules/base/profile/classes/BxBaseModProfileGridAdministration.php

@@ -52,11 +52,11 @@ class BxBaseModProfileGridAdministration extends BxBaseModGeneralGridAdministrat
 
         $aIdsResult = array();
         foreach($aIds as $iId) {
-        	$aContentInfo = $this->_oModule->_oDb->getContentInfoById($iId);
-	    	if($this->_oModule->checkAllowedSetMembership($aContentInfo) !== CHECK_ACTION_RESULT_ALLOWED)
-	    		continue;
+            $aContentInfo = $this->_oModule->_oDb->getContentInfoById($iId);
+            if($this->_oModule->checkAllowedSetMembership($aContentInfo) !== CHECK_ACTION_RESULT_ALLOWED)
+                continue;
 
-        	$aIdsResult[] = $this->_getProfileId($iId);
+            $aIdsResult[] = $this->_getProfileId($iId);
         }
 
         if(empty($aIdsResult)) {
@@ -64,19 +64,67 @@ class BxBaseModProfileGridAdministration extends BxBaseModGeneralGridAdministrat
             return;
         }
 
-		if(count($aIdsResult) == 1)
-			$aIdsResult = $aIdsResult[0];
+        if(count($aIdsResult) == 1)
+            $aIdsResult = $aIdsResult[0];
 
-		$sContent = $this->_oTemplate->parseHtmlByName('set_acl_popup.html', array(
-			'content' => $oMenu->getCode($aIdsResult)
-		));
-
-		$sContent = BxTemplFunctions::getInstance()->transBox($this->_oModule->_oConfig->getName() . 'set_acl_level_popup', $sContent);
+        $sContent = $this->_oTemplate->parseHtmlByName('set_acl_popup.html', array(
+            'content' => $oMenu->getCode($aIdsResult)
+        ));
+        $sContent = BxTemplFunctions::getInstance()->transBox($this->_oModule->_oConfig->getName() . 'set_acl_level_popup', $sContent);
 
     	echoJson(array('popup' => $sContent));
     }
 
-	public function performActionDeleteWithContent()
+    public function performActionManageCf()
+    {
+        if(!$this->_oModule->_isModerator())
+            return echoJson([]);
+
+        $sModule = $this->_oModule->_oConfig->getName();
+
+        $iContentId = $this->_getId();
+        if($iContentId === false)
+            return echoJson([]);
+
+        $aProfileInfo = BxDolProfileQuery::getInstance()->getProfileByContentAndType($iContentId, $sModule, true);
+        if(!$aProfileInfo || !is_array($aProfileInfo))
+            return echoJson([]);
+
+        $sAction = 'manage_cf';
+
+        $sForm = 'sys_profile';
+        $sFormDisplay = 'sys_profile_cf_manage';
+        $oForm = BxDolForm::getObjectInstance($sForm, $sFormDisplay);
+        if(!$oForm)
+            return echoJson([]);
+
+        $oForm->setAction(BX_DOL_URL_ROOT . 'grid.php', ['o' => $this->_sObject, 'a' => $sAction, 'id' => $iContentId]);
+        $oForm->initChecker($aProfileInfo);
+        if($oForm->isSubmittedAndValid()) {
+            if(!$oForm->update($aProfileInfo['id']))
+                $aResult = ['msg' => _t('_sys_txt_error_profile_update')];
+            else
+                $aResult = ['msg' => _t('_' . $sFormDisplay . '_successfully_submitted')];
+
+            return echoJson($aResult);
+        }
+
+        $sPopupTitle = _t('_' . $sFormDisplay . '_popup');
+        $sPopupContent = $this->_oTemplate->parseHtmlByName('content_filter_manage_popup.html', [
+            'form_id' => $oForm->getId(),
+            'form' => $oForm->getCode()
+        ]);
+
+        echoJson(['popup' => [
+            'html' => BxTemplFunctions::getInstance()->popupBox($sModule . '_manage_cf_popup',  $sPopupTitle, $sPopupContent),
+            'options' => [
+                'closeOnOuterClick' => false,
+                'removeOnClose' => true
+            ]
+        ]]);
+    }
+
+    public function performActionDeleteWithContent()
     {
     	$this->performActionDelete(array('with_content' => true));
     }
@@ -84,11 +132,16 @@ class BxBaseModProfileGridAdministration extends BxBaseModGeneralGridAdministrat
     protected function _getActionSetAclLevel($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
     {
     	if($this->_sManageType == BX_DOL_MANAGE_TOOLS_ADMINISTRATION && $this->_oModule->checkAllowedSetMembership($aRow) !== CHECK_ACTION_RESULT_ALLOWED)
-			return '';
+            return '';
 
-		return $this->_getActionDefault($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);	
+        return $this->_getActionDefault($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);	
     }
-    
+
+    protected function _getActionManageCf($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        return '';
+    }
+
     protected function _getActionAuditProfile($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
     {
         if (!getParam('sys_audit_enable') || getParam('sys_audit_acl_levels') == '')
@@ -109,10 +162,10 @@ class BxBaseModProfileGridAdministration extends BxBaseModGeneralGridAdministrat
     	return $this->_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
     }
 
-	protected function _getActionDeleteWithContent($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    protected function _getActionDeleteWithContent($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
     {
-		if($this->_sManageType != BX_DOL_MANAGE_TOOLS_ADMINISTRATION || !$this->_oModule->_isAdministrator())
-			return '';
+        if($this->_sManageType != BX_DOL_MANAGE_TOOLS_ADMINISTRATION || !$this->_oModule->_isAdministrator())
+            return '';
 
     	return $this->_getActionDefault($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
     }
@@ -241,6 +294,19 @@ class BxBaseModProfileGridAdministration extends BxBaseModGeneralGridAdministrat
 
     	return parent::_doDelete($iId, $aParams);
     }
+
+    protected function _getId()
+    {
+        $aIds = bx_get('ids');
+        if(!empty($aIds) && is_array($aIds)) 
+            return (int)reset($aIds);
+
+        $iId = bx_get('id');
+        if($iId !== false) 
+            return (int)$iId;
+
+        return false;
+    }
 }
 
 /** @} */

+ 12 - 4
modules/base/profile/classes/BxBaseModProfileMenuManageTools.php

@@ -31,26 +31,34 @@ class BxBaseModProfileMenuManageTools extends BxBaseModGeneralMenuManageTools
             return false;
 
         if(empty($this->_aContentInfo) || !is_array($this->_aContentInfo))
-        	return false;
+            return false;
 
         $sCheckFuncName = '';
+        $aCheckFuncParams = [];
         switch ($a['name']) {
+            case 'clear-reports':
+            case 'manage-cf':
+                $sCheckFuncName = 'checkAllowedEditAnyEntry';
+                break;
+
             case 'delete':
             case 'delete-with-content':
             	if($this->_oModule->checkMyself($this->_iContentId))
-            		return false;
+                    return false;
+
                 $oProfile = $this->_oModule->getProfileObject(($this->_iContentId));
                 if (!$this->_oModule->isAllowDeleteOrDisable(bx_get_logged_profile_id(), $oProfile->id()))
                     return false;
-                
+
                 $sCheckFuncName = 'checkAllowedDelete';
+                $aCheckFuncParams = [&$this->_aContentInfo];
                 break;
         }
 
         if(!$sCheckFuncName || !method_exists($this->_oModule, $sCheckFuncName))
             return true;
 
-        return $this->_oModule->{$sCheckFuncName}($this->_aContentInfo) === CHECK_ACTION_RESULT_ALLOWED;
+        return call_user_func_array([$this->_oModule, $sCheckFuncName], $aCheckFuncParams) === CHECK_ACTION_RESULT_ALLOWED;
     }
 }
 

+ 33 - 4
modules/base/text/classes/BxBaseModTextGridAdministration.php

@@ -14,6 +14,10 @@ class BxBaseModTextGridAdministration extends BxBaseModGeneralGridAdministration
     protected $_sFilter1Name;
     protected $_sFilter1Value;
     protected $_aFilter1Values;
+    protected $_sFilter2Name;
+    protected $_sFilter2Value;
+    protected $_aFilter2Values;
+    protected $_bContentFilter;
 
     public function __construct ($aOptions, $oTemplate = false)
     {
@@ -37,6 +41,16 @@ class BxBaseModTextGridAdministration extends BxBaseModGeneralGridAdministration
             $this->_sFilter1Value = bx_process_input($sFilter1);
             $this->_aQueryAppend['filter1'] = $this->_sFilter1Value;
         }
+
+        if(($this->_bContentFilter = !empty($CNF['FIELD_CF'])) !== false) {
+            $this->_sFilter2Name = 'filter2';
+            $this->_aFilter2Values = BxDolFormQuery::getDataItems('sys_content_filter');
+
+            if(($sFilter2 = bx_get($this->_sFilter2Name)) !== false) {
+                $this->_sFilter2Value = bx_process_input($sFilter2);
+                $this->_aQueryAppend[$this->_sFilter2Name] = $this->_sFilter2Value;
+            }
+        }
     }
 
     protected function _switcherChecked2State($isChecked)
@@ -51,11 +65,21 @@ class BxBaseModTextGridAdministration extends BxBaseModGeneralGridAdministration
 
     protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
     {
-        if(strpos($sFilter, $this->_sParamsDivider) !== false)
-            list($this->_sFilter1Value, $sFilter) = explode($this->_sParamsDivider, $sFilter);
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(strpos($sFilter, $this->_sParamsDivider) !== false) {
+            $aFilters = explode($this->_sParamsDivider, $sFilter);
+            if($this->_bContentFilter)
+                list($this->_sFilter1Value, $this->_sFilter2Value, $sFilter) = $aFilters;
+            else
+                list($this->_sFilter1Value, $sFilter) = $aFilters;
+        }
 
     	if(!empty($this->_sFilter1Value))
-        	$this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString(" AND `" . $this->_sStatusField . "`=?", $this->_sFilter1Value);
+            $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString(" AND `" . $this->_sStatusField . "`=?", $this->_sFilter1Value);
+
+        if(!empty($this->_sFilter2Value))
+            $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString(" AND `" . $CNF['FIELD_CF'] . "`=?", $this->_sFilter2Value);
 
         return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
     }
@@ -65,7 +89,12 @@ class BxBaseModTextGridAdministration extends BxBaseModGeneralGridAdministration
     {
         parent::_getFilterControls();
 
-        return  $this->_getFilterSelectOne($this->_sFilter1Name, $this->_sFilter1Value, $this->_aFilter1Values) . $this->_getSearchInput();
+        $sContent = $this->_getFilterSelectOne($this->_sFilter1Name, $this->_sFilter1Value, $this->_aFilter1Values);
+        if($this->_bContentFilter)
+            $sContent .= $this->_getFilterSelectOne($this->_sFilter2Name, $this->_sFilter2Value, $this->_aFilter2Values);
+        $sContent .= $this->_getSearchInput();
+
+        return $sContent;
     }
 
     protected function _getCellTitle($mixedValue, $sKey, $aField, $aRow)

+ 21 - 0
modules/boonex/artificer/data/template/system/scripts/BxTemplFormProfile.php

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

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

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

+ 43 - 22
modules/boonex/english/data/langs/system/en.xml

@@ -1533,6 +1533,7 @@
     <string name="_adm_stg_cpt_option_sys_cache_memcache_host"><![CDATA[Memcached server host]]></string>
     <string name="_adm_stg_cpt_option_sys_cache_memcache_port"><![CDATA[Memcached server port]]></string>
     <string name="_adm_stg_cpt_option_sys_captcha_default"><![CDATA[Default Сaptcha]]></string>
+    <string name="_adm_stg_cpt_option_sys_cf_prohibited"><![CDATA[Prohibited content]]></string>
     <string name="_adm_stg_cpt_option_sys_email_attachable_email_templates"><![CDATA[Attachable email templates list]]></string>
     <string name="_adm_stg_cpt_option_sys_db_cache_enable"><![CDATA[Enable DB cache]]></string>
     <string name="_adm_stg_cpt_option_sys_db_cache_engine"><![CDATA[DB cache engine (other than File option may require custom server setup)]]></string>
@@ -2476,7 +2477,25 @@ If it is not enabled then please consider implement this optimization, since it
         <string name="_sys_form_acl_input_do_submit"><![CDATA[Submit]]></string>
         <string name="_sys_form_acl_input_sys_do_cancel"><![CDATA[Cancel]]></string>
         <string name="_sys_form_acl_input_do_cancel"><![CDATA[Cancel]]></string>
+        <string name="_sys_form_entry_input_sys_cf"><![CDATA[Content Filter]]></string>
+        <string name="_sys_form_entry_input_cf"><![CDATA[Select audience]]></string>
 	<string name="_sys_form_paceholder_profiles_suggestions"><![CDATA[Type name here...]]></string>
+        <string name="_sys_form_profile"><![CDATA[Profile]]></string>
+        <string name="_sys_form_display_profile_cf_set"><![CDATA[Configure Content Filter]]></string>
+        <string name="_sys_form_display_profile_cf_manage"><![CDATA[Manage Content Filter]]></string>
+        <string name="_sys_form_profile_input_sys_cfw_value"><![CDATA[Content Filter Value]]></string>
+        <string name="_sys_form_profile_input_cfw_value"><![CDATA[Show me a content marked with]]></string>
+        <string name="_sys_form_profile_input_sys_cfu_items"><![CDATA[Content Filter Items]]></string>
+        <string name="_sys_form_profile_input_cfu_items"><![CDATA[Available content filter categories]]></string>
+        <string name="_sys_form_profile_input_sys_cfu_locked"><![CDATA[Lock Content Filter]]></string>
+        <string name="_sys_form_profile_input_cfu_locked"><![CDATA[Lock content filter categories]]></string>
+        <string name="_sys_form_profile_input_sys_do_submit"><![CDATA[Submit]]></string>
+        <string name="_sys_form_profile_input_do_submit"><![CDATA[Submit]]></string>
+        <string name="_sys_form_profile_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+        <string name="_sys_form_profile_input_do_cancel"><![CDATA[Cancel]]></string>
+        <string name="_sys_profile_cf_set_successfully_submitted"><![CDATA[Content Filter was successfully updated]]></string>
+        <string name="_sys_profile_cf_manage_successfully_submitted"><![CDATA[Content Filter was successfully updated]]></string>
+        <string name="_sys_profile_cf_manage_popup"><![CDATA[Manage Content Filter]]></string>
         <string name="_sys_form_ps_group_custom"><![CDATA[Privacy: Custom group]]></string>
         <string name="_sys_form_display_ps_gc_members"><![CDATA[Privacy: Profiles based custom group display]]></string>
         <string name="_sys_form_display_ps_gc_memberships"><![CDATA[Privacy: Memberships based custom group display]]></string>
@@ -3400,38 +3419,40 @@ If it is not enabled then please consider implement this optimization, since it
 	<string name="_sys_txt_create_post_placeholder"><![CDATA[What's happening, {0}]]></string>
 
     <string name="_sys_txt_current_picture"><![CDATA[Current Picture]]></string>
-	<string name="_sys_txt_data_successfully_submitted"><![CDATA[Done]]></string>
+    <string name="_sys_txt_data_successfully_submitted"><![CDATA[Done]]></string>
     <string name="_sys_txt_delete"><![CDATA[Delete]]></string>
     <string name="_sys_txt_discard"><![CDATA[Discard]]></string>
-	<string name="_sys_txt_empty"><![CDATA[Empty]]></string>
-	<string name="_sys_txt_error_account_creation"><![CDATA[Account creation failed]]></string>
-	<string name="_sys_txt_error_account_delete"><![CDATA[Account delete failed]]></string>
-	<string name="_sys_txt_error_account_is_not_defined"><![CDATA[Account is not defined]]></string>
-	<string name="_sys_txt_error_account_update"><![CDATA[Account update failed]]></string>
-	<string name="_sys_txt_error_entry_creation"><![CDATA[Entry creation error]]></string>
-	<string name="_sys_txt_error_entry_delete"><![CDATA[Entry delete failed]]></string>
-	<string name="_sys_txt_error_entry_is_not_defined"><![CDATA[Requested resource doesn't exist]]></string>
-	<string name="_sys_txt_error_entry_update"><![CDATA[Entry update failed]]></string>
+    <string name="_sys_txt_empty"><![CDATA[Empty]]></string>
+    <string name="_sys_txt_error_account_creation"><![CDATA[Account creation failed]]></string>
+    <string name="_sys_txt_error_account_delete"><![CDATA[Account delete failed]]></string>
+    <string name="_sys_txt_error_account_is_not_defined"><![CDATA[Account is not defined]]></string>
+    <string name="_sys_txt_error_account_update"><![CDATA[Account update failed]]></string>
+    <string name="_sys_txt_error_entry_creation"><![CDATA[Entry creation error]]></string>
+    <string name="_sys_txt_error_entry_delete"><![CDATA[Entry delete failed]]></string>
+    <string name="_sys_txt_error_entry_is_not_defined"><![CDATA[Requested resource doesn't exist]]></string>
+    <string name="_sys_txt_error_entry_update"><![CDATA[Entry update failed]]></string>
     <string name="_sys_txt_error_occured"><![CDATA[Error Occurred]]></string>
+    <string name="_sys_txt_error_profile_is_not_defined"><![CDATA[Profile is not defined]]></string>
+    <string name="_sys_txt_error_profile_update"><![CDATA[Profile update failed]]></string>
     <string name="_sys_txt_err_sound_not_transcoded_yet"><![CDATA[Sound file isn't transcoded yet]]></string>
     <string name="_sys_txt_field_created"><![CDATA[Created:]]></string>
     <string name="_sys_txt_field_updated"><![CDATA[Updated:]]></string>
-	<string name="_sys_txt_forgot_pasword"><![CDATA[Forgot password?]]></string>
-	<string name="_sys_txt_forgot_pasword_check_email"><![CDATA[Email was sent, please follow instructions in the email message.]]></string>
-	<string name="_sys_txt_forgot_pasword_check_phone"><![CDATA[SMS was sent, please follow instructions in the SMS.]]></string>
-	<string name="_sys_txt_forgot_pasword_desc"><![CDATA[Enter your account email address to get a password-reset link.]]></string>
-	<string name="_sys_txt_forgot_pasword_by_both_desc"><![CDATA[Enter your account email address or phone to get a password-reset link.]]></string>
-	<string name="_sys_txt_forgot_pasword_by_phone_desc"><![CDATA[Enter your account phone to get a password-reset link.]]></string>
-	<string name="_sys_txt_forgot_pasword_email_send_failed"><![CDATA[Email send failed, please try again later.]]></string>
+    <string name="_sys_txt_forgot_pasword"><![CDATA[Forgot password?]]></string>
+    <string name="_sys_txt_forgot_pasword_check_email"><![CDATA[Email was sent, please follow instructions in the email message.]]></string>
+    <string name="_sys_txt_forgot_pasword_check_phone"><![CDATA[SMS was sent, please follow instructions in the SMS.]]></string>
+    <string name="_sys_txt_forgot_pasword_desc"><![CDATA[Enter your account email address to get a password-reset link.]]></string>
+    <string name="_sys_txt_forgot_pasword_by_both_desc"><![CDATA[Enter your account email address or phone to get a password-reset link.]]></string>
+    <string name="_sys_txt_forgot_pasword_by_phone_desc"><![CDATA[Enter your account phone to get a password-reset link.]]></string>
+    <string name="_sys_txt_forgot_pasword_email_send_failed"><![CDATA[Email send failed, please try again later.]]></string>
     <string name="_sys_txt_forgot_pasword_error_occured"><![CDATA[Error occurred, please report error to the site administrator.]]></string>
-	<string name="_sys_txt_forgot_pasword_sms_text"><![CDATA[Click the link below to reset your password {0}]]></string>
-    
+    <string name="_sys_txt_forgot_pasword_sms_text"><![CDATA[Click the link below to reset your password {0}]]></string>
+
     <string name="_sys_txt_form_entry_input_picture_delete"><![CDATA[Delete]]></string>
     <string name="_sys_txt_form_entry_input_picture_insert"><![CDATA[Insert to Post]]></string>
-	<string name="_sys_txt_form_entry_input_picture_pic_url"><![CDATA[Attachment URL:]]></string>
+    <string name="_sys_txt_form_entry_input_picture_pic_url"><![CDATA[Attachment URL:]]></string>
     <string name="_sys_txt_form_entry_input_picture_use_as_thumb"><![CDATA[Use as Thumbnail]]></string>
-	<string name="_sys_txt_form_submission_error"><![CDATA[Incorrect info. Please, check your inputs and try to submit again.]]></string>
-	<string name="_sys_txt_form_submission_error_csrf_expired"><![CDATA[Token expired. Please try again.]]></string>
+    <string name="_sys_txt_form_submission_error"><![CDATA[Incorrect info. Please, check your inputs and try to submit again.]]></string>
+    <string name="_sys_txt_form_submission_error_csrf_expired"><![CDATA[Token expired. Please try again.]]></string>
 
     <string name="_sys_txt_homepage_cover"><![CDATA[Unity is strength... when there is teamwork and collaboration, wonderful things can be achieved.<br /><button class="bx-btn bx-hide-when-logged-in bx-def-margin-top" onclick="document.location='{0}'">Join Now!</button>]]></string>
     <string name="_sys_txt_login_2fa_description"><![CDATA[Two-factor authentication is enabled, please specify the phone number to send verification code to.]]></string>

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

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

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

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

+ 9 - 5
modules/boonex/persons/install/langs/en.xml

@@ -213,6 +213,8 @@
     <string name="_bx_persons_menu_item_title_delete_with_content"><![CDATA[Delete With Content]]></string>
     <string name="_bx_persons_menu_item_title_system_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_persons_menu_item_title_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_persons_menu_item_title_system_manage_cf"><![CDATA[Manage audience]]></string>
+    <string name="_bx_persons_menu_item_title_manage_cf"><![CDATA[Manage audience]]></string>
     <string name="_bx_persons_menu_item_title_system_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_persons_menu_item_title_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_persons_menu_item_title_system_edit_profile"><![CDATA[Edit Person]]></string>
@@ -296,12 +298,12 @@
 	<string name="_bx_persons_menu_item_title_system_entries_online"><![CDATA[Online]]></string>
 	<string name="_bx_persons_menu_item_title_entries_online"><![CDATA[Online]]></string>
 
-	<string name="_bx_persons_grid_filter_item_title_adm_select_one_filter1"><![CDATA[All Statuses]]></string>
+    <string name="_bx_persons_grid_filter_item_title_adm_select_one_filter1"><![CDATA[All Statuses]]></string>
     <string name="_bx_persons_grid_filter_item_title_adm_select_one_filter2"><![CDATA[All]]></string>
-	<string name="_bx_persons_grid_filter_item_title_adm_active"><![CDATA[Active]]></string>
-	<string name="_bx_persons_grid_filter_item_title_adm_pending"><![CDATA[Pending]]></string>
-	<string name="_bx_persons_grid_filter_item_title_adm_suspended"><![CDATA[Suspended]]></string>
-  <string name="_bx_persons_grid_filter_item_title_adm_unconfirmed"><![CDATA[Unconfirmed]]></string>
+    <string name="_bx_persons_grid_filter_item_title_adm_active"><![CDATA[Active]]></string>
+    <string name="_bx_persons_grid_filter_item_title_adm_pending"><![CDATA[Pending]]></string>
+    <string name="_bx_persons_grid_filter_item_title_adm_suspended"><![CDATA[Suspended]]></string>
+    <string name="_bx_persons_grid_filter_item_title_adm_unconfirmed"><![CDATA[Unconfirmed]]></string>
     <string name="_bx_persons_grid_column_title_adm_active"><![CDATA[Active]]></string>
     <string name="_bx_persons_grid_column_title_adm_fullname"><![CDATA[Name]]></string>
     <string name="_bx_persons_grid_column_title_adm_last_online"><![CDATA[Last Logged]]></string>
@@ -313,6 +315,8 @@
     <string name="_bx_persons_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_persons_grid_action_title_adm_audit_content"><![CDATA[Audit actions with profile]]></string>
     <string name="_bx_persons_grid_action_title_adm_audit_profile"><![CDATA[Audit profile actions]]></string>
+    <string name="_bx_persons_grid_action_title_adm_manage_cf"><![CDATA[Manage audience]]></string>
+    
     <string name="_bx_persons_grid_action_err_delete"><![CDATA[Cannot delete the selected person(s).]]></string>
     <string name="_bx_persons_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
 

+ 6 - 3
modules/boonex/persons/install/sql/enable.sql

@@ -388,9 +388,10 @@ INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
 ('bx_persons_menu_manage_tools', 'bx_persons', '_bx_persons_menu_set_title_manage_tools', 0);
 
 INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
-('bx_persons_menu_manage_tools', 'bx_persons', 'delete', '_bx_persons_menu_item_title_system_delete', '_bx_persons_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 1),
-('bx_persons_menu_manage_tools', 'bx_persons', 'delete-with-content', '_bx_persons_menu_item_title_system_delete_with_content', '_bx_persons_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2),
-('bx_persons_menu_manage_tools', 'bx_persons', 'clear-reports', '_bx_persons_menu_item_title_system_clear_reports', '_bx_persons_menu_item_title_clear_reports', 'javascript:void(0)', 'javascript:{js_object}.onClickClearReports({content_id});', '_self', 'eraser', '', 2147483647, 1, 0, 3);
+('bx_persons_menu_manage_tools', 'bx_persons', 'clear-reports', '_bx_persons_menu_item_title_system_clear_reports', '_bx_persons_menu_item_title_clear_reports', 'javascript:void(0)', 'javascript:{js_object}.onClickClearReports({content_id});', '_self', 'eraser', '', 2147483647, 1, 0, 1),
+('bx_persons_menu_manage_tools', 'bx_persons', 'manage-cf', '_bx_persons_menu_item_title_system_manage_cf', '_bx_persons_menu_item_title_manage_cf', 'javascript:void(0)', 'javascript:{js_object}.onClickManageCf({content_id});', '_self', 'filter', '', 2147483647, 1, 0, 6),
+('bx_persons_menu_manage_tools', 'bx_persons', 'delete', '_bx_persons_menu_item_title_system_delete', '_bx_persons_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 98),
+('bx_persons_menu_manage_tools', 'bx_persons', 'delete-with-content', '_bx_persons_menu_item_title_system_delete_with_content', '_bx_persons_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 99);
 
 -- MENU: dashboard manage tools
 SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
@@ -515,6 +516,8 @@ INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon
 ('bx_persons_administration', 'single', 'settings', '_bx_persons_grid_action_title_adm_more_actions', 'cog', 1, 0, 2),
 ('bx_persons_administration', 'single', 'audit_content', '_bx_persons_grid_action_title_adm_audit_content', 'search', 1, 0, 3),
 ('bx_persons_administration', 'single', 'audit_profile', '_bx_persons_grid_action_title_adm_audit_profile', 'search-location', 1, 0, 4),
+('bx_persons_administration', 'single', 'lock_cf', '_bx_persons_grid_action_title_adm_lock_cf', 'filter', 1, 0, 5),
+
 ('bx_persons_common', 'bulk', 'delete', '_bx_persons_grid_action_title_adm_delete', '', 0, 1, 1),
 ('bx_persons_common', 'bulk', 'delete_with_content', '_bx_persons_grid_action_title_adm_delete_with_content', '', 0, 1, 2),
 ('bx_persons_common', 'single', 'settings', '_bx_persons_grid_action_title_adm_more_actions', 'cog', 1, 0, 1);

+ 30 - 24
modules/boonex/persons/js/manage_tools.js

@@ -9,9 +9,9 @@
  */
 
 function BxPersonsManageTools(oOptions) {
-	this._iSearchTimeoutId = false;
-	this._sActionsUrl = oOptions.sActionUrl;
-	this._sObjNameGrid = oOptions.sObjNameGrid;
+    this._iSearchTimeoutId = false;
+    this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjNameGrid = oOptions.sObjNameGrid;
     this._sObjName = oOptions.sObjName == undefined ? 'oBxPersonsManageTools' : oOptions.sObjName;
 
     this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
@@ -23,43 +23,43 @@ function BxPersonsManageTools(oOptions) {
 }
 
 BxPersonsManageTools.prototype.onChangeFilter = function(oFilter) {
-	var $this = this;
-	var oFilter1 = $('#bx-grid-filter1-' + this._sObjNameGrid);
-	var sValueFilter1 = oFilter1.length > 0 ? oFilter1.val() : '';
+    var $this = this;
+    var oFilter1 = $('#bx-grid-filter1-' + this._sObjNameGrid);
+    var sValueFilter1 = oFilter1.length > 0 ? oFilter1.val() : '';
 
-	var oFilter2 = $('#bx-grid-filter2-' + this._sObjNameGrid);
-	var sValueFilter2 = oFilter2.length > 0 ? oFilter2.val() : '';
+    var oFilter2 = $('#bx-grid-filter2-' + this._sObjNameGrid);
+    var sValueFilter2 = oFilter2.length > 0 ? oFilter2.val() : '';
 
-	var oSearch = $('#bx-grid-search-' + this._sObjNameGrid);
-	var sValueSearch = oSearch.length > 0 ? oSearch.val() : '';
-	if(sValueSearch == _t('_sys_grid_search'))
-		sValueSearch = '';
+    var oSearch = $('#bx-grid-search-' + this._sObjNameGrid);
+    var sValueSearch = oSearch.length > 0 ? oSearch.val() : '';
+    if(sValueSearch == _t('_sys_grid_search'))
+        sValueSearch = '';
 
-	clearTimeout($this._iSearchTimeoutId);
-	$this._iSearchTimeoutId = setTimeout(function () {
-	    glGrids[$this._sObjNameGrid].setFilter(sValueFilter1 + $this._sParamsDivider + sValueFilter2 + $this._sParamsDivider + sValueSearch, true);//TODO ANT
+    clearTimeout($this._iSearchTimeoutId);
+    $this._iSearchTimeoutId = setTimeout(function () {
+        glGrids[$this._sObjNameGrid].setFilter(sValueFilter1 + $this._sParamsDivider + sValueFilter2 + $this._sParamsDivider + sValueSearch, true);//TODO ANT
     }, 500);
 };
 
 BxPersonsManageTools.prototype.onClickSettings = function(sMenuObject, oButton) {
-	if($(oButton).hasClass('bx-btn-disabled'))
-		return false;
+    if($(oButton).hasClass('bx-btn-disabled'))
+        return false;
 
-	bx_menu_popup(sMenuObject, oButton, {}, {
-		content_id: $(oButton).attr('bx_grid_action_data')
-	});
+    bx_menu_popup(sMenuObject, oButton, {}, {
+        content_id: $(oButton).attr('bx_grid_action_data')
+    });
 };
 
 BxPersonsManageTools.prototype.onClickDelete = function(iContentId) {
-	$('.bx-popup-applied:visible').dolPopupHide();
+    $('.bx-popup-applied:visible').dolPopupHide();
 
-	glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete', {}, '', false, 1);
+    glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete', {}, '', false, 1);
 };
 
 BxPersonsManageTools.prototype.onClickDeleteWithContent = function(iContentId) {
-	$('.bx-popup-applied:visible').dolPopupHide();
+    $('.bx-popup-applied:visible').dolPopupHide();
 
-	glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete_with_content', {}, '', false, 1);
+    glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete_with_content', {}, '', false, 1);
 };
 
 BxPersonsManageTools.prototype.onClickClearReports = function (iContentId) {
@@ -68,4 +68,10 @@ BxPersonsManageTools.prototype.onClickClearReports = function (iContentId) {
     glGrids[this._sObjNameGrid].actionWithId(iContentId, 'clear_reports', {}, '', false, 1);
 };
 
+BxPersonsManageTools.prototype.onClickManageCf = function (iContentId) {
+    $('.bx-popup-applied:visible').dolPopupHide();
+
+    glGrids[this._sObjNameGrid].actionWithId(iContentId, 'manage_cf', {}, '', false, false);
+};
+
 /** @} */

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

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

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

@@ -39,10 +39,8 @@
     <string name="_bx_persons_form_profile_input_allow_post_to"><![CDATA[Оставлять сообщения в профиле могут]]></string>
     <string name="_bx_persons_form_profile_input_sys_allow_contact_to"><![CDATA[Писать мне могут]]></string>
     <string name="_bx_persons_form_profile_input_allow_contact_to"><![CDATA[Писать мне могут]]></string>
-	<string name="_bx_persons_form_profile_input_sys_fullname"><![CDATA[Имя]]></string>
-	<string name="_bx_persons_form_profile_input_fullname"><![CDATA[Имя]]></string>
-    <string name="_bx_persons_form_profile_input_sys_last_name"><![CDATA[Фамилия]]></string>
-	<string name="_bx_persons_form_profile_input_last_name"><![CDATA[Фамилия]]></string>
+	<string name="_bx_persons_form_profile_input_sys_fullname"><![CDATA[Полное имя]]></string>
+	<string name="_bx_persons_form_profile_input_fullname"><![CDATA[Полное имя]]></string>
 	<string name="_bx_persons_form_profile_input_fullname_err"><![CDATA[Имя профиля обязательно для заполнения и не должно начинаться с @.]]></string>
 	<string name="_bx_persons_form_profile_input_sys_desc"><![CDATA[Описание]]></string>
 	<string name="_bx_persons_form_profile_input_desc"><![CDATA[Описание]]></string>
@@ -201,6 +199,8 @@
     <string name="_bx_persons_menu_item_title_delete_with_content"><![CDATA[Удалить вместе с содержимым]]></string>
     <string name="_bx_persons_menu_item_title_system_clear_reports"><![CDATA[Очистить жалобы]]></string>
     <string name="_bx_persons_menu_item_title_clear_reports"><![CDATA[Очистить жалобы]]></string>
+    <string name="_bx_persons_menu_item_title_system_manage_cf"><![CDATA[Управлять аудиторией]]></string>
+    <string name="_bx_persons_menu_item_title_manage_cf"><![CDATA[Управлять аудиторией]]></string>
     <string name="_bx_persons_menu_item_title_system_edit_cover"><![CDATA[Редактировать обложку]]></string>
     <string name="_bx_persons_menu_item_title_edit_cover"><![CDATA[Редактировать обложку]]></string>
     <string name="_bx_persons_menu_item_title_system_edit_profile"><![CDATA[Редактировать персону]]></string>
@@ -302,6 +302,7 @@
     <string name="_bx_persons_grid_action_title_adm_more_actions"><![CDATA[Дополнительные действия]]></string>
     <string name="_bx_persons_grid_action_title_adm_audit_content"><![CDATA[Аудит действий над профилем]]></string>
     <string name="_bx_persons_grid_action_title_adm_audit_profile"><![CDATA[Аудит действий профиля]]></string>
+    <string name="_bx_persons_grid_action_title_adm_manage_cf"><![CDATA[Управлять аудиторией]]></string>
     <string name="_bx_persons_grid_action_err_delete"><![CDATA[Невозможно удалить выбранную(ные) персону(ны).]]></string>
     <string name="_bx_persons_grid_txt_account_manager"><![CDATA[Перенаправить в Управление аккаунтом.]]></string>
 

+ 51 - 31
modules/boonex/russian/data/langs/system/ru.xml

@@ -1479,15 +1479,15 @@
 	<string name="_adm_stg_cpt_option_site_email_html_template_header"><![CDATA[Заголовок HTML шаблона email сообщения]]></string>
     <string name="_adm_stg_cpt_option_site_email_html_template_footer"><![CDATA[Основание HTML шаблона email сообщения]]></string>
     <string name="_adm_stg_cpt_option_site_login_social_compact"><![CDATA[Задействовать компактный вид входа через соц. сети]]></string>
-	<string name="_adm_stg_cpt_option_site_title"><![CDATA[Название сайта]]></string>
-	<string name="_adm_stg_cpt_option_site_tour_home"><![CDATA[Экскурсия по главной странице сайта]]></string>
+    <string name="_adm_stg_cpt_option_site_title"><![CDATA[Название сайта]]></string>
+    <string name="_adm_stg_cpt_option_site_tour_home"><![CDATA[Экскурсия по главной странице сайта]]></string>
     <string name="_adm_stg_cpt_option_site_tour_studio"><![CDATA[Экскурсия по главной странице Студии]]></string>
     <string name="_adm_stg_cpt_option_smart_app_banner"><![CDATA[Smart App Banner]]></string>
     <string name="_adm_stg_cpt_option_smart_app_banner_ios_app_id"><![CDATA[Идентификатор iOS прилижения для Smart App Banner]]></string>
-	<string name="_adm_stg_cpt_option_sys_account_activation_letter"><![CDATA[Включить сообщение об активации аккаунта]]></string>
-	<string name="_adm_stg_cpt_option_sys_account_confirmation_type"><![CDATA[Аккаунт подтвержден когда]]></string>
+    <string name="_adm_stg_cpt_option_sys_account_activation_letter"><![CDATA[Включить сообщение об активации аккаунта]]></string>
+    <string name="_adm_stg_cpt_option_sys_account_confirmation_type"><![CDATA[Аккаунт подтвержден когда]]></string>
     <string name="_adm_stg_cpt_option_sys_account_hide_unconfirmed_accounts"><![CDATA[Скрывать профили у неподтвержденных аккаунтов]]></string>
-	<string name="_adm_stg_cpt_option_sys_account_2fa_enable"><![CDATA[Включить двухфакторную аутентификацию]]></string>
+    <string name="_adm_stg_cpt_option_sys_account_2fa_enable"><![CDATA[Включить двухфакторную аутентификацию]]></string>
     <string name="_adm_stg_cpt_option_sys_account_2fa_lifetime"><![CDATA[Срок жизни 2FA кода, в секундах (0 - отправлять код при каждой авторизации)]]></string>
     <string name="_adm_stg_cpt_option_sys_account_autoapproval"><![CDATA[Автоматическая активация аккаунта после создания]]></string>
     <string name="_adm_stg_cpt_option_sys_account_auto_profile_creation"><![CDATA[Автоматическое создание профайла из аккаунта]]></string>
@@ -1518,6 +1518,7 @@
     <string name="_adm_stg_cpt_option_sys_cache_memcache_host"><![CDATA[Memcached - адрес сервера]]></string>
     <string name="_adm_stg_cpt_option_sys_cache_memcache_port"><![CDATA[Memcached - порт сервера]]></string>
     <string name="_adm_stg_cpt_option_sys_captcha_default"><![CDATA[CAPTCHA по умолчанию]]></string>
+    <string name="_adm_stg_cpt_option_sys_cf_prohibited"><![CDATA[Запрещенный контент]]></string>
     <string name="_adm_stg_cpt_option_sys_email_attachable_email_templates"><![CDATA[Список присоединяемых email темплейтов]]></string>
     <string name="_adm_stg_cpt_option_sys_db_cache_enable"><![CDATA[Задействовать кэш БД]]></string>
     <string name="_adm_stg_cpt_option_sys_db_cache_engine"><![CDATA[Кэш-движок для БД (значение, отличное от File, может потребовать дополнительной настройки сервера)]]></string>
@@ -2463,7 +2464,25 @@
         <string name="_sys_form_acl_input_do_submit"><![CDATA[Выполнить]]></string>
         <string name="_sys_form_acl_input_sys_do_cancel"><![CDATA[Отмена]]></string>
         <string name="_sys_form_acl_input_do_cancel"><![CDATA[Отмена]]></string>
+        <string name="_sys_form_entry_input_sys_cf"><![CDATA[Фильтр контента]]></string>
+        <string name="_sys_form_entry_input_cf"><![CDATA[Выберите аудиторию]]></string>
 	<string name="_sys_form_paceholder_profiles_suggestions"><![CDATA[Введите имя...]]></string>
+        <string name="_sys_form_profile"><![CDATA[Профиль]]></string>
+        <string name="_sys_form_display_profile_cf_set"><![CDATA[Настроить фильтр контента]]></string>
+        <string name="_sys_form_display_profile_cf_manage"><![CDATA[Управлять фильтром контента]]></string>
+        <string name="_sys_form_profile_input_sys_cfw_value"><![CDATA[Фильтр контента]]></string>
+        <string name="_sys_form_profile_input_cfw_value"><![CDATA[Показывать мне контент, отмеченный как]]></string>
+        <string name="_sys_form_profile_input_sys_cfu_items"><![CDATA[Содержимое контентного фильтра]]></string>
+        <string name="_sys_form_profile_input_cfu_items"><![CDATA[Доступные категории контентного фильтра]]></string>
+        <string name="_sys_form_profile_input_sys_cfu_locked"><![CDATA[Заблокировать контентный фильтр]]></string>
+        <string name="_sys_form_profile_input_cfu_locked"><![CDATA[Заблокировать контентный фильтр]]></string>
+        <string name="_sys_form_profile_input_sys_do_submit"><![CDATA[Выполнить]]></string>
+        <string name="_sys_form_profile_input_do_submit"><![CDATA[Выполнить]]></string>
+        <string name="_sys_form_profile_input_sys_do_cancel"><![CDATA[Отмена]]></string>
+        <string name="_sys_form_profile_input_do_cancel"><![CDATA[Отмена]]></string>
+        <string name="_sys_profile_cf_set_successfully_submitted"><![CDATA[Фильтр контента был успешно обновлен]]></string>
+        <string name="_sys_profile_cf_manage_successfully_submitted"><![CDATA[Фильтр контента был успешно обновлен]]></string>
+        <string name="_sys_profile_cf_manage_popup"><![CDATA[Управлять фильтром контента]]></string>
         <string name="_sys_form_ps_group_custom"><![CDATA[Конфиденциальность: Пользовательская группа]]></string>
         <string name="_sys_form_display_ps_gc_members"><![CDATA[Конфиденциальность: Пользовательская группа на основании профилей]]></string>
         <string name="_sys_form_display_ps_gc_memberships"><![CDATA[Конфиденциальность: Пользовательская группа на основании членств]]></string>
@@ -2480,8 +2499,8 @@
         <string name="_sys_form_ps_gc_input_caption_memberships"><![CDATA[Выбрать]]></string>
         <string name="_sys_form_ps_gc_input_caption_system_do_submit"><![CDATA[Отправить]]></string>
         <string name="_sys_form_ps_gc_input_caption_do_submit"><![CDATA[Отправить]]></string>
-        <string name="_sys_form_ps_gc_input_caption_system_do_cancel"><![CDATA[Отменить]]></string>
-        <string name="_sys_form_ps_gc_input_caption_do_cancel"><![CDATA[Отменить]]></string>
+        <string name="_sys_form_ps_gc_input_caption_system_do_cancel"><![CDATA[Отмена]]></string>
+        <string name="_sys_form_ps_gc_input_caption_do_cancel"><![CDATA[Отмена]]></string>
   <string name="_sys_form_favorite"><![CDATA[Избранное]]></string>
   <string name="_sys_form_display_favorite_add"><![CDATA[Добавить в список избранного]]></string>
   <string name="_sys_form_display_favorite_list_edit"><![CDATA[Изменить список избранного]]></string>
@@ -3362,37 +3381,38 @@
 	<string name="_sys_txt_create_post_placeholder"><![CDATA[Что нового, {0}]]></string>
 
     <string name="_sys_txt_current_picture"><![CDATA[Текущая картинка]]></string>
-	<string name="_sys_txt_data_successfully_submitted"><![CDATA[Выполнено]]></string>
+    <string name="_sys_txt_data_successfully_submitted"><![CDATA[Выполнено]]></string>
     <string name="_sys_txt_delete"><![CDATA[Удалить]]></string>
     <string name="_sys_txt_discard"><![CDATA[Отменить]]></string>
-	<string name="_sys_txt_empty"><![CDATA[Пусто]]></string>
-	<string name="_sys_txt_error_account_creation"><![CDATA[Не удалось создать аккаунт]]></string>
-	<string name="_sys_txt_error_account_delete"><![CDATA[Не удалось удалить аккаунт]]></string>
-	<string name="_sys_txt_error_account_is_not_defined"><![CDATA[Аккаунт не определён]]></string>
-	<string name="_sys_txt_error_account_update"><![CDATA[Не удалось обновить аккаунт]]></string>
-
-	<string name="_sys_txt_error_entry_creation"><![CDATA[Ошибка создания элемента]]></string>
-	<string name="_sys_txt_error_entry_delete"><![CDATA[Ошибка удаления элемента]]></string>
-	<string name="_sys_txt_error_entry_is_not_defined"><![CDATA[Запрашиваемый ресурс не существует]]></string>
-	<string name="_sys_txt_error_entry_update"><![CDATA[Ошибка обновления элемента]]></string>
-	<string name="_sys_txt_error_occured"><![CDATA[Произошла ошибка]]></string>
+    <string name="_sys_txt_empty"><![CDATA[Пусто]]></string>
+    <string name="_sys_txt_error_account_creation"><![CDATA[Не удалось создать аккаунт]]></string>
+    <string name="_sys_txt_error_account_delete"><![CDATA[Не удалось удалить аккаунт]]></string>
+    <string name="_sys_txt_error_account_is_not_defined"><![CDATA[Аккаунт не определён]]></string>
+    <string name="_sys_txt_error_account_update"><![CDATA[Не удалось обновить аккаунт]]></string>
+    <string name="_sys_txt_error_entry_creation"><![CDATA[Ошибка создания элемента]]></string>
+    <string name="_sys_txt_error_entry_delete"><![CDATA[Ошибка удаления элемента]]></string>
+    <string name="_sys_txt_error_entry_is_not_defined"><![CDATA[Запрашиваемый ресурс не существует]]></string>
+    <string name="_sys_txt_error_entry_update"><![CDATA[Ошибка обновления элемента]]></string>
+    <string name="_sys_txt_error_occured"><![CDATA[Произошла ошибка]]></string>
+    <string name="_sys_txt_error_profile_is_not_defined"><![CDATA[Профиль не определён]]></string>
+    <string name="_sys_txt_error_profile_update"><![CDATA[Не удалось обновить профиль]]></string>
     <string name="_sys_txt_err_sound_not_transcoded_yet"><![CDATA[Аудиофайл в обработке]]></string>
-	<string name="_sys_txt_forgot_pasword"><![CDATA[Забыли пароль?]]></string>
-	<string name="_sys_txt_forgot_pasword_check_email"><![CDATA[Email отослан, пожалуйста, следуйте инструкциям в нём.]]></string>
-	<string name="_sys_txt_forgot_pasword_check_phone"><![CDATA[SMS отослано, пожалуйста, следуйте инструкциям в нём.]]></string>
-	<string name="_sys_txt_forgot_pasword_desc"><![CDATA[Введите эл. адрес своего аккаунта, чтобы запросить ссылку на изменение пароля.]]></string>
-	<string name="_sys_txt_forgot_pasword_by_both_desc"><![CDATA[Введите эл. адрес или телефон своего аккаунта, чтобы запросить ссылку на изменение пароля.]]></string>
-	<string name="_sys_txt_forgot_pasword_by_phone_desc"><![CDATA[Введите телефон своего аккаунта, чтобы запросить ссылку на изменение пароля.]]></string>
-	<string name="_sys_txt_forgot_pasword_email_send_failed"><![CDATA[Не удалось отослать email, пожалуйста, попытайтесь позднее.]]></string>
-	<string name="_sys_txt_forgot_pasword_error_occured"><![CDATA[Произошла ошибка, пожалуйста, уведомите о ней администратора сайта.]]></string>
-	<string name="_sys_txt_forgot_pasword_sms_text"><![CDATA[Чтобы восстановить пароль нажмите на ссылку {0}]]></string>
+    <string name="_sys_txt_forgot_pasword"><![CDATA[Забыли пароль?]]></string>
+    <string name="_sys_txt_forgot_pasword_check_email"><![CDATA[Email отослан, пожалуйста, следуйте инструкциям в нём.]]></string>
+    <string name="_sys_txt_forgot_pasword_check_phone"><![CDATA[SMS отослано, пожалуйста, следуйте инструкциям в нём.]]></string>
+    <string name="_sys_txt_forgot_pasword_desc"><![CDATA[Введите эл. адрес своего аккаунта, чтобы запросить ссылку на изменение пароля.]]></string>
+    <string name="_sys_txt_forgot_pasword_by_both_desc"><![CDATA[Введите эл. адрес или телефон своего аккаунта, чтобы запросить ссылку на изменение пароля.]]></string>
+    <string name="_sys_txt_forgot_pasword_by_phone_desc"><![CDATA[Введите телефон своего аккаунта, чтобы запросить ссылку на изменение пароля.]]></string>
+    <string name="_sys_txt_forgot_pasword_email_send_failed"><![CDATA[Не удалось отослать email, пожалуйста, попытайтесь позднее.]]></string>
+    <string name="_sys_txt_forgot_pasword_error_occured"><![CDATA[Произошла ошибка, пожалуйста, уведомите о ней администратора сайта.]]></string>
+    <string name="_sys_txt_forgot_pasword_sms_text"><![CDATA[Чтобы восстановить пароль нажмите на ссылку {0}]]></string>
 
     <string name="_sys_txt_form_entry_input_picture_delete"><![CDATA[Удалить]]></string>
     <string name="_sys_txt_form_entry_input_picture_insert"><![CDATA[Вставить в сообщение]]></string>
-	<string name="_sys_txt_form_entry_input_picture_pic_url"><![CDATA[URL прикрепления:]]></string>
+    <string name="_sys_txt_form_entry_input_picture_pic_url"><![CDATA[URL прикрепления:]]></string>
     <string name="_sys_txt_form_entry_input_picture_use_as_thumb"><![CDATA[Использовать в качестве миниатюры]]></string>
-	<string name="_sys_txt_form_submission_error"><![CDATA[Неправильная информация. Пожалуйста, проверьте ввод и отправьте снова.]]></string>
-	<string name="_sys_txt_form_submission_error_csrf_expired"><![CDATA[Маркер устарел. Пожалуйста попробуйте снова.]]></string>
+    <string name="_sys_txt_form_submission_error"><![CDATA[Неправильная информация. Пожалуйста, проверьте ввод и отправьте снова.]]></string>
+    <string name="_sys_txt_form_submission_error_csrf_expired"><![CDATA[Маркер устарел. Пожалуйста попробуйте снова.]]></string>
 
     <string name="_sys_txt_homepage_cover"><![CDATA[Сплоченность людей — нерушимая крепость.<br /><button class="bx-btn bx-hide-when-logged-in bx-def-margin-top" onclick="document.location='{0}'">Присоединиться!</button>]]></string>
     <string name="_sys_txt_login_2fa_description"><![CDATA[Двухэтапная авторизация включена. В дополнении к email-у и паролю, Вам нужно будет ввести код проверки, который мы вышлем вам в SMS.]]></string>

+ 20 - 0
template/content_filter_manage_popup.html

@@ -0,0 +1,20 @@
+<div class="sys-cf-manage">
+    <div class="sys-cf-manage-cnt bx-def-padding">
+        __form__
+        <script language="javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function (formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function (oData) {
+                        $("#__form_id__").parents(".bx-popup-applied:visible:first").dolPopupHide();
+
+                        processJsonData(oData);
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 40 - 0
template/scripts/BxBaseFormProfile.php

@@ -0,0 +1,40 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaBaseView UNA Base Representation Classes
+ * @{
+ */
+
+/**
+ * Create/Edit Profile Form.
+ */
+class BxBaseFormProfile extends BxTemplFormView
+{
+    public function __construct($aInfo, $oTemplate)
+    {
+        parent::__construct($aInfo, $oTemplate);
+    }
+
+    public function setAction($sAction, $aParams = [])
+    {
+        $this->aFormAttrs['action'] = bx_append_url_params($sAction, $aParams);
+    }
+
+    public function initChecker($aValues = array (), $aSpecificValues = array())
+    {
+        parent::initChecker($aValues, $aSpecificValues);
+
+        if(isset($this->aInputs['cfw_value']) && !empty($aValues['cfw_items'])) {
+            $aCfwValueValues = [];
+            foreach($this->aInputs['cfw_value']['values'] as $iValue => $sTitle)
+                if((1 << ($iValue - 1)) & (int)$aValues['cfw_items'])
+                    $aCfwValueValues[$iValue] = $sTitle;
+
+            $this->aInputs['cfw_value']['values'] = $aCfwValueValues;
+        }
+    }
+}
+
+/** @} */

+ 19 - 18
template/scripts/BxBaseFormView.php

@@ -792,23 +792,7 @@ BLAH;
         if(!isset($aInput['value']) || !$aInput['value'] || in_array($aInput['value'], array('0000-00-00', '0000-00-00 00:00:00')))
             return null;
 
-        $sValue = $aInput['value'];
-
-        $iPosSpace = strpos($sValue, ' ');
-        if($iPosSpace !== false)
-            $sValue = trim(substr($sValue, 0, $iPosSpace));
-
-        $aDate = explode('-', $sValue);
-
-        $iCdYear = (int)date('Y');
-        $iCdMonth = (int)date('n');
-        $iCdDay = (int)date('j');
-
-        $iResult = $iCdYear - (int)$aDate[0];
-        if($iCdMonth < (int)$aDate[1] || ($iCdMonth == (int)$aDate[1] && $iCdDay < (int)$aDate[2]))
-            $iResult -= 1;           
-
-        return $iResult;
+        return bx_birthday2age($aInput['value']);
     }
 
     /**
@@ -1651,7 +1635,24 @@ BLAH;
             'input_labels' => $this->genCustomInputUsernamesSuggestions($aInputLabels)
         ));
     }
-    
+
+    protected function genCustomInputCf (&$aInput)
+    {
+        $iProfileId = bx_get_logged_profile_id();
+        $aProfileInfo = BxDolProfileQuery::getInstance()->getInfoById($iProfileId);
+
+        if(!empty($aProfileInfo) && isset($aProfileInfo['cfu_items'])) {
+            $aCfuValues = [];
+            foreach($aInput['values'] as $iValue => $sTitle)
+                if((1 << ($iValue - 1)) & (int)$aProfileInfo['cfu_items'])
+                    $aCfuValues[$iValue] = $sTitle;
+
+            $aInput['values'] = $aCfuValues;
+        }
+
+        return $this->genInputSelect($aInput);
+    }
+
     /**
      * Generate Select Element	
      *

+ 72 - 2
template/scripts/BxBaseServiceProfiles.php

@@ -495,7 +495,7 @@ class BxBaseServiceProfiles extends BxDol
         );
     }
 
-    public function serviceProfileSettingsCfilter()
+    public function serviceProfileSettingsCfilter($iProfileId = false)
     {
         // set settings submenu
         $oMenuSubmenu = BxDolMenu::getObjectInstance('sys_site_submenu');
@@ -507,7 +507,77 @@ class BxBaseServiceProfiles extends BxDol
             ));
         }
 
-        return 'Filter would be here!';
+        if($iProfileId === false)
+            $iProfileId = bx_get_logged_profile_id();
+
+        $oProfile = BxDolProfile::getInstance($iProfileId);
+        $aProfileInfo = $oProfile ? $oProfile->getInfo() : false;
+        if(empty($aProfileInfo) || !is_array($aProfileInfo))
+            return MsgBox(_t('_sys_txt_error_profile_is_not_defined'));
+
+        $sForm = 'sys_profile';
+        $sFormDisplay = 'sys_profile_cf_set';
+        $oForm = BxDolForm::getObjectInstance($sForm, $sFormDisplay);
+        if(!$oForm)
+            return MsgBox(_t('_sys_txt_error_occured'));
+
+        $oForm->initChecker($aProfileInfo);
+        if(!$oForm->isSubmittedAndValid())
+            return $sCode = $oForm->getCode();
+
+        if(!$oForm->update($iProfileId)) {
+            if (!$oForm->isValid())
+                return $oForm->getCode();
+            else
+                return MsgBox(_t('_sys_txt_error_profile_update'));
+        }
+
+        // display result message
+        $sMsg = MsgBox(_t('_' . $sFormDisplay . '_successfully_submitted'));
+        return $sMsg . $oForm->getCode();
+    }
+
+    public function serviceIsAllowedCfilter($sAction, $iValue, $aProfileInfo)
+    {
+        if(!in_array($sAction, ['watch', 'use']) || !isset($aProfileInfo['birthday']))
+            return false;
+
+        $iResult = 0;
+        if(empty($aProfileInfo['birthday']) || in_array($aProfileInfo['birthday'], array('0000-00-00', '0000-00-00 00:00:00'))) 
+            return $iResult;
+
+        $iAge = bx_birthday2age($aProfileInfo['birthday']);
+
+        switch($iValue) {
+            case 1:
+                $iResult = $iValue;
+                break;
+
+            case 2: 
+                if($iAge >= 6)
+                   $iResult = $iValue;
+                break;
+
+            case 3:
+                if($iAge >= 13)
+                   $iResult = $iValue;
+                break;
+
+            case 4:
+                if($iAge >= 17)
+                   $iResult = $iValue;
+                break;
+
+            case 5:
+                if($iAge >= 21)
+                   $iResult = $iValue;
+                break;
+        }
+
+        if($iResult != 0)
+            $iResult = 1 << ($iResult - 1);
+
+        return $iResult;
     }
 
     protected function _getIcon ($sIcon)

+ 6 - 6
template/scripts/BxBaseServices.php

@@ -1033,7 +1033,12 @@ class BxBaseServices extends BxDol implements iBxDolProfileService
 
         return $aResults;
     }
-    
+
+    public function serviceGetOptionsCfProhibited()
+    {
+        return BxDolFormQuery::getDataItems('sys_content_filter');
+    }
+
     public function serviceGetBadge($aBadge, $bIsCompact = false)
     {
         $sClass = '';
@@ -1074,11 +1079,6 @@ class BxBaseServices extends BxDol implements iBxDolProfileService
     	);
     }
 
-    public function serviceIsAllowedContentFilter($sAction, $iValue)
-    {
-        
-    }
-
     private function _getSearchObject()
     {
         $sClass = 'BxTemplSearch';