plugins/sys/classes/yf_core_blocks.class.php
<?php
/**
* Core blocks methods.
*
* @author YFix Team <yfix.dev@gmail.com>
* @version 1.0
*/
class yf_core_blocks
{
public $TASK_NOT_FOUND_404_HEADER = false;
public $TASK_DENIED_403_HEADER = false;
public $FORCE_ALLOWED_CLASSES = [
'api',
];
/**
* Catch missing method call.
* @param mixed $name
* @param mixed $args
*/
public function __call($name, $args)
{
return main()->extend_call($this, $name, $args);
}
/**
* Alias for the '_show_block'.
* @param mixed $params
*/
public function show_block($params = [])
{
return $this->_show_block($params);
}
/**
* Show custom block contents.
* @param mixed $input
*/
public function _show_block($input = [])
{
if ( ! isset($this->_blocks_infos)) {
$this->_blocks_infos = main()->get_data('blocks_all');
}
if (empty($this->_blocks_infos)) {
if ( ! $this->_error_no_blocks_raised) {
trigger_error(__CLASS__ . ': Blocks names not loaded', E_USER_WARNING);
$this->_error_no_blocks_raised = true;
}
return false;
}
$BLOCK_EXISTS = false;
if (isset($input['block_id']) && is_numeric($input['block_id'])) {
$block_info = $this->_blocks_infos[$input['block_id']];
if ($block_info && trim($block_info['type']) == MAIN_TYPE) {
$block_id = $input['block_id'];
$block_name = $block_info['name'];
$BLOCK_EXISTS = true;
}
} else {
$block_name = $input['name'];
if (empty($block_name)) {
trigger_error(__CLASS__ . ': Given empty block name to show', E_USER_WARNING);
return false;
}
foreach ((array) $this->_blocks_infos as $block_info) {
// Skip blocks from other init type ('admin' or 'user')
if (trim($block_info['type']) != MAIN_TYPE) {
continue;
}
// Found!
if ($block_info['name'] == $block_name) {
$BLOCK_EXISTS = true;
$block_id = $block_info['id'];
break;
}
}
}
if ( ! $BLOCK_EXISTS) {
trigger_error(__CLASS__ . ': block "' . _prepare_html($block_name) . '" not found in blocks list', E_USER_WARNING);
return false;
}
if ( ! $this->_blocks_infos[$block_id]['active']) {
return false;
}
if ( ! $this->_check_block_rights($block_id, $_GET['object'], $_GET['action'])) {
return $this->_action_on_block_denied($block_name);
}
$cur_block_info = $this->_blocks_infos[$block_id];
// If special object method specified - then call it
// Syntax: [path_to]$class_name.$method_name
// @example 'static_pages.show'
// @example 'classes/minicalendar.createcalendar'
if ( ! empty($cur_block_info['method_name'])) {
$special_path = '';
if (false !== strpos($cur_block_info['method_name'], '/')) {
$special_path = substr($cur_block_info['method_name'], 0, strrpos($cur_block_info['method_name'], '/') + 1);
$cur_block_info['method_name'] = substr($cur_block_info['method_name'], strrpos($cur_block_info['method_name'], '/') + 1);
}
list($special_class_name, $special_method_name) = explode('.', $cur_block_info['method_name']);
$special_params = [
'block_name' => $block_name,
'block_id' => $block_id,
];
if ( ! empty($special_class_name) && ! empty($special_method_name)) {
$obj = _class_safe($special_class_name, $special_path);
if (is_object($obj) && method_exists($obj, $special_method_name)) {
return $obj->$special_method_name($special_params);
}
trigger_error(__CLASS__ . ': block "' . _prepare_html($block_name) . '" custom php module.method not exists: ' . _prepare_html($cur_block_info['method_name']), E_USER_WARNING);
return false;
}
}
$prepend = _class('core_events')->fire('block.prepend[' . $block_name . ']');
$body = tpl()->parse($cur_block_info['stpl_name'] ?: $block_name, [
'block_name' => $block_name,
'block_id' => $block_id,
]);
$append = _class('core_events')->fire('block.append[' . $block_name . ']', [&$body]);
return ($prepend ? implode(PHP_EOL, $prepend) : '') . $body . ($append ? implode(PHP_EOL, $append) : '');
}
/**
* Action to on denied block.
* @param mixed $block_name
*/
public function _action_on_block_denied($block_name = '')
{
if ($block_name == 'center_area') {
if ($this->TASK_DENIED_403_HEADER) {
header(($_SERVER['SERVER_PROTOCOL'] ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1') . ' 403 Forbidden');
main()->IS_403 = true;
}
if (MAIN_TYPE_USER && ! main()->USER_ID) {
$redir_params = [
'%%object%%' => $_GET['object'],
'%%action%%' => $_GET['action'],
'%%add_get_vars%%' => str_replace('&', ';', _add_get(['object', 'action'])),
];
$redir_url = str_replace(array_keys($redir_params), array_values($redir_params), main()->REDIR_URL_DENIED);
if ( ! empty($redir_url)) {
if ($_GET['object'] == 'login_form') {
return 'Access to login form denied on center block (' . __CLASS__ . '.' . __FUNCTION__ . ')';
}
return js_redirect($redir_url);
}
} elseif (MAIN_TYPE_USER && main()->USER_ID) {
return '<div class="alert alert-error alert-danger">' . t('Access denied') . '</div>';
} elseif (MAIN_TYPE_ADMIN && main()->ADMIN_ID) {
return '<div class="alert alert-error alert-danger">' . t('Access denied') . '</div>';
//} elseif (MAIN_TYPE_ADMIN && !main()->ADMIN_ID) {
}
}
return false;
}
/**
* Try to find id of the center block.
*/
public function _get_center_block_id()
{
if ( ! isset($this->_blocks_infos)) {
$this->_blocks_infos = main()->get_data('blocks_all');
}
$center_block_id = 0;
foreach ((array) $this->_blocks_infos as $cur_block_id => $cur_block_info) {
if ($cur_block_info['type'] == MAIN_TYPE && trim($cur_block_info['name']) == 'center_area') {
$center_block_id = $cur_block_id;
break;
}
}
return $center_block_id;
}
/**
* Load array of blocks rules.
*/
public function _load_blocks_rules()
{
if ( ! empty($this->_blocks_rules)) {
return false;
}
$rules = main()->get_data('blocks_rules');
$rule_names_to_skip = ['id', 'block_id', 'rule_type', 'active', 'order'];
foreach ((array) $rules as $rule_id => $rule_info) {
foreach ((array) $rule_info as $rule_name => $rule_text) {
if (in_array($rule_name, $rule_names_to_skip) || empty($rule_text)) {
continue;
}
$rule_text = trim(str_replace([' ', "\t", "\r", "\n", '"', "'", ',,'], '', $rule_text), ',');
$rule_text = explode(',', $rule_text);
$rules[$rule_id][$rule_name] = $rule_text;
}
}
$this->_blocks_rules = $rules;
}
public function _get_center_block_rules()
{
$rules = &$this->CENTER_BLOCK_RULES;
if (isset($rules)) {
return $rules;
}
$this->CENTER_BLOCK_ID = $this->_get_center_block_id();
$rules = [];
foreach ((array) $this->_blocks_rules as $rid => $rinfo) {
if ($rinfo != $this->CENTER_BLOCK_ID) {
continue;
}
$rules[$rid] = $rinfo;
}
$this->CENTER_BLOCK_RULES = $rules;
return $rules;
}
/**
* Check rights for blocks.
* @param mixed $block_id
* @param mixed $OBJECT
* @param mixed $ACTION
*/
public function _check_block_rights($block_id = 0, $OBJECT = '', $ACTION = '')
{
if (empty($block_id) || empty($OBJECT)) {
return false;
}
if (empty($ACTION)) {
$ACTION = 'show';
}
$CUR_USER_GROUP = (int) (MAIN_TYPE_ADMIN ? $_SESSION['admin_group'] : $_SESSION['user_group']);
$CUR_USER_THEME = conf('theme');
$CUR_LOCALE = conf('language');
$CUR_SITE = (int) conf('SITE_ID');
$CUR_SERVER_ID = (int) conf('SERVER_ID');
$CUR_SERVER_ROLE = conf('SERVER_ROLE');
$RESULT = false;
if ( ! isset($this->_blocks_rules)) {
$this->_load_blocks_rules();
}
foreach ((array) $this->_blocks_rules as $rule_id => $rule_info) {
if ($rule_info['block_id'] != $block_id) {
continue;
}
$matched_method = false;
$matched_user_group = false;
$matched_theme = false;
$matched_locale = false;
$matched_site = false;
$matched_server_id = false;
$matched_server_role = false;
// Check matches
if (is_array($rule_info['methods']) && (in_array($OBJECT, $rule_info['methods']) || in_array($OBJECT . '.' . $ACTION, $rule_info['methods']))) {
$matched_method = true;
}
if (is_array($rule_info['user_groups']) && in_array($CUR_USER_GROUP, $rule_info['user_groups'])) {
$matched_user_group = true;
}
if (is_array($rule_info['themes']) && in_array($CUR_USER_THEME, $rule_info['themes'])) {
$matched_theme = true;
}
if (is_array($rule_info['locales']) && in_array($CUR_LOCALE, $rule_info['locales'])) {
$matched_locale = true;
}
if (is_array($rule_info['site_ids']) && in_array($CUR_SITE, $rule_info['site_ids'])) {
$matched_site = true;
}
if (is_array($rule_info['server_ids']) && in_array($CUR_SERVER_ID, $rule_info['server_ids'])) {
$matched_server_id = true;
}
if (is_array($rule_info['server_roles']) && in_array($CUR_SERVER_ROLE, $rule_info['server_roles'])) {
$matched_server_role = true;
}
if (( ! is_array($rule_info['methods']) || $matched_method)
&& ( ! is_array($rule_info['user_groups']) || $matched_user_group)
&& ( ! is_array($rule_info['themes']) || $matched_theme || ! $CUR_USER_THEME)
&& ( ! is_array($rule_info['locales']) || $matched_locale || ! $CUR_LOCALE)
&& ( ! is_array($rule_info['site_ids']) || $matched_site || ! $CUR_SITE)
&& ( ! is_array($rule_info['server_ids']) || $matched_server_id || ! $CUR_SERVER_ID)
&& ( ! is_array($rule_info['server_roles']) || $matched_server_role || ! $CUR_SERVER_ROLE)
) {
$RESULT = trim($rule_info['rule_type']) == 'ALLOW' ? true : false;
}
}
return $RESULT;
}
/**
* Try to run center block module/method if allowed.
*/
public function prefetch_center()
{
$block_name = 'center_area';
if ( ! isset($this->_blocks_infos)) {
$this->_blocks_infos = main()->get_data('blocks_all');
}
if (empty($this->_blocks_infos)) {
return false;
}
$BLOCK_EXISTS = false;
foreach ((array) $this->_blocks_infos as $block_info) {
if (trim($block_info['type']) != MAIN_TYPE) {
continue;
}
if ($block_info['name'] == $block_name) {
$BLOCK_EXISTS = true;
$block_id = $block_info['id'];
break;
}
}
if ( ! $BLOCK_EXISTS) {
return false;
}
if ( ! $this->_blocks_infos[$block_id]['active']) {
return false;
}
if ( ! $this->_check_block_rights($block_id, $_GET['object'], $_GET['action'])) {
return _class('graphics')->_action_on_block_denied($block_name);
}
return $this->tasks($allowed_check = true);
}
/**
* Display main 'center' block contents.
*/
public function show_center()
{
return $this->tasks($allowed_check = true);
}
public function _get_denied_tasks_names()
{
$cache = 'cache_' . __FUNCTION__;
if (isset($this->$cache)) {
return $this->$cache;
}
$names = [];
$ext = '.class.php';
$pattern = '{,plugins/*/}classes/*' . $ext;
$globs = [
'framework' => YF_PATH . $pattern,
'project' => PROJECT_PATH . $pattern,
'app' => APP_PATH . $pattern,
];
$ext_len = strlen($ext);
$names = [];
$prefix = YF_PREFIX;
$plen = strlen($prefix);
foreach ($globs as $glob) {
foreach (glob($glob, GLOB_BRACE) as $path) {
$name = substr(basename($path), 0, -$ext_len);
if (substr($name, 0, $plen) === $prefix) {
$name = substr($name, $plen);
}
$names[$name] = $name;
}
}
if ($exclude = $this->FORCE_ALLOWED_CLASSES) {
foreach ((array) $exclude as $name) {
if (isset($names[$name])) {
unset($names[$name]);
}
}
}
$this->$cache = $names;
return $names;
}
/**
* Main $_GET tasks handler.
* @param mixed $allowed_check
*/
public function tasks($allowed_check = false)
{
$main = main();
if ($main->is_console() || $main->is_ajax()) {
$main->no_graphics(true);
}
// Singleton
$_center_result = tpl()->_CENTER_RESULT;
if (isset($_center_result)) {
return $_center_result;
}
$not_found = false;
$access_denied = false;
$custom_handler_exists = false;
$OBJECT = &$_GET['object'];
$ACTION = &$_GET['action'];
_class('router')->_route_request();
// Check if called class method is 'private' - then do not use it
// Also we protect here core classes that can be instantinated before this method and can be allowed by mistake
// Use other module names, think about this list as "reserved" words
if (substr($ACTION, 0, 1) == '_' || ! strlen($OBJECT) || substr($OBJECT, 0, strlen(YF_PREFIX)) === YF_PREFIX || in_array($OBJECT, $this->_get_denied_tasks_names())) {
$access_denied = true;
}
if ( ! $access_denied) {
$obj = module($OBJECT);
if ( ! is_object($obj)) {
$not_found = true;
}
if ( ! $not_found && ! method_exists($obj, $ACTION)) {
$not_found = true;
}
// Check if we have custom action handler in module (catch all requests to module methods)
if (method_exists($obj, $main->MODULE_ACTION_HANDLER)) {
$custom_handler_exists = true;
}
if ( ! $not_found || $custom_handler_exists) {
if ($custom_handler_exists) {
$not_found = false;
$body = $obj->{$main->MODULE_ACTION_HANDLER}($ACTION, $main->_ARGS_DIRTY);
} else {
$is_banned = false;
if (MAIN_TYPE_USER && $main->AUTO_BAN_CHECKING) {
$is_banned = _class('ban_status')->_auto_check([]);
}
if ($is_banned) {
$body = _e();
} else {
$body = $obj->$ACTION();
}
}
}
}
$redirect_func = function ($url) {
$redir_params = [
'%%object%%' => $OBJECT,
'%%action%%' => $ACTION,
'%%add_get_vars%%' => str_replace('&', ';', _add_get(['object', 'action'])),
];
$redir_url = str_replace(array_keys($redir_params), array_values($redir_params), $url);
if ( ! empty($redir_url)) {
redirect($redir_url, 1, tpl()->parse('system/error_not_found'));
}
};
if ($not_found) {
$main->BLOCKS_TASK_404 = true;
if ($this->TASK_NOT_FOUND_404_HEADER) {
header(($_SERVER['SERVER_PROTOCOL'] ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1') . ' 404 Not Found');
$main->IS_404 = true;
}
if (_class('graphics')->NOT_FOUND_RAISE_WARNING) {
trigger_error(__CLASS__ . ': Task not found: ' . $OBJECT . '.' . $ACTION, E_USER_WARNING);
}
if (MAIN_TYPE_USER) {
$u = $main->REDIR_URL_NOT_FOUND;
if (is_array($u) && ! empty($u)) {
// Prefill GET keys from redirect url
foreach (['object', 'action', 'id', 'page'] as $k) {
$_GET[$k] = $u[$k];
}
if ( ! empty($u['object'])) {
$action = $u['action'] ?: 'show';
$body = _class_safe($u['object'], $u['path'])->$action();
} elseif (isset($u['stpl'])) {
$main->no_graphics(true);
echo tpl()->parse($u['stpl']);
}
} else {
$redir_func = $this->REDIRECT_CALLBACK_404 ?: $redirect_func;
$redir_func($u);
}
}
} elseif ($allowed_check && $access_denied) {
$main->BLOCKS_TASK_403 = true;
if ($this->TASK_DENIED_403_HEADER) {
header(($_SERVER['SERVER_PROTOCOL'] ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1') . ' 403 Forbidden');
$main->IS_403 = true;
}
trigger_error(__CLASS__ . ': Access denied: ' . $OBJECT . '.' . $ACTION, E_USER_WARNING);
if (MAIN_TYPE_USER) {
$redir_func = $this->REDIRECT_CALLBACK_403 ?: $redirect_func;
$redir_func($main->REDIR_URL_DENIED);
}
}
$block_name = 'center_area';
$events = _class('core_events');
$prepend = $events->fire('block.prepend[' . $block_name . ']');
$append = $events->fire('block.append[' . $block_name . ']', [&$body]);
$body = ($prepend ? implode(PHP_EOL, $prepend) : '') . $body . ($append ? implode(PHP_EOL, $append) : '');
// Singleton
tpl()->_CENTER_RESULT = (string) $body;
// Output only center content, when we are inside AJAX_MODE
if ($main->is_ajax()) {
echo $body;
}
return $body;
}
}