diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php
index 8e608862e..b5bed2667 100644
--- a/classes/context/Context.class.php
+++ b/classes/context/Context.class.php
@@ -318,10 +318,10 @@ class Context
}
}
- $lang_type = preg_replace('/[^a-zA-Z0-9_-]/', '', $lang_type);
+ $lang_type = preg_replace('/[^a-zA-Z0-9_-]/', '', $lang_type ?? '');
if ($set_lang_cookie)
{
- setcookie('lang_type', $lang_type, time() + 86400 * 365, \RX_BASEURL, null, !!config('session.use_ssl_cookies'));
+ setcookie('lang_type', $lang_type, time() + 86400 * 365, \RX_BASEURL, '', !!config('session.use_ssl_cookies'));
}
if(!$lang_type || !isset($enabled_langs[$lang_type]))
@@ -745,7 +745,7 @@ class Context
{
return '';
}
- return escape(self::replaceUserLang(self::$_instance->browser_title), false);
+ return escape(self::replaceUserLang(self::$_instance->browser_title, true), false);
}
/**
@@ -758,7 +758,7 @@ class Context
$domain_info = self::get('site_module_info');
if ($domain_info && $domain_info->settings && $domain_info->settings->title)
{
- return escape(self::replaceUserLang($domain_info->settings->title), false);
+ return escape(self::replaceUserLang($domain_info->settings->title, true), false);
}
else
{
@@ -776,7 +776,7 @@ class Context
$domain_info = self::get('site_module_info');
if ($domain_info && $domain_info->settings && $domain_info->settings->subtitle)
{
- return escape(self::replaceUserLang($domain_info->settings->subtitle), false);
+ return escape(self::replaceUserLang($domain_info->settings->subtitle, true), false);
}
else
{
@@ -886,9 +886,13 @@ class Context
}
/**
- * @brief Replace user-defined language codes
+ * Replace user-defined language codes
+ *
+ * @param string $string
+ * @param bool $fix_double_escape
+ * @return string
*/
- public static function replaceUserLang($output)
+ public static function replaceUserLang($string, $fix_double_escape = false)
{
static $lang = null;
if($lang === null)
@@ -900,7 +904,15 @@ class Context
}
}
- return preg_replace_callback('/\$user_lang->([a-zA-Z0-9\_]+)/', function($matches) use($lang) {
+ if ($fix_double_escape)
+ {
+ $regexp = '/\$user_lang-(?:>|>)([a-zA-Z0-9\_]+)/';
+ }
+ else
+ {
+ $regexp = '/\$user_lang->([a-zA-Z0-9\_]+)/';
+ }
+ return preg_replace_callback($regexp, function($matches) use($lang) {
if(isset($lang[$matches[1]]) && !self::get($matches[1]))
{
return $lang[$matches[1]];
@@ -909,7 +921,7 @@ class Context
{
return $matches[1];
}
- }, $output);
+ }, $string);
}
/**
@@ -1680,11 +1692,18 @@ class Context
$get_vars = array();
foreach ($args_list[0] as $key => $val)
{
- $val = trim($val);
- if ($val !== '')
+ if (is_array($val))
{
$get_vars[$key] = $val;
}
+ else
+ {
+ $val = trim(strval($val));
+ if ($val !== '')
+ {
+ $get_vars[$key] = $val;
+ }
+ }
}
}
// Otherwise, use alternating members of $args_list as keys and values, respectively.
@@ -1814,7 +1833,7 @@ class Context
// Check HTTP Request
if(!isset($_SERVER['SERVER_PROTOCOL']))
{
- return;
+ return self::getDefaultUrl();
}
$site_module_info = self::get('site_module_info');
@@ -2774,7 +2793,7 @@ class Context
{
self::$_instance->meta_tags[$name] = array(
'is_http_equiv' => (bool)$is_http_equiv,
- 'content' => self::replaceUserLang($content),
+ 'content' => self::replaceUserLang($content, true),
);
}
diff --git a/classes/db/DB.class.php b/classes/db/DB.class.php
index 21bb0595f..393d34779 100644
--- a/classes/db/DB.class.php
+++ b/classes/db/DB.class.php
@@ -1,3 +1,6 @@
gzhandler_enable)
- {
- $this->gz_enabled = TRUE;
- }
-
// Extract contents to display by the response method
$responseMethod = Context::getResponseMethod();
if(Context::get('xeVirtualRequestMethod') == 'xml')
@@ -122,21 +115,21 @@ class DisplayHandler extends Handler
}
}
- // disable gzip if output already exists
+ // Print security-related headers.
+ if($header_value = config('security.x_frame_options'))
+ {
+ header('X-Frame-Options: ' . $header_value);
+ }
+ if($header_value = config('security.x_content_type_options'))
+ {
+ header('X-Content-Type-Options: ' . $header_value);
+ }
+
+ // flush output buffer
while (ob_get_level())
{
ob_end_flush();
}
- if(headers_sent())
- {
- $this->gz_enabled = FALSE;
- }
-
- // enable gzip using zlib extension
- if($this->gz_enabled)
- {
- ini_set('zlib.output_compression', true);
- }
// call a trigger after display
self::$response_size = $this->content_size = strlen($output);
diff --git a/classes/mobile/Mobile.class.php b/classes/mobile/Mobile.class.php
index c3e857c1e..186f7eccb 100644
--- a/classes/mobile/Mobile.class.php
+++ b/classes/mobile/Mobile.class.php
@@ -46,7 +46,7 @@ class Mobile
$m = Context::get('m');
$cookie = isset($_COOKIE['rx_uatype']) ? $_COOKIE['rx_uatype'] : null;
$uahash = base64_encode_urlsafe(md5($_SERVER['HTTP_USER_AGENT'] ?? '', true));
- if (strncmp($cookie, $uahash . ':', strlen($uahash) + 1) !== 0)
+ if (strncmp($cookie ?? '', $uahash . ':', strlen($uahash) + 1) !== 0)
{
$cookie = null;
}
@@ -72,7 +72,7 @@ class Mobile
$uatype = $uahash . ':' . (self::$_ismobile ? '1' : '0');
if ($cookie !== $uatype)
{
- setcookie('rx_uatype', $uatype, 0, \RX_BASEURL, null, !!config('session.use_ssl_cookies'));
+ setcookie('rx_uatype', $uatype, 0, \RX_BASEURL, '', !!config('session.use_ssl_cookies'));
$_COOKIE['rx_uatype'] = $uatype;
}
diff --git a/classes/module/ModuleHandler.class.php b/classes/module/ModuleHandler.class.php
index c69d8f641..cb78aa4cd 100644
--- a/classes/module/ModuleHandler.class.php
+++ b/classes/module/ModuleHandler.class.php
@@ -193,6 +193,15 @@ class ModuleHandler extends Handler
if(!$module_info && $this->mid)
{
$module_info = ModuleModel::getModuleInfoByMid($this->mid);
+ if($module_info && isset($module_info->domain_srl) && $module_info->domain_srl > -1)
+ {
+ if($module_info->domain_srl != $site_module_info->domain_srl)
+ {
+ $this->error = 'msg_module_is_not_exists';
+ $this->httpStatusCode = 404;
+ return true;
+ }
+ }
}
// Set module info as the default module for the domain.
@@ -983,7 +992,7 @@ class ModuleHandler extends Handler
}
// If connection to DB has a problem even though it's not install module, set error
- if($this->module != 'install' && !DB::getInstance()->isConnected())
+ if($this->module != 'install' && !DB::getInstance()->getHandle())
{
$this->error = 'msg_dbconnect_failed';
}
diff --git a/classes/module/ModuleObject.class.php b/classes/module/ModuleObject.class.php
index d9bb9d894..b882609db 100644
--- a/classes/module/ModuleObject.class.php
+++ b/classes/module/ModuleObject.class.php
@@ -595,6 +595,100 @@ class ModuleObject extends BaseObject
return $this->layout_path;
}
+ /**
+ * Automatically set layout and template path based on skin settings.
+ *
+ * @param string $type 'P' or 'M'
+ * @param object $config
+ * @return void
+ */
+ public function setLayoutAndTemplatePaths($type, $config)
+ {
+ // Set the layout path.
+ if ($type === 'P')
+ {
+ $layout_srl = $config->layout_srl ?? 0;
+ if ($layout_srl > 0)
+ {
+ $layout_info = LayoutModel::getInstance()->getLayout($layout_srl);
+ if($layout_info)
+ {
+ $this->module_info->layout_srl = $layout_srl;
+ $this->setLayoutPath($layout_info->path);
+ }
+ }
+ }
+ else
+ {
+ $layout_srl = $config->mlayout_srl ?? 0;
+ if ($layout_srl == -2)
+ {
+ $layout_srl = $config->layout_srl ?: -1;
+ if ($layout_srl == -1)
+ {
+ $layout_srl = LayoutAdminModel::getInstance()->getSiteDefaultLayout('P');
+ }
+ }
+ elseif ($layout_srl == -1)
+ {
+ $layout_srl = LayoutAdminModel::getInstance()->getSiteDefaultLayout('M');
+ }
+
+ $layout_info = LayoutModel::getInstance()->getLayout($layout_srl);
+ if($layout_info)
+ {
+ $this->module_info->mlayout_srl = $layout_srl;
+ $this->setLayoutPath($layout_info->path);
+ }
+ }
+
+ // Set the skin path.
+ if ($type === 'P')
+ {
+ $skin = ($config->skin ?? '') ?: 'default';
+ if ($skin === '/USE_DEFAULT/')
+ {
+ $skin = ModuleModel::getModuleDefaultSkin($this->module, 'P') ?: 'default';
+ }
+ $template_path = sprintf('%sskins/%s', $this->module_path, $skin);
+ if (!Rhymix\Framework\Storage::exists($template_path))
+ {
+ $template_path = sprintf('%sskins/%s', $this->module_path, 'default');
+ }
+ }
+ else
+ {
+ $mskin = ($config->mskin ?? '') ?: 'default';
+ if ($mskin === '/USE_DEFAULT/')
+ {
+ $mskin = ModuleModel::getModuleDefaultSkin($this->module, 'M') ?: 'default';
+ }
+
+ if($mskin === '/USE_RESPONSIVE/')
+ {
+ $skin = ($config->skin ?? '') ?: 'default';
+ if ($skin === '/USE_DEFAULT/')
+ {
+ $skin = ModuleModel::getModuleDefaultSkin($this->module, 'P') ?: 'default';
+ }
+ $template_path = sprintf('%sskins/%s', $this->module_path, $skin);
+ if (!Rhymix\Framework\Storage::exists($template_path))
+ {
+ $template_path = sprintf('%sskins/%s', $this->module_path, 'default');
+ }
+ }
+ else
+ {
+ $template_path = sprintf('%sm.skins/%s', $this->module_path, $mskin);
+ if (!Rhymix\Framework\Storage::exists($template_path))
+ {
+ $template_path = sprintf("%sm.skins/%s/", $this->module_path, 'default');
+ }
+ }
+ }
+ $this->setTemplatePath($template_path);
+ }
+
/**
* excute the member method specified by $act variable
* @return bool
@@ -641,37 +735,11 @@ class ModuleObject extends BaseObject
// Set module skin
if(isset($this->module_info->skin) && $this->module_info->module === $this->module && strpos($this->act, 'Admin') === false)
{
- $skin_type = $is_mobile ? 'M' : 'P';
- $skin_key = $is_mobile ? 'mskin' : 'skin';
- $skin_dir = $is_mobile ? 'm.skins' : 'skins';
- $module_skin = $this->module_info->{$skin_key} ?: '/USE_DEFAULT/';
- $use_default_skin = $this->module_info->{'is_' . $skin_key . '_fix'} === 'N';
-
- // Set default skin
+ $use_default_skin = $this->module_info->{$is_mobile ? 'is_mskin_fix' : 'is_skin_fix'} === 'N';
if(!$this->getTemplatePath() || $use_default_skin)
{
- if($module_skin === '/USE_DEFAULT/')
- {
- $module_skin = ModuleModel::getModuleDefaultSkin($this->module, $skin_type);
- $this->module_info->{$skin_key} = $module_skin;
- }
- if($module_skin === '/USE_RESPONSIVE/')
- {
- $skin_dir = 'skins';
- $module_skin = $this->module_info->skin ?: '/USE_DEFAULT/';
- if($module_skin === '/USE_DEFAULT/')
- {
- $module_skin = ModuleModel::getModuleDefaultSkin($this->module, 'P');
- }
- }
- if(!is_dir(sprintf('%s%s/%s', $this->module_path, $skin_dir, $module_skin)))
- {
- $module_skin = 'default';
- }
- $this->setTemplatePath(sprintf('%s%s/%s', $this->module_path, $skin_dir, $module_skin));
+ $this->setLayoutAndTemplatePaths($is_mobile ? 'M' : 'P', $this->module_info);
}
-
- // Set skin variable
ModuleModel::syncSkinInfoToModuleInfo($this->module_info);
Context::set('module_info', $this->module_info);
}
diff --git a/classes/template/TemplateHandler.class.php b/classes/template/TemplateHandler.class.php
index bc8abfc49..851e21049 100644
--- a/classes/template/TemplateHandler.class.php
+++ b/classes/template/TemplateHandler.class.php
@@ -16,6 +16,7 @@ class TemplateHandler
private $config = NULL;
private $skipTags = NULL;
private $handler_mtime = 0;
+ private $delay_compile = 0;
private static $rootTpl = NULL;
/**
@@ -32,6 +33,7 @@ class TemplateHandler
ini_set('pcre.jit', false);
$this->config = new stdClass;
$this->handler_mtime = filemtime(__FILE__);
+ $this->delay_compile = config('view.delay_compile') ?? 0;
$this->user = Rhymix\Framework\Session::getMemberInfo();
}
@@ -161,13 +163,27 @@ class TemplateHandler
self::$rootTpl = $this->file;
}
- $latest_mtime = max(filemtime($this->file), $this->handler_mtime);
+ // Don't try to compile files that are less than 1 second old
+ $filemtime = filemtime($this->file);
+ if ($filemtime > time() - $this->delay_compile)
+ {
+ $latest_mtime = $this->handler_mtime;
+ }
+ else
+ {
+ $latest_mtime = max($filemtime, $this->handler_mtime);
+ }
// make compiled file
if(!file_exists($this->compiled_file) || filemtime($this->compiled_file) < $latest_mtime)
{
$buff = $this->parse();
- if(Rhymix\Framework\Storage::write($this->compiled_file, $buff) === false)
+ if($buff === null && file_exists($this->compiled_file))
+ {
+ $error_message = 'Template compile failed: Source file is unreadable: ' . $this->file;
+ trigger_error($error_message, \E_USER_WARNING);
+ }
+ elseif(Rhymix\Framework\Storage::write($this->compiled_file, $buff) === false)
{
$tmpfilename = tempnam(sys_get_temp_dir(), 'rx-compiled');
if($tmpfilename === false || Rhymix\Framework\Storage::write($tmpfilename, $buff) === false)
@@ -245,7 +261,11 @@ class TemplateHandler
}
// read tpl file
- $buff = FileHandler::readFile($this->file);
+ $buff = Rhymix\Framework\Storage::read($this->file);
+ if ($buff === false)
+ {
+ return;
+ }
}
// HTML tags to skip
@@ -1029,7 +1049,7 @@ class TemplateHandler
{
if (preg_match('/^\$[\\\\\w\[\]\'":>-]+$/i', $str))
{
- $str = "$str ?? ''";
+ $str = preg_match('/^\$lang->/', $str) ? $str : "$str ?? ''";
}
switch($escape_option)
diff --git a/common/constants.php b/common/constants.php
index c41c47102..c5c850128 100644
--- a/common/constants.php
+++ b/common/constants.php
@@ -3,7 +3,7 @@
/**
* RX_VERSION is the version number of the Rhymix CMS.
*/
-define('RX_VERSION', '2.1.0-dev');
+define('RX_VERSION', '2.1.1');
/**
* RX_MICROTIME is the startup time of the current script, in microseconds since the Unix epoch.
diff --git a/common/defaults/blacklist.php b/common/defaults/blacklist.php
index eb0241e39..c99583abc 100644
--- a/common/defaults/blacklist.php
+++ b/common/defaults/blacklist.php
@@ -2,11 +2,11 @@
/**
* Rhymix Default Blacklist for Deprecated Plugins
- *
+ *
* Copyright (c) Rhymix Developers and Contributors
*/
return array(
-
+
// Addons
'addon' => array(
'autolang' => true,
@@ -16,11 +16,12 @@ return array(
'fix_mysql_utf8' => true,
'jquerycdn' => true,
'member_communication' => true,
+ 'mobile' => true,
'session_shield' => true,
'smartphone' => true,
'zipperupper' => true,
),
-
+
// Modules
'module' => array(
'auto_login' => true,
@@ -30,9 +31,9 @@ return array(
'seo' => true,
'trackback' => true,
),
-
+
// Widgets
'widget' => array(
-
+
),
);
diff --git a/common/defaults/config.php b/common/defaults/config.php
index df7da8db8..e786796ad 100644
--- a/common/defaults/config.php
+++ b/common/defaults/config.php
@@ -61,6 +61,7 @@ return array(
'use_keys' => false,
'use_ssl' => false,
'use_ssl_cookies' => false,
+ 'samesite' => 'Lax',
'domain' => null,
'path' => null,
'lifetime' => 0,
@@ -77,9 +78,9 @@ return array(
'manager_layout' => 'module',
'minify_scripts' => 'common',
'concat_scripts' => 'none',
+ 'delay_compile' => 0,
'jquery_version' => 2,
'server_push' => false,
- 'use_gzip' => false,
),
'admin' => array(
'allow' => array(),
@@ -126,6 +127,8 @@ return array(
'robot_user_agents' => array(),
'check_csrf_token' => false,
'nofollow' => false,
+ 'x_frame_options' => 'SAMEORIGIN',
+ 'x_content_type_options' => 'nosniff',
),
'mobile' => array(
'enabled' => true,
diff --git a/common/defaults/reserved.php b/common/defaults/reserved.php
index f789ce5c3..8f9500381 100644
--- a/common/defaults/reserved.php
+++ b/common/defaults/reserved.php
@@ -2,7 +2,7 @@
/**
* Reserved words for Rhymix
- *
+ *
* Copyright (c) Rhymix Developers and Contributors
*/
return array(
@@ -12,6 +12,7 @@ return array(
'admin' => true,
'module' => true,
'module_srl' => true,
+ 'member' => true,
'member_srl' => true,
'menu_srl' => true,
'menu_item_srl' => true,
diff --git a/common/framework/DB.php b/common/framework/DB.php
index 6b3d17d15..bc0a2c250 100644
--- a/common/framework/DB.php
+++ b/common/framework/DB.php
@@ -101,40 +101,66 @@ class DB
return;
}
+ // Cache the debug comment setting.
+ $this->_debug_queries = in_array('queries', Config::get('debug.display_content') ?: []);
+ $this->_debug_comment = !!config('debug.query_comment');
+ $this->_debug_full_stack = !!Config::get('debug.query_full_stack');
+
// Connect to the DB.
+ $this->connect($config);
+ }
+
+ /**
+ * Connect to the database.
+ *
+ * @param array $config
+ * @return void
+ */
+ public function connect(array $config): void
+ {
+ // Assemble the DSN and default options.
$dsn = 'mysql:host=' . $config['host'];
$dsn .= (isset($config['port']) && $config['port'] != 3306) ? (';port=' . $config['port']) : '';
$dsn .= ';dbname=' . $config['database'];
$dsn .= ';charset=' . $this->_charset;
- class_exists('\Rhymix\Framework\Helpers\DBStmtHelper');
$options = array(
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_EMULATE_PREPARES => false,
\PDO::ATTR_STATEMENT_CLASS => array('\Rhymix\Framework\Helpers\DBStmtHelper'),
\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false,
);
+
+ // Preload the statement helper class.
+ class_exists('\Rhymix\Framework\Helpers\DBStmtHelper');
+
try
{
$this->_handle = new Helpers\DBHelper($dsn, $config['user'], $config['pass'], $options);
- $this->_handle->setType($type);
+ $this->_handle->setType($this->_type);
}
catch (\PDOException $e)
{
throw new Exceptions\DBError($e->getMessage());
}
+ }
- // Cache the debug comment setting.
- $this->_debug_queries = in_array('queries', Config::get('debug.display_content') ?: []);
- $this->_debug_comment = !!config('debug.query_comment');
- $this->_debug_full_stack = !!Config::get('debug.query_full_stack');
+ /**
+ * Disconnect from the database.
+ *
+ * @return void
+ */
+ public function disconnect(): void
+ {
+ $this->_handle = null;
+ unset(self::$_instances[$this->_type]);
}
/**
* Get the PDO handle for direct manipulation.
*
- * @return Helpers\DBHelper
+ * @return ?Helpers\DBHelper
*/
- public function getHandle(): Helpers\DBHelper
+ public function getHandle(): ?Helpers\DBHelper
{
return $this->_handle;
}
@@ -360,7 +386,7 @@ class DB
}
elseif ($query->type === 'SELECT')
{
- $result = $this->_fetch($this->_last_stmt, $last_index, $result_type, $result_class);
+ $result = $this->fetch($this->_last_stmt, $last_index, $result_type, $result_class);
}
else
{
@@ -473,40 +499,16 @@ class DB
return $output;
}
- /**
- * Execute a literal query string.
- *
- * This method should not be public, as it starts with an underscore.
- * But since there are many legacy apps that rely on it, we will leave it public.
- *
- * @param string $query_string
- * @return Helpers\DBStmtHelper
- */
- public function _query($query_string)
- {
- if ($this->_debug_comment)
- {
- $query_string .= "\n" . sprintf('/* _query() %s */', \RX_CLIENT_IP);
- }
-
- $this->_last_stmt = null;
- $this->_last_stmt = $this->_handle->query($query_string);
- return $this->_last_stmt;
- }
-
/**
* Fetch results from a query.
*
- * This method should not be public, as it starts with an underscore.
- * But since there are many legacy apps that rely on it, we will leave it public.
- *
* @param \PDOStatement $stmt
* @param int $last_index
* @param string $result_type
* @param string $result_class
* @return mixed
*/
- public function _fetch($stmt, $last_index = 0, $result_type = 'auto', $result_class = '')
+ public function fetch($stmt, $last_index = 0, $result_type = 'auto', $result_class = '')
{
if (!($stmt instanceof \PDOStatement))
{
@@ -748,7 +750,7 @@ class DB
public function isTableExists(string $table_name): bool
{
$stmt = $this->_handle->query(sprintf("SHOW TABLES LIKE '%s'", $this->addQuotes($this->_prefix . $table_name)));
- $result = $this->_fetch($stmt);
+ $result = $this->fetch($stmt);
return $result ? true : false;
}
@@ -800,7 +802,7 @@ class DB
public function isColumnExists(string $table_name, string $column_name): bool
{
$stmt = $this->_handle->query(sprintf("SHOW FIELDS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name)));
- $result = $this->_fetch($stmt);
+ $result = $this->fetch($stmt);
return $result ? true : false;
}
@@ -946,7 +948,7 @@ class DB
{
// If column information is not found, return false.
$stmt = $this->_handle->query(sprintf("SHOW FIELDS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name)));
- $column_info = $this->_fetch($stmt);
+ $column_info = $this->fetch($stmt);
if (!$column_info)
{
return false;
@@ -986,7 +988,7 @@ class DB
public function isIndexExists(string $table_name, string $index_name): bool
{
$stmt = $this->_handle->query(sprintf("SHOW INDEX FROM `%s` WHERE Key_name = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($index_name)));
- $result = $this->_fetch($stmt);
+ $result = $this->fetch($stmt);
return $result ? true : false;
}
@@ -1104,7 +1106,7 @@ class DB
*/
public function getBestSupportedCharset(): string
{
- $output = $this->_fetch($this->_handle->query("SHOW CHARACTER SET LIKE 'utf8%'"), 1);
+ $output = $this->fetch($this->_handle->query("SHOW CHARACTER SET LIKE 'utf8%'"), 1);
$utf8mb4_support = ($output && count(array_filter($output, function($row) {
return $row->Charset === 'utf8mb4';
})));
@@ -1285,6 +1287,42 @@ class DB
* ==================== KEPT FOR COMPATIBILITY WITH XE ====================
*/
+ /**
+ * Execute a literal query string.
+ *
+ * Use query() instead, or call methods directly on the handle.
+ *
+ * @deprecated
+ * @param string $query_string
+ * @return Helpers\DBStmtHelper
+ */
+ public function _query($query_string)
+ {
+ if ($this->_debug_comment)
+ {
+ $query_string .= "\n" . sprintf('/* _query() %s */', \RX_CLIENT_IP);
+ }
+
+ $this->_last_stmt = null;
+ $this->_last_stmt = $this->_handle->query($query_string);
+ return $this->_last_stmt;
+ }
+
+ /**
+ * Fetch results from a query.
+ *
+ * Use query() and fetch() instead.
+ *
+ * @deprecated
+ * @param \PDOStatement $stmt
+ * @param int $last_index
+ * @return mixed
+ */
+ public function _fetch($stmt, $last_index = 0)
+ {
+ return $this->fetch($stmt, $last_index);
+ }
+
/**
* Old alias to getInstance().
*
@@ -1392,7 +1430,7 @@ class DB
*/
public function isConnected(): bool
{
- return true;
+ return $this->_handle ? true : false;
}
/**
@@ -1458,7 +1496,7 @@ class DB
{
return 0;
}
- public function _getConnection(): \PDO
+ public function _getConnection(): ?Helpers\DBHelper
{
return $this->getHandle();
}
diff --git a/common/framework/Password.php b/common/framework/Password.php
index 07a1a1f0c..0049758fe 100644
--- a/common/framework/Password.php
+++ b/common/framework/Password.php
@@ -138,6 +138,33 @@ class Password
return $algorithm;
}
+ /**
+ * Get the current default hashing algorithm, unless it will produce
+ * hashes that are longer than 60 characters.
+ *
+ * In that case, this method returns the next best supported algorithm
+ * that produces 60-character (or shorter) hashes. This helps maintain
+ * compatibility with old tables that still have varchar(60) columns.
+ *
+ * @return string
+ */
+ public static function getBackwardCompatibleAlgorithm()
+ {
+ $algorithm = self::getDefaultAlgorithm();
+ if (!in_array($algorithm, ['bcrypt', 'pbkdf2', 'sha1', 'md5']))
+ {
+ $candidates = self::getSupportedAlgorithms();
+ foreach ($candidates as $algorithm)
+ {
+ if (in_array($algorithm, ['bcrypt', 'pbkdf2', 'sha1', 'md5']))
+ {
+ return $algorithm;
+ }
+ }
+ }
+ return $algorithm;
+ }
+
/**
* Get the currently configured work factor for bcrypt and other adjustable algorithms.
*
diff --git a/common/framework/Session.php b/common/framework/Session.php
index ad6392b9e..be51d52e2 100644
--- a/common/framework/Session.php
+++ b/common/framework/Session.php
@@ -480,7 +480,7 @@ class Session
public static function refresh($set_session_cookie = false)
{
// Get session parameters.
- $domain = self::getDomain() ?: preg_replace('/:\\d+$/', '', strtolower($_SERVER['HTTP_HOST']));
+ $domain = self::getDomain() ?: preg_replace('/:\\d+$/', '', strtolower($_SERVER['HTTP_HOST'] ?? ''));
// Set the domain initialization timestamp.
if (!isset($_SESSION['RHYMIX']['keys'][$domain]['started']))
@@ -722,12 +722,12 @@ class Session
// Check member information to see if denied or limited.
$member_info = \MemberModel::getMemberInfo($member_srl);
- if ($member_info->denied === 'Y')
+ if (!empty($member_info->denied) && $member_info->denied === 'Y')
{
trigger_error('Session is invalid for member_srl=' . intval($_SESSION['RHYMIX']['login']) . ' (denied)', \E_USER_WARNING);
return false;
}
- if ($member_info->limit_date && substr($member_info->limit_date, 0, 8) >= date('Ymd'))
+ if (!empty($member_info->limit_date) && substr($member_info->limit_date, 0, 8) >= date('Ymd'))
{
trigger_error('Session is invalid for member_srl=' . intval($_SESSION['RHYMIX']['login']) . ' (limited)', \E_USER_WARNING);
return false;
@@ -844,7 +844,7 @@ class Session
*/
public static function getDomain()
{
- if (self::$_domain || (self::$_domain = ltrim(Config::get('session.domain'), '.')))
+ if (self::$_domain || (self::$_domain = ltrim(Config::get('session.domain') ?? '', '.')))
{
return self::$_domain;
}
@@ -1172,7 +1172,7 @@ class Session
{
// Get session parameters.
list($lifetime, $refresh_interval, $domain, $path, $secure, $samesite) = self::_getParams();
- $alt_domain = $domain ?: preg_replace('/:\\d+$/', '', strtolower($_SERVER['HTTP_HOST']));
+ $alt_domain = $domain ?: preg_replace('/:\\d+$/', '', strtolower($_SERVER['HTTP_HOST'] ?? ''));
$lifetime = $lifetime ? ($lifetime + time()) : 0;
$options = array(
'expires' => $lifetime,
@@ -1261,7 +1261,7 @@ class Session
* @param string $domain (optional)
* @return bool
*/
- protected static function _unsetCookie($name, $path = null, $domain = null)
+ protected static function _unsetCookie($name, $path = '', $domain = '')
{
$result = setcookie($name, 'deleted', time() - (86400 * 366), $path, $domain, false, false);
if ($result)
diff --git a/common/framework/UA.php b/common/framework/UA.php
index 9ee264dce..521b707db 100644
--- a/common/framework/UA.php
+++ b/common/framework/UA.php
@@ -468,12 +468,12 @@ class UA
if (in_array($color_scheme, ['light', 'dark']))
{
$_COOKIE['rx_color_scheme'] = $color_scheme;
- setcookie('rx_color_scheme', $color_scheme, time() + 86400 * 365, \RX_BASEURL, null, !!config('session.use_ssl_cookies'));
+ setcookie('rx_color_scheme', $color_scheme, time() + 86400 * 365, \RX_BASEURL, '', !!config('session.use_ssl_cookies'));
}
else
{
unset($_COOKIE['rx_color_scheme']);
- setcookie('rx_color_scheme', 'deleted', time() - 86400, \RX_BASEURL, null);
+ setcookie('rx_color_scheme', 'deleted', time() - 86400, \RX_BASEURL);
}
}
}
diff --git a/common/framework/parsers/ConfigParser.php b/common/framework/parsers/ConfigParser.php
index e19f5ec72..4a7921670 100644
--- a/common/framework/parsers/ConfigParser.php
+++ b/common/framework/parsers/ConfigParser.php
@@ -206,7 +206,6 @@ class ConfigParser
// Convert view configuration.
$config['view']['minify_scripts'] = $db_info->minify_scripts ?: 'common';
- $config['view']['use_gzip'] = (defined('__OB_GZHANDLER_ENABLE__') && constant('__OB_GZHANDLER_ENABLE__'));
// Convert admin IP whitelist.
if (isset($db_info->admin_ip_list) && is_array($db_info->admin_ip_list) && count($db_info->admin_ip_list))
diff --git a/common/legacy.php b/common/legacy.php
index c7056c52f..6a35e076c 100644
--- a/common/legacy.php
+++ b/common/legacy.php
@@ -110,8 +110,7 @@ function getMobile($module_name)
/**
* Create a wap instance of the module
*
- * @param string $module_name The module name to get a wap instance
- * @return mixed Module wap class instance
+ * @deprecated
*/
function getWAP($module_name)
{
@@ -1129,6 +1128,7 @@ function recurciveExposureCheck(&$menu)
if(!$value['isShow'])
{
unset($menu[$key]);
+ continue;
}
if(is_array($value['list']) && count($value['list']) > 0)
{
diff --git a/common/scripts/update_all_modules.php b/common/scripts/update_all_modules.php
index 2f9c88dd8..768fc8e77 100644
--- a/common/scripts/update_all_modules.php
+++ b/common/scripts/update_all_modules.php
@@ -2,7 +2,7 @@
/**
* This script updates all modules.
- *
+ *
* Running this script on the CLI is better than clicking 'update' in the
* admin dashboard because some module updates may take a long time.
*/
@@ -47,10 +47,13 @@ $oInstallAdminController = getAdminController('install');
foreach ($need_update as $module)
{
echo 'Updating ' . $module . '...' . PHP_EOL;
- $oModule = getModule($module, 'class');
- if ($oModule)
+ $oModule = ModuleModel::getModuleInstallClass($module);
+ if (is_object($oModule) && method_exists($oModule, 'checkUpdate') && method_exists($oModule, 'moduleUpdate'))
{
- $oModule->moduleUpdate();
+ if ($oModule->checkUpdate())
+ {
+ return $oModule->moduleUpdate();
+ }
}
}
diff --git a/common/tpl/common_layout.html b/common/tpl/common_layout.html
index 885be45a7..a52702fe0 100644
--- a/common/tpl/common_layout.html
+++ b/common/tpl/common_layout.html
@@ -10,7 +10,7 @@
{$lang->about_captcha_position}
diff --git a/modules/spamfilter/tpl/js/turnstile.js b/modules/spamfilter/tpl/js/turnstile.js new file mode 100644 index 000000000..39a7d4fbf --- /dev/null +++ b/modules/spamfilter/tpl/js/turnstile.js @@ -0,0 +1,60 @@ + +function turnstileCallback() { + var recaptcha_config = $("#turnstile-config"); + var recaptcha_instances = $(".turnstile-captcha"); + var recaptcha_instance_id = 1; + var recaptcha_targets = String(recaptcha_config.data("targets")).split(","); + + if (recaptcha_instances.length === 0) { + var autoinsert_candidates = $("form").filter(function() { + var actinput = $("input[name='act']", this); + if (actinput.length && actinput.val()) { + var act = String(actinput.val()); + if (act.match(/^procMemberInsert$/i) && recaptcha_targets.indexOf("signup") > -1) { + return true; + } + if (act.match(/^procMemberLogin$/i) && recaptcha_targets.indexOf("login") > -1) { + return true; + } + if (act.match(/^procMember(FindAccount|ResendAuthMail)$/i) && recaptcha_targets.indexOf("recovery") > -1) { + return true; + } + if (act.match(/^proc[A-Z][a-zA-Z0-9_]+InsertDocument$/i) && recaptcha_targets.indexOf("document") > -1) { + return true; + } + if (act.match(/^proc[A-Z][a-zA-Z0-9_]+InsertComment$/i) && recaptcha_targets.indexOf("comment") > -1) { + return true; + } + } + var procfilter = $(this).attr("onsubmit"); + if (procfilter && procfilter.match(/procFilter\b.+\binsert/i) && (recaptcha_targets.indexOf("document") > -1 || recaptcha_targets.indexOf("comment") > -1)) { + return true; + } + return false; + }); + autoinsert_candidates.each(function() { + var new_instance = $(''); + new_instance.attr("id", "turnstile-instance-" + recaptcha_instance_id++); + var autoinsert_point = $(this).find("button[type='submit'],input[type='submit']").parent(); + if (autoinsert_point.size()) { + new_instance.insertBefore(autoinsert_point); + } else { + new_instance.appendTo($(this)); + } + }); + var recaptcha_instances = $(".turnstile-captcha"); + } + + recaptcha_instances.each(function() { + var instance = $(this); + var theme = recaptcha_config.data("theme"); + if (theme === 'auto') { + theme = getColorScheme(); + } + grecaptcha.render(`#${instance.attr("id")}`, { + sitekey: recaptcha_config.data("sitekey"), + size: recaptcha_config.data("size"), + theme: theme + }); + }); +} diff --git a/tests/unit/classes/TemplateHandlerTest.php b/tests/unit/classes/TemplateHandlerTest.php index 2bff4a567..41e587fe0 100644 --- a/tests/unit/classes/TemplateHandlerTest.php +++ b/tests/unit/classes/TemplateHandlerTest.php @@ -211,7 +211,7 @@ class TemplateHandlerTest extends \Codeception\TestCase\Test // issue 512 - ignores