diff --git a/.github/workflows/setup-php.sh b/.github/workflows/setup-php.sh index e978e92a0..ef9b1656c 100644 --- a/.github/workflows/setup-php.sh +++ b/.github/workflows/setup-php.sh @@ -4,47 +4,27 @@ sudo add-apt-repository -y ppa:ondrej/php # Install all required packages -if [[ "$1" == "8.5" ]]; then - sudo apt -y install \ - php$1-bcmath \ - php$1-cli \ - php$1-common \ - php$1-curl \ - php$1-gd \ - php$1-intl \ - php$1-mbstring \ - php$1-mysql \ - php$1-readline \ - php$1-sqlite3 \ - php$1-xml \ - php$1-zip -else - sudo apt -y install \ - php$1-apcu \ - php$1-bcmath \ - php$1-cli \ - php$1-common \ - php$1-curl \ - php$1-gd \ - php$1-intl \ - php$1-mbstring \ - php$1-mysql \ - php$1-opcache \ - php$1-readline \ - php$1-sqlite3 \ - php$1-xml \ - php$1-zip -fi +sudo apt -y install \ + php$1-apcu \ + php$1-bcmath \ + php$1-cli \ + php$1-common \ + php$1-curl \ + php$1-gd \ + php$1-intl \ + php$1-mbstring \ + php$1-mysql \ + php$1-readline \ + php$1-sqlite3 \ + php$1-xml \ + php$1-zip # Adjust php.ini settings -if [[ "$1" == "8.5" ]]; then - sudo bash -c "echo 'register_argc_argv = On' >> /etc/php/$1/cli/php.ini" -else - sudo bash -c "echo 'opcache.enable = 1' >> /etc/php/$1/cli/conf.d/10-opcache.ini" - sudo bash -c "echo 'opcache.enable_cli = 1' >> /etc/php/$1/cli/conf.d/10-opcache.ini" - sudo bash -c "echo 'opcache.jit = tracing' >> /etc/php/$1/cli/conf.d/10-opcache.ini" - sudo bash -c "echo 'opcache.jit_buffer_size = 128M' >> /etc/php/$1/cli/conf.d/10-opcache.ini" -fi +sudo bash -c "echo 'register_argc_argv = On' >> /etc/php/$1/cli/php.ini" +sudo bash -c "echo 'opcache.enable = 1' >> /etc/php/$1/cli/conf.d/10-opcache.ini" +sudo bash -c "echo 'opcache.enable_cli = 1' >> /etc/php/$1/cli/conf.d/10-opcache.ini" +sudo bash -c "echo 'opcache.jit = tracing' >> /etc/php/$1/cli/conf.d/10-opcache.ini" +sudo bash -c "echo 'opcache.jit_buffer_size = 128M' >> /etc/php/$1/cli/conf.d/10-opcache.ini" # Enable APCu if [ -f "/etc/php/$1/cli/conf.d/20-apcu.ini" ]; then diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php index 0c9f71190..a24ed24e3 100644 --- a/classes/context/Context.class.php +++ b/classes/context/Context.class.php @@ -139,7 +139,7 @@ class Context */ private static $_check_patterns = array( '@<(?:\?|%)@' => 'DENY ALL', - '@ 'DENY ALL', + '@ 'DENY ALL', '@ 'ALLOW ADMIN ONLY', ); diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index 609fe787a..c2f570ad5 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -728,7 +728,7 @@ class HTMLDisplayHandler * import basic .js files. * @return void */ - function _loadDesktopJSCSS() + public function _loadDesktopJSCSS() { $this->_loadCommonJSCSS(); } diff --git a/classes/file/FileHandler.class.php b/classes/file/FileHandler.class.php index f80ed9dfd..58c6898e2 100644 --- a/classes/file/FileHandler.class.php +++ b/classes/file/FileHandler.class.php @@ -461,18 +461,28 @@ class FileHandler * Check available memory to load image file * * @param array $imageInfo Image info retrieved by getimagesize function + * @param array $resizeInfo Resize width and height * @return bool TRUE: it's ok, FALSE: otherwise */ - public static function checkMemoryLoadImage(&$imageInfo) + public static function checkMemoryLoadImage($imageInfo, $resizeInfo = []) { - $K64 = 65536; - $TWEAKFACTOR = 2.0; - $channels = $imageInfo['channels'] ?? 6; - if(!$channels) + $bits = $imageInfo['bits'] ?? 8; + $channels = ($imageInfo['channels'] ?? 6) ?: 6; // for png + if (!$resizeInfo) { - $channels = 6; //for png + $resizeInfo = $imageInfo; } - $memoryNeeded = round(($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $channels / 8 + $K64 ) * $TWEAKFACTOR); + $src_memory = round($imageInfo[0] * $imageInfo[1] * $bits * $channels / 8) + 65536; + $dst_memory = round($resizeInfo[0] * $resizeInfo[1] * 8 * $channels / 8) + 65536; + + $gd_info = gd_info(); + $gd_version = $gd_info['GD Version'] ?? ''; + $gd_type = str_contains($gd_version, 'bundled') ? 'bundled' : 'external'; + if ($gd_type === 'external') + { + $dst_memory = 0; + } + $memoryLimit = ini_get('memory_limit'); if($memoryLimit <= 0) { @@ -484,7 +494,7 @@ class FileHandler return true; } $availableMemory = $memoryLimit - memory_get_usage(); - if($availableMemory < $memoryNeeded) + if($availableMemory < ($src_memory + $dst_memory)) { return false; } @@ -559,11 +569,6 @@ class FileHandler // retrieve source image's information $imageInfo = getimagesize($source_file); - if(!self::checkMemoryLoadImage($imageInfo)) - { - return false; - } - list($width, $height, $type) = $imageInfo; if($width < 1 || $height < 1) { @@ -596,6 +601,12 @@ class FileHandler $resize_height = round($resize_width / ($width / $height)); } + // Check memory usage + if(!self::checkMemoryLoadImage($imageInfo, [$resize_width, $resize_height])) + { + return false; + } + // create temporary image having original type if ($type === 'gif' && function_exists('imagecreatefromgif')) { @@ -646,6 +657,14 @@ class FileHandler $thumb = imagecreatetruecolor($resize_width, $resize_height); if (!$thumb) { + if (version_compare(PHP_VERSION, '8.0', '<')) + { + imagedestroy($source); + } + else + { + unset($source); + } return false; } @@ -706,6 +725,11 @@ class FileHandler imagecopyresampled($thumb, $source, $dst_x, $dst_y, 0, 0, $dst_width, $dst_height, $width, $height); } + if (version_compare(PHP_VERSION, '8.0', '>=')) + { + unset($source); + } + // create directory self::makeDir(dirname($target_file)); @@ -736,12 +760,27 @@ class FileHandler } else { + if (version_compare(PHP_VERSION, '8.0', '<')) + { + imagedestroy($thumb); + } + else + { + unset($thumb); + } return false; } - imagedestroy($thumb); - imagedestroy($source); @chmod($target_file, 0666 & ~Rhymix\Framework\Storage::getUmask()); + + if (version_compare(PHP_VERSION, '8.0', '<')) + { + imagedestroy($thumb); + } + else + { + unset($thumb); + } return $output; } diff --git a/classes/module/ModuleHandler.class.php b/classes/module/ModuleHandler.class.php index e0faee25e..1d8acd7fe 100644 --- a/classes/module/ModuleHandler.class.php +++ b/classes/module/ModuleHandler.class.php @@ -393,7 +393,7 @@ class ModuleHandler extends Handler // check CSRF for non-GET (POST, PUT, etc.) actions if(!in_array(Context::getRequestMethod(), self::$_nocsrf_methods) && Context::isInstalled()) { - if(isset($xml_info->action->{$this->act}) && $xml_info->action->{$this->act}->check_csrf !== 'false' && !checkCSRF()) + if(isset($xml_info->action->{$this->act}) && $xml_info->action->{$this->act}->check_csrf !== 'false' && !Rhymix\Framework\Security::checkCSRF()) { return self::_createErrorMessage(-1, 'msg_security_violation', 403, 'ERR_CSRF_CHECK_FAILED'); } @@ -555,7 +555,7 @@ class ModuleHandler extends Handler // check CSRF for non-GET (POST, PUT, etc.) actions if(!in_array(Context::getRequestMethod(), self::$_nocsrf_methods) && Context::isInstalled()) { - if($xml_info->action->{$this->act} && $xml_info->action->{$this->act}->check_csrf !== 'false' && !checkCSRF()) + if($xml_info->action->{$this->act} && $xml_info->action->{$this->act}->check_csrf !== 'false' && !Rhymix\Framework\Security::checkCSRF()) { return self::_createErrorMessage(-1, 'msg_security_violation', 403, 'ERR_CSRF_CHECK_FAILED'); } diff --git a/common/constants.php b/common/constants.php index 341009623..0ecf769c8 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.27'); +define('RX_VERSION', '2.1.29'); /** * 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 0f30246e8..31fb26104 100644 --- a/common/framework/DB.php +++ b/common/framework/DB.php @@ -962,7 +962,7 @@ class DB public function getColumnInfo(string $table_name, string $column_name): ?object { // If column information is not found, return null. - $stmt = $this->_handle->query(sprintf("SHOW FIELDS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); + $stmt = $this->_handle->query(sprintf("SHOW FULL COLUMNS FROM `%s` WHERE Field = '%s'", $this->addQuotes($this->_prefix . $table_name), $this->addQuotes($column_name))); $column_info = $this->fetch($stmt); if (!$column_info) { @@ -982,6 +982,16 @@ class DB } $xetype = Parsers\DBTableParser::getXEType($dbtype, $size ?: ''); + // Detect the character set. + if (preg_match('/^([a-zA-Z0-9]+)/', $column_info->{'Collation'} ?? '', $matches)) + { + $charset = $matches[1] === 'utf8mb3' ? 'utf8' : $matches[1]; + } + else + { + $charset = null; + } + // Return the result as an object. return (object)array( 'name' => $column_name, @@ -990,6 +1000,8 @@ class DB 'size' => $size, 'default_value' => $column_info->{'Default'}, 'notnull' => strncmp($column_info->{'Null'}, 'NO', 2) == 0 ? true : false, + 'charset' => $charset, + 'collation' => $column_info->{'Collation'} ?: null, ); } @@ -1100,6 +1112,7 @@ class DB return (object)array( 'name' => $column->Key_name, 'table' => $column->Table, + 'type' => $column->Index_type, 'is_unique' => $is_unique, 'columns' => $columns, ); diff --git a/common/framework/DateTime.php b/common/framework/DateTime.php index 9f24cf0ae..b74bffec9 100644 --- a/common/framework/DateTime.php +++ b/common/framework/DateTime.php @@ -28,11 +28,11 @@ class DateTime { if ($format === self::FORMAT_RELATIVE) { - return self::getRelativeTimestamp($timestamp ?: time()); + return self::getRelativeTimestamp($timestamp ?? time()); } - $offset = Config::get('locale.internal_timezone') ?: date('Z', $timestamp); - return gmdate($format, ($timestamp ?: time()) + $offset); + $offset = Config::get('locale.internal_timezone') ?: date('Z', $timestamp ?? time()); + return gmdate($format, ($timestamp ?? time()) + $offset); } /** @@ -46,7 +46,7 @@ class DateTime { if ($format === self::FORMAT_RELATIVE) { - return self::getRelativeTimestamp($timestamp ?: time()); + return self::getRelativeTimestamp($timestamp ?? time()); } $timezone = self::getTimezoneForCurrentUser(); @@ -55,7 +55,7 @@ class DateTime self::$_timezones[$timezone] = new \DateTimeZone($timezone); } $datetime = new \DateTime(); - $datetime->setTimestamp($timestamp ?: time()); + $datetime->setTimestamp($timestamp ?? time()); $datetime->setTimezone(self::$_timezones[$timezone]); return $datetime->format($format); } @@ -123,7 +123,7 @@ class DateTime self::$_timezones[$timezone] = new \DateTimeZone($timezone); } $datetime = new \DateTime(); - $datetime->setTimestamp($timestamp ?: time()); + $datetime->setTimestamp($timestamp ?? time()); $datetime->setTimezone(self::$_timezones[$timezone]); return $datetime->getOffset(); } @@ -137,7 +137,7 @@ class DateTime */ public static function getTimezoneOffsetFromInternal(string $timezone, ?int $timestamp = null): int { - return self::getTimezoneOffset($timezone, $timestamp ?: time()) - Config::get('locale.internal_timezone'); + return self::getTimezoneOffset($timezone, $timestamp ?? time()) - Config::get('locale.internal_timezone'); } /** @@ -192,7 +192,7 @@ class DateTime */ public static function getRelativeTimestamp(?int $timestamp = null): string { - $diff = \RX_TIME - intval($timestamp ?: time()); + $diff = \RX_TIME - intval($timestamp ?? time()); $langs = lang('common.time_gap'); if ($diff < 3) diff --git a/common/framework/Template.php b/common/framework/Template.php index d7b164f2a..387c3ba6a 100644 --- a/common/framework/Template.php +++ b/common/framework/Template.php @@ -865,7 +865,14 @@ class Template } } - return sprintf(' %s="%s"', $attribute, escape(implode($delimiters[$attribute], $values), false)); + if (count($values)) + { + return sprintf(' %s="%s"', $attribute, escape(implode($delimiters[$attribute], $values), false)); + } + else + { + return ''; + } } /** diff --git a/common/framework/parsers/template/TemplateParser_v1.php b/common/framework/parsers/template/TemplateParser_v1.php index a2ab34197..eca23801b 100644 --- a/common/framework/parsers/template/TemplateParser_v1.php +++ b/common/framework/parsers/template/TemplateParser_v1.php @@ -156,7 +156,7 @@ class TemplateParser_v1 // if not exists default hidden tag, generate hidden tag if ($autoform) { - preg_match_all('/]* name="(act|mid)"/is', $matches[2], $m2); + preg_match_all('/<(?:input|select)[^>]* name="(act|mid)"/is', $matches[2], $m2); $missing_inputs = array_diff(['act', 'mid'], $m2[1]); if(is_array($missing_inputs)) { diff --git a/common/framework/parsers/template/TemplateParser_v2.php b/common/framework/parsers/template/TemplateParser_v2.php index 7251ab1e0..f85f30ccc 100644 --- a/common/framework/parsers/template/TemplateParser_v2.php +++ b/common/framework/parsers/template/TemplateParser_v2.php @@ -1084,6 +1084,37 @@ class TemplateParser_v2 */ protected function _convertVariableScope(string $content): string { + // Pre-escape function declarations and closures so that variables inside them are not converted. + $used_vars = []; + $function_regexp = '#\b(function(?:\s+[a-zA-Z_][a-zA-Z0-9_]*)?)' . + '(\s*)(' . self::_getRegexpForParentheses(3) . ')' . + '(\s*)(use\s*\([^()]+\))?' . + '(\s*:\s*\w+)?' . + '(\s*)(' . self::_getRegexpForCurlyBraces(8) . ')#'; + $content = preg_replace_callback($function_regexp, function($match) use (&$used_vars) { + $fn = $match[1] . $match[2] . self::_escapeVars($match[3]) . + $match[4] . self::_escapeVars($match[5]) . + $match[6] . $match[7] . self::_escapeVars($match[8]); + if (str_starts_with($match[5], 'use')) + { + preg_match_all('#\$([a-zA-Z_][a-zA-Z0-9_]*)#', $match[5], $uses); + foreach ($uses[1] as $var) + { + $used_vars[$var] = true; + } + } + return $fn; + }, $content); + if (count($used_vars)) + { + $prefix = ' '; + foreach ($used_vars as $var => $unused) + { + $prefix .= self::_escapeVars('$' . $var) . ' = &$__Context->' . $var . '; '; + } + $content = $prefix . $content; + } + // Replace variables that need to be enclosed in curly braces, using temporary entities to prevent double-replacement. $content = preg_replace_callback('#(?\$([a-zA-Z_][a-zA-Z0-9_]*)#', function($match) { return '->' . self::_escapeCurly('{') . '$__Context->' . $match[1] . self::_escapeCurly('}'); @@ -1144,6 +1175,17 @@ class TemplateParser_v2 return '\([^)(]*+(?:(?' . $position_in_regexp . ')[^)(]*)*+\)'; } + /** + * Same as above, but for curly braces. + * + * @param int $position_in_regexp + * @return string + */ + protected static function _getRegexpForCurlyBraces(int $position_in_regexp): string + { + return '\{[^}{]*+(?:(?' . $position_in_regexp . ')[^}{]*)*+\}'; + } + /** * Escape curly braces so that they will not be interpreted as echo statements. * diff --git a/modules/admin/controllers/maintenance/Cleanup.php b/modules/admin/controllers/maintenance/Cleanup.php index 350b6f232..1ea397361 100644 --- a/modules/admin/controllers/maintenance/Cleanup.php +++ b/modules/admin/controllers/maintenance/Cleanup.php @@ -284,6 +284,7 @@ class Cleanup extends Base 'addons/member_communication/' => 'deleted:xe', 'addons/mobile/' => 'deleted:xe', 'addons/openid_delegation_id/' => 'deleted:xe', + 'admin/' => 'deleted:xe', 'classes/cache/CacheApc.class.php' => 'deleted:xe', 'classes/cache/CacheFile.class.php' => 'deleted:xe', 'classes/cache/CacheMemcache.class.php' => 'deleted:xe', diff --git a/modules/admin/models/Utility.php b/modules/admin/models/Utility.php index d0c8edd56..b14202981 100644 --- a/modules/admin/models/Utility.php +++ b/modules/admin/models/Utility.php @@ -13,7 +13,10 @@ class Utility public static function cleanHeaderAndFooterScripts(string $content) { $content = utf8_clean($content); - $content = preg_replace('!]*>!', '', $content); + $content = preg_replace('!]*>!i', '', $content); + $content = preg_replace_callback('!]*?)language=[\'"]javascript[\'"]!i', function ($matches) { + return trim(' diff --git a/modules/editor/editor.model.php b/modules/editor/editor.model.php index bcc20d225..f6a430aef 100644 --- a/modules/editor/editor.model.php +++ b/modules/editor/editor.model.php @@ -469,6 +469,11 @@ class EditorModel extends Editor if ($type === 'document') { $option->upload_target_type = 'doc'; + // For dispWidgetAdminAddContent + if ($primary_key_name === 'module_srl') + { + $option->upload_target_type = 'mod'; + } } elseif ($type === 'comment') { diff --git a/modules/file/file.controller.php b/modules/file/file.controller.php index cf1842a9e..ba8876dbf 100644 --- a/modules/file/file.controller.php +++ b/modules/file/file.controller.php @@ -307,7 +307,7 @@ class FileController extends File $file_srl = Context::get('file_srl'); $sid = Context::get('sid'); - $filename_arg = Context::get('filename'); + $filename_arg = htmlspecialchars_decode(Context::get('filename') ?? ''); // Get file information from the DB $file_obj = FileModel::getFile($file_srl); @@ -318,7 +318,7 @@ class FileController extends File { throw new Rhymix\Framework\Exceptions\TargetNotFound('msg_file_not_found'); } - if ($filename_arg !== null && $filename_arg !== $filename) + if ($filename_arg !== '' && $filename_arg !== $filename) { throw new Rhymix\Framework\Exceptions\TargetNotFound('msg_file_not_found'); } @@ -434,7 +434,7 @@ class FileController extends File // Get requsted file info $file_srl = Context::get('file_srl'); $file_key = Context::get('file_key'); - $filename_arg = Context::get('filename'); + $filename_arg = htmlspecialchars_decode(Context::get('filename') ?? ''); $columnList = array('source_filename', 'uploaded_filename', 'file_size'); $file_obj = FileModel::getFile($file_srl, $columnList); @@ -460,7 +460,7 @@ class FileController extends File } // Check filename if given - if ($filename_arg !== null && $filename_arg !== $filename) + if ($filename_arg !== '' && $filename_arg !== $filename) { throw new Rhymix\Framework\Exceptions\TargetNotFound('msg_file_not_found'); } diff --git a/modules/file/file.model.php b/modules/file/file.model.php index 4a1c40e7b..ba69f6c6e 100644 --- a/modules/file/file.model.php +++ b/modules/file/file.model.php @@ -476,8 +476,8 @@ class FileModel extends File $nullList = array(); foreach ($output->data as $file) { - $file->source_filename = escape($file->source_filename, false); $file->download_url = self::getDownloadUrl($file->file_srl, $file->sid, 0, $file->source_filename); + $file->source_filename = escape($file->source_filename, false); $fileList[] = $file; if ($file->upload_target_type === null) { diff --git a/modules/layout/layout.view.php b/modules/layout/layout.view.php index ee6e87b7c..b83713a16 100644 --- a/modules/layout/layout.view.php +++ b/modules/layout/layout.view.php @@ -320,7 +320,7 @@ class LayoutView extends Layout */ function dispLayoutPreview() { - if(!checkCSRF()) + if(!Rhymix\Framework\Security::checkCSRF()) { throw new Rhymix\Framework\Exceptions\InvalidRequest; } diff --git a/modules/member/lang/en.php b/modules/member/lang/en.php index 305947b29..874008eb8 100644 --- a/modules/member/lang/en.php +++ b/modules/member/lang/en.php @@ -152,6 +152,7 @@ $lang->cmd_modify_nickname_allow = 'Allow Nickname Change'; $lang->cmd_modify_nickname_log = 'Nickname Change Log'; $lang->cmd_nickname_symbols = 'Allow Symbols in Nickname'; $lang->cmd_nickname_symbols_list = 'Only Allow:'; +$lang->cmd_nickname_allow_spaces = 'Allow Spaces'; $lang->cmd_member_profile_view = 'Show member profile picture'; $lang->cmd_allow_duplicate_nickname = 'Allow Duplicate Nicknames'; $lang->about_allow_duplicate_nickname = 'Allow more than one member to use the same nickname.'; diff --git a/modules/member/lang/ko.php b/modules/member/lang/ko.php index d49ba42ea..fd29a4d81 100644 --- a/modules/member/lang/ko.php +++ b/modules/member/lang/ko.php @@ -154,6 +154,7 @@ $lang->cmd_modify_nickname_allow = '닉네임 변경 허용'; $lang->cmd_modify_nickname_log = '닉네임 변경 기록'; $lang->cmd_nickname_symbols = '닉네임에 특수문자 허용'; $lang->cmd_nickname_symbols_list = '다음의 문자만 허용:'; +$lang->cmd_nickname_allow_spaces = '띄어쓰기 허용'; $lang->cmd_member_profile_view = '회원 프로필사진 보이기'; $lang->cmd_allow_duplicate_nickname = '닉네임 중복 허용'; $lang->about_allow_duplicate_nickname = '여러 회원이 동일한 닉네임을 사용하는 것을 허용합니다. 주의: 잘못 사용할 경우 혼란이 발생할 수 있습니다.'; diff --git a/modules/member/member.admin.controller.php b/modules/member/member.admin.controller.php index c9aebbfae..1bc72fe41 100644 --- a/modules/member/member.admin.controller.php +++ b/modules/member/member.admin.controller.php @@ -24,7 +24,7 @@ class MemberAdminController extends Member // if(Context::getRequestMethod() == "GET") return new Object(-1, "msg_invalid_request"); // Extract the necessary information in advance $logged_info = Context::get('logged_info'); - if($logged_info->is_admin != 'Y' || !checkCSRF()) + if($logged_info->is_admin != 'Y' || !Rhymix\Framework\Security::checkCSRF()) { throw new Rhymix\Framework\Exceptions\InvalidRequest; } @@ -127,18 +127,26 @@ class MemberAdminController extends Member } // remove whitespace - foreach(['user_id', 'nick_name', 'email_address'] as $val) + foreach (['user_id', 'email_address'] as $val) { - if(isset($args->{$val})) + if (isset($args->{$val})) { $args->{$val} = preg_replace('/[\pZ\pC]+/u', '', utf8_clean(html_entity_decode($args->{$val}))); } } - foreach(['user_name'] as $val) + if (isset($args->user_name)) { - if(isset($args->{$val})) + $args->user_name = utf8_normalize_spaces(utf8_clean(html_entity_decode($args->user_name))); + } + if (isset($args->nick_name)) + { + if (isset($config->nickname_spaces) && $config->nickname_spaces === 'Y') { - $args->{$val} = utf8_normalize_spaces(utf8_clean(html_entity_decode($args->{$val}))); + $args->nick_name = utf8_normalize_spaces(utf8_clean(html_entity_decode($args->nick_name))); + } + else + { + $args->nick_name = preg_replace('/[\pZ\pC]+/u', '', utf8_clean(html_entity_decode($args->nick_name))); } } @@ -256,6 +264,7 @@ class MemberAdminController extends Member 'update_nickname_log', 'nickname_symbols', 'nickname_symbols_allowed_list', + 'nickname_spaces', 'allow_duplicate_nickname', 'member_profile_view' ); @@ -347,6 +356,7 @@ class MemberAdminController extends Member $args->nickname_symbols = 'Y'; } $args->nickname_symbols_allowed_list = utf8_trim($args->nickname_symbols_allowed_list); + $args->nickname_spaces = (isset($args->nickname_spaces) && $args->nickname_spaces === 'Y') ? 'Y' : 'N'; $oModuleController = getController('module'); $output = $oModuleController->updateModuleConfig('member', $args); diff --git a/modules/member/member.controller.php b/modules/member/member.controller.php index 8796115ad..a47d72a55 100644 --- a/modules/member/member.controller.php +++ b/modules/member/member.controller.php @@ -163,7 +163,7 @@ class MemberController extends Member */ function procMemberScrapDocument() { - $document_srl = (int) (Context::get('document_srl') ?: Context::get('target_srl')); + $document_srl = intval(Context::get('document_srl') ?: Context::get('target_srl')); if(!$document_srl) { throw new Rhymix\Framework\Exceptions\InvalidRequest; @@ -272,7 +272,7 @@ class MemberController extends Member if(!Context::get('is_logged')) throw new Rhymix\Framework\Exceptions\MustLogin; $logged_info = Context::get('logged_info'); - $document_srl = (int)Context::get('document_srl'); + $document_srl = intval(Context::get('document_srl') ?: Context::get('target_srl')); if(!$document_srl) { throw new Rhymix\Framework\Exceptions\InvalidRequest; @@ -312,8 +312,8 @@ class MemberController extends Member if(!Context::get('is_logged')) throw new Rhymix\Framework\Exceptions\MustLogin; $logged_info = Context::get('logged_info'); - $document_srl = (int)Context::get('document_srl'); - $folder_srl = (int)Context::get('folder_srl'); + $document_srl = intval(Context::get('document_srl') ?: Context::get('target_srl')); + $folder_srl = intval(Context::get('folder_srl')); if(!$document_srl || !$folder_srl) { throw new Rhymix\Framework\Exceptions\InvalidRequest; @@ -524,7 +524,7 @@ class MemberController extends Member if(!Context::get('is_logged')) throw new Rhymix\Framework\Exceptions\MustLogin; $logged_info = Context::get('logged_info'); - $document_srl = (int)Context::get('document_srl'); + $document_srl = intval(Context::get('document_srl') ?: Context::get('target_srl')); if(!$document_srl) throw new Rhymix\Framework\Exceptions\InvalidRequest; $oDocument = DocumentModel::getDocument($document_srl); @@ -796,25 +796,34 @@ class MemberController extends Member } // remove whitespace - foreach(['user_id', 'nick_name', 'email_address'] as $val) + foreach (['user_id', 'email_address'] as $val) { - if(isset($args->{$val})) + if (isset($args->{$val})) { $args->{$val} = preg_replace('/[\pZ\pC]+/u', '', utf8_clean(html_entity_decode($args->{$val}))); } } - foreach(['user_name'] as $val) + if (isset($args->user_name)) { - if(isset($args->{$val})) + $args->user_name = utf8_normalize_spaces(utf8_clean(html_entity_decode($args->user_name))); + } + if (isset($args->nick_name)) + { + if (isset($config->nickname_spaces) && $config->nickname_spaces === 'Y') { - $args->{$val} = utf8_normalize_spaces(utf8_clean(html_entity_decode($args->{$val}))); + $args->nick_name = utf8_normalize_spaces(utf8_clean(html_entity_decode($args->nick_name))); } + else + { + $args->nick_name = preg_replace('/[\pZ\pC]+/u', '', utf8_clean(html_entity_decode($args->nick_name))); + } + } // Check symbols in nickname if($config->nickname_symbols === 'N') { - if(preg_match('/[^\pL\d]/u', $args->nick_name, $matches)) + if(preg_match('/[^\pL\d\s]/u', $args->nick_name, $matches)) { throw new Rhymix\Framework\Exception(sprintf(lang('msg_invalid_symbol_in_nickname'), escape($matches[0]))); } @@ -822,7 +831,7 @@ class MemberController extends Member elseif($config->nickname_symbols === 'LIST') { $list = preg_quote($config->nickname_symbols_allowed_list, '/'); - if(preg_match('/[^\pL\d' . $list . ']/u', $args->nick_name, $matches)) + if(preg_match('/[^\pL\d\s' . $list . ']/u', $args->nick_name, $matches)) { throw new Rhymix\Framework\Exception(sprintf(lang('msg_invalid_symbol_in_nickname'), escape($matches[0]))); } @@ -1098,19 +1107,28 @@ class MemberController extends Member $args->extra_vars = serialize($extra_vars); // remove whitespace - foreach(['user_id', 'nick_name', 'email_address'] as $val) + foreach (['user_id', 'email_address'] as $val) { - if(isset($args->{$val})) + if (isset($args->{$val})) { $args->{$val} = preg_replace('/[\pZ\pC]+/u', '', utf8_clean(html_entity_decode($args->{$val}))); } } - foreach(['user_name'] as $val) + if (isset($args->user_name)) { - if(isset($args->{$val})) + $args->user_name = utf8_normalize_spaces(utf8_clean(html_entity_decode($args->user_name))); + } + if (isset($args->nick_name)) + { + if (isset($config->nickname_spaces) && $config->nickname_spaces === 'Y') { - $args->{$val} = utf8_normalize_spaces(utf8_clean(html_entity_decode($args->{$val}))); + $args->nick_name = utf8_normalize_spaces(utf8_clean(html_entity_decode($args->nick_name))); } + else + { + $args->nick_name = preg_replace('/[\pZ\pC]+/u', '', utf8_clean(html_entity_decode($args->nick_name))); + } + } // Check if nickname change is allowed @@ -1134,7 +1152,7 @@ class MemberController extends Member // Check symbols in nickname if($config->nickname_symbols === 'N') { - if(preg_match('/[^\pL\d]/u', $args->nick_name, $matches)) + if(preg_match('/[^\pL\d\s]/u', $args->nick_name, $matches)) { throw new Rhymix\Framework\Exception(sprintf(lang('msg_invalid_symbol_in_nickname'), escape($matches[0]))); } @@ -1142,7 +1160,7 @@ class MemberController extends Member elseif($config->nickname_symbols === 'LIST') { $list = preg_quote($config->nickname_symbols_allowed_list, '/'); - if(preg_match('/[^\pL\d' . $list . ']/u', $args->nick_name, $matches)) + if(preg_match('/[^\pL\d\s' . $list . ']/u', $args->nick_name, $matches)) { throw new Rhymix\Framework\Exception(sprintf(lang('msg_invalid_symbol_in_nickname'), escape($matches[0]))); } diff --git a/modules/member/tpl/default_config.html b/modules/member/tpl/default_config.html index ceb2b259e..0155e3140 100644 --- a/modules/member/tpl/default_config.html +++ b/modules/member/tpl/default_config.html @@ -83,6 +83,7 @@

{$lang->about_nickname_symbols}

+ + +suffix; + $__Context->employees = [ + ['name' => 'Alice', 'age' => 30], + ['name' => 'Bob', 'age' => 25], + ['name' => 'Charlie', 'age' => 35], + ]; + $__Context->suffix = '님'; + $__Context->names = array_map(function($e, $key = 'name') use ($suffix) { + return $e[$key] . $suffix; + }, $__Context->employees); + function convert_names(array $names = array()): array + { + return array_map(function($name) { + return ucfirst($name); + }, $names); + } +?> +
+ config->context === 'HTML' ? htmlspecialchars(implode(', ', convert_names($__Context->names)), \ENT_QUOTES, 'UTF-8', false) : $this->_v2_escape(implode(', ', convert_names($__Context->names))); ?> welcome!