Current Path : /var/www/www-root/data/www.catalog.monolith-realty.ru/bitrix/modules/landing/lib/ |
Current File : /var/www/www-root/data/www.catalog.monolith-realty.ru/bitrix/modules/landing/lib/publicaction.php |
<?php namespace Bitrix\Landing; use Bitrix\Rest\AppTable; use Bitrix\Landing\Site\Type; use Bitrix\Main\Localization\Loc; use Bitrix\Main\ModuleManager; Loc::loadMessages(__FILE__); class PublicAction { /** * Scope for REST (default commands). */ const REST_SCOPE_DEFAULT = 'landing'; /** * Scope for REST (cloud repo commands). */ const REST_SCOPE_CLOUD = 'landing_cloud'; /** * Code indicating used blocks in REST statistics. */ public const REST_USAGE_TYPE_BLOCK = 'blocks'; /** * Code indicating used pages in REST statistics. */ public const REST_USAGE_TYPE_PAGE = 'pages'; /** * REST application. * @var array */ protected static $restApp = null; /** * Raw data from waf. * @var mixed */ protected static $rawData = null; /** * Get full namespace for public classes. * @return string */ protected static function getNamespacePublicClasses() { return __NAMESPACE__ . '\\PublicAction'; } /** * Get info about method - class/method/params. * @param string $action Full name of action (\Namespace\Class::method). * @param array $data Array of data. * @return array * @throws \ReflectionException */ protected static function getMethodInfo($action, $data = array()) { $info = array(); // if action exist and is callable if ($action && mb_strpos($action, '::')) { $actionOriginal = $action; $action = self::getNamespacePublicClasses() . '\\' . $action; if (is_callable(explode('::', $action))) { [$class, $method] = explode('::', $action); $info = array( 'action' => $actionOriginal, 'class' => $class, 'method' => $method, 'params_init' => array(), 'params_missing' => array() ); // parse func params $reflection = new \ReflectionMethod($class, $method); $static = $reflection->getStaticVariables(); $mixedParams = isset($static['mixedParams']) ? $static['mixedParams'] : []; foreach ($reflection->getParameters() as $param) { $name = $param->getName(); if (isset($data[$name])) { if (!in_array($name, $mixedParams)) { if ( $param->isArray() && !is_array($data[$name]) || !$param->isArray() && is_array($data[$name]) ) { throw new \Bitrix\Main\ArgumentTypeException( $name ); } } $info['params_init'][$name] = $data[$name]; } elseif ($param->isDefaultValueAvailable()) { $info['params_init'][$name] = $param->getDefaultValue(); } else { $info['params_missing'][] = $name; } } } } return $info; } /** * Returns true if current user out of the extranet. * In extranet case allowed only GROUP scope. * * @return bool */ protected static function checkForExtranet(): bool { if (\Bitrix\Landing\Manager::isAdmin()) { return true; } if (Type::getCurrentScopeId() === Type::SCOPE_CODE_GROUP) { return true; } if (\Bitrix\Main\Loader::includeModule('extranet')) { return \CExtranet::isIntranetUser( \CExtranet::getExtranetSiteID(), \Bitrix\Landing\Manager::getUserId() ); } return true; } /** * Processing the AJAX/REST action. * @param string $action Action name. * @param mixed $data Data. * @param boolean $isRest Is rest call. * @return array|null * @throws \ReflectionException */ protected static function actionProcessing($action, $data, $isRest = false) { if (!is_array($data)) { $data = array(); } if (isset($data['scope'])) { Type::setScope($data['scope']); } if (!$isRest && (!defined('BX_UTF') || BX_UTF !== true)) { $data = Manager::getApplication()->convertCharsetArray( $data, 'UTF-8', SITE_CHARSET ); } $error = new Error; // not for guest if (!Manager::getUserId() || !self::checkForExtranet()) { $error->addError( 'ACCESS_DENIED', Loc::getMessage('LANDING_ACCESS_DENIED2') ); } // tmp flag for compatibility else if ( ModuleManager::isModuleInstalled('bitrix24') && Manager::getOption('temp_permission_admin_only') && !\CBitrix24::isPortalAdmin(Manager::getUserId()) ) { $error->addError( 'ACCESS_DENIED', Loc::getMessage('LANDING_ACCESS_DENIED2') ); } // check common permission else if ( !Rights::hasAdditionalRight( Rights::ADDITIONAL_RIGHTS['menu24'], null, true ) ) { $error->addError( 'ACCESS_DENIED', Loc::getMessage('LANDING_ACCESS_DENIED2') ); } else if (!Manager::isB24() && Manager::getApplication()->getGroupRight('landing') < 'W') { $error->addError( 'ACCESS_DENIED', Loc::getMessage('LANDING_ACCESS_DENIED2') ); } // if method::action exist in PublicAction, call it elseif (($action = self::getMethodInfo($action, $data))) { if (!$isRest && !check_bitrix_sessid()) { $error->addError( 'SESSION_EXPIRED', Loc::getMessage('LANDING_SESSION_EXPIRED') ); } if (!empty($action['params_missing'])) { $error->addError( 'MISSING_PARAMS', Loc::getMessage('LANDING_MISSING_PARAMS', array( '#MISSING#' => implode(', ', $action['params_missing']) )) ); } if (method_exists($action['class'], 'init')) { $result = call_user_func_array( array($action['class'], 'init'), [] ); if (!$result->isSuccess()) { $error->copyError($result->getError()); } } // all right - execute if ($error->isEmpty()) { try { $result = call_user_func_array( array($action['class'], $action['method']), $action['params_init'] ); // answer if ($result === null)// void is accepted as success { return array( 'type' => 'success', 'result' => true ); } else if ($result->isSuccess()) { $restResult = $result->getResult(); $event = new \Bitrix\Main\Event('landing', 'onSuccessRest', [ 'result' => $restResult, 'action' => $action ]); $event->send(); foreach ($event->getResults() as $eventResult) { if (($modified = $eventResult->getModified())) { if (isset($modified['result'])) { $restResult = $modified['result']; } } } return array( 'type' => 'success', 'result' => $restResult ); } else { $error->copyError($result->getError()); } } catch (\TypeError $e) { $error->addError( 'TYPE_ERROR', $e->getMessage() ); } catch (\Exception $e) { $error->addError( 'SYSTEM_ERROR', $e->getMessage() ); } } } // error $errors = array(); foreach ($error->getErrors() as $error) { $errors[] = array( 'error' => $error->getCode(), 'error_description' => $error->getMessage() ); } if (!$isRest) { return [ 'sessid' => bitrix_sessid(), 'type' => 'error', 'result' => $errors ]; } else { return [ 'type' => 'error', 'result' => $errors ]; } } /** * Get raw data of curring processing. * @return mixed */ public static function getRawData() { return self::$rawData; } /** * Listen commands from ajax. * @return array|null * @throws \ReflectionException */ public static function ajaxProcessing() { $context = \Bitrix\Main\Application::getInstance()->getContext(); $request = $context->getRequest(); $files = $request->getFileList(); $postlist = $context->getRequest()->getPostList(); Type::setScope($request->get('type')); // multiple commands if ( $request->offsetExists('batch') && is_array($request->get('batch')) ) { $result = array(); // additional site id detect if ($request->offsetExists('site_id')) { $siteId = $request->get('site_id'); } foreach ($request->get('batch') as $key => $batchItem) { if ( isset($batchItem['action']) && isset($batchItem['data']) ) { $batchItem['data'] = (array)$batchItem['data']; if (isset($siteId)) { $batchItem['data']['siteId'] = $siteId; } if ($files) { foreach ($files as $code => $file) { $batchItem['data'][$code] = $file; } } $rawData = $postlist->getRaw('batch'); if (isset($rawData[$key]['data'])) { self::$rawData = $rawData[$key]['data']; } $result[$key] = self::actionProcessing( $batchItem['action'], $batchItem['data'] ); } } return $result; } // or single command else if ( $request->offsetExists('action') && $request->offsetExists('data') && is_array($request->get('data')) ) { $data = $request->get('data'); // additional site id detect if ($request->offsetExists('site_id')) { $data['siteId'] = $request->get('site_id'); } if ($files) { foreach ($files as $code => $file) { $data[$code] = $file; } } $rawData = $postlist->getRaw('data'); if (isset($rawData['data'])) { self::$rawData = $rawData['data']; } return self::actionProcessing( $request->get('action'), $data ); } return null; } /** * Register methods in REST. * @return array * @throws \ReflectionException */ public static function restBase() { static $restMethods = array(); if (empty($restMethods)) { $restMethods[self::REST_SCOPE_DEFAULT] = array(); $restMethods[self::REST_SCOPE_CLOUD] = array(); $classes = array( self::REST_SCOPE_DEFAULT => array( 'block', 'site', 'landing', 'repo', 'template', 'demos', 'role', 'syspage', 'chat' ), self::REST_SCOPE_CLOUD => array( 'cloud' ) ); // then methods list for each class foreach ($classes as $scope => $classList) { foreach ($classList as $className) { $fullClassName = self::getNamespacePublicClasses() . '\\' . $className; $class = new \ReflectionClass($fullClassName); $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($methods as $method) { $static = $method->getStaticVariables(); if (!isset($static['internal']) || !$static['internal']) { $command = $scope.'.'. mb_strtolower($className).'.'. mb_strtolower($method->getName()); $restMethods[$scope][$command] = array( __CLASS__, 'restGateway' ); } } } } } return array( self::REST_SCOPE_DEFAULT => $restMethods[self::REST_SCOPE_DEFAULT], self::REST_SCOPE_CLOUD => $restMethods[self::REST_SCOPE_CLOUD] ); } /** * Gateway between REST and publicaction. * @param array $fields Rest fields. * @param mixed $t Var. * @param \CRestServer $server Server instance. * @return mixed * @throws \ReflectionException */ public static function restGateway($fields, $t, $server) { // get context app self::$restApp = AppTable::getByClientId($server->getClientId()); // prepare method and call action $method = $server->getMethod(); $method = mb_substr($method, mb_strpos($method, '.') + 1);// delete module-prefix $method = preg_replace('/\./', '\\', $method, substr_count($method, '.') - 1); $method = str_replace('.', '::', $method); $result = self::actionProcessing( $method, $fields, true ); // prepare answer if ($result['type'] == 'error') { foreach ($result['result'] as $error) { throw new \Bitrix\Rest\RestException( $error['error_description'], $error['error'] ); } } else { return $result['result']; } } /** * Get current REST application. * @return array */ public static function restApplication() { return self::$restApp; } /** * On REST app delete. * @param array $app App info. * @return void */ public static function restApplicationDelete($app) { if (isset($app['APP_ID']) && $app['APP_ID']) { if (($app = AppTable::getByClientId($app['APP_ID']))) { Rights::setOff(); Repo::deleteByAppCode($app['CODE']); Placement::deleteByAppId($app['ID']); Demos::deleteByAppCode($app['CODE']); Rights::setOn(); } } } /** * Before REST app delete. * @param \Bitrix\Main\Event $event Event data. * @return \Bitrix\Main\EventResult */ public static function beforeRestApplicationDelete(\Bitrix\Main\Event $event) { $parameters = $event->getParameters(); if ($app = AppTable::getByClientId($parameters['ID'])) { $stat = self::getRestStat(true); if (isset($stat[self::REST_USAGE_TYPE_BLOCK][$app['CODE']])) { $eventResult = new \Bitrix\Main\EventResult( \Bitrix\Main\EventResult::ERROR, new \Bitrix\Main\Error( Loc::getMessage('LANDING_REST_DELETE_EXIST_BLOCKS'), 'LANDING_EXISTS_BLOCKS' ) ); return $eventResult; } else if (isset($stat[self::REST_USAGE_TYPE_PAGE][$app['CODE']])) { $eventResult = new \Bitrix\Main\EventResult( \Bitrix\Main\EventResult::ERROR, new \Bitrix\Main\Error( Loc::getMessage('LANDING_REST_DELETE_EXIST_PAGES'), 'LANDING_EXISTS_PAGES' ) ); return $eventResult; } } } /** * Gets stat data of using rest app. * @param bool $humanFormat Gets data in human format. * @param bool $onlyActive Gets data only in active states. * @param array $additionalFilter Additional filter array. * @return array */ public static function getRestStat(bool $humanFormat = false, bool $onlyActive = true, array $additionalFilter = []): array { $blockCnt = []; $fullStat = [ self::REST_USAGE_TYPE_BLOCK => [], self::REST_USAGE_TYPE_PAGE => [] ]; $activeValues = $onlyActive ? 'Y' : ['Y', 'N']; $filter = [ 'CODE' => 'repo_%', '=DELETED' => 'N', '=PUBLIC' => $activeValues, '=LANDING.ACTIVE' => $activeValues, '=LANDING.SITE.ACTIVE' => $activeValues ]; if (isset($additionalFilter['SITE_ID'])) { $filter['LANDING.SITE_ID'] = $additionalFilter['SITE_ID']; } Rights::setOff(); // gets all partners active block, placed on pages $res = Internals\BlockTable::getList([ 'select' => [ 'CODE', 'CNT' ], 'filter' => $filter, 'group' => [ 'CODE' ], 'runtime' => [ new \Bitrix\Main\Entity\ExpressionField('CNT', 'COUNT(*)') ] ]); while ($row = $res->fetch()) { $blockCnt[mb_substr($row['CODE'], 5)] = $row['CNT']; } // gets apps for this blocks $res = Repo::getList([ 'select' => [ 'ID', 'APP_CODE' ], 'filter' => [ 'ID' => array_keys($blockCnt) ] ]); while ($row = $res->fetch()) { if (!$row['APP_CODE']) { continue; } if (!isset($fullStat[self::REST_USAGE_TYPE_BLOCK][$row['APP_CODE']])) { $fullStat[self::REST_USAGE_TYPE_BLOCK][$row['APP_CODE']] = 0; } $fullStat[self::REST_USAGE_TYPE_BLOCK][$row['APP_CODE']] += $blockCnt[$row['ID']]; } unset($blockCnt); // gets additional partners active block with not empty INITIATOR_APP_CODE, placed on pages $filter['!CODE'] = $filter['CODE']; unset($filter['CODE']); $filter['!=INITIATOR_APP_CODE'] = null; $res = Internals\BlockTable::getList([ 'select' => [ 'INITIATOR_APP_CODE', 'CNT' ], 'filter' => $filter, 'group' => [ 'INITIATOR_APP_CODE' ], 'runtime' => [ new \Bitrix\Main\Entity\ExpressionField('CNT', 'COUNT(*)') ] ]); while ($row = $res->fetch()) { $appCode = $row['INITIATOR_APP_CODE']; if (!isset($fullStat[self::REST_USAGE_TYPE_BLOCK][$appCode])) { $fullStat[self::REST_USAGE_TYPE_BLOCK][$appCode] = 0; } $fullStat[self::REST_USAGE_TYPE_BLOCK][$appCode] += $row['CNT']; } // gets all partners active pages $filter = [ '=DELETED' => 'N', '=ACTIVE' => $activeValues, '=SITE.ACTIVE' => $activeValues, '!=INITIATOR_APP_CODE' => null ]; if (isset($additionalFilter['SITE_ID'])) { $filter['SITE_ID'] = $additionalFilter['SITE_ID']; } $res = Landing::getList([ 'select' => [ 'INITIATOR_APP_CODE', 'CNT' ], 'filter' => $filter, 'group' => [ 'INITIATOR_APP_CODE' ], 'runtime' => [ new \Bitrix\Main\Entity\ExpressionField('CNT', 'COUNT(*)') ] ]); while ($row = $res->fetch()) { $appCode = $row['INITIATOR_APP_CODE']; if (!isset($fullStat[self::REST_USAGE_TYPE_PAGE][$appCode])) { $fullStat[self::REST_USAGE_TYPE_PAGE][$appCode] = 0; } $fullStat[self::REST_USAGE_TYPE_PAGE][$appCode] += $row['CNT']; } // get client id for apps if (!$humanFormat && \Bitrix\Main\Loader::includeModule('rest')) { $appsCode = array_merge( array_keys($fullStat[self::REST_USAGE_TYPE_BLOCK]), array_keys($fullStat[self::REST_USAGE_TYPE_PAGE]) ); $fullStatNew = [ self::REST_USAGE_TYPE_BLOCK => [], self::REST_USAGE_TYPE_PAGE => [] ]; if ($appsCode) { $appsCode = array_unique($appsCode); $res = AppTable::getList([ 'select' => [ 'CLIENT_ID', 'CODE' ], 'filter' => [ '=CODE' => $appsCode ] ]); while ($row = $res->fetch()) { foreach ($fullStat as $code => $stat) { if (isset($stat[$row['CODE']])) { $fullStatNew[$code][$row['CLIENT_ID']] = $stat[$row['CODE']]; } } } } return $fullStatNew; } Rights::setOn(); return $fullStat; } }