Merge branch 'next' into next-push

This commit is contained in:
Kijin Sung 2020-06-22 16:46:38 +09:00 committed by GitHub
commit b986f826ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1860 additions and 931 deletions

View file

@ -13,32 +13,7 @@ RewriteRule ^(.+)/(addons|files|layouts|m\.layouts|modules|widgets|widgetstyles)
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^(.+)\.min\.(css|js)$ ./$1.$2 [L]
# rss, blogAPI
RewriteRule ^(rss|atom)$ ./index.php?module=rss&act=$1 [L]
RewriteRule ^([a-zA-Z0-9_]+)/(rss|atom|api)$ ./index.php?mid=$1&act=$2 [L]
# trackback
RewriteRule ^([0-9]+)/(.+)/trackback$ ./index.php?document_srl=$1&key=$2&act=trackback [L]
RewriteRule ^([a-zA-Z0-9_]+)/([0-9]+)/(.+)/trackback$ ./index.php?mid=$1&document_srl=$2&key=$3&act=trackback [L]
# document category
RewriteRule ^([a-zA-Z0-9_]+)/category/([0-9]+)$ ./index.php?mid=$1&category=$2 [L,QSA]
# document permanent link
RewriteRule ^([0-9]+)$ ./index.php?document_srl=$1 [L,QSA]
# admin module link
RewriteRule ^admin/?$ ./index.php?module=admin [L,QSA]
# mid link
# all other short URLs
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9_]+)/?$ ./index.php?mid=$1 [L,QSA]
# mid + document link
RewriteRule ^([a-zA-Z0-9_]+)/([0-9]+)$ ./index.php?mid=$1&document_srl=$2 [L,QSA]
# mid + entry title
RewriteRule ^([a-zA-Z0-9_]+)/entry/(.+)$ ./index.php?mid=$1&entry=$2 [L,QSA]
# file download
RewriteRule ^files/download/([0-9]+)/([a-zA-Z0-9_-]+)/(.+)$ ./index.php?act=procFileOutput&file_srl=$1&file_key=$2&filename=$3 [L]
RewriteRule . index.php [L]

View file

