%PDF- %PDF-
Direktori : /home/bitrix/www/bitrix/modules/lists/lib/internals/ |
Current File : /home/bitrix/www/bitrix/modules/lists/lib/internals/controller.php |
<?php namespace Bitrix\Lists\Internals; use Bitrix\Lists\Internals\Error\Error; use Bitrix\Lists\Internals\Error\ErrorCollection; use Bitrix\Lists\Internals\Error\IErrorable; use Bitrix\Main\Application; use Bitrix\Main\Context; use Bitrix\Main\Localization\Loc; use Bitrix\Main\Web\PostDecodeFilter; use Bitrix\Main\Web\Json; Loc::loadMessages(__FILE__); abstract class Controller implements IErrorable { const ERROR_REQUIRED_PARAMETER = 'LISTS_CONTROLLER_22001'; const ERROR_UNKNOWN_ACTION = 'LISTS_CONTROLLER_22002'; const STATUS_SUCCESS = 'success'; const STATUS_PROCESSING = 'processing'; const STATUS_COMPLETED = 'completed'; const STATUS_DENIED = 'denied'; const STATUS_ERROR = 'error'; const STATUS_NEED_AUTH = 'need_auth'; const STATUS_INVALID_SIGN = 'invalid_sign'; /** @var string */ protected $action; /** @var array */ protected $actionDescription; /** @var string */ protected $realActionName; /** @var ErrorCollection */ protected $errorCollection; /** @var \Bitrix\Main\HttpRequest */ protected $request; public function __construct() { $this->errorCollection = new ErrorCollection; $this->request = Context::getCurrent()->getRequest(); } protected function end() { include($_SERVER["DOCUMENT_ROOT"].BX_ROOT."/modules/main/include/epilog_after.php"); die; } public function exec() { try { if($this->request->isPost()) { \CUtil::jSPostUnescape(); $this->request->addFilter(new PostDecodeFilter); } $this->resolveAction(); $this->checkAction(); $this->checkRequiredModules(); if(!$this->prepareParams()) { $this->sendJsonErrorResponse(); } //todo create Event! if($this->processBeforeAction($this->getAction()) !== false) { $this->runAction(); } } catch(\Exception $e) { $this->runProcessingException($e); } } /** * @return array|bool|\CAllUser|\CUser */ protected function getUser() { global $USER; return $USER; } protected function sendJsonResponse($response, $params = null) { if(!defined('PUBLIC_AJAX_MODE')) { define('PUBLIC_AJAX_MODE', true); } global $APPLICATION; $APPLICATION->restartBuffer(); if(!empty($params['http_status']) && $params['http_status'] == 403) { header('HTTP/1.0 403 Forbidden', true, 403); } if(!empty($params['http_status']) && $params['http_status'] == 500) { header('HTTP/1.0 500 Internal Server Error', true, 500); } header('Content-Type:application/json; charset=UTF-8'); echo Json::encode($response); $this->end(); } protected function sendJsonErrorResponse() { $errors = array(); foreach($this->getErrors() as $error) { /** @var Error $error */ $errors[] = array( 'message' => $error->getMessage(), 'code' => $error->getCode(), ); } unset($error); $this->sendJsonResponse(array( 'status' => self::STATUS_ERROR, 'errors' => $errors, )); } protected function sendJsonAccessDeniedResponse($message = '') { $this->sendJsonResponse(array( 'status' => self::STATUS_DENIED, 'message' => $message, )); } protected function sendJsonInvalidSignResponse($message = '') { $this->sendJsonResponse(array( 'status' => self::STATUS_INVALID_SIGN, 'message' => $message, )); } protected function sendJsonSuccessResponse(array $response = array()) { $response['status'] = self::STATUS_SUCCESS; $this->sendJsonResponse($response); } protected function sendJsonProcessingResponse(array $response = array()) { $response['status'] = self::STATUS_PROCESSING; $this->sendJsonResponse($response); } protected function sendJsonCompletedResponse(array $response = array()) { $response['status'] = self::STATUS_COMPLETED; $this->sendJsonResponse($response); } protected function sendResponse($response) { global $APPLICATION; $APPLICATION->restartBuffer(); echo $response; $this->end(); } /** * @return Error[] */ public function getErrors() { return $this->errorCollection->toArray(); } /** * @inheritdoc */ public function getErrorsByCode($code) { return $this->errorCollection->getErrorsByCode($code); } /** * @inheritdoc */ public function getErrorByCode($code) { return $this->errorCollection->getErrorByCode($code); } protected function resolveAction() { $listOfActions = $this->normalizeListOfAction($this->listOfActions()); $action = strtolower($this->action); if(!isset($listOfActions[$action])) { $this->errorCollection->add(array(new Error(Loc::getMessage('LISTS_CONTROLLER_ERROR_UNKNOWN_ACTION', array('#ACTION#' => $action)), self::ERROR_UNKNOWN_ACTION))); return $this; } $this->realActionName = $action; $description = $listOfActions[$this->realActionName]; $this->setAction($description['name'], $description); return $this; } //todo refactor BaseComponent + Controller normalizeListOfAction, resolveAction. //you can use composition in BaseComponent protected function normalizeListOfAction(array $listOfActions) { $normalized = array(); foreach($listOfActions as $action => $description) { if(!is_string($action)) { $normalized[$description] = $this->normalizeActionDescription($description, $description); } else { $normalized[$action] = $this->normalizeActionDescription($action, $description); } } unset($action, $description); return array_change_key_case($normalized, CASE_LOWER); } protected function normalizeActionDescription($action, $description) { if(!is_array($description)) { $description = array( 'method' => array('GET'), 'name' => $description, 'check_csrf_token' => false, 'redirect_on_auth' => true, 'close_session' => false, ); } if(empty($description['name'])) { $description['name'] = $action; } if(!isset($description['redirect_on_auth'])) { $description['redirect_on_auth'] = false; } if(!isset($description['close_session'])) { $description['close_session'] = false; } return $description; } protected function checkAction() { if($this->errorCollection->hasErrors()) { $this->sendJsonErrorResponse(); } $description = $this->getActionDescription(); if(!$this->getUser() || !$this->getUser()->getId()) { if($description['redirect_on_auth']) { LocalRedirect(SITE_DIR . 'auth/?backurl=' . urlencode(Application::getInstance()->getContext()->getRequest()->getRequestUri())); } else { $this->runProcessingIfUserNotAuthorized(); } } //if does not exist check_csrf_token we have to check csrf for only POST method. if($description['check_csrf_token'] === true || ($this->request->isPost() && !isset($description['check_csrf_token']))) { //in BDisk we have token_sid if(!check_bitrix_sessid() && !check_bitrix_sessid('token_sid')) { $this->runProcessingIfInvalidCsrfToken(); } } if(!in_array($this->request->getRequestMethod(), $description['method'])) { $this->sendJsonAccessDeniedResponse('Wrong method for current action'); } } protected function listOfActions() { return array(); } /** * @return string */ public function getAction() { return $this->action; } /** * @return array */ public function getActionDescription() { return $this->actionDescription; } /** * @param string $action * @param array $description * @return $this */ public function setAction($action, array $description) { $this->action = $action; $this->actionDescription = $description; return $this; } /** * @param string $action * @return $this */ public function setActionName($action) { $this->action = $action; return $this; } protected function checkRequiredModules() {} protected function prepareParams() { return true; } /** * Common operations before run action. * @param string $actionName Action name which will be run. * @return bool If method will return false, then action will not execute. */ protected function processBeforeAction($actionName) { return true; } protected function runAction() { $description = $this->getActionDescription(); if($description['close_session'] === true) { //todo be careful by using this features. session_write_close(); } $actionMethod = 'processAction' . $this->getAction(); return $this->$actionMethod(); } protected function runProcessingException(\Exception $e) { // throw $e; $this->errorCollection->add(array(new Error($e->getMessage()))); $this->sendJsonErrorResponse(); } protected function runProcessingIfUserNotAuthorized() { $this->sendJsonAccessDeniedResponse(); } protected function runProcessingIfInvalidCsrfToken() { $this->sendJsonAccessDeniedResponse('Wrong csrf token'); } /** * @return Application|\Bitrix\Main\HttpApplication|\CAllMain|\CMain */ protected function getApplication() { global $APPLICATION; return $APPLICATION; } /** * @param array $inputParams * @param array $required * @return bool */ protected function checkRequiredInputParams(array $inputParams, array $required) { foreach ($required as $item) { if(!isset($inputParams[$item]) || (!$inputParams[$item] && !(is_string($inputParams[$item]) && strlen($inputParams[$item])))) { $this->errorCollection->add(array(new Error(Loc::getMessage('LISTS_CONTROLLER_ERROR_REQUIRED_PARAMETER', array('#PARAM#' => $item)), self::ERROR_REQUIRED_PARAMETER))); return false; } } return true; } protected function checkRequiredPostParams(array $required) { $params = array(); foreach($required as $item) { $params[$item] = $this->request->getPost($item); } unset($item); return $this->checkRequiredInputParams($params, $required); } protected function checkRequiredGetParams(array $required) { $params = array(); foreach($required as $item) { $params[$item] = $this->request->getQuery($item); } unset($item); return $this->checkRequiredInputParams($params, $required); } protected function checkRequiredFilesParams(array $required) { $params = array(); foreach($required as $item) { $params[$item] = $this->request->getFile($item); } unset($item); return $this->checkRequiredInputParams($params, $required); } /** * Returns whether this is an AJAX (XMLHttpRequest) request. * @return boolean */ protected function isAjaxRequest() { return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'; } }