diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 034d190aa..bca493014 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -624,7 +624,8 @@ class FrontEndFileHandler extends Handler { $url .= '?t=' . filemtime($file->fileFullPath); } - $result[] = array('file' => $url); + $attrs = empty($file->jstype) ? '' : (' type="' . $file->jstype . '"'); + $result[] = array('file' => $url, 'attrs' => $attrs); } else { @@ -642,7 +643,7 @@ class FrontEndFileHandler extends Handler Rhymix\Framework\Storage::write(\RX_BASEDIR . $concat_filename, $concat_content); } $concat_filename .= '?t=' . filemtime(\RX_BASEDIR . $concat_filename); - $result[] = array('file' => \RX_BASEURL . $concat_filename); + $result[] = array('file' => \RX_BASEURL . $concat_filename, 'attrs' => ''); } } } diff --git a/classes/module/ModuleObject.class.php b/classes/module/ModuleObject.class.php index 0d5b4d3d8..e2ede1a5b 100644 --- a/classes/module/ModuleObject.class.php +++ b/classes/module/ModuleObject.class.php @@ -235,7 +235,7 @@ class ModuleObject extends BaseObject catch (Rhymix\Framework\Exception $e) { $this->stop($e->getMessage(), -2); - $this->add('rx_error_location', $e->getFile() . ':' . $e->getLine()); + $this->add('rx_error_location', $e->getUserFileAndLine()); } } @@ -857,8 +857,7 @@ class ModuleObject extends BaseObject catch (Rhymix\Framework\Exception $e) { $output = new BaseObject(-2, $e->getMessage()); - $location = $e->getFile() . ':' . $e->getLine(); - $output->add('rx_error_location', $location); + $output->add('rx_error_location', $e->getUserFileAndLine()); } // Trigger after specific action diff --git a/classes/validator/Validator.class.php b/classes/validator/Validator.class.php index b8f563648..f6ba5ba19 100644 --- a/classes/validator/Validator.class.php +++ b/classes/validator/Validator.class.php @@ -389,8 +389,8 @@ class Validator } /** - * Returns the last error infomation including a field name and an error message. - * @return array The last error infomation + * Returns the last error information including a field name and an error message. + * @return array The last error information */ function getLastError() { diff --git a/common/constants.php b/common/constants.php index fde0b485f..9a11bed4a 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.25'); +define('RX_VERSION', '2.1.26'); /** * RX_MICROTIME is the startup time of the current script, in microseconds since the Unix epoch. diff --git a/common/framework/DB.php b/common/framework/DB.php index f01339d36..b436ce9e8 100644 --- a/common/framework/DB.php +++ b/common/framework/DB.php @@ -1113,29 +1113,43 @@ class DB */ public function addPrefixes(string $query_string): string { + // Return early if no prefix is set. if (!$this->_prefix) { return $query_string; } + + // Generate a list of common table expressions (CTEs) to exclude from prefixing. + if (preg_match_all('/\bWITH(?:\s+RECURSIVE)?\s+`?(\w+)`?\s+AS\b/', $query_string, $matches)) + { + $exceptions = $matches[1]; + } else { - return preg_replace_callback('/((?:DELETE\s+)?FROM|JOIN|INTO|UPDATE)(?i)\s+((?:`?\w+\`?)(?:\s+AS\s+`?\w+`?)?(?:\s*,\s*(?:`?\w+\`?)(?:\s+AS\s+`?\w+`?)?)*)/', function($m) { - $type = strtoupper($m[1]); - $tables = array_map(function($str) use($type) { - return preg_replace_callback('/`?(\w+)`?(?:\s+AS\s+`?(\w+)`?)?/i', function($m) use($type) { - if ($type === 'FROM' || $type === 'JOIN') - { - return isset($m[2]) ? sprintf('`%s%s` AS `%s`', $this->_prefix, $m[1], $m[2]) : sprintf('`%s%s` AS `%s`', $this->_prefix, $m[1], $m[1]); - } - else - { - return isset($m[2]) ? sprintf('`%s%s` AS `%s`', $this->_prefix, $m[1], $m[2]) : sprintf('`%s%s`', $this->_prefix, $m[1]); - } - }, trim($str)); - }, explode(',', $m[2])); - return $m[1] . ' ' . implode(', ', $tables); - }, $query_string); + $exceptions = []; } + + // Add prefixes to all other table names in the query string. + return preg_replace_callback('/\b((?:DELETE\s+)?FROM|JOIN|INTO|(?_prefix, $m[1], $m[2]) : sprintf('`%s%s` AS `%s`', $this->_prefix, $m[1], $m[1]); + } + else + { + return isset($m[2]) ? sprintf('`%s%s` AS `%s`', $this->_prefix, $m[1], $m[2]) : sprintf('`%s%s`', $this->_prefix, $m[1]); + } + }, trim($str)); + }, explode(',', $m[2])); + return $m[1] . ' ' . implode(', ', $tables); + }, $query_string); } /** @@ -1254,7 +1268,7 @@ class DB if (isset($backtrace[$no])) { $result['called_method'] = ($backtrace[$no]['class'] ?? '') . ($backtrace[$no]['type'] ?? '') . ($backtrace[$no]['function'] ?? ''); - $result['backtrace'] = $this->_debug_full_stack ? array_slice($backtrace, $no) : []; + $result['backtrace'] = $this->_debug_full_stack ? array_slice($backtrace, $no - 1) : []; } else { diff --git a/common/framework/Exception.php b/common/framework/Exception.php index 0ff93aa68..6cd07d412 100644 --- a/common/framework/Exception.php +++ b/common/framework/Exception.php @@ -7,5 +7,27 @@ namespace Rhymix\Framework; */ class Exception extends \Exception { + /** + * Get the file and line, skipping Rhymix framework files. + * + * This can be more helpful than just using getFile() and getLine() + * when the exception is thrown from a Rhymix framework file + * but the actual error is caused by a module or theme. + * + * @return string + */ + public function getUserFileAndLine(): string + { + $regexp = '!^' . preg_quote(\RX_BASEDIR, '!') . '(?:classes|common)/!'; + $trace = $this->getTrace(); + foreach ($trace as $frame) + { + if (!preg_match($regexp, $frame['file'])) + { + return $frame['file'] . ':' . $frame['line']; + } + } + return $this->getFile() . ':' . $this->getLine(); + } } diff --git a/common/js/debug.js b/common/js/debug.js index 9214ec41f..86ab6f548 100644 --- a/common/js/debug.js +++ b/common/js/debug.js @@ -148,7 +148,7 @@ $(function() { if (data.queries[i].backtrace && data.queries[i].backtrace.length) { backtrace = $('