@ -8,12 +8,6 @@
*/
class Context
{
/**
* Allow rewrite
* @var bool TRUE: using rewrite mod, FALSE: otherwise
*/
public $allow_rewrite = FALSE;
/**
* Request method
* @var string GET|POST|XMLRPC|JSON
@ -133,18 +127,16 @@ class Context
*/
private static $_init_called = false;
/**
* Current route information
*/
private static $_route_info = null;
/**
* object oFrontEndFileHandler()
* @var object
*/
private static $_oFrontEndFileHandler = null;
/**
* SSL action cache file
* @var array
*/
private static $_ssl_actions_cache_file = 'files/cache/common/ssl_actions.php';
/**
* SSL action cache
*/
@ -205,13 +197,6 @@ class Context
self::$_oFrontEndFileHandler = self::$_instance->oFrontEndFileHandler = new FrontEndFileHandler();
self::$_get_vars = self::$_get_vars ?: new stdClass;
self::$_tpl_vars = self::$_tpl_vars ?: new stdClass;
// Include SSL action cache file.
self::$_ssl_actions_cache_file = RX_BASEDIR . self::$_ssl_actions_cache_file;
if(Rhymix\Framework\Storage::exists(self::$_ssl_actions_cache_file))
{
self::$_ssl_actions = (include self::$_ssl_actions_cache_file) ?: array();
}
}
return self::$_instance;
}
@ -246,15 +231,26 @@ class Context
self::$_instance = self::getInstance();
}
// Load system configuration.
self::loadDBInfo();
// Set information about the current request.
self::_checkGlobalVars();
self::setRequestMethod();
self::setRequestArguments();
if (in_array(self::$_instance->request_method, array('GET', 'POST')))
{
$method = $_SERVER['REQUEST_METHOD'] ?: 'GET';
$url = $_SERVER['REQUEST_URI'];
$route_info = Rhymix\Framework\Router::parseURL($method, $url, Rhymix\Framework\Router::getRewriteLevel());
self::setRequestArguments($route_info->args);
self::$_route_info = $route_info;
}
else
{
self::setRequestArguments();
}
self::setUploadInfo();
// Load system configuration.
self::loadDBInfo();
// If Rhymix is installed, get virtual site information.
if(self::isInstalled())
{
@ -266,8 +262,7 @@ class Context
define('RX_BASEURL', parse_url($default_url, PHP_URL_PATH));
}
}
$oModuleModel = ModuleModel::getInstance();
$site_module_info = $oModuleModel->getDefaultMid() ?: new stdClass;
$site_module_info = ModuleModel::getDefaultMid() ?: new stdClass;
self::set('site_module_info', $site_module_info);
self::set('_default_timezone', ($site_module_info->settings && $site_module_info->settings->timezone) ? $site_module_info->settings->timezone : null);
self::set('_default_url', self::$_instance->db_info->default_url = self::getDefaultUrl($site_module_info));
@ -286,9 +281,19 @@ class Context
}
// Redirect to SSL if the current domain always uses SSL.
if ($site_module_info->security === 'always' && !RX_SSL && PHP_SAPI !== 'cli' && !$site_module_info->is_default_replaced)
if (!RX_SSL && PHP_SAPI !== 'cli' && $site_module_info->security === 'always' && !$site_module_info->is_default_replaced)
{
$ssl_url = self::getDefaultUrl($site_module_info) . RX_REQUEST_URL;
$ssl_url = self::getDefaultUrl($site_module_info, true) . RX_REQUEST_URL;
self::setCacheControl(0);
header('Location: ' . $ssl_url, true, 301);
exit;
}
// Redirect to SSL if the current action requires SSL.
self::$_ssl_actions = $site_module_info->security === 'optional' ? ModuleModel::getActionSecurity() : array();
if (!RX_SSL && count(self::$_ssl_actions) && self::isExistsSSLAction(self::get('act')) && self::getRequestMethod() === 'GET')
{
$ssl_url = self::getDefaultUrl($site_module_info, true) . RX_REQUEST_URL;
self::setCacheControl(0);
header('Location: ' . $ssl_url, true, 301);
exit;
@ -380,8 +385,6 @@ class Context
// set authentication information in Context and session
if (self::isInstalled())
{
$oModuleModel->loadModuleExtends();
if (Rhymix\Framework\Session::getMemberSrl())
{
MemberController::getInstance()->setSessionInfo();
@ -507,7 +510,6 @@ class Context
// Copy to old format for backward compatibility.
self::$_instance->db_info = self::convertDBInfo($config);
self::$_instance->allow_rewrite = self::$_instance->db_info->use_rewrite === 'Y';
}
/**
@ -616,12 +618,22 @@ class Context
return self::$_instance->db_info;
}
/**
* Get current route information
*
* @return object
*/
public static function getRouteInfo()
{
return self::$_route_info;
}
/**
* Return ssl status
*
* @return object SSL status (Optional - none|always|optional)
*/
public static function getSslStatus()
public static function getSSLStatus()
{
return self::get('_use_ssl');
}
@ -630,9 +642,10 @@ class Context
* Return default URL
*
* @param object $site_module_info (optional)
* @param bool $use_ssl (optional)
* @return string Default URL
*/
public static function getDefaultUrl($site_module_info = null)
public static function getDefaultUrl($site_module_info = null, $use_ssl = null)
{
if ($site_module_info === null && ($default_url = self::get('_default_url')))
{
@ -644,9 +657,9 @@ class Context
$site_module_info = self::get('site_module_info');
}
$prefix = $site_module_info->security === 'always' ? 'https://' : 'http://';
$prefix = ($site_module_info->security === 'always' || $use_ssl) ? 'https://' : 'http://';
$hostname = $site_module_info->domain;
$port = $site_module_info->security === 'always' ? $site_module_info->https_port : $site_module_info->http_port;
$port = ($prefix === 'https://') ? $site_module_info->https_port : $site_module_info->http_port;
$result = $prefix . $hostname . ($port ? sprintf(':%d', $port) : '') . RX_BASEURL;
return $result;
}
@ -1183,11 +1196,21 @@ class Context
/**
* handle request arguments for GET/POST
*
* @param array $router_args
* @return void
*/
public static function setRequestArguments()
public static function setRequestArguments(array $router_args = [])
{
foreach($_REQUEST as $key => $val)
$request_args = $_SERVER['REQUEST_METHOD'] === 'GET' ? $_GET : $_POST;
if (count($router_args))
{
foreach ($router_args as $key => $val)
{
$request_args[$key] = $val;
}
}
foreach($request_args as $key => $val)
{
if($val === '' || isset(self::$_reserved_keys[$key]) || self::get($key))
{
@ -1196,22 +1219,11 @@ class Context
$key = escape($key);
$val = self::_filterRequestVar($key, $val);
$set_to_vars = false;
if($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET[$key]))
{
$set_to_vars = true;
}
elseif($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST[$key]))
{
$set_to_vars = true;
}
self::set($key, $val, $set_to_vars);
self::set($key, $val, true);
}
// Set deprecated request parameters.
if(!$_POST && !empty($GLOBALS['HTTP_RAW_POST_DATA']))
if($_SERVER['REQUEST_METHOD'] === 'POST' && !$_POST && !empty($GLOBALS['HTTP_RAW_POST_DATA']))
{
if(self::getRequestMethod() === 'XMLRPC')
{
@ -1231,6 +1243,10 @@ class Context
foreach((array)$params as $key => $val)
{
if (isset($request_args[$key]))
{
continue;
}
$key = escape($key);
$val = self::_filterXmlVars($key, $val);
self::set($key, $val, true);
@ -1242,6 +1258,10 @@ class Context
parse_str($GLOBALS['HTTP_RAW_POST_DATA'], $params);
foreach($params as $key => $val)
{
if (isset($request_args[$key]))
{
continue;
}
$key = escape($key);
$val = self::_filterRequestVar($key, $val);
self::set($key, $val, true);
@ -1434,7 +1454,15 @@ class Context
{
$_val = (int)$_val;
}
elseif(in_array($key, array('mid', 'vid', 'search_target', 'search_keyword', 'xe_validator_id')) || $_SERVER['REQUEST_METHOD'] === 'GET')
elseif(in_array($key, array('mid', 'vid', 'act', 'module')))
{
$_val = preg_match('/^[a-zA-Z0-9_-]*$/', $_val) ? $_val : null;
if($_val === null)
{
self::$_instance->security_check = 'DENY ALL';
}
}
elseif(in_array($key, array('search_target', 'search_keyword', 'xe_validator_id')) || $_SERVER['REQUEST_METHOD'] === 'GET')
{
$_val = escape($_val, false);
if(ends_with('url', $key, false))
@ -1613,6 +1641,12 @@ class Context
{
static $current_domain = null;
static $site_module_info = null;
static $rewrite_level = null;
if ($rewrite_level === null)
{
$rewrite_level = Rhymix\Framework\Router::getRewriteLevel();
}
if ($site_module_info === null)
{
$site_module_info = self::get('site_module_info');
@ -1721,35 +1755,7 @@ class Context
$query = '';
if(count($get_vars) > 0)
{
// if using rewrite mod
if(self::$_instance->allow_rewrite)
{
$var_keys = array_keys($get_vars);
sort($var_keys);
$target = join('.', $var_keys);
$act = $get_vars['act'];
$mid = $get_vars['mid'];
$key = $get_vars['key'];
$srl = $get_vars['document_srl'];
$tmpArray = array('rss' => 1, 'atom' => 1, 'api' => 1);
$is_feed = isset($tmpArray[$act]);
$target_map = array(
'mid' => $mid,
'category.mid' => "$mid/category/" . $get_vars['category'],
'entry.mid' => "$mid/entry/" . $get_vars['entry'],
'document_srl' => $srl,
'document_srl.mid' => "$mid/$srl",
'act' => ($is_feed && $act !== 'api') ? $act : '',
'act.mid' => $is_feed ? "$mid/$act" : '',
'act.document_srl.key' => ($act == 'trackback') ? "$srl/$key/$act" : '',
'act.document_srl.key.mid' => ($act == 'trackback') ? "$mid/$srl/$key/$act" : '',
);
$query = $target_map[$target];
}
if(!$query && count($get_vars) > 0)
{
$query = 'index.php?' . http_build_query($get_vars);
}
$query = Rhymix\Framework\Router::getURL($get_vars, $rewrite_level);
}
// If using SSL always
@ -2008,40 +2014,25 @@ class Context
*/
public static function addSSLAction($action)
{
if(isset(self::$_ssl_actions[$action]))
if (!ModuleModel::getActionSecurity($action))
{
return;
getController('module')->insertActionSecurity($action);
}
self::$_ssl_actions[$action] = 1;
$buff = '<?php return ' . var_export(self::$_ssl_actions, true) . ';';
Rhymix\Framework\Storage::write(self::$_ssl_actions_cache_file, $buff);
self::$_ssl_actions[$action] = true;
}
/**
* Register if actions are to be encrypted by SSL. Those actions are sent to https in common/js/xml_handler.js
*
* @param string $action act name
* @param array $action_array
* @return void
*/
public static function addSSLActions($action_array)
{
$changed = false;
foreach($action_array as $action)
{
if(!isset(self::$_ssl_actions[$action]))
{
self::$_ssl_actions[$action] = 1;
$changed = true;
}
self::addSSLAction($action);
}
if(!$changed)
{
return;
}
$buff = '<?php return ' . var_export(self::$_ssl_actions, true) . ';';
Rhymix\Framework\Storage::write(self::$_ssl_actions_cache_file, $buff);
}
/**
@ -2052,14 +2043,11 @@ class Context
*/
public static function subtractSSLAction($action)
{
if(!isset(self::$_ssl_actions[$action]))
if (ModuleModel::getActionSecurity($action))
{
return;
getController('module')->deleteActionSecurity($action);
}
unset(self::$_ssl_actions[$action]);
$buff = '<?php return ' . var_export(self::$_ssl_actions, true) . ';';
Rhymix\Framework\Storage::write(self::$_ssl_actions_cache_file, $buff);
}
/**
@ -2069,7 +2057,7 @@ class Context
*/
public static function getSSLActions()
{
if(self::getSslStatus() == 'optional')
if(self::getSSLStatus() == 'optional')
{
return self::$_ssl_actions;
}
@ -2607,11 +2595,11 @@ class Context
/**
* Check whether it is allowed to use rewrite mod
*
* @return bool True if it is allowed to use rewrite mod, otherwise FALSE
* @return int The currently configured rewrite level
*/
public static function isAllowRewrite()
{
return self::$_instance->allow_rewrite;
return Rhymix\Framework\Router::getRewriteLevel();
}
/**

View file

@ -204,17 +204,14 @@ class HTMLDisplayHandler
// handles a relative path generated by using the rewrite module
if(Context::isAllowRewrite())
{
$pattern = '/src=("|\'){1}(?:\.\/)?((?:files\/(?:attach|cache|faceOff|member_extra_info|thumbnails)|addons|common|(?:m\.)?layouts|modules|widgets|widgetstyle)\/[^"\']+)("|\'){1}/s';
$pattern = '/(action|src|href)=(["\'])(?:\.\/([^"\']*))?(["\'])/s';
$output = preg_replace($pattern, '$1=$2' . \RX_BASEURL . '$3$4', $output);
$pattern = '/src=(["\'])((?:files\/(?:attach|cache|faceOff|member_extra_info|thumbnails)|addons|common|(?:m\.)?layouts|modules|widgets|widgetstyle)\/[^"\']+)(["\'])/s';
$output = preg_replace($pattern, 'src=$1' . \RX_BASEURL . '$2$3', $output);
$pattern = '/href=("|\'){1}(\?[^"\']+)/s';
$pattern = '/href=(["\'])(\?[^"\']+)/s';
$output = preg_replace($pattern, 'href=$1' . \RX_BASEURL . '$2', $output);
if(Context::get('vid'))
{
$pattern = '/\/' . Context::get('vid') . '\?([^=]+)=/is';
$output = preg_replace($pattern, '/?$1=', $output);
}
}
// prevent the 2nd request due to url(none) of the background-image

View file

@ -12,15 +12,17 @@
* */
class ModuleHandler extends Handler
{
var $module = NULL; ///< Module
var $act = NULL; ///< action
var $mid = NULL; ///< Module ID
var $document_srl = NULL; ///< Document Number
var $module_srl = NULL; ///< Module Number
var $module_info = NULL; ///< Module Info
var $error = NULL; ///< an error code.
var $httpStatusCode = NULL; ///< http status code.
var $method = 'GET';
var $module_info = null;
var $module_srl = null;
var $module = null;
var $act = null;
var $mid = null;
var $document_srl = null;
var $route = null;
var $error = null;
var $is_mobile = false;
var $httpStatusCode = 200;
/**
* Valid types and kinds of module instances.
@ -79,11 +81,14 @@ class ModuleHandler extends Handler
}
// Set variables from request arguments
$this->method = Context::getRequestMethod();
$this->module = $module ? $module : Context::get('module');
$this->act = $act ? $act : Context::get('act');
$this->mid = $mid ? $mid : Context::get('mid');
$this->document_srl = $document_srl ? (int) $document_srl : (int) Context::get('document_srl');
$this->module_srl = $module_srl ? (int) $module_srl : (int) Context::get('module_srl');
$this->route = Context::getRouteInfo() ?: new stdClass;
$this->is_mobile = Mobile::isFromMobilePhone();
if($entry = Context::get('entry'))
{
$this->entry = Context::convertEncodingStr($entry);
@ -94,49 +99,13 @@ class ModuleHandler extends Handler
Context::set('mid', $this->mid = null);
}
// Validate variables to prevent XSS
$isInvalid = false;
if($this->module && !preg_match('/^[a-zA-Z0-9_-]+$/', $this->module))
{
$isInvalid = true;
}
if($this->mid && !preg_match('/^[a-zA-Z0-9_-]+$/', $this->mid))
{
$isInvalid = true;
}
if($this->act && !preg_match('/^[a-zA-Z0-9_-]+$/', $this->act))
{
$isInvalid = true;
}
if($isInvalid)
{
$this->error = 'msg_security_violation';
return;
}
if(isset($this->act) && (strlen($this->act) >= 4 && substr_compare($this->act, 'disp', 0, 4) === 0))
{
if(Context::get('_use_ssl') == 'optional' && Context::isExistsSSLAction($this->act) && !RX_SSL)
{
if(Context::get('_https_port') != null)
{
header('location: https://' . $_SERVER['HTTP_HOST'] . ':' . Context::get('_https_port') . $_SERVER['REQUEST_URI']);
}
else
{
header('location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
}
return;
}
}
// call a trigger before moduleHandler init
self::triggerCall('moduleHandler.init', 'before', $this);
// execute addon (before module initialization)
$called_position = 'before_module_init';
$oAddonController = AddonController::getInstance();
$addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone() ? 'mobile' : 'pc');
$addon_file = $oAddonController->getCacheFilePath($this->is_mobile ? 'mobile' : 'pc');
if(file_exists($addon_file)) include($addon_file);
}
@ -146,13 +115,12 @@ class ModuleHandler extends Handler
* */
public function init()
{
$oModuleModel = ModuleModel::getInstance();
$site_module_info = Context::get('site_module_info');
// Check unregistered domain action.
if (!$site_module_info || !isset($site_module_info->domain_srl) || $site_module_info->is_default_replaced)
{
$site_module_info = $oModuleModel->getDefaultDomainInfo();
$site_module_info = ModuleModel::getDefaultDomainInfo();
if ($site_module_info)
{
$domain_action = config('url.unregistered_domain_action') ?: 'redirect_301';
@ -177,112 +145,65 @@ class ModuleHandler extends Handler
}
}
// if success_return_url and error_return_url is incorrect
$urls = array(Context::get('success_return_url'), Context::get('error_return_url'));
foreach($urls as $url)
// Check success_return_url and error_return_url to prevent dangerous redirects.
$urls = array('success_return_url', 'error_return_url');
foreach($urls as $key)
{
if(empty($url))
$url = Context::get($key);
if ($url && !Rhymix\Framework\URL::isInternalURL($url))
{
continue;
}
if($host = parse_url($url, PHP_URL_HOST))
{
$defaultHost = parse_url(Context::getDefaultUrl(), PHP_URL_HOST);
if($host !== $defaultHost)
{
$siteModuleHost = $site_module_info->domain;
if(strpos($siteModuleHost, '/') !== false)
{
$siteModuleHost = parse_url($siteModuleHost, PHP_URL_HOST);
}
if($host !== $siteModuleHost)
{
Context::set('success_return_url', null);
Context::set('error_return_url', null);
}
}
Context::set($key, null);
}
}
// If the Router returned an error earlier, show an error here.
if($this->route && $this->route->status > 200)
{
$this->error = 'msg_module_is_not_exists';
$this->httpStatusCode = 404;
return true;
}
// Convert document alias (entry) to document_srl
if(!$this->document_srl && $this->mid && $this->entry)
{
$oDocumentModel = DocumentModel::getInstance();
$this->document_srl = $oDocumentModel->getDocumentSrlByAlias($this->mid, $this->entry);
$this->document_srl = DocumentModel::getDocumentSrlByAlias($this->mid, $this->entry);
if($this->document_srl)
{
Context::set('document_srl', $this->document_srl);
}
}
// Get module's information based on document_srl, if it's specified
// Get module info from document_srl.
if($this->document_srl)
{
$module_info = $oModuleModel->getModuleInfoByDocumentSrl($this->document_srl);
if($module_info)
$module_info = $this->_checkDocumentSrl();
if ($module_info === false)
{
// If it exists, compare mid based on the module information
// if mids are not matching, set it as the document's mid
if(!$this->mid || ($this->mid != $module_info->mid))
{
if(Context::getRequestMethod() == 'GET')
{
Context::setCacheControl(0);
header('location: ' . getNotEncodedSiteUrl($site_module_info->domain, 'mid', $module_info->mid, 'document_srl', $this->document_srl), true, 301);
return false;
}
else
{
$this->mid = $module_info->mid;
Context::set('mid', $this->mid);
}
}
// if requested module is different from one of the document, remove the module information retrieved based on the document number
if($this->module && $module_info->module != $this->module)
{
unset($module_info);
}
}
// Block access to secret or temporary documents.
if(Context::getRequestMethod() == 'GET')
{
$oDocumentModel = DocumentModel::getInstance();
$oDocument = $oDocumentModel->getDocument($this->document_srl);
if($oDocument->isExists() && !$oDocument->isAccessible())
{
$this->httpStatusCode = '403';
}
return false;
}
}
// If module_info is not set yet, and there exists mid information, get module information based on the mid
// Get module info from mid.
if(!$module_info && $this->mid)
{
$module_info = $oModuleModel->getModuleInfoByMid($this->mid, $site_module_info->site_srl);
//if($this->module && $module_info->module != $this->module) unset($module_info);
$module_info = ModuleModel::getModuleInfoByMid($this->mid);
}
// If module_info is not set still, and $module does not exist, find the default module
// Set module info as the default module for the domain.
if(!$module_info && !$this->module && !$this->mid)
{
$module_info = $site_module_info;
}
if(!$module_info && !$this->module && $site_module_info->module_site_srl)
{
$module_info = $site_module_info;
}
// Set index document
if($site_module_info->index_document_srl && !$this->module && !$this->mid && !$this->document_srl && Context::getRequestMethod() === 'GET' && !count($_GET))
// Set the index document.
if($site_module_info->index_document_srl && !$this->module && !$this->mid && !$this->document_srl && $this->method === 'GET' && !$this->route->args)
{
Context::set('document_srl', $this->document_srl = $site_module_info->index_document_srl, true);
}
// redirect, if site start module
if(!$site_module_info->index_document_srl && Context::getRequestMethod() === 'GET' && isset($_GET['mid']) && $_GET['mid'] === $site_module_info->mid && count($_GET) === 1)
// Redirect if the index module was requested.
if(!$site_module_info->index_document_srl && $this->method === 'GET' && isset($this->route->args['mid']) && $this->route->args['mid'] === $site_module_info->mid && count($this->route->args) === 1)
{
Context::setCacheControl(0);
header('location: ' . getNotEncodedSiteUrl($site_module_info->domain), true, 301);
@ -292,57 +213,18 @@ class ModuleHandler extends Handler
// If module info was set, retrieve variables from the module information
if($module_info)
{
// Set instance variables and SEO info.
$this->module = $module_info->module;
$this->mid = $module_info->mid;
$this->module_info = $module_info;
if ($module_info->mid == $site_module_info->mid)
{
$seo_title = config('seo.main_title') ?: '$SITE_TITLE - $SITE_SUBTITLE';
}
else
{
$seo_title = config('seo.subpage_title') ?: '$SITE_TITLE - $SUBPAGE_TITLE';
}
$this->_setModuleSEOInfo($module_info, $site_module_info);
ModuleController::getInstance()->replaceDefinedLangCode($seo_title);
Context::setBrowserTitle($seo_title, array(
'site_title' => Context::getSiteTitle(),
'site_subtitle' => Context::getSiteSubtitle(),
'subpage_title' => $module_info->browser_title,
'page' => Context::get('page') ?: 1,
));
$module_config = $oModuleModel->getModuleConfig('module');
if ($module_info->meta_keywords)
{
Context::addMetaTag('keywords', $module_info->meta_keywords);
}
elseif ($site_module_info->settings->meta_keywords)
{
Context::addMetaTag('keywords', $site_module_info->settings->meta_keywords);
}
elseif ($module_config->meta_keywords)
{
Context::addMetaTag('keywords', $module_config->meta_keywords);
}
if ($module_info->meta_description)
{
Context::addMetaTag('description', $module_info->meta_description);
}
elseif ($site_module_info->settings->meta_description)
{
Context::addMetaTag('description', $site_module_info->settings->meta_description);
}
elseif($module_config->meta_description)
{
Context::addMetaTag('description', $module_config->meta_description);
}
$viewType = (Mobile::isFromMobilePhone()) ? 'M' : 'P';
// Check if the current request is from a mobile device.
$this->is_mobile = Mobile::isFromMobilePhone();
$viewType = $this->is_mobile ? 'M' : 'P';
$targetSrl = $viewType === 'M' ? 'mlayout_srl' : 'layout_srl';
// use the site default layout.
// Apply default layouts.
if($module_info->{$targetSrl} == -1)
{
$oLayoutAdminModel = getAdminModel('layout');
@ -363,29 +245,28 @@ class ModuleHandler extends Handler
$layoutSrl = $module_info->{$targetSrl};
}
// reset a layout_srl in module_info.
// Reset layout_srl in module_info.
$module_info->{$targetSrl} = $layoutSrl;
$part_config = $oModuleModel->getModulePartConfig('layout', $layoutSrl);
// Add layout header script.
$part_config = ModuleModel::getModulePartConfig('layout', $layoutSrl);
Context::addHtmlHeader($part_config->header_script);
}
// Set module and mid into module_info
if(!isset($this->module_info))
else
{
$this->module_info = new stdClass();
$this->module_info = new stdClass;
$this->module_info->module = $this->module;
$this->module_info->mid = $this->mid;
}
$this->module_info->module = $this->module;
$this->module_info->mid = $this->mid;
// Set site_srl add 2011 08 09
// Always overwrite site_srl (deprecated)
$this->module_info->site_srl = $site_module_info->site_srl;
// Still no module? it's an error
if(!$this->module)
{
$this->error = 'msg_module_is_not_exists';
$this->httpStatusCode = '404';
$this->httpStatusCode = 404;
return true;
}
@ -415,26 +296,17 @@ class ModuleHandler extends Handler
* */
public function procModule()
{
$oModuleModel = ModuleModel::getInstance();
$display_mode = Mobile::isFromMobilePhone() ? 'mobile' : 'view';
// Set the display mode for the current device type.
$this->is_mobile = Mobile::isFromMobilePhone();
// If error occurred while preparation, return a message instance
if($this->error)
{
self::_setInputErrorToContext();
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
if($this->httpStatusCode)
{
$oMessageObject->setHttpStatusCode($this->httpStatusCode);
}
return $oMessageObject;
return self::_createErrorMessage(-1, $this->error, $this->httpStatusCode);
}
// Get action information with conf/module.xml
$xml_info = $oModuleModel->getModuleActionXml($this->module);
$xml_info = ModuleModel::getModuleActionXml($this->module);
// If not installed yet, modify act
if($this->module == "install")
@ -454,19 +326,7 @@ class ModuleHandler extends Handler
// still no act means error
if(!$this->act)
{
$this->error = 'msg_module_is_not_exists';
$this->httpStatusCode = '404';
self::_setInputErrorToContext();
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
if($this->httpStatusCode)
{
$oMessageObject->setHttpStatusCode($this->httpStatusCode);
}
return $oMessageObject;
return self::_createErrorMessage(-1, 'msg_module_is_not_exists', 404);
}
// get type, kind
@ -500,12 +360,7 @@ class ModuleHandler extends Handler
if(!in_array(strtoupper($_SERVER['REQUEST_METHOD']), $allowedMethodList))
{
$this->error = 'msg_invalid_request';
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
return $oMessageObject;
return self::_createErrorMessage(-1, 'msg_method_not_allowed', 405);
}
}
@ -514,13 +369,7 @@ class ModuleHandler extends Handler
{
if($xml_info->action->{$this->act} && $xml_info->action->{$this->act}->check_csrf !== 'false' && !checkCSRF())
{
$this->_setInputErrorToContext();
$this->error = 'msg_invalid_request';
$oMessageObject = ModuleHandler::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
return $oMessageObject;
return self::_createErrorMessage(-1, 'msg_security_violation');
}
}
@ -532,7 +381,7 @@ class ModuleHandler extends Handler
$logged_info = Context::get('logged_info');
// if(type == view, and case for using mobilephone)
if($type == "view" && Mobile::isFromMobilePhone() && Context::isInstalled())
if($type == "view" && $this->is_mobile && Context::isInstalled())
{
$orig_type = "view";
$type = "mobile";
@ -553,16 +402,7 @@ class ModuleHandler extends Handler
if(!is_object($oModule))
{
self::_setInputErrorToContext();
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
if($this->httpStatusCode)
{
$oMessageObject->setHttpStatusCode($this->httpStatusCode);
}
return $oMessageObject;
return self::_createErrorMessage(-1, $this->error, $this->httpStatusCode);
}
// If there is no such action in the module object
@ -570,24 +410,14 @@ class ModuleHandler extends Handler
{
if(!Context::isInstalled())
{
self::_setInputErrorToContext();
$this->error = 'msg_invalid_request';
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
if($this->httpStatusCode)
{
$oMessageObject->setHttpStatusCode($this->httpStatusCode);
}
return $oMessageObject;
return self::_createErrorMessage(-1, 'msg_invalid_request');
}
// 1. Look for the module with action name
if(preg_match('/^([a-z]+)([A-Z])([a-z0-9\_]+)(.*)$/', $this->act, $matches))
{
$module = strtolower($matches[2] . $matches[3]);
$xml_info = $oModuleModel->getModuleActionXml($module);
$xml_info = ModuleModel::getModuleActionXml($module);
if($xml_info->action->{$this->act} && ($this->module == 'admin' || $xml_info->action->{$this->act}->standalone != 'false'))
{
@ -600,19 +430,13 @@ class ModuleHandler extends Handler
}
else
{
$this->error = 'msg_invalid_request';
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
return $oMessageObject;
return self::_createErrorMessage(-1, 'msg_invalid_request');
}
}
if(empty($forward->module))
{
$forward = $oModuleModel->getActionForward($this->act);
$forward = ModuleModel::getActionForward($this->act);
}
if(!empty($forward->module))
@ -627,20 +451,14 @@ class ModuleHandler extends Handler
Context::addMetaTag('robots', 'noindex');
}
$xml_info = $oModuleModel->getModuleActionXml($forward->module);
$xml_info = ModuleModel::getModuleActionXml($forward->module);
// Protect admin action
if(($this->module == 'admin' || $kind == 'admin') && !$oModuleModel->getGrant($forward, $logged_info)->root)
if(($this->module == 'admin' || $kind == 'admin') && !ModuleModel::getGrant($forward, $logged_info)->root)
{
if($this->module == 'admin' || empty($xml_info->permission->{$this->act}))
if($this->module == 'admin' || empty($xml_info->action->{$this->act}->permission->target))
{
self::_setInputErrorToContext();
$this->error = 'admin.msg_is_not_administrator';
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
return $oMessageObject;
return self::_createErrorMessage(-1, 'admin.msg_is_not_administrator');
}
}
@ -661,12 +479,7 @@ class ModuleHandler extends Handler
if(!in_array(strtoupper($_SERVER['REQUEST_METHOD']), $allowedMethodList))
{
$this->error = 'msg_security_violation';
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
return $oMessageObject;
return self::_createErrorMessage(-1, 'msg_method_not_allowed', 405);
}
}
@ -675,17 +488,11 @@ class ModuleHandler extends Handler
{
if($xml_info->action->{$this->act} && $xml_info->action->{$this->act}->check_csrf !== 'false' && !checkCSRF())
{
$this->_setInputErrorToContext();
$this->error = 'msg_security_violation';
$oMessageObject = ModuleHandler::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage();
return $oMessageObject;
return self::_createErrorMessage(-1, 'msg_security_violation');
}
}
if($type == "view" && Mobile::isFromMobilePhone())
if($type == "view" && $this->is_mobile)
{
$orig_type = "view";
$type = "mobile";
@ -705,16 +512,7 @@ class ModuleHandler extends Handler
if(!is_object($oModule))
{
self::_setInputErrorToContext();
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError(-1);
$oMessageObject->setMessage('msg_module_is_not_exists');
$oMessageObject->dispMessage();
if($this->httpStatusCode)
{
$oMessageObject->setHttpStatusCode($this->httpStatusCode);
}
return $oMessageObject;
return self::_createErrorMessage(-1, 'msg_module_is_not_exists', 404);
}
// Admin page layout
@ -743,7 +541,7 @@ class ModuleHandler extends Handler
if(!empty($ruleset))
{
$rulesetModule = !empty($forward->module) ? $forward->module : $this->module;
$rulesetFile = $oModuleModel->getValidatorFilePath($rulesetModule, $ruleset, $this->mid);
$rulesetFile = ModuleModel::getValidatorFilePath($rulesetModule, $ruleset, $this->mid);
if(!empty($rulesetFile))
{
if($_SESSION['XE_VALIDATOR_ERROR_LANG'])
@ -877,10 +675,135 @@ class ModuleHandler extends Handler
}
/**
* set error message to Session.
* Check the value of $document_srl. This method is called during init().
*
* @return object|false
*/
protected function _checkDocumentSrl()
{
// Get the module that the document belongs to.
$module_info = ModuleModel::getModuleInfoByDocumentSrl($this->document_srl);
if($module_info)
{
// Compare the current mid to the module that the document belongs to.
if(!$this->mid || ($this->mid !== $module_info->mid))
{
// If this is a GET request, redirect to the correct mid.
if(Context::getRequestMethod() === 'GET')
{
Context::setCacheControl(0);
header('Location: ' . getNotEncodedUrl('', 'mid', $module_info->mid, 'document_srl', $this->document_srl), true, 301);
return false;
}
// If this is NOT a GET request, don't redirect. Just overwrite the mid for the current request.
else
{
$this->mid = $module_info->mid;
Context::set('mid', $this->mid);
}
}
// Remove module info if a different module has already been selected for the current request.
if($this->module && $module_info->module !== $this->module)
{
$module_info = null;
}
}
// Block access to secret or temporary documents.
if(Context::getRequestMethod() === 'GET')
{
$oDocument = DocumentModel::getDocument($this->document_srl);
if($oDocument->isExists())
{
$this->httpStatusCode = 404;
}
elseif(!$oDocument->isAccessible())
{
$this->httpStatusCode = 403;
}
}
// Return the module info for further processing.
return $module_info;
}
/**
* Set SEO information to Context.
*
* @param object $module_info
* @param object $site_module_info
*/
protected function _setModuleSEOInfo($module_info, $site_module_info)
{
// Set the browser title.
if ($module_info->mid == $site_module_info->mid)
{
$seo_title = config('seo.main_title') ?: '$SITE_TITLE - $SITE_SUBTITLE';
}
else
{
$seo_title = config('seo.subpage_title') ?: '$SITE_TITLE - $SUBPAGE_TITLE';
}
ModuleController::getInstance()->replaceDefinedLangCode($seo_title);
Context::setBrowserTitle($seo_title, array(
'site_title' => Context::getSiteTitle(),
'site_subtitle' => Context::getSiteSubtitle(),
'subpage_title' => $module_info->browser_title,
'page' => Context::get('page') ?: 1,
));
// Set meta keywords.
$module_config = ModuleModel::getModuleConfig('module');
if ($module_info->meta_keywords)
{
Context::addMetaTag('keywords', $module_info->meta_keywords);
}
elseif ($site_module_info->settings->meta_keywords)
{
Context::addMetaTag('keywords', $site_module_info->settings->meta_keywords);
}
elseif ($module_config->meta_keywords)
{
Context::addMetaTag('keywords', $module_config->meta_keywords);
}
// Set meta description.
if ($module_info->meta_description)
{
Context::addMetaTag('description', $module_info->meta_description);
}
elseif ($site_module_info->settings->meta_description)
{
Context::addMetaTag('description', $site_module_info->settings->meta_description);
}
elseif($module_config->meta_description)
{
Context::addMetaTag('description', $module_config->meta_description);
}
}
/**
* Save input values to session so that they can be recovered after returning to the previous form.
*
* @return void
* */
public static function _setInputErrorToContext()
*/
protected static function _setInputValueToSession()
{
$requestVars = getDestroyXeVars(Context::getRequestVars());
unset($requestVars->act, $requestVars->mid, $requestVars->vid);
foreach($requestVars as $key => $value)
{
$_SESSION['INPUT_ERROR'][$key] = $value;
}
}
/**
* Get previous error information and restore it to Context so that it is available to templates.
*
* @return void
*/
protected static function _setInputErrorToContext()
{
if($_SESSION['XE_VALIDATOR_ERROR'] && !Context::get('XE_VALIDATOR_ERROR'))
{
@ -914,7 +837,7 @@ class ModuleHandler extends Handler
* clear error message to Session.
* @return void
* */
public static function _clearErrorSession()
protected static function _clearErrorSession()
{
unset($_SESSION['XE_VALIDATOR_ERROR']);
unset($_SESSION['XE_VALIDATOR_MESSAGE']);
@ -925,17 +848,25 @@ class ModuleHandler extends Handler
}
/**
* occured error when, set input values to session.
* @return void
* */
public static function _setInputValueToSession()
* Create a message module instance with an error message.
*/
protected static function _createErrorMessage($error, $message, $status_code = 403, $location = null)
{
$requestVars = Context::getRequestVars();
unset($requestVars->act, $requestVars->mid, $requestVars->vid, $requestVars->success_return_url, $requestVars->error_return_url, $requestVars->xe_validator_id);
foreach($requestVars AS $key => $value)
$display_mode = Mobile::isFromMobilePhone() ? 'mobile' : 'view';
if (!$location)
{
$_SESSION['INPUT_ERROR'][$key] = $value;
$backtrace = debug_backtrace(false);
$caller = array_shift($backtrace);
$location = $caller['file'] . ':' . $caller['line'];
}
self::_setInputErrorToContext();
$oMessageObject = self::getModuleInstance('message', $display_mode);
$oMessageObject->setError($error);
$oMessageObject->setMessage($message);
$oMessageObject->setHttpStatusCode($status_code ?: 403);
$oMessageObject->dispMessage('', $location);
return $oMessageObject;
}
/**
@ -945,11 +876,14 @@ class ModuleHandler extends Handler
* */
public function displayContent($oModule = NULL)
{
// Set the display mode for the current device type.
$this->is_mobile = Mobile::isFromMobilePhone();
// If the module is not set or not an object, set error
if(!$oModule || !is_object($oModule))
{
$this->error = 'msg_module_is_not_exists';
$this->httpStatusCode = '404';
$this->httpStatusCode = 404;
}
// If connection to DB has a problem even though it's not install module, set error
@ -1014,11 +948,7 @@ class ModuleHandler extends Handler
if($this->error)
{
// display content with message module instance
$type = Mobile::isFromMobilePhone() ? 'mobile' : 'view';
$oMessageObject = self::getModuleInstance('message', $type);
$oMessageObject->setError(-1);
$oMessageObject->setMessage($this->error);
$oMessageObject->dispMessage(null, $oModule->get('rx_error_location'));
$oMessageObject = self::_createErrorMessage(-1, $this->error, $this->httpStatusCode, $oModule->get('rx_error_location'));
// display Error Page
if(!in_array($oMessageObject->getHttpStatusCode(), array(200, 403)))
@ -1032,8 +962,8 @@ class ModuleHandler extends Handler
$oModule->setTemplatePath($oMessageObject->getTemplatePath());
$oModule->setTemplateFile($oMessageObject->getTemplateFile());
$oModule->setHttpStatusCode($oMessageObject->getHttpStatusCode());
// Otherwise, set message instance as the target module
}
// Otherwise, set message instance as the target module
else
{
$oModule = $oMessageObject;
@ -1043,7 +973,7 @@ class ModuleHandler extends Handler
}
// Check if layout_srl exists for the module
$viewType = (Mobile::isFromMobilePhone()) ? 'M' : 'P';
$viewType = $this->is_mobile ? 'M' : 'P';
if($viewType === 'M')
{
$layout_srl = $oModule->module_info->mlayout_srl;
@ -1234,8 +1164,7 @@ class ModuleHandler extends Handler
return new BaseObject();
}
$oModuleModel = ModuleModel::getInstance();
$triggers = $oModuleModel->getTriggers($trigger_name, $called_position);
$triggers = ModuleModel::getTriggers($trigger_name, $called_position);
if(!$triggers)
{
$triggers = array();
@ -1290,7 +1219,7 @@ class ModuleHandler extends Handler
unset($oModule);
}
$trigger_functions = $oModuleModel->getTriggerFunctions($trigger_name, $called_position);
$trigger_functions = ModuleModel::getTriggerFunctions($trigger_name, $called_position);
foreach($trigger_functions as $item)
{
try

View file

@ -251,10 +251,10 @@ class ModuleObject extends BaseObject
if(Context::get('logged_info')->is_admin !== 'Y')
{
// Get privileges(granted) information for target module by <permission check> of module.xml
if(($permission_check = $this->xml_info->permission_check->{$this->act}) && $permission_check->key)
if(($permission = $this->xml_info->action->{$this->act}->permission) && $permission->check_var)
{
// Check parameter
if(empty($check_module_srl = trim(Context::get($permission_check->key))))
if(empty($check_module_srl = trim(Context::get($permission->check_var))))
{
return false;
}
@ -277,7 +277,7 @@ class ModuleObject extends BaseObject
foreach($check_module_srl as $target_srl)
{
// Get privileges(granted) information of current user for target module
if(($grant = ModuleModel::getInstance()->getPrivilegesBySrl($target_srl, $permission_check->type)) === false)
if(($grant = ModuleModel::getInstance()->getPrivilegesBySrl($target_srl, $permission->check_type)) === false)
{
return false;
}
@ -335,10 +335,9 @@ class ModuleObject extends BaseObject
}
// Get privileges(granted) information of the member for current module
$oModuleModel = ModuleModel::getInstance();
if(!$grant)
{
$grant = $oModuleModel->getGrant($this->module_info, $member_info, $this->xml_info);
$grant = ModuleModel::getGrant($this->module_info, $member_info, $this->xml_info);
}
// If an administrator, Pass
@ -348,7 +347,7 @@ class ModuleObject extends BaseObject
}
// Get permission types(guest, member, manager, root) of the currently requested action
$permission = $this->xml_info->permission->{$this->act};
$permission = $this->xml_info->action->{$this->act}->permission->target ?: $this->xml_info->permission->{$this->act};
// If admin action, set default permission
if(empty($permission) && stripos($this->act, 'admin') !== false)
@ -381,17 +380,17 @@ class ModuleObject extends BaseObject
if(Context::get('is_logged') && isset($type[2]))
{
// Manager privilege of the member is found by search all modules, Pass
if($type[2] == 'all' && $oModuleModel->findManagerPrivilege($member_info) !== false)
if($type[2] == 'all' && ModuleModel::findManagerPrivilege($member_info) !== false)
{
return true;
}
// Manager privilege of the member is found by search same module as this module, Pass
elseif($type[2] == 'same' && $oModuleModel->findManagerPrivilege($member_info, $this->module) !== false)
elseif($type[2] == 'same' && ModuleModel::findManagerPrivilege($member_info, $this->module) !== false)
{
return true;
}
// Manager privilege of the member is found by search same module as the module, Pass
elseif($oModuleModel->findManagerPrivilege($member_info, $type[2]) !== false)
elseif(ModuleModel::findManagerPrivilege($member_info, $type[2]) !== false)
{
return true;
}
@ -650,7 +649,6 @@ class ModuleObject extends BaseObject
// Set module skin
if(isset($this->module_info->skin) && $this->module_info->module === $this->module && strpos($this->act, 'Admin') === false)
{
$oModuleModel = ModuleModel::getInstance();
$skin_type = $is_mobile ? 'M' : 'P';
$skin_key = $is_mobile ? 'mskin' : 'skin';
$skin_dir = $is_mobile ? 'm.skins' : 'skins';
@ -662,7 +660,7 @@ class ModuleObject extends BaseObject
{
if($module_skin === '/USE_DEFAULT/')
{
$module_skin = $oModuleModel->getModuleDefaultSkin($this->module, $skin_type);
$module_skin = ModuleModel::getModuleDefaultSkin($this->module, $skin_type);
$this->module_info->{$skin_key} = $module_skin;
}
if($module_skin === '/USE_RESPONSIVE/')
@ -671,7 +669,7 @@ class ModuleObject extends BaseObject
$module_skin = $this->module_info->skin ?: '/USE_DEFAULT/';
if($module_skin === '/USE_DEFAULT/')
{
$module_skin = $oModuleModel->getModuleDefaultSkin($this->module, 'P');
$module_skin = ModuleModel::getModuleDefaultSkin($this->module, 'P');
}
}
if(!is_dir(sprintf('%s%s/%s', $this->module_path, $skin_dir, $module_skin)))
@ -682,7 +680,7 @@ class ModuleObject extends BaseObject
}
// Set skin variable
$oModuleModel->syncSkinInfoToModuleInfo($this->module_info);
ModuleModel::syncSkinInfoToModuleInfo($this->module_info);
Context::set('module_info', $this->module_info);
}

View file

@ -50,6 +50,7 @@ return array(
'http_port' => null,
'https_port' => null,
'ssl' => 'none',
'rewrite' => 1,
),
'session' => array(
'delay' => false,

View file

@ -0,0 +1,257 @@
<?php
namespace Rhymix\Framework\Parsers;
/**
* Module action (conf/module.xml) parser class for XE compatibility.
*/
class ModuleActionParser
{
/**
* Shortcuts for route definition.
*/
protected static $_shortcuts = array(
'int' => '[0-9]+',
'float' => '[0-9]+(?:\.[0-9]+)?',
'alpha' => '[a-zA-Z]+',
'alnum' => '[a-zA-Z0-9]+',
'hex' => '[0-9a-f]+',
'word' => '[a-zA-Z0-9_]+',
'any' => '[^/]+',
'delete' => '[^/]+',
);
/**
* Load an XML file.
*
* @param string $filename
* @return object|false
*/
public static function loadXML(string $filename)
{
// Load the XML file.
$xml = simplexml_load_string(file_get_contents($filename));
if ($xml === false)
{
return false;
}
// Get the current language.
$lang = \Context::getLangType() ?: 'en';
// Initialize the module definition.
$info = new \stdClass;
$info->admin_index_act = '';
$info->default_index_act = '';
$info->setup_index_act = '';
$info->simple_setup_index_act = '';
$info->route = new \stdClass;
$info->route->GET = [];
$info->route->POST = [];
$info->action = new \stdClass;
$info->grant = new \stdClass;
$info->menu = new \stdClass;
$info->error_handlers = [];
// Parse grants.
foreach ($xml->grants->grant ?: [] as $grant)
{
$grant_info = new \stdClass;
$grant_info->title = self::_getElementsByLang($grant, 'title', $lang);
$grant_info->default = trim($grant['default']);
$grant_name = trim($grant['name']);
$info->grant->{$grant_name} = $grant_info;
}
// Parse menus.
foreach ($xml->menus->menu ?: [] as $menu)
{
$menu_info = new \stdClass;
$menu_info->title = self::_getElementsByLang($menu, 'title', $lang);
$menu_info->index = null;
$menu_info->acts = array();
$menu_info->type = trim($menu['type']);
$menu_name = trim($menu['name']);
$info->menu->{$menu_name} = $menu_info;
}
// Parse actions.
foreach ($xml->actions->action ?: [] as $action)
{
// Parse permissions.
$action_name = trim($action['name']);
$permission = trim($action['permission']);
$permission_info = (object)['target' => '', 'check_var' => '', 'check_type' => ''];
if ($permission)
{
$permission_info->target = $permission;
$permission_info->check_var = trim($action['check_var']) ?: trim($action['check-var']);
$permission_info->check_type = trim($action['check_type']) ?: trim($action['check-type']);
}
// Parse routes.
$route_attr = trim($action['route']);
$route_tags = $action->route ?: [];
$method = trim($action['method']);
$route_arg = [];
if ($route_attr || count($route_tags))
{
$methods = $method ? explode('|', strtoupper($method)) : (starts_with('proc', $action_name) ? ['POST'] : ['GET']);
$routes = $route_attr ? array_map(function($route) {
return ['route' => trim($route), 'priority' => 0];
}, explode_with_escape('|', $route_attr)) : array();
foreach ($route_tags as $route_tag)
{
$routes[] = ['route' => trim($route_tag['route']), 'priority' => intval($route_tag['priority'] ?: 0)];
}
foreach ($routes as $route)
{
$route_info = self::analyzeRoute($route);
$route_arg[$route_info->route] = ['priority' => intval($route_info->priority), 'vars' => $route_info->vars];
foreach ($methods as $method)
{
$info->route->{$method}[$route_info->regexp] = $action_name;
}
}
}
// Parse other information about this action.
$action_info = new \stdClass;
$action_info->type = trim($action['type']);
$action_info->grant = trim($action['grant']) ?: 'guest';
$action_info->permission = $permission_info;
$action_info->ruleset = trim($action['ruleset']);
$action_info->method = $method;
$action_info->route = $route_arg;
$action_info->standalone = trim($action['standalone']) === 'false' ? 'false' : 'true';
$action_info->check_csrf = (trim($action['check_csrf']) ?: trim($action['check-csrf'])) === 'false' ? 'false' : 'true';
$action_info->meta_noindex = (trim($action['meta_noindex']) ?: trim($action['meta-noindex'])) === 'true' ? 'true' : 'false';
$action_info->global_route = (trim($action['global_route']) ?: trim($action['global-route'])) === 'true' ? 'true' : 'false';
$action_info->use_ssl = (trim($action['use_ssl']) ?: trim($action['use-ssl'])) === 'true' ? 'true' : 'false';
$info->action->{$action_name} = $action_info;
// Set the menu name and index settings.
$menu_name = trim($action['menu_name']);
if ($menu_name)
{
$info->menu->{$menu_name}->acts[] = $action_name;
if (toBool($action['menu_index']))
{
$info->menu->{$menu_name}->index = $action_name;
}
}
if (toBool($action['index']))
{
$info->default_index_act = $action_name;
}
if (toBool($action['admin_index']))
{
$info->admin_index_act = $action_name;
}
if (toBool($action['setup_index']))
{
$info->setup_index_act = $action_name;
}
if (toBool($action['simple_setup_index']))
{
$info->simple_setup_index_act = $action_name;
}
// Set error handler settings.
$error_handlers = explode(',', trim($action['error_handlers']) ?: trim($action['error-handlers']));
foreach ($error_handlers as $error_handler)
{
if (intval($error_handler) > 200)
{
$info->error_handlers[intval($error_handler)] = $action_name;
}
}
}
// Parse permissions not defined in the <actions> section.
foreach ($xml->permissions->permission ?: [] as $permission)
{
$action_name = trim($permission['action']);
if (isset($info->action->{$action_name}))
{
$info->action->{$action_name}->permission->target = trim($permission['target']);
$info->action->{$action_name}->permission->check_var = trim($permission['check_var']) ?: trim($permission['check-var']);
$info->action->{$action_name}->permission->check_type = trim($permission['check_type']) ?: trim($permission['check-type']);
}
}
// Return the complete result.
return $info;
}
/**
* Convert route definition into a regular expression.
*
* @param array $route
* @return object
*/
public static function analyzeRoute(array $route)
{
// Replace variables in the route definition into appropriate regexp.
$var_regexp = '#\\$([a-zA-Z0-9_]+)(?::(' . implode('|', array_keys(self::$_shortcuts)) . '))?#';
$vars = array();
$regexp = preg_replace_callback($var_regexp, function($match) use(&$vars) {
if (isset($match[2]))
{
$var_type = $match[2];
$var_pattern = self::$_shortcuts[$match[2]];
}
else
{
$var_type = ends_with('_srl', $match[1]) ? 'int' : 'any';
$var_pattern = self::$_shortcuts[$var_type];
}
$named_group = '(?P<' . $match[1] . '>' . $var_pattern . ')';
$vars[$match[1]] = $var_type;
return $named_group;
}, $route['route']);
// Anchor the regexp at both ends.
$regexp = '#^' . strtr($regexp, ['#' => '\\#']) . '$#u';
// Return the regexp and variable list.
$result = new \stdClass;
$result->route = preg_replace_callback($var_regexp, function($match) {
return '$' . ((isset($match[2]) && $match[2] === 'delete') ? ($match[1] . ':' . $match[2]) : $match[1]);
}, $route['route']);
$result->priority = $route['priority'] ?: 0;
$result->regexp = $regexp;
$result->vars = $vars;
return $result;
}
/**
* Get child elements that match a language.
*
* @param SimpleXMLElement $parent
* @param string $tag_name
* @param string $lang
* @return string
*/
protected static function _getElementsByLang(\SimpleXMLElement $parent, string $tag_name, string $lang): string
{
// If there is a child element that matches the language, return it.
foreach ($parent->{$tag_name} as $child)
{
$attribs = $child->attributes('xml', true);
if (strval($attribs['lang']) === $lang)
{
return trim($child);
}
}
// Otherwise, return the first child element.
foreach ($parent->{$tag_name} as $child)
{
return trim($child);
}
// If there are no child elements, return an empty string.
return '';
}
}

View file

@ -0,0 +1,121 @@
<?php
namespace Rhymix\Framework\Parsers;
/**
* Module info (conf/info.xml) parser class for XE compatibility.
*/
class ModuleInfoParser
{
/**
* Load an XML file.
*
* @param string $filename
* @return object|false
*/
public static function loadXML(string $filename)
{
// Load the XML file.
$xml = simplexml_load_string(file_get_contents($filename));
if ($xml === false)
{
return false;
}
// Get the current language.
$lang = \Context::getLangType() ?: 'en';
// Initialize the module definition.
$info = new \stdClass;
// Get the XML schema version.
$version = strval($xml['version']) ?: '0.1';
// Parse version 0.2
if ($version === '0.2')
{
$info->title = self::_getElementsByLang($xml, 'title', $lang);
$info->description = self::_getElementsByLang($xml, 'description', $lang);
$info->version = trim($xml->version);
$info->homepage = trim($xml->homepage);
$info->category = trim($xml->category) ?: 'service';
$info->date = date('Ymd', strtotime($xml->date . 'T12:00:00Z'));
$info->license = trim($xml->license);
$info->license_link = trim($xml->license['link']);
$info->author = array();
foreach ($xml->author as $author)
{
$author_info = new \stdClass;
$author_info->name = self::_getElementsByLang($author, 'name', $lang);
$author_info->email_address = trim($author['email_address']);
$author_info->homepage = trim($author['link']);
$info->author[] = $author_info;
}
}
// Parse version 0.1
else
{
$info->title = self::_getElementsByLang($xml, 'title', $lang);
$info->description = self::_getElementsByLang($xml->author, 'description', $lang);
$info->version = trim($xml['version']);
$info->homepage = trim($xml->homepage);
$info->category = trim($xml['category']) ?: 'service';
$info->date = date('Ymd', strtotime($xml->author['date'] . 'T12:00:00Z'));
$info->license = trim($xml->license);
$info->license_link = trim($xml->license['link']);
$info->author = array();
foreach ($xml->author as $author)
{
$author_info = new \stdClass;
$author_info->name = self::_getElementsByLang($author, 'name', $lang);
$author_info->email_address = trim($author['email_address']);
$author_info->homepage = trim($author['link']);
$info->author[] = $author_info;
}
}
// Add information about actions.
$action_info = ModuleActionParser::loadXML(strtr($filename, ['info.xml' => 'module.xml']));
$info->admin_index_act = $action_info->admin_index_act;
$info->default_index_act = $action_info->default_index_act;
$info->setup_index_act = $action_info->setup_index_act;
$info->simple_setup_index_act = $action_info->simple_setup_index_act;
$info->error_handlers = $action_info->error_handlers ?: [];
// Return the complete result.
return $info;
}
/**
* Get child elements that match a language.
*
* @param SimpleXMLElement $parent
* @param string $tag_name
* @param string $lang
* @return string
*/
protected static function _getElementsByLang(\SimpleXMLElement $parent, string $tag_name, string $lang): string
{
// If there is a child element that matches the language, return it.
foreach ($parent->{$tag_name} as $child)
{
$attribs = $child->attributes('xml', true);
if (strval($attribs['lang']) === $lang)
{
return trim($child);
}
}
// Otherwise, return the first child element.
foreach ($parent->{$tag_name} as $child)
{
return trim($child);
}
// If there are no child elements, return an empty string.
return '';
}
}

570
common/framework/router.php Normal file
View file

@ -0,0 +1,570 @@
<?php
namespace Rhymix\Framework;
/**
* The router class.
*/
class Router
{
/**
* List of XE-compatible global routes.
*/
protected static $_global_routes = array(
'$document_srl' => array(
'regexp' => '#^(?<document_srl>[0-9]+)$#',
'vars' => ['document_srl' => 'int'],
'priority' => 0,
),
'$mid' => array(
'regexp' => '#^(?<mid>[a-zA-Z0-9_-]+)/?$#',
'vars' => ['mid' => 'any'],
'priority' => 0,
),
'$act' => array(
'regexp' => '#^(?<act>rss|atom)$#',
'vars' => ['act' => 'word'],
'priority' => 0,
),
'$mid/$document_srl' => array(
'regexp' => '#^(?<mid>[a-zA-Z0-9_-]+)/(?<document_srl>[0-9]+)$#',
'vars' => ['mid' => 'any', 'document_srl' => 'int'],
'priority' => 30,
),
'$mid/category/$category' => array(
'regexp' => '#^(?<mid>[a-zA-Z0-9_-]+)/category/(?<category>[0-9]+)$#',
'vars' => ['mid' => 'any', 'category' => 'int'],
'priority' => 10,
),
'$mid/entry/$entry' => array(
'regexp' => '#^(?<mid>[a-zA-Z0-9_-]+)/entry/(?<entry>[^/]+)$#',
'vars' => ['mid' => 'any', 'entry' => 'any'],
'priority' => 0,
),
'$mid/$act' => array(
'regexp' => '#^(?<mid>[a-zA-Z0-9_-]+)/(?<act>rss|atom|api)$#',
'vars' => ['mid' => 'any', 'act' => 'word'],
'priority' => 20,
),
'files/download/$file_srl/$file_key/$filename' => array(
'regexp' => '#^files/download/(?<file_srl>[0-9]+)/(?<file_key>[a-zA-Z0-9_-]+)/(?<filename>[^/]+)$#',
'vars' => ['file_srl' => 'int', 'file_key' => 'any', 'filename' => 'any'],
'extra_vars' => ['act' => 'procFileOutput'],
'priority' => 0,
),
);
/**
* List of legacy modules whose URLs should not be shortened.
*/
protected static $_except_modules = array(
'socialxe' => true,
);
/**
* Internal cache for module and route information.
*/
protected static $_action_cache_prefix = array();
protected static $_action_cache_module = array();
protected static $_global_forwarded_cache = array();
protected static $_internal_forwarded_cache = array();
protected static $_route_cache = array();
/**
* Return the currently configured rewrite level.
*
* 0 = None
* 1 = XE-compatible rewrite rules only
* 2 = Full rewrite support
*
* @return int
*/
public static function getRewriteLevel(): int
{
$level = Config::get('url.rewrite');
if ($level === null)
{
$level = Config::get('use_rewrite') ? 1 : 0;
}
return intval($level);
}
/**
* Extract request arguments from the current URL.
*
* @param string $method
* @param string $url
* @param int $rewrite_level
* @return object
*/
public static function parseURL(string $method, string $url, int $rewrite_level)
{
// Get the local part of the current URL.
if (starts_with(\RX_BASEURL, $url))
{
$url = substr($url, strlen(\RX_BASEURL));
}
// Prepare the return object.
$result = new \stdClass;
$result->status = 200;
$result->url = '';
$result->module = '';
$result->mid = '';
$result->act = '';
$result->forwarded = false;
$result->args = array();
// Separate additional arguments from the URL.
$args = array();
$argstart = strpos($url, '?');
if ($argstart !== false)
{
@parse_str(substr($url, $argstart + 1), $args);
$url = substr($url, 0, $argstart);
}
// Decode the URL into plain UTF-8.
$url = $result->url = urldecode($url);
if ($url === '')
{
return $result;
}
if (function_exists('mb_check_encoding') && !mb_check_encoding($url, 'UTF-8'))
{
$result->status = 404;
return $result;
}
// Try to detect the prefix. This might be $mid.
if ($rewrite_level >= 2 && preg_match('#^([a-zA-Z0-9_-]+)(?:/(.*))?#s', $url, $matches))
{
// Separate the prefix and the internal part of the URL.
$prefix = $matches[1];
$internal_url = $matches[2] ?? '';
$prefix_type = 'mid';
// Find the module associated with this prefix.
$action_info = self::_getActionInfoByPrefix($prefix, $module_name = '');
if ($action_info === false)
{
$action_info = self::_getActionInfoByModule($prefix);
if ($action_info !== false)
{
$module_name = $prefix;
$prefix_type = 'module';
}
}
// If a module is found, try its routes.
if ($action_info)
{
// Try the list of routes defined by the module.
foreach ($action_info->route->{$method} as $regexp => $action)
{
if (preg_match($regexp, $internal_url, $matches))
{
$matches = array_filter($matches, 'is_string', \ARRAY_FILTER_USE_KEY);
$allargs = array_merge($args, $matches, [$prefix_type => $prefix, 'act' => $action]);
$result->module = $module_name;
$result->mid = $prefix_type === 'mid' ? $prefix : '';
$result->act = $action;
$result->args = $allargs;
return $result;
}
}
// Check other modules.
if ($prefix_type === 'mid')
{
$forwarded_routes = self::_getForwardedRoutes('internal');
foreach ($forwarded_routes[$method] ?: [] as $regexp => $action)
{
if (preg_match($regexp, $internal_url, $matches))
{
$matches = array_filter($matches, 'is_string', \ARRAY_FILTER_USE_KEY);
$allargs = array_merge($args, $matches, [$prefix_type => $prefix, 'act' => $action[1]]);
$result->module = $action[0];
$result->mid = $prefix;
$result->act = $action[1];
$result->forwarded = true;
$result->args = $allargs;
return $result;
}
}
}
// Try the generic mid/act pattern.
if (preg_match('#^[a-zA-Z0-9_]+$#', $internal_url))
{
$allargs = array_merge($args, [$prefix_type => $prefix, 'act' => $internal_url]);
$result->module = $module_name;
$result->mid = $prefix_type === 'mid' ? $prefix : '';
$result->act = $internal_url;
$result->forwarded = true;
$result->args = $allargs;
return $result;
}
// If the module defines a 404 error handler, call it.
if ($internal_url && isset($action_info->error_handlers[404]))
{
$allargs = array_merge($args, [$prefix_type => $prefix, 'act' => $action_info->error_handlers[404]]);
$result->module = $module_name;
$result->mid = $prefix_type === 'mid' ? $prefix : '';
$result->act = $action_info->error_handlers[404];
$result->forwarded = false;
$result->args = $allargs;
return $result;
}
}
}
// Try registered global routes.
if ($rewrite_level >= 2)
{
$global_routes = self::_getForwardedRoutes('global');
foreach ($global_routes[$method] ?: [] as $regexp => $action)
{
if (preg_match($regexp, $url, $matches))
{
$matches = array_filter($matches, 'is_string', \ARRAY_FILTER_USE_KEY);
$allargs = array_merge($args, $matches, ['act' => $action[1]]);
$result->module = $action[0];
$result->act = $action[1];
$result->forwarded = true;
$result->args = $allargs;
return $result;
}
}
}
// Try XE-compatible global routes.
foreach (self::$_global_routes as $route_info)
{
if (preg_match($route_info['regexp'], $url, $matches))
{
$matches = array_filter($matches, 'is_string', \ARRAY_FILTER_USE_KEY);
$allargs = array_merge($args, $matches, $route_info['extra_vars'] ?? []);
$result->module = $allargs['module'] ?? '';
$result->mid = $allargs['mid'] ?: '';
$result->act = $allargs['act'] ?: '';
$result->forwarded = false;
$result->args = $allargs;
return $result;
}
}
// If no pattern matches, return either an empty route or a 404 error.
$result->module = isset($args['module']) ? $args['module'] : '';
$result->mid = isset($args['mid']) ? $args['mid'] : '';
$result->act = isset($args['act']) ? $args['act'] : '';
$result->args = $args;
if ($url === '' || $url === 'index.php')
{
$result->url = '';
return $result;
}
else
{
$result->status = 404;
return $result;
}
}
/**
* Create a URL for the given set of arguments.
*
* @param array $args
* @param int $rewrite_level
* @return string
*/
public static function getURL(array $args, int $rewrite_level): string
{
// If rewrite is turned off, just create a query string.
if ($rewrite_level == 0)
{
return 'index.php?' . http_build_query($args);
}
// Cache the number of arguments and their keys.
$count = count($args);
$keys = array_keys($args);
// If there are no arguments, return the URL of the main page.
if ($count == 0)
{
return '';
}
// If there is only one argument, try either $mid or $document_srl.
if ($rewrite_level >= 1 && $count == 1 && ($keys[0] === 'mid' || $keys[0] === 'document_srl'))
{
return urlencode($args[$keys[0]]);
}
// If the list of keys is already cached, return the corresponding route.
$keys_sorted = $keys; sort($keys_sorted);
$keys_string = implode('.', $keys_sorted) . ':' . ($args['mid'] ?? '') . ':' . ($args['act'] ?? '');
if (isset(self::$_route_cache[$rewrite_level][$keys_string]))
{
return self::_insertRouteVars(self::$_route_cache[$rewrite_level][$keys_string], $args);
}
// Remove $mid and $act from arguments and work with the remainder.
$args2 = $args; unset($args2['module'], $args2['mid'], $args2['act']);
// If $mid exists, try routes defined in the module.
if ($rewrite_level >= 2 && (isset($args['mid']) || isset($args['module'])))
{
// Get module action info.
if (isset($args['mid']))
{
$action_info = self::_getActionInfoByPrefix($args['mid']);
$prefix_type = 'mid';
}
elseif (isset($args['module']))
{
$action_info = self::_getActionInfoByModule($args['module']);
$prefix_type = 'module';
}
// If there is no $act, use the default action.
$act = isset($args['act']) ? $args['act'] : $action_info->default_index_act;
// Check if $act has any routes defined.
$action = $action_info->action->{$act} ?? null;
if ($action && $action->route)
{
$result = self::_getBestMatchingRoute($action->route, $args2);
if ($result !== false)
{
self::$_route_cache[$rewrite_level][$keys_string] = '$' . $prefix_type . '/' . $result . '$act:delete';
return $args[$prefix_type] . '/' . self::_insertRouteVars($result, $args2);
}
}
// Check other modules for $act.
if ($prefix_type === 'mid')
{
$forwarded_routes = self::_getForwardedRoutes('internal');
if (isset($forwarded_routes['reverse'][$act]))
{
$result = self::_getBestMatchingRoute($forwarded_routes['reverse'][$act], $args2);
if ($result !== false)
{
self::$_route_cache[$rewrite_level][$keys_string] = '$' . $prefix_type . '/' . $result . '$act:delete';
return $args[$prefix_type] . '/' . self::_insertRouteVars($result, $args2);
}
}
}
// Try the generic mid/act pattern.
if ($prefix_type !== 'module' || !isset(self::$_except_modules[$args[$prefix_type]]))
{
self::$_route_cache[$rewrite_level][$keys_string] = '$' . $prefix_type . '/$act';
return $args[$prefix_type] . '/' . $args['act'] . (count($args2) ? ('?' . http_build_query($args2)) : '');
}
}
// Try registered global routes.
if ($rewrite_level >= 2 && isset($args['act']))
{
$global_routes = self::_getForwardedRoutes('global');
if (isset($global_routes['reverse'][$args['act']]))
{
$result = self::_getBestMatchingRoute($global_routes['reverse'][$args['act']], $args2);
if ($result !== false)
{
self::$_route_cache[$rewrite_level][$keys_string] = $result . '$act:delete';
return self::_insertRouteVars($result, $args2);
}
}
}
// Try XE-compatible global routes.
if ($rewrite_level >= 1)
{
if (!isset($args['act']) || ($args['act'] === 'rss' || $args['act'] === 'atom'))
{
$result = self::_getBestMatchingRoute(self::$_global_routes, $args);
if ($result !== false)
{
self::$_route_cache[$rewrite_level][$keys_string] = $result;
return self::_insertRouteVars($result, $args);
}
}
}
// If no route matches, just create a query string.
self::$_route_cache[$rewrite_level][$keys_string] = 'index.php';
return 'index.php?' . http_build_query($args);
}
/**
* Load and cache module action info.
*
* @param string $prefix
* @return object
*/
protected static function _getActionInfoByPrefix(string $prefix, string &$module_name = '')
{
if (isset(self::$_action_cache_prefix[$prefix]))
{
$module_name = self::$_action_cache_prefix[$prefix];
return self::_getActionInfoByModule(self::$_action_cache_prefix[$prefix]) ?: false;
}
$module_info = \ModuleModel::getModuleInfoByMid($prefix);
if ($module_info && $module_info->module)
{
$module_name = self::$_action_cache_prefix[$prefix] = $module_info->module;
return self::_getActionInfoByModule(self::$_action_cache_prefix[$prefix]) ?: false;
}
else
{
return self::$_action_cache_prefix[$prefix] = false;
}
}
/**
* Load and cache module action info.
*
* @param string $prefix
* @return object
*/
protected static function _getActionInfoByModule(string $module)
{
if (isset(self::$_action_cache_module[$module]))
{
return self::$_action_cache_module[$module];
}
$action_info = \ModuleModel::getModuleActionXml($module);
return self::$_action_cache_module[$module] = $action_info ?: false;
}
/**
* Get the list of routes that are registered for action-forward.
*
* @param string $type
* @return array
*/
protected static function _getForwardedRoutes(string $type): array
{
if ($type === 'internal' && count(self::$_internal_forwarded_cache))
{
return self::$_internal_forwarded_cache;
}
if ($type === 'global' && count(self::$_global_forwarded_cache))
{
return self::$_global_forwarded_cache;
}
self::$_global_forwarded_cache['GET'] = array();
self::$_global_forwarded_cache['POST'] = array();
self::$_global_forwarded_cache['reverse'] = array();
self::$_internal_forwarded_cache['GET'] = array();
self::$_internal_forwarded_cache['POST'] = array();
self::$_internal_forwarded_cache['reverse'] = array();
$action_forward = \ModuleModel::getActionForward();
foreach ($action_forward as $action_name => $action_info)
{
if ($action_info->route_regexp)
{
foreach ($action_info->route_regexp as $regexp_info)
{
if ($action_info->global_route === 'Y')
{
self::$_global_forwarded_cache[$regexp_info[0]][$regexp_info[1]] = [$action_info->module, $action_name];
}
else
{
self::$_internal_forwarded_cache[$regexp_info[0]][$regexp_info[1]] = [$action_info->module, $action_name];
}
}
if ($action_info->global_route === 'Y')
{
self::$_global_forwarded_cache['reverse'][$action_name] = $action_info->route_config;
}
else
{
self::$_internal_forwarded_cache['reverse'][$action_name] = $action_info->route_config;
}
}
}
return $type === 'internal' ? self::$_internal_forwarded_cache : self::$_global_forwarded_cache;
}
/**
* Find the best matching route for an array of variables.
*
* @param array $routes
* @param array $vars
* @return string|false
*/
protected static function _getBestMatchingRoute(array $routes, array $vars)
{
// If the action only has one route, select it.
if (count($routes) == 1)
{
$only_route = key($routes);
$matched_arguments = array_intersect_key($routes[$only_route]['vars'], $vars);
if (count($matched_arguments) !== count($routes[$only_route]['vars']))
{
return false;
}
return $only_route;
}
// If the action has multiple routes, select the one that matches the most arguments.
else
{
// Order the routes by the number of matched arguments.
$reordered_routes = array();
foreach ($routes as $route => $route_vars)
{
$matched_arguments = array_intersect_key($route_vars['vars'], $vars);
if (count($matched_arguments) === count($route_vars['vars']))
{
$reordered_routes[$route] = ($route_vars['priority'] * 1000) + count($matched_arguments);
}
}
if (!count($reordered_routes))
{
return false;
}
arsort($reordered_routes);
$best_route = array_first_key($reordered_routes);
return $best_route;
}
}
/**
* Insert variables into a route.
*
* @param string $route
* @param array $vars
* @return string
*/
protected static function _insertRouteVars(string $route, array $vars): string
{
// Replace variable placeholders with actual variable values.
$route = preg_replace_callback('#\\$([a-zA-Z0-9_]+)(:[a-z]+)?#i', function($match) use(&$vars) {
if (isset($vars[$match[1]]))
{
$replacement = urlencode($vars[$match[1]]);
unset($vars[$match[1]]);
return (isset($match[2]) && $match[2] === ':delete') ? '' : $replacement;
}
else
{
return '';
}
}, $route);
// Add a query string for the remaining arguments.
return $route . (count($vars) ? ('?' . http_build_query($vars)) : '');
}
}

View file

@ -235,6 +235,7 @@ $lang->msg_invalid_document = 'Invalid Article Number';
$lang->msg_invalid_request = 'Invalid Request';
$lang->msg_invalid_password = 'The password you entered is incorrect.';
$lang->msg_security_violation = 'Security Violation';
$lang->msg_method_not_allowed = 'This HTTP method is not allowed for this action.';
$lang->msg_feature_disabled = 'This feature is disabled.';
$lang->msg_error_occured = 'An error has occured.';
$lang->msg_not_founded = 'Cannot find the target.';

View file

@ -237,6 +237,7 @@ $lang->msg_invalid_document = '잘못된 문서번호입니다.';
$lang->msg_invalid_request = '잘못된 요청입니다.';
$lang->msg_invalid_password = '비밀번호가 올바르지 않습니다.';
$lang->msg_security_violation = '보안정책상 허용되지 않습니다.';
$lang->msg_method_not_allowed = '이 요청에 사용할 수 없는 HTTP 메소드입니다.';
$lang->msg_feature_disabled = '사용할 수 없는 기능입니다.';
$lang->msg_error_occured = '오류가 발생했습니다.';
$lang->msg_not_founded = '대상을 찾을 수 없습니다.';

View file

@ -25,33 +25,7 @@ location ~ ^/rhymix/(.+)\.min\.(css|js)$ {
try_files $uri $uri/ /rhymix/$1.$2;
}
# rss, blogAPI
rewrite ^/rhymix/(rss|atom)$ /rhymix/index.php?module=rss&act=$1 last;
rewrite ^/rhymix/([a-zA-Z0-9_]+)/(rss|atom|api)$ /rhymix/index.php?mid=$1&act=$2 last;
# trackback
rewrite ^/rhymix/([0-9]+)/(.+)/trackback$ /rhymix/index.php?document_srl=$1&key=$2&act=trackback last;
rewrite ^/rhymix/([a-zA-Z0-9_]+)/([0-9]+)/(.+)/trackback$ /rhymix/index.php?mid=$1&document_srl=$2&key=$3&act=trackback last;
# administrator page
rewrite ^/rhymix/admin/?$ /rhymix/index.php?module=admin last;
# document category
rewrite ^/rhymix/([a-zA-Z0-9_]+)/category/([0-9]+)$ /rhymix/index.php?mid=$1&category=$2 last;
# document permanent link
rewrite ^/rhymix/([0-9]+)$ /rhymix/index.php?document_srl=$1 last;
# mid link
location ~ ^/rhymix/([a-zA-Z0-9_]+)/?$ {
try_files $uri $uri/ /rhymix/index.php?mid=$1;
# all other short URLs
location /rhymix/ {
try_files $uri $uri/ /rhymix/index.php$is_args$args;
}
# mid + document link
rewrite ^/rhymix/([a-zA-Z0-9_]+)/([0-9]+)$ /rhymix/index.php?mid=$1&document_srl=$2 last;
# mid + entry title
rewrite ^/rhymix/([a-zA-Z0-9_]+)/entry/(.+)$ /rhymix/index.php?mid=$1&entry=$2 last;
# file download
rewrite ^/rhymix/files/download/([0-9]+)/([a-zA-Z0-9_-]+)/(.+)$ /rhymix/index.php?act=procFileOutput&file_srl=$1&file_key=$2&filename=$3 last;

View file

@ -25,33 +25,7 @@ location ~ ^/(.+)\.min\.(css|js)$ {
try_files $uri $uri/ /$1.$2;
}
# rss, blogAPI
rewrite ^/(rss|atom)$ /index.php?module=rss&act=$1 last;
rewrite ^/([a-zA-Z0-9_]+)/(rss|atom|api)$ /index.php?mid=$1&act=$2 last;
# trackback
rewrite ^/([0-9]+)/(.+)/trackback$ /index.php?document_srl=$1&key=$2&act=trackback last;
rewrite ^/([a-zA-Z0-9_]+)/([0-9]+)/(.+)/trackback$ /index.php?mid=$1&document_srl=$2&key=$3&act=trackback last;
# administrator page
rewrite ^/admin/?$ /index.php?module=admin last;
# document category
rewrite ^/([a-zA-Z0-9_]+)/category/([0-9]+)$ /index.php?mid=$1&category=$2 last;
# document permanent link
rewrite ^/([0-9]+)$ /index.php?document_srl=$1 last;
# mid link
location ~ ^/([a-zA-Z0-9_]+)/?$ {
try_files $uri $uri/ /index.php?mid=$1;
# all other short URLs
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# mid + document link
rewrite ^/([a-zA-Z0-9_]+)/([0-9]+)$ /index.php?mid=$1&document_srl=$2 last;
# mid + entry title
rewrite ^/([a-zA-Z0-9_]+)/entry/(.+)$ /index.php?mid=$1&entry=$2 last;
# file download
rewrite ^/files/download/([0-9]+)/([a-zA-Z0-9_-]+)/(.+)$ /index.php?act=procFileOutput&file_srl=$1&file_key=$2&filename=$3 last;

View file

@ -847,7 +847,8 @@ class adminAdminController extends admin
Rhymix\Framework\Config::set('locale.default_timezone', $vars->default_timezone);
// Other settings
Rhymix\Framework\Config::set('use_rewrite', $vars->use_rewrite === 'Y');
Rhymix\Framework\Config::set('url.rewrite', intval($vars->use_rewrite));
Rhymix\Framework\Config::set('use_rewrite', $vars->use_rewrite > 0);
Rhymix\Framework\Config::set('session.delay', $vars->delay_session === 'Y');
Rhymix\Framework\Config::set('session.use_db', $vars->use_db_session === 'Y');
Rhymix\Framework\Config::set('view.manager_layout', $vars->manager_layout ?: 'module');

View file

@ -312,7 +312,26 @@ class adminAdminView extends admin
{
$needUpdate = FALSE;
$addTables = FALSE;
foreach($module_list AS $key => $value)
$priority = array(
'module' => 1000000,
'member' => 100000,
'document' => 10000,
'comment' => 1000,
'file' => 100,
);
usort($module_list, function($a, $b) use($priority) {
$a_priority = isset($priority[$a->module]) ? $priority[$a->module] : 0;
$b_priority = isset($priority[$b->module]) ? $priority[$b->module] : 0;
if ($a_priority == 0 && $b_priority == 0)
{
return strcmp($a->module, $b->module);
}
else
{
return $b_priority - $a_priority;
}
});
foreach($module_list as $value)
{
if($value->need_install)
{
@ -556,7 +575,7 @@ class adminAdminView extends admin
Context::set('selected_timezone', Rhymix\Framework\Config::get('locale.default_timezone'));
// Other settings
Context::set('use_rewrite', Rhymix\Framework\Config::get('use_rewrite'));
Context::set('use_rewrite', Rhymix\Framework\Router::getRewriteLevel());
Context::set('use_mobile_view', (config('mobile.enabled') !== null ? config('mobile.enabled') : config('use_mobile_view')) ? true : false);
Context::set('tablets_as_mobile', config('mobile.tablets') ? true : false);
Context::set('mobile_viewport', config('mobile.viewport') ?: 'width=device-width, initial-scale=1.0, user-scalable=yes');

View file

@ -264,7 +264,11 @@ $lang->trash = 'Recycle Bin';
$lang->accusation = 'Report';
$lang->status = 'Status';
$lang->action = 'Execute';
$lang->use_rewrite = 'Use Rewrite Mode';
$lang->use_rewrite = 'Use Short URLs';
$lang->use_rewrite_0 = 'None';
$lang->use_rewrite_1 = 'XE-compatible URLs only';
$lang->use_rewrite_2 = 'All supported URLs';
$lang->about_use_rewrite = 'Your web server must support mod_rewrite in order for short URLs to work. Apache usually detects the .htaccess file automatically.<br />nginx users should configure rewrite rules according to <a href="https://github.com/rhymix/rhymix-docs/blob/master/ko/introduction/nginx.md> target="_blank">the manual</a>. Outdated versions of nginx rewrite rules only support XE-compatible short URLs.';
$lang->timezone = 'Time Zone';
$lang->use_mobile_view = 'Enable Mobile View';
$lang->about_use_mobile_view = 'Show mobile page when visitors access with mobile devices.';

View file

@ -261,6 +261,10 @@ $lang->accusation = '신고';
$lang->status = '상태';
$lang->action = '실행';
$lang->use_rewrite = '짧은 주소 사용';
$lang->use_rewrite_0 = '사용하지 않음';
$lang->use_rewrite_1 = 'XE와 호환되는 주소 형태만 사용';
$lang->use_rewrite_2 = '모든 주소 형태를 사용';
$lang->about_use_rewrite = '짧은 주소를 사용하려면 웹서버에서 rewrite 기능을 지원해야 합니다. 아파치의 경우 .htaccess 파일이 있으면 대부분 자동으로 인식합니다.<br />nginx 사용시 <a href="https://github.com/rhymix/rhymix-docs/blob/master/ko/introduction/nginx.md> target="_blank">매뉴얼</a>을 참고하여 직접 설정하셔야 합니다. 구 버전의 nginx 설정은 XE와 호환되는 주소만 지원하니 주의하십시오.';
$lang->timezone = '표준 시간대';
$lang->use_mobile_view = '모바일 뷰 사용';
$lang->about_use_mobile_view = '모바일 기기로 접속시 모바일 페이지를 보여줍니다.';

View file

@ -13,8 +13,10 @@
<div class="x_control-group">
<label class="x_control-label">{$lang->use_rewrite}</label>
<div class="x_controls">
<label for="use_rewrite_y" class="x_inline"><input type="radio" name="use_rewrite" id="use_rewrite_y" value="Y" checked="checked"|cond="$use_rewrite" /> {$lang->cmd_yes}</label>
<label for="use_rewrite_n" class="x_inline"><input type="radio" name="use_rewrite" id="use_rewrite_n" value="N" checked="checked"|cond="!$use_rewrite" /> {$lang->cmd_no}</label>
<label for="use_rewrite_0" class="x_inline"><input type="radio" name="use_rewrite" id="use_rewrite_0" value="0" checked="checked"|cond="$use_rewrite == 0" /> {$lang->use_rewrite_0}</label>
<label for="use_rewrite_1" class="x_inline"><input type="radio" name="use_rewrite" id="use_rewrite_1" value="1" checked="checked"|cond="$use_rewrite == 1" /> {$lang->use_rewrite_1}</label>
<label for="use_rewrite_2" class="x_inline"><input type="radio" name="use_rewrite" id="use_rewrite_2" value="2" checked="checked"|cond="$use_rewrite == 2" /> {$lang->use_rewrite_2}</label>
<p class="x_help-block">{$lang->about_use_rewrite}</p>
</div>
</div>
<div class="x_control-group">

View file

@ -25,18 +25,6 @@ class board extends ModuleObject
*/
function __construct()
{
if(!Context::isInstalled()) return;
if(!Context::isExistsSSLAction('dispBoardWrite') && Context::getSslStatus() == 'optional')
{
$ssl_actions = array('dispBoardWrite', 'dispBoardWriteComment', 'dispBoardReplyComment', 'dispBoardModifyComment', 'dispBoardDelete', 'dispBoardDeleteComment', 'procBoardInsertDocument', 'procBoardDeleteDocument', 'procBoardInsertComment', 'procBoardDeleteComment', 'procBoardVerificationPassword');
Context::addSSLActions($ssl_actions);
}
if(!Context::isExistsSSLAction('dispTempSavedList') && Context::getSslStatus() == 'optional')
{
Context::addSSLAction('dispTempSavedList');
}
parent::__construct();
}

View file

@ -1364,6 +1364,11 @@ class boardView extends board
Context::set('blame_member_info', $blame_member_infos);
$this->setTemplateFile('vote_log');
}
function dispBoardNotFound()
{
$this->alertMessage('msg_not_founded', 404);
}
/**
* @brief the method for displaying the warning messages

View file

@ -56,19 +56,43 @@
</grant>
</grants>
<actions>
<action name="dispBoardContent" type="view" permission="list" standalone="false" index="true" />
<action name="dispBoardWrite" type="view" permission="write_document" standalone="false" meta-noindex="true" />
<action name="dispBoardDelete" type="view" permission="write_document" standalone="false" meta-noindex="true" />
<action name="dispBoardWriteComment" type="view" permission="write_comment" standalone="false" meta-noindex="true" />
<action name="dispBoardReplyComment" type="view" permission="write_comment" standalone="false" meta-noindex="true" />
<action name="dispBoardModifyComment" type="view" permission="write_comment" standalone="false" meta-noindex="true" />
<action name="dispBoardDeleteComment" type="view" permission="write_comment" standalone="false" meta-noindex="true" />
<action name="dispBoardDeleteTrackback" type="view" permission="list,view" standalone="false" meta-noindex="true" />
<action name="dispBoardContent" type="view" permission="list" standalone="false" index="true">
<route route="$document_srl:int" priority="100" />
<route route="$document_srl:int/comment/$comment_srl:int" priority="100" />
<route route="$document_srl:int/page/$page:int" priority="100" />
<route route="category/$category:int/search/$search_target:word/$search_keyword:any" priority="60" />
<route route="category/$category:int/search/$search_target:word/$search_keyword:any/page/$page:int" priority="60" />
<route route="search/$search_target:word/$search_keyword:any" priority="50" />
<route route="search/$search_target:word/$search_keyword:any/page/$page:int" priority="50" />
<route route="category/$category:int" priority="40" />
<route route="category/$category:int/page/$page:int" priority="40" />
<route route="page/$page:int" priority="10" />
</action>
<action name="dispBoardWrite" type="view" permission="write_document" standalone="false" meta-noindex="true" use-ssl="true">
<route route="write" />
<route route="$document_srl/edit" />
</action>
<action name="dispBoardDelete" type="view" permission="write_document" standalone="false" meta-noindex="true" use-ssl="true" route="$document_srl/delete" />
<action name="dispBoardWriteComment" type="view" permission="write_comment" standalone="false" meta-noindex="true" use-ssl="true" route="$document_srl/comment" />
<action name="dispBoardReplyComment" type="view" permission="write_comment" standalone="false" meta-noindex="true" use-ssl="true">
<route route="comment/$comment_srl/reply" />
<route route="comment/$comment_srl/reply$document_srl:delete" />
</action>
<action name="dispBoardModifyComment" type="view" permission="write_comment" standalone="false" meta-noindex="true" use-ssl="true">
<route route="comment/$comment_srl/edit" />
<route route="comment/$comment_srl/edit$document_srl:delete" />
</action>
<action name="dispBoardDeleteComment" type="view" permission="write_comment" standalone="false" meta-noindex="true" use-ssl="true">
<route route="comment/$comment_srl/delete" />
<route route="comment/$comment_srl/delete$document_srl:delete" />
</action>
<action name="dispBoardDeleteTrackback" type="view" permission="list,view" standalone="false" meta-noindex="true" use-ssl="true" />
<action name="dispBoardContentList" type="view" permission="list" standalone="false" />
<action name="dispBoardContentView" type="view" permission="view" standalone="false" />
<action name="dispBoardUpdateLog" type="view" permission="update_view" standalone="false" />
<action name="dispBoardUpdateLogView" type="view" permission="update_view" standalone="false" />
<action name="dispBoardVoteLog" type="view" permission="vote_log_view" standalone="false" />
<action name="dispBoardNotFound" type="view" standalone="false" error-handlers="404" />
<action name="dispBoardNoticeList" type="view" permission="list" standalone="false" />
<action name="dispBoardCategoryList" type="view" permission="list" standalone="false" />
@ -79,13 +103,13 @@
<action name="dispBoardCommentPage" type="view" permission="view" standalone="false" />
<action name="getBoardCommentPage" type="mobile" permission="view" standalone="false" />
<action name="procBoardInsertDocument" type="controller" permission="write_document" standalone="false" ruleset="insertDocument" />
<action name="procBoardDeleteDocument" type="controller" permission="write_document" standalone="false" />
<action name="procBoardRevertDocument" type="controller" permission="update_view" standalone="false" />
<action name="procBoardInsertComment" type="controller" permission="write_comment" standalone="false" />
<action name="procBoardDeleteComment" type="controller" permission="write_comment" standalone="false" />
<action name="procBoardDeleteTrackback" type="controller" permission="list,view" standalone="false" />
<action name="procBoardVerificationPassword" type="controller" permission="view" standalone="false" />
<action name="procBoardInsertDocument" type="controller" permission="write_document" standalone="false" use-ssl="true" ruleset="insertDocument" />
<action name="procBoardDeleteDocument" type="controller" permission="write_document" standalone="false" use-ssl="true" />
<action name="procBoardRevertDocument" type="controller" permission="update_view" standalone="false" use-ssl="true" />
<action name="procBoardInsertComment" type="controller" permission="write_comment" standalone="false" use-ssl="true" />
<action name="procBoardDeleteComment" type="controller" permission="write_comment" standalone="false" use-ssl="true" />
<action name="procBoardDeleteTrackback" type="controller" permission="list,view" standalone="false" use-ssl="true" />
<action name="procBoardVerificationPassword" type="controller" permission="view" standalone="false" use-ssl="true" />
<action name="procBoardVoteDocument" type="controller" permission="view" standalone="false" />
<action name="dispBoardAdminContent" type="view" admin_index="true" menu_name="board" menu_index="true" />
@ -102,7 +126,7 @@
<action name="getBoardAdminSimpleSetup" type="model" simple_setup_index="true" />
<action name="procBoardAdminInsertBoard" type="controller" permission="manager" check_var="module_srl" ruleset="insertBoard" />
<action name="procBoardAdminDeleteBoard" type="controller" />
<action name="procBoardAdminDeleteBoard" type="controller" permission="manager" check_var="module_srl" />
<action name="procBoardAdminUpdateBoardFroBasic" type="controller" ruleset="insertBoardForBasic" />
<action name="procBoardAdminSaveCategorySettings" type="controller" permission="manager" check_var="module_srl" ruleset="saveCategorySettings" />
</actions>

View file

@ -378,16 +378,13 @@ class communicationModel extends communication
$args = new stdClass();
$args->member_srl = $logged_info->member_srl;
$output = executeQueryArray('communication.getFriendGroups', $args);
$group_list = $output->data;
if(!$group_list)
$friend_group_list = array();
foreach ($output->data as $item)
{
return;
$friend_group_list[$item->friend_group_srl] = $item;
}
return $group_list;
return $friend_group_list;
}
/**

View file

@ -294,14 +294,7 @@ class communicationView extends communication
$oCommunicationModel = getModel('communication');
// get a group list
$tmp_group_list = $oCommunicationModel->getFriendGroups();
$group_count = count($tmp_group_list);
for($i = 0; $i < $group_count; $i++)
{
$friend_group_list[$tmp_group_list[$i]->friend_group_srl] = $tmp_group_list[$i];
}
$friend_group_list = $oCommunicationModel->getFriendGroups();
Context::set('friend_group_list', $friend_group_list);
// get a list of friends

View file

@ -4,7 +4,7 @@
<actions>
<action name="dispDocumentPrint" type="view" meta-noindex="true"/>
<action name="dispDocumentPreview" type="view" meta-noindex="true"/>
<action name="dispTempSavedList" type="view" permission="member" meta-noindex="true"/>
<action name="dispTempSavedList" type="view" permission="member" meta-noindex="true" use-ssl="true" />
<action name="dispDocumentDeclare" type="view" permission="member" meta-noindex="true" />
<action name="dispDocumentManageDocument" type="view" permission="all-managers" meta-noindex="true" />

View file

@ -49,9 +49,10 @@
html {
&.cke_panel_container, &.cke_panel_container body {
background-color: #fff;
.light_dark(default);
}
body.cke_editable {
min-height: 100vh;
padding: 10px;
.light_dark(@colorset);
}

View file

@ -24,7 +24,9 @@ class installAdminController extends install
$oInstallController = getController('install');
$oInstallController->installModule($module_name, './modules/'.$module_name);
$oModuleController = getController('module');
$oModuleController->registerActionForwardRoutes($module_name);
$oModuleController->registerSecureActions($module_name);
$this->setMessage('success_installed');
}
@ -41,13 +43,29 @@ class installAdminController extends install
if(!$oModule) throw new Rhymix\Framework\Exceptions\InvalidRequest;
Rhymix\Framework\Session::close();
$output = $oModule->moduleUpdate();
Rhymix\Framework\Session::start();
if($output instanceof BaseObject && !$output->toBool())
{
Rhymix\Framework\Session::start();
return $output;
}
$oModuleController = getController('module');
$output = $oModuleController->registerActionForwardRoutes($module_name);
if($output instanceof BaseObject && !$output->toBool())
{
Rhymix\Framework\Session::start();
return $output;
}
$output = $oModuleController->registerSecureActions($module_name);
if($output instanceof BaseObject && !$output->toBool())
{
Rhymix\Framework\Session::start();
return $output;
}
Rhymix\Framework\Session::start();
$this->setMessage('success_updated');
}

View file

@ -2,7 +2,11 @@
<module>
<grants />
<actions>
<action name="IS" type="view" meta-noindex="true" />
<action name="IS" type="view" meta-noindex="true" global-route="true">
<route route="search" />
<route route="search/$search_keyword" />
<route route="search/$search_target:word/$search_keyword" />
</action>
<action name="dispIntegration_searchAdminContent" type="view" admin_index="true" />
<action name="dispIntegration_searchAdminSkinInfo" type="view" />

View file

@ -38,6 +38,7 @@ class integration_search extends ModuleObject
if(is_dir($template_path)) return true;
}
}
return false;
}

View file

@ -2,44 +2,44 @@
<module>
<grants />
<actions>
<action name="dispMemberSignUpForm" type="view" meta-noindex="true" />
<action name="dispMemberLoginForm" type="view" meta-noindex="true" />
<action name="dispMemberFindAccount" type="view" meta-noindex="true" />
<action name="dispMemberResendAuthMail" type="view" meta-noindex="true" />
<action name="dispMemberInfo" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberModifyInfo" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberModifyPassword" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberModifyEmailAddress" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberLeave" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberScrappedDocument" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberSavedDocument" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberOwnDocument" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberOwnComment" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberActiveLogins" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberModifyNicknameLog" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberSignUpForm" type="view" meta-noindex="true" use-ssl="true" route="signup" />
<action name="dispMemberLoginForm" type="view" meta-noindex="true" use-ssl="true" route="login" />
<action name="dispMemberFindAccount" type="view" meta-noindex="true" use-ssl="true" />
<action name="dispMemberResendAuthMail" type="view" meta-noindex="true" use-ssl="true" />
<action name="dispMemberInfo" type="view" permission="member" meta-noindex="true" route="member_info" />
<action name="dispMemberModifyInfo" type="view" permission="member" meta-noindex="true" use-ssl="true" />
<action name="dispMemberModifyPassword" type="view" permission="member" meta-noindex="true" use-ssl="true" />
<action name="dispMemberModifyEmailAddress" type="view" permission="member" meta-noindex="true" use-ssl="true" />
<action name="dispMemberLeave" type="view" permission="member" meta-noindex="true" use-ssl="true" />
<action name="dispMemberScrappedDocument" type="view" permission="member" meta-noindex="true" use-ssl="true" route="my_scrap" />
<action name="dispMemberSavedDocument" type="view" permission="member" meta-noindex="true" use-ssl="true" route="my_saved_documents" />
<action name="dispMemberOwnDocument" type="view" permission="member" meta-noindex="true" use-ssl="true" route="my_documents" />
<action name="dispMemberOwnComment" type="view" permission="member" meta-noindex="true" use-ssl="true" route="my_comments" />
<action name="dispMemberActiveLogins" type="view" permission="member" meta-noindex="true" use-ssl="true" route="active_logins" />
<action name="dispMemberModifyNicknameLog" type="view" permission="member" meta-noindex="true" use-ssl="true" />
<action name="dispMemberLogout" type="view" permission="member" meta-noindex="true" />
<action name="dispMemberSpammer" type="view" permission="manager" check_var="module_srl" meta-noindex="true" />
<action name="getMemberMenu" type="model" />
<action name="getApiGroups" type="model" permission="root" />
<action name="procMemberInsert" type="controller" ruleset="@insertMember" />
<action name="procMemberInsert" type="controller" ruleset="@insertMember" use-ssl="true" route="signup" />
<action name="procMemberCheckValue" type="controller" />
<action name="procMemberLogin" type="controller" ruleset="@login" />
<action name="procMemberLogin" type="controller" ruleset="@login" use-ssl="true" route="login" />
<action name="procMemberRegisterDevice" type="controller" />
<action name="procMemberLoginWithDevice " type="controller" />
<action name="procMemberFindAccount" type="controller" method="GET|POST" ruleset="findAccount" />
<action name="procMemberFindAccountByQuestion" type="controller" method="GET|POST" />
<action name="procMemberAuthAccount" type="controller" method="GET|POST" />
<action name="procMemberAuthEmailAddress" type="controller" method="GET|POST" />
<action name="procMemberResendAuthMail" type="controller" ruleset="resendAuthMail" />
<action name="procMemberSendVerificationSMS" type="controller" />
<action name="procMemberConfirmVerificationSMS" type="controller" />
<action name="procMemberModifyInfoBefore" type="controller" permission="member" ruleset="recheckedPassword" />
<action name="procMemberModifyInfo" type="controller" permission="member" ruleset="@insertMember" />
<action name="procMemberModifyPassword" type="controller" permission="member" ruleset="modifyPassword" />
<action name="procMemberModifyEmailAddress" type="controller" permission="member" ruleset="modifyEmailAddress" />
<action name="procMemberLeave" type="controller" permission="member" ruleset="leaveMember" />
<action name="procMemberFindAccount" type="controller" method="GET|POST" ruleset="findAccount" use-ssl="true" />
<action name="procMemberFindAccountByQuestion" type="controller" method="GET|POST" use-ssl="true" />
<action name="procMemberAuthAccount" type="controller" method="GET|POST" use-ssl="true" />
<action name="procMemberAuthEmailAddress" type="controller" method="GET|POST" use-ssl="true" />
<action name="procMemberResendAuthMail" type="controller" ruleset="resendAuthMail" use-ssl="true" />
<action name="procMemberSendVerificationSMS" type="controller" use-ssl="true" />
<action name="procMemberConfirmVerificationSMS" type="controller" use-ssl="true" />
<action name="procMemberModifyInfoBefore" type="controller" permission="member" ruleset="recheckedPassword" use-ssl="true" />
<action name="procMemberModifyInfo" type="controller" permission="member" ruleset="@insertMember" use-ssl="true" />
<action name="procMemberModifyPassword" type="controller" permission="member" ruleset="modifyPassword" use-ssl="true" />
<action name="procMemberModifyEmailAddress" type="controller" permission="member" ruleset="modifyEmailAddress" use-ssl="true" />
<action name="procMemberLeave" type="controller" permission="member" ruleset="leaveMember" use-ssl="true" />
<action name="procMemberInsertProfileImage" type="controller" permission="member" ruleset="insertProfileImage" />
<action name="procMemberDeleteProfileImage" type="controller" permission="member" />
<action name="procMemberInsertImageName" type="controller" permission="member" ruleset="insertImageName" />

View file

@ -20,15 +20,6 @@ class member extends ModuleObject {
*/
function __construct()
{
if(!Context::isInstalled()) return;
// Set to use SSL upon actions related member join/information/password and so on. 2013.02.15
if(!Context::isExistsSSLAction('dispMemberModifyPassword') && Context::getSslStatus() == 'optional')
{
$ssl_actions = array('dispMemberModifyPassword', 'dispMemberSignUpForm', 'dispMemberModifyInfo', 'dispMemberModifyEmailAddress', 'dispMemberResendAuthMail', 'dispMemberLoginForm', 'dispMemberFindAccount', 'dispMemberLeave', 'procMemberLogin', 'procMemberModifyPassword', 'procMemberInsert', 'procMemberModifyInfo', 'procMemberFindAccount', 'procMemberModifyEmailAddress', 'procMemberResendAuthMail', 'procMemberLeave'/*, 'getMemberMenu'*/, 'procMemberFindAccountByQuestion');
Context::addSSLActions($ssl_actions);
}
parent::__construct();
}

View file

@ -131,6 +131,11 @@ class module extends ModuleObject
{
return true;
}
// check route columns in action_forward table
if(!$oDB->isColumnExists('action_forward', 'route_regexp')) return true;
if(!$oDB->isColumnExists('action_forward', 'route_config')) return true;
if(!$oDB->isColumnExists('action_forward', 'global_route')) return true;
}
/**
@ -448,6 +453,20 @@ class module extends ModuleObject
$oDB->addIndex('module_part_config', 'unique_module_part_config', array('module', 'module_srl'), false);
}
}
// check route columns in action_forward table
if(!$oDB->isColumnExists('action_forward', 'route_regexp'))
{
$oDB->addColumn('action_forward', 'route_regexp', 'text');
}
if(!$oDB->isColumnExists('action_forward', 'route_config'))
{
$oDB->addColumn('action_forward', 'route_config', 'text');
}
if(!$oDB->isColumnExists('action_forward', 'global_route'))
{
$oDB->addColumn('action_forward', 'global_route', 'char', 1, 'N', true);
}
}
/**

View file

@ -19,13 +19,15 @@ class moduleController extends module
* Action forward finds and forwards if an action is not in the requested module
* This is used when installing a module
*/
function insertActionForward($module, $type, $act)
function insertActionForward($module, $type, $act, $route_regexp = null, $route_config = null, $global_route = 'N')
{
$args = new stdClass();
$args->module = $module;
$args->type = $type;
$args->act = $act;
$args->route_regexp = is_scalar($route_regexp) ? $route_regexp : serialize($route_regexp);
$args->route_config = is_scalar($route_config) ? $route_config : serialize($route_config);
$args->global_route = $global_route === 'Y' ? 'Y' : 'N';
$output = executeQuery('module.insertActionForward', $args);
Rhymix\Framework\Cache::delete('action_forward');
@ -48,6 +50,32 @@ class moduleController extends module
return $output;
}
/**
* @brief Add action security
*/
function insertActionSecurity($act)
{
$args = new stdClass();
$args->act = $act;
$output = executeQuery('module.insertActionSecurity', $args);
Rhymix\Framework\Cache::delete('action_security');
return $output;
}
/**
* @brief Delete action security
*/
function deleteActionSecurity($act)
{
$args = new stdClass();
$args->act = $act;
$output = executeQuery('module.deleteActionSecurity', $args);
Rhymix\Framework\Cache::delete('action_security');
return $output;
}
/**
* @brief Add trigger callback function
*
@ -955,9 +983,8 @@ class moduleController extends module
{
$this->deleteModuleExtraVars($module_srl);
getDestroyXeVars($obj);
if(!$obj || !is_countable($obj) || !count($obj)) return;
foreach($obj as $key => $val)
foreach(get_object_vars($obj) as $key => $val)
{
if(is_object($val) || is_array($val)) continue;
@ -1289,6 +1316,102 @@ class moduleController extends module
Rhymix\Framework\Cache::clearGroup('site_and_module');
return $output;
}
/**
* Check if all action-forwardable routes are registered. If not, register them.
*
* @param string $module_name
* @return object
*/
public function registerActionForwardRoutes(string $module_name)
{
$action_forward = ModuleModel::getActionForward();
$module_action_info = ModuleModel::getModuleActionXml($module_name);
// Get the list of forwardable actions and their routes.
$forwardable_routes = array();
foreach ($module_action_info->action ?: [] as $action_name => $action_info)
{
if (count($action_info->route) && $action_info->standalone !== 'false')
{
$forwardable_routes[$action_name] = array(
'type' => $module_action_info->action->{$action_name}->type,
'regexp' => array(),
'config' => $action_info->route,
'global_route' => $action_info->global_route === 'true' ? 'Y' : 'N',
);
}
}
foreach ($module_action_info->route->GET as $regexp => $action_name)
{
if (isset($forwardable_routes[$action_name]))
{
$forwardable_routes[$action_name]['regexp'][] = ['GET', $regexp];
}
}
foreach ($module_action_info->route->POST as $regexp => $action_name)
{
if (isset($forwardable_routes[$action_name]))
{
$forwardable_routes[$action_name]['regexp'][] = ['POST', $regexp];
}
}
// Insert or delete from the action_forward table.
foreach ($forwardable_routes as $action_name => $route_info)
{
if (!isset($action_forward[$action_name]))
{
$output = $this->insertActionForward($module_name, $route_info['type'], $action_name,
$route_info['regexp'], $route_info['config'], $route_info['global_route']);
if (!$output->toBool())
{
return $output;
}
}
elseif ($action_forward[$action_name]->route_regexp !== $route_info['regexp'] ||
$action_forward[$action_name]->route_config !== $route_info['config'] ||
$action_forward[$action_name]->global_route !== $route_info['global_route'])
{
$output = $this->deleteActionForward($module_name, $route_info['type'], $action_name);
if (!$output->toBool())
{
return $output;
}
$output = $this->insertActionForward($module_name, $route_info['type'], $action_name,
$route_info['regexp'], $route_info['config'], $route_info['global_route']);
if (!$output->toBool())
{
return $output;
}
}
}
return new BaseObject();
}
/**
* Check if all secure actions are registered. If not, register them.
*
* @param string $module_name
* @return object
*/
public function registerSecureActions(string $module_name)
{
$action_security = ModuleModel::getActionSecurity();
$module_action_info = ModuleModel::getModuleActionXml($module_name);
foreach ($module_action_info->action ?: [] as $action_name => $action_info)
{
if ($action_info->use_ssl === 'true' && !isset($action_security[$action_name]))
{
$output = $this->insertActionSecurity($action_name);
}
}
return new BaseObject();
}
}
/* End of file module.controller.php */
/* Location: ./modules/module/module.controller.php */

View file

@ -211,7 +211,7 @@ class moduleModel extends module
*/
public static function getModuleInfoByMid($mid, $site_srl = 0, $columnList = array())
{
if(!$mid || ($mid && !preg_match("/^[a-z][a-z0-9_]+$/i", $mid)))
if(!$mid || ($mid && !preg_match("/^[a-z][a-z0-9_-]+$/i", $mid)))
{
return;
}
@ -567,7 +567,7 @@ class moduleModel extends module
/**
* @brief Get forward value by the value of act
*/
public static function getActionForward($act)
public static function getActionForward($act = null)
{
$action_forward = Rhymix\Framework\Cache::get('action_forward');
if($action_forward === null)
@ -582,12 +582,18 @@ class moduleModel extends module
$action_forward = array();
foreach($output->data as $item)
{
if ($item->route_regexp) $item->route_regexp = unserialize($item->route_regexp);
if ($item->route_config) $item->route_config = unserialize($item->route_config);
$action_forward[$item->act] = $item;
}
Rhymix\Framework\Cache::set('action_forward', $action_forward, 0, true);
}
if(!isset($act))
{
return $action_forward;
}
if(!isset($action_forward[$act]))
{
return;
@ -596,6 +602,38 @@ class moduleModel extends module
return $action_forward[$act];
}
/**
* @brief Get SSL action setting
*/
public static function getActionSecurity($act = null)
{
$action_security = Rhymix\Framework\Cache::get('action_security');
if($action_security === null)
{
$args = new stdClass();
$output = executeQueryArray('module.getActionSecurity', $args);
if(!$output->toBool())
{
return;
}
$action_security = array();
foreach($output->data as $item)
{
$action_security[$item->act] = true;
}
Rhymix\Framework\Cache::set('action_security', $action_security, 0, true);
}
if(!isset($act))
{
return $action_security;
}
return isset($action_security[$act]) ? true : false;
}
/**
* @brief Get trigger functions
*/
@ -686,321 +724,50 @@ class moduleModel extends module
*/
public static function getModuleInfoXml($module)
{
// Get a path of the requested module. Return if not exists.
// Check the path and XML file name.
$module_path = ModuleHandler::getModulePath($module);
if(!$module_path) return;
// Read the xml file for module skin information
$xml_file = sprintf("%s/conf/info.xml", $module_path);
if(!file_exists($xml_file)) return;
$oXmlParser = new XmlParser();
$tmp_xml_obj = $oXmlParser->loadXmlFile($xml_file);
$xml_obj = $tmp_xml_obj->module;
if(!$xml_obj) return;
// Module Information
$module_info = new stdClass();
if($xml_obj->version && $xml_obj->attrs->version == '0.2')
if (!$module_path) return;
$xml_file = $module_path . 'conf/info.xml';
if (!file_exists($xml_file)) return;
// Load the XML file and cache the definition.
$mtime1 = filemtime($xml_file);
$mtime2 = file_exists($module_path . 'conf/module.xml') ? filemtime($module_path . 'conf/module.xml') : 0;
$cache_key = sprintf('site_and_module:module_info_xml:%s:%d:%d', $module, $mtime1, $mtime2);
$info = Rhymix\Framework\Cache::get($cache_key);
if($info === null)
{
// module format 0.2
$module_info->title = $xml_obj->title->body;
$module_info->description = $xml_obj->description->body;
$module_info->version = $xml_obj->version->body;
$module_info->homepage = $xml_obj->link->body;
$module_info->category = $xml_obj->category->body;
if(!$module_info->category) $module_info->category = 'service';
$date_obj = (object)array('y' => 0, 'm' => 0, 'd' => 0);
sscanf($xml_obj->date->body, '%d-%d-%d', $date_obj->y, $date_obj->m, $date_obj->d);
$module_info->date = sprintf('%04d%02d%02d', $date_obj->y, $date_obj->m, $date_obj->d);
$module_info->license = $xml_obj->license->body;
$module_info->license_link = $xml_obj->license->attrs->link;
if(!is_array($xml_obj->author)) $author_list[] = $xml_obj->author;
else $author_list = $xml_obj->author;
foreach($author_list as $author)
{
$author_obj = new stdClass();
$author_obj->name = $author->name->body;
$author_obj->email_address = $author->attrs->email_address;
$author_obj->homepage = $author->attrs->link;
$module_info->author[] = $author_obj;
}
$info = Rhymix\Framework\Parsers\ModuleInfoParser::loadXML($xml_file);
Rhymix\Framework\Cache::set($cache_key, $info, 0, true);
}
else
{
// module format 0.1
$module_info->title = $xml_obj->title->body;
$module_info->description = $xml_obj->author->description->body;
$module_info->version = $xml_obj->attrs->version;
$module_info->category = $xml_obj->attrs->category;
if(!$module_info->category) $module_info->category = 'service';
$date_obj = (object)array('y' => 0, 'm' => 0, 'd' => 0);
sscanf($xml_obj->author->attrs->date, '%d. %d. %d', $date_obj->y, $date_obj->m, $date_obj->d);
$module_info->date = sprintf('%04d%02d%02d', $date_obj->y, $date_obj->m, $date_obj->d);
$author_obj = new stdClass();
$author_obj->name = $xml_obj->author->name->body;
$author_obj->email_address = $xml_obj->author->attrs->email_address;
$author_obj->homepage = $xml_obj->author->attrs->link;
$module_info->author[] = $author_obj;
}
// Add admin_index by using action information
$action_info = self::getModuleActionXml($module);
$module_info->admin_index_act = $action_info->admin_index_act;
$module_info->default_index_act = $action_info->default_index_act;
$module_info->setup_index_act = $action_info->setup_index_act;
$module_info->simple_setup_index_act = $action_info->simple_setup_index_act;
return $module_info;
return $info;
}
/**
* @brief Return permisson and action data by conf/module.xml in the module
* Cache it because it takes too long to parse module.xml file
* When caching, add codes so to include it directly
* This is apparently good for performance, but not sure about its side-effects
* @brief Return permisson and action data by conf/module.xml
*/
public static function getModuleActionXml($module)
{
// Get a path of the requested module. Return if not exists.
$class_path = ModuleHandler::getModulePath($module);
if(!$class_path) return;
// Check if module.xml exists in the path. Return if not exist
$xml_file = sprintf("%sconf/module.xml", $class_path);
if(!file_exists($xml_file)) return;
// Check if cached file exists
$cache_file = sprintf(_XE_PATH_ . "files/cache/module_info/%s.%s.%s.php", $module, Context::getLangType(), __XE_VERSION__);
// Update if no cache file exists or it is older than xml file
if(!file_exists($cache_file) || filemtime($cache_file) < filemtime($xml_file) || $re_cache)
// Check the path and XML file name.
$module_path = ModuleHandler::getModulePath($module);
if (!$module_path) return;
$xml_file = $module_path . 'conf/module.xml';
if (!file_exists($xml_file)) return;
// Load the XML file and cache the definition.
$mtime = filemtime($xml_file);
$cache_key = sprintf('site_and_module:module_action_xml:%s:%d', $module, $mtime);
$info = Rhymix\Framework\Cache::get($cache_key);
if($info === null)
{
$info = new stdClass();
$buff = array(); // /< Set buff variable to use in the cache file
$buff[] = '<?php if(!defined("__XE__")) exit();';
$buff[] = '$info = new stdClass;';
$buff['default_index_act'] = '$info->default_index_act = \'%s\';';
$buff['setup_index_act'] = '$info->setup_index_act=\'%s\';';
$buff['simple_setup_index_act'] = '$info->simple_setup_index_act=\'%s\';';
$buff['admin_index_act'] = '$info->admin_index_act = \'%s\';';
$xml_obj = XmlParser::loadXmlFile($xml_file); // /< Read xml file and convert it to xml object
if(!countobj($xml_obj->module)) return; // /< Error occurs if module tag doesn't included in the xml
$grants = $xml_obj->module->grants->grant; // /< Permission information
$permissions = $xml_obj->module->permissions->permission; // /< Acting permission
$menus = $xml_obj->module->menus->menu;
$actions = $xml_obj->module->actions->action; // /< Action list (required)
$default_index = $admin_index = '';
// Arrange permission information
if($grants)
{
if(is_array($grants)) $grant_list = $grants;
else $grant_list[] = $grants;
$info->grant = new stdClass();
$buff[] = '$info->grant = new stdClass;';
foreach($grant_list as $grant)
{
$name = $grant->attrs->name;
$default = $grant->attrs->default?$grant->attrs->default:'guest';
$title = $grant->title->body;
$info->grant->{$name} = new stdClass();
$info->grant->{$name}->title = $title;
$info->grant->{$name}->default = $default;
$buff[] = sprintf('$info->grant->%s = new stdClass;', $name);
$buff[] = sprintf('$info->grant->%s->title=\'%s\';', $name, $title);
$buff[] = sprintf('$info->grant->%s->default=\'%s\';', $name, $default);
}
}
// Permissions to grant
if($permissions)
{
if(is_array($permissions)) $permission_list = $permissions;
else $permission_list[] = $permissions;
$buff[] = '$info->permission = new stdClass;';
$buff[] = '$info->permission_check = new stdClass;';
$info->permission = new stdClass;
$info->permission_check = new stdClass;
foreach($permission_list as $permission)
{
$action = $permission->attrs->action;
$target = $permission->attrs->target;
$info->permission->$action = $target;
$buff[] = sprintf('$info->permission->%s = \'%s\';', $action, $target);
$info->permission_check->$action = new stdClass;
$info->permission_check->$action->key = $permission->attrs->check_var ?: '';
$info->permission_check->$action->type = $permission->attrs->check_type ?: '';
$buff[] = sprintf('$info->permission_check->%s = new stdClass;', $action);
$buff[] = sprintf('$info->permission_check->%s->key = \'%s\';', $action, $info->permission_check->$action->key);
$buff[] = sprintf('$info->permission_check->%s->type = \'%s\';', $action, $info->permission_check->$action->type);
}
}
// for admin menus
if($menus)
{
if(is_array($menus)) $menu_list = $menus;
else $menu_list[] = $menus;
$buff[] = '$info->menu = new stdClass;';
$info->menu = new stdClass();
foreach($menu_list as $menu)
{
$menu_name = $menu->attrs->name;
$menu_title = is_array($menu->title) ? $menu->title[0]->body : $menu->title->body;
$menu_type = $menu->attrs->type;
$info->menu->{$menu_name} = new stdClass();
$info->menu->{$menu_name}->title = $menu_title;
$info->menu->{$menu_name}->acts = array();
$info->menu->{$menu_name}->type = $menu_type;
$buff[] = sprintf('$info->menu->%s = new stdClass;', $menu_name);
$buff[] = sprintf('$info->menu->%s->title=\'%s\';', $menu_name, $menu_title);
$buff[] = sprintf('$info->menu->%s->type=\'%s\';', $menu_name, $menu_type);
}
}
// actions
if($actions)
{
if(is_array($actions)) $action_list = $actions;
else $action_list[] = $actions;
if(!isset($info->permission))
{
$buff[] = '$info->permission = new stdClass;';
$buff[] = '$info->permission_check = new stdClass;';
$info->permission = new stdClass;
$info->permission_check = new stdClass;
}
$buff[] = '$info->action = new stdClass;';
$info->action = new stdClass();
foreach($action_list as $action)
{
$name = $action->attrs->name;
// <action permission="...">
if($action->attrs->permission)
{
$info->permission->$name = $action->attrs->permission;
$buff[] = sprintf('$info->permission->%s = \'%s\';', $name, $info->permission->$name);
$info->permission_check->$name = new stdClass;
$info->permission_check->$name->key = $action->attrs->check_var ?: '';
$info->permission_check->$name->type = $action->attrs->check_type ?: '';
$buff[] = sprintf('$info->permission_check->%s = new stdClass;', $name);
$buff[] = sprintf('$info->permission_check->%s->key = \'%s\';', $name, $info->permission_check->$name->key);
$buff[] = sprintf('$info->permission_check->%s->type = \'%s\';', $name, $info->permission_check->$name->type);
}
$type = $action->attrs->type;
$grant = $action->attrs->grant?$action->attrs->grant:'guest';
$standalone = $action->attrs->standalone=='false'?'false':'true';
$ruleset = $action->attrs->ruleset?$action->attrs->ruleset:'';
$method = $action->attrs->method?$action->attrs->method:'';
$check_csrf = $action->attrs->check_csrf=='false'?'false':'true';
$meta_noindex = $action->attrs->{'meta-noindex'} === 'true' ? 'true' : 'false';
$index = $action->attrs->index;
$admin_index = $action->attrs->admin_index;
$setup_index = $action->attrs->setup_index;
$simple_setup_index = $action->attrs->simple_setup_index;
$menu_index = $action->attrs->menu_index;
$info->action->{$name} = new stdClass();
$info->action->{$name}->type = $type;
$info->action->{$name}->grant = $grant;
$info->action->{$name}->standalone = $standalone;
$info->action->{$name}->ruleset = $ruleset;
$info->action->{$name}->method = $method;
$info->action->{$name}->check_csrf = $check_csrf;
$info->action->{$name}->meta_noindex = $meta_noindex;
if($action->attrs->menu_name)
{
$info->menu->{$action->attrs->menu_name} = new stdClass();
if($menu_index == 'true')
{
$info->menu->{$action->attrs->menu_name}->index = $name;
$buff[] = sprintf('$info->menu->%s->index=\'%s\';', $action->attrs->menu_name, $name);
}
if(is_array($info->menu->{$action->attrs->menu_name}->acts))
{
$info->menu->{$action->attrs->menu_name}->acts[] = $name;
}
$buff[] = sprintf('$info->menu->%s->acts[]=\'%s\';', $action->attrs->menu_name, $name);
$i++;
}
$buff[] = sprintf('$info->action->%s = new stdClass;', $name);
$buff[] = sprintf('$info->action->%s->type=\'%s\';', $name, $type);
$buff[] = sprintf('$info->action->%s->grant=\'%s\';', $name, $grant);
$buff[] = sprintf('$info->action->%s->standalone=\'%s\';', $name, $standalone);
$buff[] = sprintf('$info->action->%s->ruleset=\'%s\';', $name, $ruleset);
$buff[] = sprintf('$info->action->%s->method=\'%s\';', $name, $method);
$buff[] = sprintf('$info->action->%s->check_csrf=\'%s\';', $name, $check_csrf);
$buff[] = sprintf('$info->action->%s->meta_noindex=\'%s\';', $name, $meta_noindex);
if($index=='true')
{
$default_index_act = $name;
$info->default_index_act = $name;
}
if($admin_index=='true')
{
$admin_index_act = $name;
$info->admin_index_act = $name;
}
if($setup_index=='true')
{
$setup_index_act = $name;
$info->setup_index_act = $name;
}
if($simple_setup_index=='true')
{
$simple_setup_index_act = $name;
$info->simple_setup_index_act = $name;
}
}
}
$buff['default_index_act'] = sprintf($buff['default_index_act'], $default_index_act);
$buff['setup_index_act'] = sprintf($buff['setup_index_act'], $setup_index_act);
$buff['simple_setup_index_act'] = sprintf($buff['simple_setup_index_act'], $simple_setup_index_act);
$buff['admin_index_act'] = sprintf($buff['admin_index_act'], $admin_index_act);
$buff[] = 'return $info;';
$buff = implode(PHP_EOL, $buff);
FileHandler::writeFile($cache_file, $buff);
return $info;
$info = Rhymix\Framework\Parsers\ModuleActionParser::loadXML($xml_file);
Rhymix\Framework\Cache::set($cache_key, $info, 0, true);
}
if(file_exists($cache_file)) return include($cache_file);
return $info;
}
/**
* Get a skin list for js API.
* return void
@ -1609,12 +1376,15 @@ class moduleModel extends module
$searched_count = count($searched_list);
if(!$searched_count) return;
// Get action forward
$action_forward = self::getActionForward();
// Get action security
$action_security = self::getActionSecurity();
for($i=0;$i<$searched_count;$i++)
foreach ($searched_list as $module_name)
{
// module name
$module_name = $searched_list[$i];
$path = ModuleHandler::getModulePath($module_name);
if(!is_dir(FileHandler::getRealPath($path))) continue;
@ -1651,16 +1421,59 @@ class moduleModel extends module
{
$info->need_install = false;
}
// Check if it is upgraded to module.class.php on each module
$oDummy = null;
$oDummy = getModule($module_name, 'class');
if($oDummy && method_exists($oDummy, "checkUpdate"))
{
$info->need_update = $oDummy->checkUpdate();
}
else
unset($oDummy);
// Check if all action-forwardable routes are registered
$module_action_info = self::getModuleActionXml($module_name);
$forwardable_routes = array();
foreach ($module_action_info->action ?: [] as $action_name => $action_info)
{
continue;
if (count($action_info->route) && $action_info->standalone !== 'false')
{
$forwardable_routes[$action_name] = array(
'regexp' => array(),
'config' => $action_info->route,
);
}
}
foreach ($module_action_info->route->GET ?: [] as $regexp => $action_name)
{
if (isset($forwardable_routes[$action_name]))
{
$forwardable_routes[$action_name]['regexp'][] = ['GET', $regexp];
}
}
foreach ($module_action_info->route->POST ?: [] as $regexp => $action_name)
{
if (isset($forwardable_routes[$action_name]))
{
$forwardable_routes[$action_name]['regexp'][] = ['POST', $regexp];
}
}
foreach ($forwardable_routes as $action_name => $route_info)
{
if (!isset($action_forward[$action_name]) ||
$action_forward[$action_name]->route_regexp !== $route_info['regexp'] ||
$action_forward[$action_name]->route_config !== $route_info['config'])
{
$info->need_update = true;
}
}
// Check if all secure actions are registered
foreach ($module_action_info->action ?: [] as $action_name => $action_info)
{
if ($action_info->use_ssl === 'true' && !isset($action_security[$action_name]))
{
$info->need_update = true;
}
}
}
$list[] = $info;

View file

@ -0,0 +1,8 @@
<query id="deleteActionSecurity" action="delete">
<tables>
<table name="action_security" />
</tables>
<conditions>
<condition operation="equal" column="act" var="act" notnull="notnull" />
</conditions>
</query>

View file

@ -0,0 +1,11 @@
<query id="getActionSecurity" action="select">
<tables>
<table name="action_security" />
</tables>
<columns>
<column name="*" />
</columns>
<conditions>
<condition operation="equal" column="act" var="act" />
</conditions>
</query>

View file

@ -6,5 +6,8 @@
<column name="act" var="act" notnull="notnull" />
<column name="module" var="module" notnull="notnull" />
<column name="type" var="type" notnull="notnull" />
<column name="route_regexp" var="route_regexp" />
<column name="route_config" var="route_config" />
<column name="global_route" var="global_route" default="N" />
</columns>
</query>

View file

@ -0,0 +1,8 @@
<query id="insertActionSecurity" action="insert">
<tables>
<table name="action_security" />
</tables>
<columns>
<column name="act" var="act" notnull="notnull" />
</columns>
</query>

View file

@ -1,5 +1,8 @@
<table name="action_forward">
<column name="act" type="varchar" size="80" notnull="notnull" unique="idx_foward" />
<column name="act" type="varchar" size="80" notnull="notnull" primary_key="primary_key" />
<column name="module" type="varchar" size="60" notnull="notnull" />
<column name="type" type="varchar" size="15" notnull="notnull" />
<column name="route_regexp" type="text" />
<column name="route_config" type="text" />
<column name="global_route" type="char" size="1" notnull="notnull" default="N" />
</table>

View file

@ -0,0 +1,3 @@
<table name="action_security">
<column name="act" type="varchar" size="80" notnull="notnull" primary_key="primary_key" />
</table>

View file

@ -96,7 +96,7 @@ class ContextTest extends \Codeception\TestCase\Test
Context::setRequestArguments();
$this->assertEquals('POST', Context::getRequestMethod());
$this->assertNull(Context::getRequestVars()->foo);
$this->assertEquals('bazz', Context::get('foo')); // XE Compatibility behavior
$this->assertNull(Context::get('foo')); // This is different from XE behavior
$_SERVER['REQUEST_METHOD'] = 'POST';
$_GET = array();

View file

@ -0,0 +1,100 @@
<?php
class RouterTest extends \Codeception\TestCase\Test
{
public function testGetRewriteLevel()
{
Rhymix\Framework\Config::set('url.rewrite', null);
Rhymix\Framework\Config::set('use_rewrite', false);
$this->assertEquals(0, Rhymix\Framework\Router::getRewriteLevel());
Rhymix\Framework\Config::set('url.rewrite', 1);
Rhymix\Framework\Config::set('use_rewrite', false);
$this->assertEquals(1, Rhymix\Framework\Router::getRewriteLevel());
Rhymix\Framework\Config::set('url.rewrite', 1);
Rhymix\Framework\Config::set('use_rewrite', true);
$this->assertEquals(1, Rhymix\Framework\Router::getRewriteLevel());
Rhymix\Framework\Config::set('url.rewrite', 2);
Rhymix\Framework\Config::set('use_rewrite', true);
$this->assertEquals(2, Rhymix\Framework\Router::getRewriteLevel());
}
public function testGetURL()
{
$info = Rhymix\Framework\Parsers\ModuleActionParser::loadXML(\RX_BASEDIR . 'modules/board/conf/module.xml');
$this->assertEquals('dispBoardContent', $info->default_index_act);
$this->assertFalse(isset($info->permission));
$this->assertGreaterThan(0, count($info->route->GET));
$this->assertGreaterThan(0, count($info->action->dispBoardContent->route));
getController('module')->registerActionForwardRoutes('member');
$args = array('mid' => 'board', 'document_srl' => 123);
$this->assertEquals('board/123', Rhymix\Framework\Router::getURL($args, 2));
$this->assertEquals('board/123', Rhymix\Framework\Router::getURL($args, 1));
$this->assertEquals('index.php?mid=board&document_srl=123', Rhymix\Framework\Router::getURL($args, 0));
$args = array('mid' => 'board', 'act' => 'dispBoardWrite', 'document_srl' => 123);
$this->assertEquals('board/123/edit', Rhymix\Framework\Router::getURL($args, 2));
$this->assertEquals('index.php?mid=board&act=dispBoardWrite&document_srl=123', Rhymix\Framework\Router::getURL($args, 1));
$this->assertEquals('index.php?mid=board&act=dispBoardWrite&document_srl=123', Rhymix\Framework\Router::getURL($args, 0));
$args = array('mid' => 'board', 'act' => 'dispBoardWrite');
$this->assertEquals('board/write', Rhymix\Framework\Router::getURL($args, 2));
$this->assertEquals('index.php?mid=board&act=dispBoardWrite', Rhymix\Framework\Router::getURL($args, 1));
$this->assertEquals('index.php?mid=board&act=dispBoardWrite', Rhymix\Framework\Router::getURL($args, 0));
$args = array('mid' => 'board', 'act' => 'dispBoardModifyComment', 'document_srl' => 123, 'comment_srl' => 456, 'extra_param' => 'foo bar');
$this->assertEquals('board/comment/456/edit?extra_param=foo+bar', Rhymix\Framework\Router::getURL($args, 2));
$this->assertEquals('index.php?mid=board&act=dispBoardModifyComment&document_srl=123&comment_srl=456&extra_param=foo+bar', Rhymix\Framework\Router::getURL($args, 1));
$this->assertEquals('index.php?mid=board&act=dispBoardModifyComment&document_srl=123&comment_srl=456&extra_param=foo+bar', Rhymix\Framework\Router::getURL($args, 0));
$args = array('mid' => 'board', 'act' => 'dispMemberInfo');
$this->assertEquals('board/member_info', Rhymix\Framework\Router::getURL($args, 2));
$this->assertEquals('index.php?mid=board&act=dispMemberInfo', Rhymix\Framework\Router::getURL($args, 1));
$this->assertEquals('index.php?mid=board&act=dispMemberInfo', Rhymix\Framework\Router::getURL($args, 0));
$args = array('module' => 'document', 'act' => 'procDocumentVoteUp');
$this->assertEquals('document/procDocumentVoteUp', Rhymix\Framework\Router::getURL($args, 2));
$this->assertEquals('index.php?module=document&act=procDocumentVoteUp', Rhymix\Framework\Router::getURL($args, 1));
$this->assertEquals('index.php?module=document&act=procDocumentVoteUp', Rhymix\Framework\Router::getURL($args, 0));
}
public function testParseURL()
{
$args = array('mid' => 'board', 'act' => 'dispBoardContent', 'document_srl' => '123');
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', 'board/123', 2)->args);
$this->assertEquals('board/123', Rhymix\Framework\Router::parseURL('GET', 'board/123', 2)->url);
$this->assertEquals('board', Rhymix\Framework\Router::parseURL('GET', 'board/123', 2)->mid);
$this->assertEquals('dispBoardContent', Rhymix\Framework\Router::parseURL('GET', 'board/123', 2)->act);
$args = array('mid' => 'board', 'document_srl' => '123');
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', 'board/123', 1)->args);
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', 'index.php?mid=board&document_srl=123', 0)->args);
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', \RX_BASEURL . 'board/123', 1)->args);
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', \RX_BASEURL . 'index.php?mid=board&document_srl=123', 0)->args);
$args = array('mid' => 'board', 'act' => 'dispBoardModifyComment', 'comment_srl' => '456', 'extra_param' => 'foo bar');
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', 'board/comment/456/edit?extra_param=foo+bar', 2)->args);
$this->assertEquals('board', Rhymix\Framework\Router::parseURL('GET', 'board/comment/456/edit?extra_param=foo+bar', 2)->mid);
$this->assertEquals('dispBoardModifyComment', Rhymix\Framework\Router::parseURL('GET', 'board/comment/456/edit?extra_param=foo+bar', 2)->act);
$this->assertEquals('', Rhymix\Framework\Router::parseURL('GET', 'board/comment/456/edit?extra_param=foo+bar', 2)->document_srl);
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', 'index.php?mid=board&act=dispBoardModifyComment&comment_srl=456&extra_param=foo+bar', 1)->args);
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', 'index.php?mid=board&act=dispBoardModifyComment&comment_srl=456&extra_param=foo+bar', 0)->args);
$args = array('mid' => 'board', 'act' => 'dispMemberInfo');
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', 'board/member_info', 2)->args);
$this->assertEquals('board', Rhymix\Framework\Router::parseURL('GET', 'board/member_info', 2)->mid);
$args = array('mid' => 'board', 'act' => 'dispMemberLoginForm');
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('GET', 'board/login', 2)->args);
$this->assertEquals('member', Rhymix\Framework\Router::parseURL('GET', 'board/login', 2)->module);
$args = array('mid' => 'board', 'act' => 'procMemberLogin');
$this->assertEquals($args, Rhymix\Framework\Router::parseURL('POST', 'board/login', 2)->args);
$this->assertEquals('member', Rhymix\Framework\Router::parseURL('POST', 'board/login', 2)->module);
}
}