Compare commits

...

36 commits

Author SHA1 Message Date
Lastorder-DC
85f9c4d031 Merge branch 'master' of github.com:Lastorder-DC/rhymix 2026-04-01 12:04:44 +09:00
Lastorder-DC
add448db7b MemberController::getInstance 2026-04-01 12:04:30 +09:00
Lastorder
dd8fc890f4
Merge branch 'rhymix:master' into master 2026-04-01 12:04:22 +09:00
Lastorder-DC
bdeffb61a5 추천버그 수정 2026-04-01 12:04:06 +09:00
Kijin Sung
530464289a Version 2.1.32 2026-04-01 10:54:41 +09:00
Kijin Sung
90f1238b23 Use DocumentModel::getBlankDocument() to obtain a dummy document with module_srl in BoardView 2026-03-31 22:28:39 +09:00
Kijin Sung
cbb363671a Add DocumentModel::getBlankDocument() and fix some incorrect return type comments 2026-03-31 22:27:20 +09:00
Kijin Sung
99d6182f89 Update actions/checkout to v5 because github is deprecating node.js 20 2026-03-31 21:08:40 +09:00
Kijin Sung
6be98ff58c Simplify RVE-2026-6 patch using R\F\Security::sanitize() 2026-03-31 21:04:39 +09:00
Kijin Sung
b1f84365a5 Add 'command' type to R\F\Security::sanitize() 2026-03-31 21:02:33 +09:00
Kijin Sung
ae44685306 Merge branch 'security/rve-2026-6' 2026-03-31 20:51:14 +09:00
Kijin Sung
b7489e6e7b Merge branch 'security/rve-2026-5' 2026-03-31 20:51:11 +09:00
Kijin Sung
f3a08ba8f3 Merge branch 'security/rve-2026-4' 2026-03-31 20:51:08 +09:00
Kijin Sung
ea4f116b4c Merge branch 'security/rve-2026-3' 2026-03-31 20:51:05 +09:00
Kijin Sung
1d8718a256 Remove unused methods in menu module 2026-03-31 20:50:58 +09:00
Kijin Sung
01d65dee7f Fix pre-conversion file size limit applying to admins 2026-03-31 19:54:53 +09:00
Kijin Sung
81b32378ca Support timeouts for ffmpeg and magick commands
https://rhymix.org/qna/1935749
2026-03-31 19:53:08 +09:00
Kijin Sung
ba49fe7b70 Fix warnings when migrating XE config to Rhymix format 2026-03-31 19:30:09 +09:00
Kijin Sung
7c30af23c5 Allow admin to delete comment placeholders from list #967 2026-03-29 16:37:23 +09:00
Kijin Sung
5c858806b2 Treat comma as a regular character when parsing search string #2687 2026-03-29 16:26:10 +09:00
Kijin Sung
057507d3d7 Fix member extra field being reset to public after editing 2026-03-29 16:23:38 +09:00
Kijin Sung
7a3d759e62 Fix undefined property when XML query has <navigation> without <page_count> #2688 2026-03-22 15:17:50 +09:00
Kijin Sung
63d2582c8e Fix invalid query when search contains zero conditions #2687 2026-03-22 15:09:45 +09:00
Kijin Sung
e61ccf84b8 Always cast module_srl to int when parsing include_modules 2026-03-22 14:59:01 +09:00
Kijin Sung
4ee0699dea Fix RVE-2026-6 possible command injection via magick 2026-03-19 17:50:44 +09:00
Kijin Sung
c906eae5d3 Disallow GET requests to procMemberFindAccount, and add route to procMemberAuthEmailAddress 2026-03-19 17:35:42 +09:00
Kijin Sung
00c9a5316c Fix RVE-2026-5 unconfigured domain in auth email 2026-03-19 17:33:08 +09:00
Kijin Sung
94008fbe9b Allow larger images/videos to be uploaded if they are going to be converted
- 변환 대상인 이미지나 동영상 파일은 용량 제한을 더 느슨하게 설정할 수 있도록 함
- 변환 후에 다시 용량을 체크하여 각 게시판의 업로드 정책 적용
- https://rhymix.org/qna/1926104
2026-03-10 19:45:38 +09:00
Kijin Sung
44cf008ac7 Allow setting list_count in various admin list pages #2549 2026-03-10 13:32:42 +09:00
Kijin Sung
8901cb6e36 Fix duplicate message content when document is moved #2686 2026-03-09 21:28:51 +09:00
Kijin Sung
3ca12cca6f Always set correct module_srl, even on empty documents 2026-03-09 20:34:34 +09:00
Kijin Sung
8b8dc99431 Replace $oDocument with empty DocumentItem if access is not allowed 2026-03-09 20:29:59 +09:00
Kijin Sung
4fe87edd1d Don't call API class if HTTP status code is 4xx or 5xx 2026-03-09 20:29:18 +09:00
Kijin Sung
a03c33381f Fix error when updating a document with a required file #2685 2026-03-04 18:51:15 +09:00
Kijin Sung
bf0899973a Fix missing validation of xe_run_method 2026-03-03 18:07:56 +09:00
Kijin Sung
cdc713301f Prevent saving layout HTML/CSS if it was not previously edited 2026-03-03 18:02:42 +09:00
40 changed files with 400 additions and 225 deletions

View file

@ -12,7 +12,7 @@ jobs:
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Install PHP - name: Install PHP
run: chmod +x .github/workflows/setup-php.sh && .github/workflows/setup-php.sh ${{ matrix.php }} run: chmod +x .github/workflows/setup-php.sh && .github/workflows/setup-php.sh ${{ matrix.php }}

View file

@ -921,7 +921,7 @@ class ModuleObject extends BaseObject
// execute api methods of the module if view action is and result is XMLRPC or JSON // execute api methods of the module if view action is and result is XMLRPC or JSON
if(isset($this->module_info->module_type) && in_array($this->module_info->module_type, ['view', 'mobile'])) if(isset($this->module_info->module_type) && in_array($this->module_info->module_type, ['view', 'mobile']))
{ {
if(Context::getResponseMethod() == 'XMLRPC' || Context::getResponseMethod() == 'JSON') if ($this->getHttpStatusCode() < 400 && in_array(Context::getResponseMethod(), ['JSON', 'XMLRPC']))
{ {
$oAPI = getAPI($this->module_info->module); $oAPI = getAPI($this->module_info->module);
if($oAPI instanceof ModuleObject && method_exists($oAPI, $this->act)) if($oAPI instanceof ModuleObject && method_exists($oAPI, $this->act))

View file

@ -3,7 +3,7 @@
/** /**
* RX_VERSION is the version number of the Rhymix CMS. * RX_VERSION is the version number of the Rhymix CMS.
*/ */
define('RX_VERSION', '2.1.31'); define('RX_VERSION', '2.1.32');
/** /**
* RX_MICROTIME is the startup time of the current script, in microseconds since the Unix epoch. * RX_MICROTIME is the startup time of the current script, in microseconds since the Unix epoch.

View file

@ -496,9 +496,9 @@ class DB
} }
// Collect various counts used in the page calculation. // Collect various counts used in the page calculation.
$list_count = $query->navigation->list_count->getValue($args)[0]; $list_count = $query->navigation->list_count ? $query->navigation->list_count->getValue($args)[0] : 10;
$page_count = $query->navigation->page_count->getValue($args)[0]; $page_count = $query->navigation->page_count ? $query->navigation->page_count->getValue($args)[0] : 10;
$page = $query->navigation->page->getValue($args)[0]; $page = $query->navigation->page ? $query->navigation->page->getValue($args)[0] : 1;
$total_count = intval($count); $total_count = intval($count);
$total_page = max(1, intval(ceil($total_count / $list_count))); $total_page = max(1, intval(ceil($total_count / $list_count)));
$last_index = $total_count - (($page - 1) * $list_count); $last_index = $total_count - (($page - 1) * $list_count);

View file

@ -37,13 +37,19 @@ class Security
case 'filename': case 'filename':
if (!utf8_check($input)) return false; if (!utf8_check($input)) return false;
return Filters\FilenameFilter::clean($input); return Filters\FilenameFilter::clean($input);
// Clean up SVG content to prevent various attacks. // Clean up SVG content to prevent various attacks.
case 'svg': case 'svg':
if (!utf8_check($input)) return false; if (!utf8_check($input)) return false;
$sanitizer = new \enshrined\svgSanitize\Sanitizer(); $sanitizer = new \enshrined\svgSanitize\Sanitizer();
return strval($sanitizer->sanitize($input)); return strval($sanitizer->sanitize($input));
// Clean up a path to prevent argument injection.
case 'command':
if (!utf8_check($input)) return false;
if (\RX_WINDOWS || preg_match('![^a-z0-9/._-]!', $input)) return escapeshellarg($input);
return strval($input);
// Unknown filters. // Unknown filters.
default: default:
throw new Exception('Unknown filter type for sanitize: ' . $type); throw new Exception('Unknown filter type for sanitize: ' . $type);

View file

@ -84,7 +84,7 @@ class ConfigParser
$config['db']['master']['prefix'] .= '_'; $config['db']['master']['prefix'] .= '_';
} }
$config['db']['master']['charset'] = $db_info->master_db['db_charset'] ?: 'utf8'; $config['db']['master']['charset'] = empty($db_info->master_db['db_charset']) ? 'utf8' : $db_info->master_db['db_charset'];
if (strpos($config['db']['master']['type'], 'innodb') !== false) if (strpos($config['db']['master']['type'], 'innodb') !== false)
{ {
@ -105,7 +105,7 @@ class ConfigParser
$slave_id = 'slave' . $slave_id; $slave_id = 'slave' . $slave_id;
$config['db'][$slave_id]['type'] = strtolower($slave_db['db_type']); $config['db'][$slave_id]['type'] = strtolower($slave_db['db_type']);
$config['db'][$slave_id]['host'] = $slave_db['db_hostname']; $config['db'][$slave_id]['host'] = $slave_db['db_hostname'];
$config['db'][$slave_id]['port'] = $slave_db['db_type']; $config['db'][$slave_id]['port'] = $slave_db['db_port'];
$config['db'][$slave_id]['user'] = $slave_db['db_userid']; $config['db'][$slave_id]['user'] = $slave_db['db_userid'];
$config['db'][$slave_id]['pass'] = $slave_db['db_password']; $config['db'][$slave_id]['pass'] = $slave_db['db_password'];
$config['db'][$slave_id]['database'] = $slave_db['db_database']; $config['db'][$slave_id]['database'] = $slave_db['db_database'];
@ -116,7 +116,7 @@ class ConfigParser
$config['db'][$slave_id]['prefix'] .= '_'; $config['db'][$slave_id]['prefix'] .= '_';
} }
$config['db'][$slave_id]['charset'] = $slave_db['db_charset'] ?: 'utf8'; $config['db'][$slave_id]['charset'] = empty($slave_db['db_charset']) ? 'utf8' : $slave_db['db_charset'];
if (strpos($config['db'][$slave_id]['type'], 'innodb') !== false) if (strpos($config['db'][$slave_id]['type'], 'innodb') !== false)
{ {
@ -145,7 +145,7 @@ class ConfigParser
// Create new crypto keys. // Create new crypto keys.
$config['crypto']['encryption_key'] = Security::getRandom(64, 'alnum'); $config['crypto']['encryption_key'] = Security::getRandom(64, 'alnum');
$config['crypto']['authentication_key'] = $db_info->secret_key ?: Security::getRandom(64, 'alnum'); $config['crypto']['authentication_key'] = empty($db_info->secret_key) ? Security::getRandom(64, 'alnum') : $db_info->secret_key;
$config['crypto']['session_key'] = Security::getRandom(64, 'alnum'); $config['crypto']['session_key'] = Security::getRandom(64, 'alnum');
// Convert language configuration. // Convert language configuration.
@ -177,8 +177,8 @@ class ConfigParser
$default_url = \Context::decodeIdna($default_url); $default_url = \Context::decodeIdna($default_url);
} }
$config['url']['default'] = $default_url ?: (\RX_SSL ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . \RX_BASEURL; $config['url']['default'] = $default_url ?: (\RX_SSL ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . \RX_BASEURL;
$config['url']['http_port'] = $db_info->http_port ?: null; $config['url']['http_port'] = $db_info->http_port ?? null;
$config['url']['https_port'] = $db_info->https_port ?: null; $config['url']['https_port'] = $db_info->https_port ?? null;
// Convert SSL configuration. // Convert SSL configuration.
if (isset($db_info->use_ssl) && in_array($db_info->use_ssl, ['always', 'optional'])) if (isset($db_info->use_ssl) && in_array($db_info->use_ssl, ['always', 'optional']))
@ -193,11 +193,11 @@ class ConfigParser
} }
// Convert session configuration. // Convert session configuration.
$config['session']['delay'] = $db_info->delay_session === 'Y' ? true : false; $config['session']['delay'] = ($db_info->delay_session ?? 'N') === 'Y' ? true : false;
$config['session']['use_db'] = $db_info->use_db_session === 'Y' ? true : false; $config['session']['use_db'] = ($db_info->use_db_session ?? 'N') === 'Y' ? true : false;
// Convert view configuration. // Convert view configuration.
$config['view']['minify_scripts'] = $db_info->minify_scripts ?: 'common'; $config['view']['minify_scripts'] = $db_info->minify_scripts ?? 'common';
// Convert admin IP whitelist. // Convert admin IP whitelist.
if (isset($db_info->admin_ip_list) && is_array($db_info->admin_ip_list) && count($db_info->admin_ip_list)) if (isset($db_info->admin_ip_list) && is_array($db_info->admin_ip_list) && count($db_info->admin_ip_list))
@ -206,9 +206,9 @@ class ConfigParser
} }
// Convert sitelock configuration. // Convert sitelock configuration.
$config['lock']['locked'] = $db_info->use_sitelock === 'Y' ? true : false; $config['lock']['locked'] = ($db_info->use_sitelock ?? 'N') === 'Y' ? true : false;
$config['lock']['title'] = strval($db_info->sitelock_title); $config['lock']['title'] = strval($db_info->sitelock_title ?? '');
$config['lock']['message'] = strval($db_info->sitelock_message); $config['lock']['message'] = strval($db_info->sitelock_message ?? '');
if (!is_array($db_info->sitelock_whitelist)) if (!is_array($db_info->sitelock_whitelist))
{ {
$db_info->sitelock_whitelist = $db_info->sitelock_whitelist ? array_map('trim', explode(',', trim($db_info->sitelock_whitelist))) : array(); $db_info->sitelock_whitelist = $db_info->sitelock_whitelist ? array_map('trim', explode(',', trim($db_info->sitelock_whitelist))) : array();
@ -220,7 +220,7 @@ class ConfigParser
$config['lock']['allow'] = array_values($db_info->sitelock_whitelist); $config['lock']['allow'] = array_values($db_info->sitelock_whitelist);
// Convert media filter configuration. // Convert media filter configuration.
if (is_array($db_info->embed_white_iframe)) if (is_array($db_info->embed_white_iframe ?? null))
{ {
$whitelist = array_unique(array_map(function($item) { $whitelist = array_unique(array_map(function($item) {
return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item; return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item;
@ -228,7 +228,7 @@ class ConfigParser
natcasesort($whitelist); natcasesort($whitelist);
$config['mediafilter']['iframe'] = $whitelist; $config['mediafilter']['iframe'] = $whitelist;
} }
if (is_array($db_info->embed_white_object)) if (is_array($db_info->embed_white_object ?? null))
{ {
$whitelist = array_unique(array_map(function($item) { $whitelist = array_unique(array_map(function($item) {
return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item; return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item;
@ -240,9 +240,9 @@ class ConfigParser
// Convert miscellaneous configuration. // Convert miscellaneous configuration.
$config['file']['folder_structure'] = 1; $config['file']['folder_structure'] = 1;
$config['file']['umask'] = Storage::recommendUmask(); $config['file']['umask'] = Storage::recommendUmask();
$config['mobile']['enabled'] = $db_info->use_mobile_view === 'N' ? false : true; $config['mobile']['enabled'] = ($db_info->use_mobile_view ?? 'N') === 'N' ? false : true;
$config['use_rewrite'] = $db_info->use_rewrite === 'Y' ? true : false; $config['use_rewrite'] = ($db_info->use_rewrite ?? 'N') === 'Y' ? true : false;
$config['use_sso'] = $db_info->use_sso === 'Y' ? true : false; $config['use_sso'] = ($db_info->use_sso ?? 'N') === 'Y' ? true : false;
// Copy other configuration. // Copy other configuration.
unset($db_info->master_db, $db_info->slave_db); unset($db_info->master_db, $db_info->slave_db);

View file

@ -254,8 +254,11 @@ class VariableBase
break; break;
case 'search': case 'search':
$parsed_keywords = $this->_parseSearchKeywords($column, $value); $parsed_keywords = $this->_parseSearchKeywords($column, $value);
$where = $parsed_keywords[0]; if (count($parsed_keywords))
$params = array_merge($params, $parsed_keywords[1]); {
$where = $parsed_keywords[0];
$params = array_merge($params, $parsed_keywords[1]);
}
break; break;
case 'plus': case 'plus':
$where = sprintf('%s = %s + %s', $column, $column, $is_expression ? $value : '?'); $where = sprintf('%s = %s + %s', $column, $column, $is_expression ? $value : '?');
@ -500,7 +503,7 @@ class VariableBase
// parse the value (text); // parse the value (text);
$value = str_replace('&quot;', '"', $value); $value = str_replace('&quot;', '"', $value);
$keywords = preg_split('/(\([^\)]*?\))|(\-?\"[^\"]*?\")|[\s,]+/', trim($value), 10, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE); $keywords = preg_split('/(\([^\)]*?\))|(\-?\"[^\"]*?\")|[\s]+/', trim($value), 10, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE);
$conditions = array(); $conditions = array();
$operators = array('AND' => 'AND', 'OR' => 'OR', '|' => 'OR'); $operators = array('AND' => 'AND', 'OR' => 'OR', '|' => 'OR');
@ -521,9 +524,12 @@ class VariableBase
if ($item !== '') if ($item !== '')
{ {
$parsed_keywords = $this->_parseSearchKeywords($column, $item); $parsed_keywords = $this->_parseSearchKeywords($column, $item);
$conditions[] = $parsed_keywords[0]; if (count($parsed_keywords))
$conditions[] = 'AND'; {
$params = array_merge($params, $parsed_keywords[1]); $conditions[] = $parsed_keywords[0];
$conditions[] = 'AND';
$params = array_merge($params, $parsed_keywords[1]);
}
} }
continue; continue;
} }
@ -563,9 +569,17 @@ class VariableBase
// remove the last point (would be an operator) // remove the last point (would be an operator)
array_pop($conditions); array_pop($conditions);
$conditions = implode(' ', $conditions);
$where = count($keywords) === 1 ? $conditions : "($conditions)";
return [$where, $params]; // combine the conditions and return
if (count($params) === 0)
{
return [];
}
else
{
$conditions = implode(' ', $conditions);
$where = count($params) === 1 ? $conditions : "($conditions)";
return [$where, $params];
}
} }
} }

View file

@ -104,7 +104,14 @@
var dfd = jQuery.Deferred(); var dfd = jQuery.Deferred();
$.each(item.files, function(index, file) { $.each(item.files, function(index, file) {
if(data.settings.maxFileSize > 0 && data.settings.maxFileSize < file.size) { var extension = file.name.split('.').pop().toLowerCase();
var preConversionTypes = data.settings.preConversionTypes || [];
var limit = data.settings.maxFileSize;
if (preConversionTypes.length > 0 && preConversionTypes.indexOf(extension) > -1) {
limit = data.settings.preConversionSize || limit;
}
console.log('file size: ' + file.size + ', limit: ' + limit);
if (limit > 0 && limit < file.size) {
dfd.reject(); dfd.reject();
alert(window.xe.lang.msg_exceeds_limit_size); alert(window.xe.lang.msg_exceeds_limit_size);
return false; return false;

View file

@ -209,6 +209,10 @@ class addonAdminController extends addonController
$args->{$key} = $vars->{$key} ?? ''; $args->{$key} = $vars->{$key} ?? '';
} }
$args->xe_run_method = $vars->xe_run_method ?? ''; $args->xe_run_method = $vars->xe_run_method ?? '';
if (!in_array($args->xe_run_method, ['run_selected', 'no_run_selected']))
{
$args->xe_run_method = 'run_selected';
}
$args->mid_list = $vars->mid_list ?? []; $args->mid_list = $vars->mid_list ?? [];
$output = $this->doSetup($addon_name, $args, $site_module_info->site_srl, 'site'); $output = $this->doSetup($addon_name, $args, $site_module_info->site_srl, 'site');

View file

@ -102,8 +102,8 @@ class addonController extends addon
$buff[] = '$before_time = microtime(true);'; $buff[] = '$before_time = microtime(true);';
// Run method and mid list // Run method and mid list
$run_method = ($extra_vars->xe_run_method ?? null) ?: 'run_selected'; $run_method = strval($extra_vars->xe_run_method ?? 'run_selected');
$buff[] = '$rm = \'' . $run_method . "';"; $buff[] = '$rm = ' . var_export($run_method, true) . ';';
$buff[] = '$ml = ' . var_export(array_fill_keys($mid_list, true), true) . ';'; $buff[] = '$ml = ' . var_export(array_fill_keys($mid_list, true), true) . ';';
$buff[] = '$_m = Context::get(\'mid\');'; $buff[] = '$_m = Context::get(\'mid\');';

View file

@ -56,12 +56,19 @@ class BoardAPI extends Board
public function dispBoardContentView($oModule) public function dispBoardContentView($oModule)
{ {
$oDocument = Context::get('oDocument'); $oDocument = Context::get('oDocument');
if($oDocument->isGranted()) if ($oDocument->isExists() && $oDocument->isAccessible())
{ {
$extra_vars = $oDocument->getExtraVars() ?: []; if ($oDocument->isGranted())
$oDocument->add('extra_vars', $this->_arrangeExtraVars($extra_vars)); {
$extra_vars = $oDocument->getExtraVars() ?: [];
$oDocument->add('extra_vars', $this->_arrangeExtraVars($extra_vars));
}
$oModule->add('oDocument', $this->_arrangeContent($oDocument, $oModule->grant));
}
else
{
$oModule->add('oDocument', null);
} }
$oModule->add('oDocument', $this->_arrangeContent($oDocument, $oModule->grant));
} }
/** /**
@ -70,13 +77,13 @@ class BoardAPI extends Board
public function dispBoardContentFileList($oModule) public function dispBoardContentFileList($oModule)
{ {
$oDocument = Context::get('oDocument'); $oDocument = Context::get('oDocument');
if($oDocument->isAccessible()) if ($oDocument->isExists() && $oDocument->isAccessible())
{ {
$oModule->add('file_list', $this->_arrangeFiles(Context::get('file_list') ?: [])); $oModule->add('file_list', $this->_arrangeFiles(Context::get('file_list') ?: []));
} }
else else
{ {
$oModule->add('file_list', array()); $oModule->add('file_list', []);
} }
} }
@ -93,12 +100,20 @@ class BoardAPI extends Board
**/ **/
public function dispBoardContentCommentList($oModule) public function dispBoardContentCommentList($oModule)
{ {
$comment_list = Context::get('comment_list'); $oDocument = Context::get('oDocument');
if (!is_array($comment_list)) if ($oDocument->isExists() && $oDocument->isAccessible())
{ {
$comment_list = []; $comment_list = Context::get('comment_list');
if (!is_array($comment_list))
{
$comment_list = [];
}
$oModule->add('comment_list', $this->_arrangeComments($comment_list));
}
else
{
$oModule->add('comment_list', []);
} }
$oModule->add('comment_list', $this->_arrangeComments($comment_list));
} }
/** /**

View file

@ -30,7 +30,7 @@ class BoardView extends Board
$this->search_list_count = $m ? ($this->module_info->mobile_search_list_count ?? 20) : ($this->module_info->search_list_count ?? 20); $this->search_list_count = $m ? ($this->module_info->mobile_search_list_count ?? 20) : ($this->module_info->search_list_count ?? 20);
$this->page_count = $m ? ($this->module_info->mobile_page_count ?? 5) : ($this->module_info->page_count ?? 10); $this->page_count = $m ? ($this->module_info->mobile_page_count ?? 5) : ($this->module_info->page_count ?? 10);
$this->except_notice = ($this->module_info->except_notice ?? '') == 'N' ? FALSE : TRUE; $this->except_notice = ($this->module_info->except_notice ?? '') == 'N' ? FALSE : TRUE;
$this->include_modules = ($this->module_info->include_modules ?? []) ? explode(',', $this->module_info->include_modules) : []; $this->include_modules = ($this->module_info->include_modules ?? []) ? array_map('intval', explode(',', $this->module_info->include_modules)) : [];
if (count($this->include_modules) && !in_array($this->module_info->module_srl, $this->include_modules)) if (count($this->include_modules) && !in_array($this->module_info->module_srl, $this->include_modules))
{ {
$this->include_modules[] = $this->module_info->module_srl; $this->include_modules[] = $this->module_info->module_srl;
@ -318,6 +318,7 @@ class BoardView extends Board
{ {
if (abs($oDocument->get('member_srl')) != $this->user->member_srl) if (abs($oDocument->get('member_srl')) != $this->user->member_srl)
{ {
$oDocument = DocumentModel::getBlankDocument($this->module_srl);
Context::set('document_srl', null, true); Context::set('document_srl', null, true);
$this->dispBoardMessage('msg_not_founded', 404); $this->dispBoardMessage('msg_not_founded', 404);
} }
@ -326,6 +327,7 @@ class BoardView extends Board
// if the document is TEMP saved, pretend that it doesn't exist. // if the document is TEMP saved, pretend that it doesn't exist.
if($oDocument->getStatus() == 'TEMP') if($oDocument->getStatus() == 'TEMP')
{ {
$oDocument = DocumentModel::getBlankDocument($this->module_srl);
Context::set('document_srl', null, true); Context::set('document_srl', null, true);
$this->dispBoardMessage('msg_not_founded', 404); $this->dispBoardMessage('msg_not_founded', 404);
} }
@ -343,8 +345,7 @@ class BoardView extends Board
*/ */
else else
{ {
$oDocument = DocumentModel::getDocument(0); $oDocument = DocumentModel::getBlankDocument($this->module_srl);
$oDocument->add('module_srl', $this->module_srl);
} }
/** /**
@ -354,7 +355,7 @@ class BoardView extends Board
{ {
if(!$this->grant->view && !$oDocument->isGranted()) if(!$this->grant->view && !$oDocument->isGranted())
{ {
$oDocument = DocumentModel::getDocument(0); $oDocument = DocumentModel::getBlankDocument($this->module_srl);
Context::set('document_srl', null, true); Context::set('document_srl', null, true);
$this->dispBoardMessage($this->user->isMember() ? 'msg_not_permitted' : 'msg_not_logged'); $this->dispBoardMessage($this->user->isMember() ? 'msg_not_permitted' : 'msg_not_logged');
} }

View file

@ -237,7 +237,8 @@ class CommentAdminController extends Comment
$module_infos[$module_srl] = ModuleModel::getModuleInfoByModuleSrl($module_srl)->comment_delete_message ?? ''; $module_infos[$module_srl] = ModuleModel::getModuleInfoByModuleSrl($module_srl)->comment_delete_message ?? '';
} }
if($module_infos[$module_srl] === 'yes') $policy = $module_infos[$module_srl];
if ($policy === 'yes' && !in_array($comment->get('status'), [\RX_STATUS_DELETED, \RX_STATUS_DELETED_BY_ADMIN]))
{ {
$output = $oCommentController->updateCommentByDelete($comment, true); $output = $oCommentController->updateCommentByDelete($comment, true);
if(!$output->toBool() && $output->error !== -2) if(!$output->toBool() && $output->error !== -2)
@ -246,7 +247,7 @@ class CommentAdminController extends Comment
return $output; return $output;
} }
} }
elseif(starts_with('only_comm', $module_infos[$module_srl])) elseif (starts_with('only_comm', $policy) && !in_array($comment->get('status'), [\RX_STATUS_DELETED, \RX_STATUS_DELETED_BY_ADMIN]))
{ {
$childs = CommentModel::getChildComments($comment_srl); $childs = CommentModel::getChildComments($comment_srl);
if(count($childs) > 0) if(count($childs) > 0)

View file

@ -29,13 +29,12 @@ class CommentAdminView extends Comment
{ {
// option to get a list // option to get a list
$args = new stdClass(); $args = new stdClass();
$args->page = Context::get('page'); // /< Page $args->list_count = intval(Context::get('list_count')) ?: 20;
$args->list_count = 30; // / the number of postings to appear on a single page $args->page_count = 5;
$args->page_count = 5; // / the number of pages to appear on the page navigation $args->page = max(1, intval(Context::get('page')));
$args->sort_index = 'list_order'; // /< Sorting values $args->sort_index = 'list_order'; // /< Sorting values
$args->module_srl = Context::get('module_srl'); $args->module_srl = Context::get('module_srl');
/* /*
$search_target = Context::get('search_target'); $search_target = Context::get('search_target');
$search_keyword = Context::get('search_keyword'); $search_keyword = Context::get('search_keyword');
@ -118,10 +117,10 @@ class CommentAdminView extends Comment
{ {
// option to get a blacklist // option to get a blacklist
$args = new stdClass(); $args = new stdClass();
$args->page = Context::get('page'); // /< Page $args->list_count = intval(Context::get('list_count')) ?: 20;
$args->list_count = 30; // /< the number of comment postings to appear on a single page $args->page_count = 5;
$args->page_count = 10; // /< the number of pages to appear on the page navigation $args->page = max(1, intval(Context::get('page')));
$args->order_type = 'desc'; // /< sorted value $args->order_type = 'desc';
// select sort method // select sort method
$sort_index = Context::get('sort_index'); $sort_index = Context::get('sort_index');
@ -214,11 +213,10 @@ class CommentAdminView extends Comment
{ {
// option for a list // option for a list
$args = new stdClass; $args = new stdClass;
$args->page = Context::get('page'); // /< Page
$args->list_count = 30; // /< the number of posts to display on a single page
$args->page_count = 10; // /< the number of pages that appear in the page navigation
$args->comment_srl = intval(Context::get('target_srl')); $args->comment_srl = intval(Context::get('target_srl'));
$args->list_count = intval(Context::get('list_count')) ?: 20;
$args->page_count = 5;
$args->page = max(1, intval(Context::get('page')));
// get Status name list // get Status name list
$oCommentModel = getModel('comment'); $oCommentModel = getModel('comment');

View file

@ -58,9 +58,9 @@ class CommentController extends Comment
} }
} }
} }
$yeokka_member_srl = Rhymix\Modules\Yeokbox\Models\Config::getConfig()->yeokka_member_srl;
$logged_info = Context::get('logged_info'); $logged_info = Context::get('logged_info');
if($logged_info->member_srl != $yeokka_member_srl && $oComment->getRegdateTime() < (time() - (86400 * 7))) if($logged_info->is_admin !== 'Y' && $oComment->getRegdateTime() < (time() - (86400 * 7)))
{ {
throw new Rhymix\Framework\Exception('작성 이후 7일 이상이 경과한 댓글은 추천할 수 없습니다.'); throw new Rhymix\Framework\Exception('작성 이후 7일 이상이 경과한 댓글은 추천할 수 없습니다.');
} }

View file

@ -37,10 +37,15 @@ class DocumentAdminView extends Document
{ {
// option to get a list // option to get a list
$args = new stdClass(); $args = new stdClass();
$args->page = Context::get('page'); // /< Page $args->list_count = intval(Context::get('list_count')) ?: 30;
$args->list_count = 30; // /< the number of posts to display on a single page $args->page_count = 5;
$args->page_count = 5; // /< the number of pages that appear in the page navigation $args->page = max(1, intval(Context::get('page')));
$args->sort_index = 'list_order';
$args->module_srl = Context::get('module_srl');
$args->statusList = [];
$args->use_division = false;
// search options
$args->search_target = Context::get('search_target'); // /< search (title, contents ...) $args->search_target = Context::get('search_target'); // /< search (title, contents ...)
$args->search_keyword = Context::get('search_keyword'); // /< keyword to search $args->search_keyword = Context::get('search_keyword'); // /< keyword to search
if ($args->search_target === 'member_srl') if ($args->search_target === 'member_srl')
@ -53,11 +58,6 @@ class DocumentAdminView extends Document
} }
} }
$args->sort_index = 'list_order'; // /< sorting value
$args->module_srl = Context::get('module_srl');
$args->statusList = [];
$args->use_division = false;
// get a list // get a list
$columnList = array('document_srl', 'module_srl', 'category_srl', 'member_srl', 'title', 'nick_name', 'comment_count', 'trackback_count', 'readed_count', 'voted_count', 'blamed_count', 'regdate', 'ipaddress', 'status'); $columnList = array('document_srl', 'module_srl', 'category_srl', 'member_srl', 'title', 'nick_name', 'comment_count', 'trackback_count', 'readed_count', 'voted_count', 'blamed_count', 'regdate', 'ipaddress', 'status');
$output = DocumentModel::getDocumentList($args, false, true, $columnList); $output = DocumentModel::getDocumentList($args, false, true, $columnList);
@ -160,9 +160,9 @@ class DocumentAdminView extends Document
// option for a list // option for a list
$args = new stdClass(); $args = new stdClass();
$args->page = intval(Context::get('page')) ?: 1; // /< Page $args->list_count = intval(Context::get('list_count')) ?: 20;
$args->list_count = 20; // /< the number of posts to display on a single page $args->page_count = 5;
$args->page_count = 10; // /< the number of pages that appear in the page navigation $args->page = max(1, intval(Context::get('page')));
$args->order_type = strtolower(Context::get('order_type')) === 'asc' ? 'asc' : 'desc'; $args->order_type = strtolower(Context::get('order_type')) === 'asc' ? 'asc' : 'desc';
// select sort method // select sort method
@ -254,11 +254,10 @@ class DocumentAdminView extends Document
{ {
// option for a list // option for a list
$args = new stdClass; $args = new stdClass;
$args->page = Context::get('page'); // /< Page
$args->list_count = 30; // /< the number of posts to display on a single page
$args->page_count = 10; // /< the number of pages that appear in the page navigation
$args->document_srl = intval(Context::get('target_srl')); $args->document_srl = intval(Context::get('target_srl'));
$args->list_count = intval(Context::get('list_count')) ?: 20;
$args->page_count = 5;
$args->page = max(1, intval(Context::get('page')));
// get Status name list // get Status name list
$oDocumentModel = getModel('document'); $oDocumentModel = getModel('document');
@ -330,12 +329,11 @@ class DocumentAdminView extends Document
{ {
// options for a list // options for a list
$args = new stdClass(); $args = new stdClass();
$args->page = Context::get('page'); // /< Page $args->list_count = intval(Context::get('list_count')) ?: 30;
$args->list_count = 30; // /< the number of posts to display on a single page $args->page_count = 5;
$args->page_count = 10; // /< the number of pages that appear in the page navigation $args->page = max(1, intval(Context::get('page')));
$args->sort_index = 'list_order';
$args->sort_index = 'list_order'; // /< sorting values $args->order_type = 'desc';
$args->order_type = 'desc'; // /< sorting values by order
$args->module_srl = Context::get('module_srl'); $args->module_srl = Context::get('module_srl');

View file

@ -70,9 +70,8 @@ class DocumentController extends Document
} }
} }
} }
$yeokka_member_srl = Rhymix\Modules\Yeokbox\Models\Config::getConfig()->yeokka_member_srl;
$logged_info = Context::get('logged_info'); $logged_info = Context::get('logged_info');
if($logged_info->member_srl != $yeokka_member_srl && $oDocument->getRegdateTime() < (time() - (86400 * 7))) if($logged_info->is_admin !== 'Y' && $oDocument->getRegdateTime() < (time() - (86400 * 7)))
{ {
throw new Rhymix\Framework\Exception('작성 이후 7일 이상이 경과한 글은 추천할 수 없습니다.'); throw new Rhymix\Framework\Exception('작성 이후 7일 이상이 경과한 글은 추천할 수 없습니다.');
} }
@ -3586,7 +3585,7 @@ class DocumentController extends Document
{ {
// Set message // Set message
$title = sprintf(lang('default_message_format'), $actions[$obj->type]); $title = sprintf(lang('default_message_format'), $actions[$obj->type]);
$content = <<<EOT $common_content = <<<EOT
<div style="padding:10px 0;"><strong>{$title}</strong></div> <div style="padding:10px 0;"><strong>{$title}</strong></div>
<p>{$obj->manager_message}</p> <p>{$obj->manager_message}</p>
<hr> <hr>
@ -3616,7 +3615,7 @@ class DocumentController extends Document
$oCommunicationController = CommunicationController::getInstance(); $oCommunicationController = CommunicationController::getInstance();
foreach ($recipients as $member_srl => $items) foreach ($recipients as $member_srl => $items)
{ {
$content = sprintf($content, implode('', $items)); $content = sprintf($common_content, implode('', $items));
$oCommunicationController->sendMessage($this->user->member_srl, $member_srl, $title, $content, true, null, false); $oCommunicationController->sendMessage($this->user->member_srl, $member_srl, $title, $content, true, null, false);
} }
} }

View file

@ -146,22 +146,23 @@ class DocumentModel extends Document
} }
/** /**
* Import Document * Get a document.
*
* @param int $document_srl * @param int $document_srl
* @param bool $is_admin * @param bool $is_admin
* @param bool $load_extra_vars * @param bool $load_extra_vars
* @param bool $reload_counts * @param bool $reload_counts
* @return documentItem * @return DocumentItem
*/ */
public static function getDocument($document_srl = 0, $is_admin = false, $load_extra_vars = true, $reload_counts = true) public static function getDocument($document_srl = 0, $is_admin = false, $load_extra_vars = true, $reload_counts = true)
{ {
if(!$document_srl) if(!$document_srl)
{ {
return new documentItem(); return new DocumentItem();
} }
if(!isset($GLOBALS['XE_DOCUMENT_LIST'][$document_srl])) if(!isset($GLOBALS['XE_DOCUMENT_LIST'][$document_srl]))
{ {
$oDocument = new documentItem($document_srl, $load_extra_vars, $reload_counts); $oDocument = new DocumentItem($document_srl, $load_extra_vars, $reload_counts);
if(!$oDocument->isExists()) if(!$oDocument->isExists())
{ {
return $oDocument; return $oDocument;
@ -176,13 +177,26 @@ class DocumentModel extends Document
return $GLOBALS['XE_DOCUMENT_LIST'][$document_srl]; return $GLOBALS['XE_DOCUMENT_LIST'][$document_srl];
} }
/**
* Create a blank document.
*
* @param int $module_srl
* @return DocumentItem
*/
public static function getBlankDocument($module_srl = 0): DocumentItem
{
$oDocument = new DocumentItem();
$oDocument->add('module_srl', $module_srl);
return $oDocument;
}
/** /**
* Bringing multiple documents (or paging) * Bringing multiple documents (or paging)
* @param array|string $document_srls * @param array|string $document_srls
* @param bool $is_admin * @param bool $is_admin
* @param bool $load_extra_vars * @param bool $load_extra_vars
* @param array $columnList * @param array $columnList
* @return array value type is documentItem * @return array value type is DocumentItem
*/ */
public static function getDocuments($document_srls, $is_admin = false, $load_extra_vars = true, $columnList = array()) public static function getDocuments($document_srls, $is_admin = false, $load_extra_vars = true, $columnList = array())
{ {
@ -207,7 +221,7 @@ class DocumentModel extends Document
{ {
if(!isset($GLOBALS['XE_DOCUMENT_LIST'][$attribute->document_srl])) if(!isset($GLOBALS['XE_DOCUMENT_LIST'][$attribute->document_srl]))
{ {
$oDocument = new documentItem(); $oDocument = new DocumentItem();
$oDocument->setAttribute($attribute, false); $oDocument->setAttribute($attribute, false);
} }
if($is_admin) if($is_admin)
@ -233,7 +247,7 @@ class DocumentModel extends Document
* @param bool $except_notice * @param bool $except_notice
* @param bool $load_extra_vars * @param bool $load_extra_vars
* @param array $columnList * @param array $columnList
* @return Object * @return BaseObject
*/ */
public static function getDocumentList($obj, $except_notice = false, $load_extra_vars = true, $columnList = array()) public static function getDocumentList($obj, $except_notice = false, $load_extra_vars = true, $columnList = array())
{ {
@ -300,7 +314,7 @@ class DocumentModel extends Document
* Module_srl value, bringing the document's gongjisa Port * Module_srl value, bringing the document's gongjisa Port
* @param object $obj * @param object $obj
* @param array $columnList * @param array $columnList
* @return object|void * @return BaseObject
*/ */
public static function getNoticeList($obj, $columnList = array()) public static function getNoticeList($obj, $columnList = array())
{ {
@ -338,7 +352,7 @@ class DocumentModel extends Document
{ {
if(!isset($GLOBALS['XE_DOCUMENT_LIST'][$attribute->document_srl])) if(!isset($GLOBALS['XE_DOCUMENT_LIST'][$attribute->document_srl]))
{ {
$oDocument = new documentItem(); $oDocument = new DocumentItem();
$oDocument->setAttribute($attribute, false); $oDocument->setAttribute($attribute, false);
} }

View file

@ -235,13 +235,17 @@ class EditorModel extends Editor
{ {
// Get file upload limits // Get file upload limits
$file_config = FileModel::getUploadConfig(); $file_config = FileModel::getUploadConfig();
$file_config->allowed_attach_size = $file_config->allowed_attach_size*1024*1024; $file_config->allowed_attach_size = $file_config->allowed_attach_size * 1048576;
$file_config->allowed_filesize = $file_config->allowed_filesize*1024*1024; $file_config->allowed_filesize = $file_config->allowed_filesize * 1048576;
if (isset($option->allowed_filesize) && $option->allowed_filesize > 0) if (isset($option->allowed_filesize) && $option->allowed_filesize > 0)
{ {
$file_config->allowed_attach_size = $option->allowed_filesize; $file_config->allowed_attach_size = $option->allowed_filesize;
$file_config->allowed_filesize = $option->allowed_filesize; $file_config->allowed_filesize = $option->allowed_filesize;
} }
if (isset($file_config->pre_conversion_filesize))
{
$file_config->pre_conversion_filesize = $file_config->pre_conversion_filesize * 1048576;
}
// Calculate the appropriate chunk size. // Calculate the appropriate chunk size.
$file_config->allowed_chunk_size = min(FileHandler::returnBytes(ini_get('upload_max_filesize')), FileHandler::returnBytes(ini_get('post_max_size')) * 0.95, 64 * 1024 * 1024); $file_config->allowed_chunk_size = min(FileHandler::returnBytes(ini_get('upload_max_filesize')), FileHandler::returnBytes(ini_get('post_max_size')) * 0.95, 64 * 1024 * 1024);

View file

@ -12,6 +12,8 @@
data-editor-status="{json_encode(FileModel::getInstance()->getFileList($editor_sequence), JSON_UNESCAPED_UNICODE)}" data-editor-status="{json_encode(FileModel::getInstance()->getFileList($editor_sequence), JSON_UNESCAPED_UNICODE)}"
data-max-file-size="{$this->user->isAdmin() ? 0 : $file_config->allowed_filesize}" data-max-file-size="{$this->user->isAdmin() ? 0 : $file_config->allowed_filesize}"
data-max-chunk-size="{$file_config->allowed_chunk_size ?: 0}" data-max-chunk-size="{$file_config->allowed_chunk_size ?: 0}"
data-pre-conversion-size="{$this->user->isAdmin() ? 0 : intval($file_config->pre_conversion_filesize ?? 0)}"
data-pre-conversion-types="{implode(',', $file_config->pre_conversion_types ?? [])}"
data-autoinsert-types="{json_encode($editor_autoinsert_types)}" data-autoinsert-types="{json_encode($editor_autoinsert_types)}"
data-autoinsert-position="{$editor_autoinsert_position ?: 'paragraph'}"> data-autoinsert-position="{$editor_autoinsert_position ?: 'paragraph'}">

View file

@ -10,6 +10,8 @@ $(function() {
container.data('instance', container.xeUploader({ container.data('instance', container.xeUploader({
maxFileSize: parseInt(data.maxFileSize, 10), maxFileSize: parseInt(data.maxFileSize, 10),
maxChunkSize: parseInt(data.maxChunkSize, 10), maxChunkSize: parseInt(data.maxChunkSize, 10),
preConversionSize: parseInt(data.preConversionSize || 0, 10),
preConversionTypes: data.preConversionTypes ? data.preConversionTypes.split(',') : [],
autoinsertTypes: data.autoinsertTypes, autoinsertTypes: data.autoinsertTypes,
autoinsertPosition: data.autoinsertPosition, autoinsertPosition: data.autoinsertPosition,
singleFileUploads: true singleFileUploads: true

View file

@ -323,7 +323,7 @@ class Value
} }
// Check that a file value is actually an uploaded file. // Check that a file value is actually an uploaded file.
if ($this->type === 'file' && $value) if ($this->type === 'file' && $value && $value !== $old_value)
{ {
if (!isset($value['tmp_name']) || !is_uploaded_file($value['tmp_name'])) if (!isset($value['tmp_name']) || !is_uploaded_file($value['tmp_name']))
{ {

View file

@ -67,9 +67,10 @@ class FileAdminController extends File
{ {
// Default settings // Default settings
$config = getModel('module')->getModuleConfig('file') ?: new stdClass; $config = getModel('module')->getModuleConfig('file') ?: new stdClass;
$config->allowed_filesize = Context::get('allowed_filesize'); $config->allowed_filesize = intval(Context::get('allowed_filesize'));
$config->allowed_attach_size = Context::get('allowed_attach_size'); $config->allowed_attach_size = intval(Context::get('allowed_attach_size'));
$config->allowed_filetypes = Context::get('allowed_filetypes'); $config->allowed_filetypes = Context::get('allowed_filetypes');
$config->pre_conversion_filesize = intval(Context::get('pre_conversion_filesize')) ?: null;
// Image settings // Image settings
$config->image_autoconv = []; $config->image_autoconv = [];
@ -121,6 +122,10 @@ class FileAdminController extends File
$config->magick_command = escape(utf8_trim(Context::get('magick_command'))) ?: ''; $config->magick_command = escape(utf8_trim(Context::get('magick_command'))) ?: '';
} }
// Timeouts
$config->ffmpeg_timeout = max(0, intval(Context::get('ffmpeg_timeout'))) ?: null;
$config->magick_timeout = max(0, intval(Context::get('magick_timeout'))) ?: null;
// Check maximum file size (probably not necessary anymore) // Check maximum file size (probably not necessary anymore)
if (PHP_INT_SIZE < 8) if (PHP_INT_SIZE < 8)
{ {
@ -147,6 +152,28 @@ class FileAdminController extends File
$config->allowed_filetypes = '*.*'; $config->allowed_filetypes = '*.*';
} }
// Generate pre-conversion whitelist
$config->pre_conversion_types = [];
foreach ($config->image_autoconv ?? [] as $source_type => $target_type)
{
if (!empty($target_type) && $target_type !== true)
{
$config->pre_conversion_types[] = $source_type;
if ($source_type === 'jpg')
{
$config->pre_conversion_types[] = 'jpeg';
}
}
elseif ($source_type === 'gif2mp4' && $target_type === true)
{
$config->pre_conversion_types[] = 'gif';
}
}
if ($config->video_autoconv['any2mp4'])
{
$config->pre_conversion_types = array_merge($config->pre_conversion_types, ['mp4', 'webm', 'ogv', 'avi', 'mkv', 'mov', 'mpg', 'mpe', 'mpeg', 'wmv', 'm4v', 'flv']);
}
// Save and redirect // Save and redirect
$output = getController('module')->insertModuleConfig('file', $config); $output = getController('module')->insertModuleConfig('file', $config);
$returnUrl = Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispFileAdminUploadConfig'); $returnUrl = Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispFileAdminUploadConfig');
@ -206,9 +233,10 @@ class FileAdminController extends File
if(!Context::get('use_default_file_config')) if(!Context::get('use_default_file_config'))
{ {
$config->use_default_file_config = 'N'; $config->use_default_file_config = 'N';
$config->allowed_filesize = Context::get('allowed_filesize'); $config->allowed_filesize = intval(Context::get('allowed_filesize'));
$config->allowed_attach_size = Context::get('allowed_attach_size'); $config->allowed_attach_size = intval(Context::get('allowed_attach_size'));
$config->allowed_filetypes = Context::get('allowed_filetypes'); $config->allowed_filetypes = Context::get('allowed_filetypes');
$config->pre_conversion_filesize = intval(Context::get('pre_conversion_filesize')) ?: null;
// Check maximum file size // Check maximum file size
if (PHP_INT_SIZE < 8) if (PHP_INT_SIZE < 8)
@ -274,6 +302,20 @@ class FileAdminController extends File
$download_grant = Context::get('download_grant'); $download_grant = Context::get('download_grant');
$config->download_grant = is_array($download_grant) ? array_values($download_grant) : array($download_grant); $config->download_grant = is_array($download_grant) ? array_values($download_grant) : array($download_grant);
// Create pre-conversion whitelist
$config->pre_conversion_types = [];
foreach ($config->image_autoconv ?? [] as $source_type => $target_type)
{
if ($target_type && $target_type !== true)
{
$config->pre_conversion_types[] = $source_type;
if ($source_type === 'jpg')
{
$config->pre_conversion_types[] = 'jpeg';
}
}
}
// Update // Update
$oModuleController = getController('module'); $oModuleController = getController('module');
foreach(explode(',', Context::get('target_module_srl')) as $module_srl) foreach(explode(',', Context::get('target_module_srl')) as $module_srl)
@ -388,7 +430,7 @@ class FileAdminController extends File
// Resize the image using GD or ImageMagick. // Resize the image using GD or ImageMagick.
$config = FileModel::getFileConfig(); $config = FileModel::getFileConfig();
$result = FileHandler::createImageFile(FileHandler::getRealPath($file->uploaded_filename), $temp_filename, $width, $height, $format, 'fill', $quality); $result = FileHandler::createImageFile(FileHandler::getRealPath($file->uploaded_filename), $temp_filename, $width, $height, $format, 'fill', $quality);
if (!$result && !empty($config->magick_command)) if (!$result && !empty($config->magick_command) && Rhymix\Framework\Storage::isExecutable($config->magick_command))
{ {
$temp_dir = dirname($temp_filename); $temp_dir = dirname($temp_filename);
if (!Rhymix\Framework\Storage::isDirectory($temp_dir)) if (!Rhymix\Framework\Storage::isDirectory($temp_dir))
@ -396,13 +438,17 @@ class FileAdminController extends File
Rhymix\Framework\Storage::createDirectory($temp_dir); Rhymix\Framework\Storage::createDirectory($temp_dir);
} }
$command = vsprintf('%s %s -resize %dx%d -quality %d %s %s %s', [ $command = vsprintf('%s %s -resize %dx%d -quality %d %s %s %s', [
\RX_WINDOWS ? escapeshellarg($config->magick_command) : $config->magick_command, Rhymix\Framework\Security::sanitize($config->magick_command, 'command'),
escapeshellarg(FileHandler::getRealPath($file->uploaded_filename)), escapeshellarg(FileHandler::getRealPath($file->uploaded_filename)),
$width, $height, $quality, $width, $height, $quality,
'-auto-orient -strip', '-auto-orient -strip',
'-limit memory 64MB -limit map 128MB -limit disk 1GB', '-limit memory 64MB -limit map 128MB -limit disk 1GB',
escapeshellarg($temp_filename), escapeshellarg($temp_filename),
]); ]);
if (!\RX_WINDOWS && isset($config->magick_timeout) && $config->magick_timeout > 0)
{
$command = 'timeout -k1 ' . intval($config->magick_timeout) . ' ' . $command;
}
@exec($command, $output, $return_var); @exec($command, $output, $return_var);
$result = $return_var === 0 ? true : false; $result = $return_var === 0 ? true : false;
} }

View file

@ -15,14 +15,14 @@ class FileAdminView extends File
{ {
// Options to get a list // Options to get a list
$args = new stdClass(); $args = new stdClass();
$args->page = Context::get('page'); // /< Page $args->list_count = intval(Context::get('list_count')) ?: 30;
$args->list_count = 30; // /< Number of documents that appear on a single page $args->page_count = 10;
$args->page_count = 10; // /< Number of pages that appear in the page navigation $args->page = max(1, intval(Context::get('page')));
$args->sort_index = Context::get('sort_index') ?: 'file_srl';
$args->sort_index = Context::get('sort_index') ?? 'file_srl'; // /< Sorting values $args->order_type = strtolower(Context::get('order_type')) === 'asc' ? 'asc' : 'desc';
$args->order_type = Context::get('order_type') ?? null;
$args->isvalid = Context::get('isvalid'); $args->isvalid = Context::get('isvalid');
$args->module_srl = Context::get('module_srl'); $args->module_srl = Context::get('module_srl');
// Get a list // Get a list
$oFileAdminModel = getAdminModel('file'); $oFileAdminModel = getAdminModel('file');
$output = $oFileAdminModel->getFileList($args); $output = $oFileAdminModel->getFileList($args);

View file

@ -102,6 +102,15 @@ class FileController extends File
$module_config = FileModel::getFileConfig($module_srl); $module_config = FileModel::getFileConfig($module_srl);
$allowed_attach_size = $module_config->allowed_attach_size * 1024 * 1024; $allowed_attach_size = $module_config->allowed_attach_size * 1024 * 1024;
$allowed_filesize = $module_config->allowed_filesize * 1024 * 1024; $allowed_filesize = $module_config->allowed_filesize * 1024 * 1024;
if (!empty($module_config->pre_conversion_filesize) && !empty($module_config->pre_conversion_types))
{
$extension = strtolower(array_last(explode('.', $file_info['name'])));
if ($extension && in_array($extension, $module_config->pre_conversion_types))
{
$allowed_attach_size = ($allowed_attach_size - $allowed_filesize) + ($module_config->pre_conversion_filesize * 1024 * 1024);
$allowed_filesize = $module_config->pre_conversion_filesize * 1024 * 1024;
}
}
} }
if ($total_size > $allowed_filesize) if ($total_size > $allowed_filesize)
{ {
@ -1158,10 +1167,14 @@ class FileController extends File
public function adjustUploadedImage($file_info, $config) public function adjustUploadedImage($file_info, $config)
{ {
// Get image information // Get image information
if (in_array($file_info['extension'], ['avif', 'heic', 'heif']) && !empty($config->magick_command)) if (in_array($file_info['extension'], ['avif', 'heic', 'heif']) && !empty($config->magick_command) && Rhymix\Framework\Storage::isExecutable($config->magick_command))
{ {
$command = \RX_WINDOWS ? escapeshellarg($config->magick_command) : $config->magick_command; $command = Rhymix\Framework\Security::sanitize($config->magick_command, 'command');
$command .= ' identify ' . escapeshellarg($file_info['tmp_name']); $command .= ' identify ' . escapeshellarg($file_info['tmp_name']);
if (!\RX_WINDOWS && isset($config->magick_timeout) && $config->magick_timeout > 0)
{
$command = 'timeout -k1 ' . intval($config->magick_timeout) . ' ' . $command;
}
@exec($command, $output, $return_var); @exec($command, $output, $return_var);
if ($return_var === 0 && preg_match('/([A-Z]+) ([0-9]+)x([0-9]+)/', substr(array_last($output), strlen($file_info['tmp_name'])), $matches)) if ($return_var === 0 && preg_match('/([A-Z]+) ([0-9]+)x([0-9]+)/', substr(array_last($output), strlen($file_info['tmp_name'])), $matches))
{ {
@ -1326,11 +1339,15 @@ class FileController extends File
$adjusted['height'] -= $adjusted['height'] % 2; $adjusted['height'] -= $adjusted['height'] % 2;
// Convert using ffmpeg // Convert using ffmpeg
$command = \RX_WINDOWS ? escapeshellarg($config->ffmpeg_command) : $config->ffmpeg_command; $command = Rhymix\Framework\Security::sanitize($config->ffmpeg_command, 'command');
$command .= ' -nostdin -i ' . escapeshellarg($file_info['tmp_name']); $command .= ' -nostdin -i ' . escapeshellarg($file_info['tmp_name']);
$command .= ' -movflags +faststart -pix_fmt yuv420p -c:v libx264 -crf 23'; $command .= ' -movflags +faststart -pix_fmt yuv420p -c:v libx264 -crf 23';
$command .= sprintf(' -vf "scale=%d:%d"', $adjusted['width'], $adjusted['height']); $command .= sprintf(' -vf "scale=%d:%d"', $adjusted['width'], $adjusted['height']);
$command .= ' ' . escapeshellarg($output_name); $command .= ' ' . escapeshellarg($output_name);
if (!\RX_WINDOWS && isset($config->ffmpeg_timeout) && $config->ffmpeg_timeout > 0)
{
$command = 'timeout -k1 ' . intval($config->ffmpeg_timeout) . ' ' . $command;
}
@exec($command, $output, $return_var); @exec($command, $output, $return_var);
$result = $return_var === 0 ? true : false; $result = $return_var === 0 ? true : false;
@ -1352,7 +1369,7 @@ class FileController extends File
// Convert using magick // Convert using magick
$command = vsprintf('%s %s -resize %dx%d -quality %d %s %s %s', [ $command = vsprintf('%s %s -resize %dx%d -quality %d %s %s %s', [
\RX_WINDOWS ? escapeshellarg($config->magick_command) : $config->magick_command, Rhymix\Framework\Security::sanitize($config->magick_command, 'command'),
escapeshellarg($file_info['tmp_name']), escapeshellarg($file_info['tmp_name']),
$adjusted['width'], $adjusted['width'],
$adjusted['height'], $adjusted['height'],
@ -1361,6 +1378,10 @@ class FileController extends File
'-limit memory 64MB -limit map 128MB -limit disk 1GB', '-limit memory 64MB -limit map 128MB -limit disk 1GB',
escapeshellarg($output_name), escapeshellarg($output_name),
]); ]);
if (!\RX_WINDOWS && isset($config->magick_timeout) && $config->magick_timeout > 0)
{
$command = 'timeout -k1 ' . intval($config->magick_timeout) . ' ' . $command;
}
@exec($command, $output, $return_var); @exec($command, $output, $return_var);
$result = $return_var === 0 ? true : false; $result = $return_var === 0 ? true : false;
} }
@ -1370,10 +1391,10 @@ class FileController extends File
$result = FileHandler::createImageFile($file_info['tmp_name'], $output_name, $adjusted['width'], $adjusted['height'], $adjusted['type'], 'fill', $adjusted['quality'], $adjusted['rotate']); $result = FileHandler::createImageFile($file_info['tmp_name'], $output_name, $adjusted['width'], $adjusted['height'], $adjusted['type'], 'fill', $adjusted['quality'], $adjusted['rotate']);
// If the image cannot be resized using GD, try ImageMagick. // If the image cannot be resized using GD, try ImageMagick.
if (!$result && !empty($config->magick_command)) if (!$result && !empty($config->magick_command) && Rhymix\Framework\Storage::isExecutable($config->magick_command))
{ {
$command = vsprintf('%s %s -resize %dx%d -quality %d %s %s %s', [ $command = vsprintf('%s %s -resize %dx%d -quality %d %s %s %s', [
\RX_WINDOWS ? escapeshellarg($config->magick_command) : $config->magick_command, Rhymix\Framework\Security::sanitize($config->magick_command, 'command'),
escapeshellarg($file_info['tmp_name']), escapeshellarg($file_info['tmp_name']),
$adjusted['width'], $adjusted['width'],
$adjusted['height'], $adjusted['height'],
@ -1382,6 +1403,10 @@ class FileController extends File
'-limit memory 64MB -limit map 128MB -limit disk 1GB', '-limit memory 64MB -limit map 128MB -limit disk 1GB',
escapeshellarg($output_name), escapeshellarg($output_name),
]); ]);
if (!\RX_WINDOWS && isset($config->magick_timeout) && $config->magick_timeout > 0)
{
$command = 'timeout -k1 ' . intval($config->magick_timeout) . ' ' . $command;
}
@exec($command, $output, $return_var); @exec($command, $output, $return_var);
$result = $return_var === 0 ? true : false; $result = $return_var === 0 ? true : false;
} }
@ -1414,7 +1439,7 @@ class FileController extends File
} }
// Analyze video file // Analyze video file
$command = \RX_WINDOWS ? escapeshellarg($config->ffprobe_command) : $config->ffprobe_command; $command = Rhymix\Framework\Security::sanitize($config->ffprobe_command, 'command');
$command .= ' -v quiet -print_format json -show_streams'; $command .= ' -v quiet -print_format json -show_streams';
$command .= ' ' . escapeshellarg($file_info['tmp_name']); $command .= ' ' . escapeshellarg($file_info['tmp_name']);
@exec($command, $output, $return_var); @exec($command, $output, $return_var);
@ -1558,7 +1583,7 @@ class FileController extends File
$adjusted['height'] -= $adjusted['height'] % 2; $adjusted['height'] -= $adjusted['height'] % 2;
// Convert using ffmpeg // Convert using ffmpeg
$command = \RX_WINDOWS ? escapeshellarg($config->ffmpeg_command) : $config->ffmpeg_command; $command = Rhymix\Framework\Security::sanitize($config->ffmpeg_command, 'command');
$command .= ' -nostdin -i ' . escapeshellarg($file_info['tmp_name']); $command .= ' -nostdin -i ' . escapeshellarg($file_info['tmp_name']);
if ($adjusted['duration'] !== $file_info['duration']) if ($adjusted['duration'] !== $file_info['duration'])
{ {
@ -1568,6 +1593,10 @@ class FileController extends File
$command .= empty($stream_info['audio']) ? ' -an' : ' -acodec aac'; $command .= empty($stream_info['audio']) ? ' -an' : ' -acodec aac';
$command .= sprintf(' -vf "scale=%d:%d"', $adjusted['width'], $adjusted['height']); $command .= sprintf(' -vf "scale=%d:%d"', $adjusted['width'], $adjusted['height']);
$command .= ' ' . escapeshellarg($output_name); $command .= ' ' . escapeshellarg($output_name);
if (!\RX_WINDOWS && isset($config->ffmpeg_timeout) && $config->ffmpeg_timeout > 0)
{
$command = 'timeout -k1 ' . intval($config->ffmpeg_timeout) . ' ' . $command;
}
@exec($command, $output, $return_var); @exec($command, $output, $return_var);
$result = $return_var === 0 ? true : false; $result = $return_var === 0 ? true : false;
@ -1597,9 +1626,13 @@ class FileController extends File
if ($config->video_thumbnail) if ($config->video_thumbnail)
{ {
$thumbnail_name = $file_info['tmp_name'] . '.thumbnail.jpeg'; $thumbnail_name = $file_info['tmp_name'] . '.thumbnail.jpeg';
$command = \RX_WINDOWS ? escapeshellarg($config->ffmpeg_command) : $config->ffmpeg_command; $command = Rhymix\Framework\Security::sanitize($config->ffmpeg_command, 'command');
$command .= sprintf(' -ss 00:00:00.%d -i %s -vframes 1', mt_rand(0, 99), escapeshellarg($file_info['tmp_name'])); $command .= sprintf(' -ss 00:00:00.%d -i %s -vframes 1', mt_rand(0, 99), escapeshellarg($file_info['tmp_name']));
$command .= ' -nostdin ' . escapeshellarg($thumbnail_name); $command .= ' -nostdin ' . escapeshellarg($thumbnail_name);
if (!\RX_WINDOWS && isset($config->ffmpeg_timeout) && $config->ffmpeg_timeout > 0)
{
$command = 'timeout -k1 ' . intval($config->ffmpeg_timeout) . ' ' . $command;
}
@exec($command, $output, $return_var); @exec($command, $output, $return_var);
if ($return_var === 0) if ($return_var === 0)
{ {

View file

@ -21,6 +21,7 @@ $lang->allowed_filesize = 'Maximum File Size';
$lang->allowed_filesize_exceeded = 'The file is too large. The maximum allowed filesize is %s.'; $lang->allowed_filesize_exceeded = 'The file is too large. The maximum allowed filesize is %s.';
$lang->allowed_attach_size = 'Maximum Attachments'; $lang->allowed_attach_size = 'Maximum Attachments';
$lang->allowed_filetypes = 'Allowed extentsions'; $lang->allowed_filetypes = 'Allowed extentsions';
$lang->pre_conversion_filesize = 'Pre-conversion Grace Size';
$lang->download_short_url = 'Use short URL'; $lang->download_short_url = 'Use short URL';
$lang->inline_download_format = 'Open in current window'; $lang->inline_download_format = 'Open in current window';
$lang->inline_download_image = 'Image'; $lang->inline_download_image = 'Image';
@ -45,6 +46,7 @@ $lang->about_allowed_filesize_global = 'This is the global limit on the size of
$lang->about_allowed_attach_size_global = 'This is the global limit on the combined size of all attachments in one document.'; $lang->about_allowed_attach_size_global = 'This is the global limit on the combined size of all attachments in one document.';
$lang->about_allowed_size_limits = 'The file size will be limited to the value set in php.ini (%sB) in IE9 and below and older Android browsers.'; $lang->about_allowed_size_limits = 'The file size will be limited to the value set in php.ini (%sB) in IE9 and below and older Android browsers.';
$lang->about_allowed_filetypes = 'Rhymix no longer uses the old *.* syntax. Simply list the extensions you wish to allow.<br />Please use a comma (,) to separate items: e.g. doc, zip, pdf'; $lang->about_allowed_filetypes = 'Rhymix no longer uses the old *.* syntax. Simply list the extensions you wish to allow.<br />Please use a comma (,) to separate items: e.g. doc, zip, pdf';
$lang->about_pre_conversion_filesize = 'If an image or video might be converted as configured below, it will be allowed up to this size, and checked again after conversion.<br>If this configuration is empty, the file must be below the allowed size both before and after conversion.';
$lang->about_save_changelog = 'Keep a log of new and deleted files in the database.'; $lang->about_save_changelog = 'Keep a log of new and deleted files in the database.';
$lang->cmd_delete_checked_file = 'Delete Selected Item(s)'; $lang->cmd_delete_checked_file = 'Delete Selected Item(s)';
$lang->cmd_move_to_document = 'Move to Document'; $lang->cmd_move_to_document = 'Move to Document';
@ -102,15 +104,15 @@ $lang->max_image_size_same_format_to_jpg = 'Convert to JPG';
$lang->max_image_size_same_format_to_webp = 'Convert to WebP'; $lang->max_image_size_same_format_to_webp = 'Convert to WebP';
$lang->max_image_size_admin = 'Also apply to administrator'; $lang->max_image_size_admin = 'Also apply to administrator';
$lang->image_quality_adjustment = 'Image Quality'; $lang->image_quality_adjustment = 'Image Quality';
$lang->about_image_quality_adjustment = 'adjust the quality of images that will is converted by other settings.<br />If set to more than 75% (Standard), the file size may be larger than the original.'; $lang->about_image_quality_adjustment = 'Adjust the quality of images that will be converted by other settings.<br />If set to more than 75% (Standard), the file size may be larger than the original.';
$lang->image_autorotate = 'Fix Image Rotation'; $lang->image_autorotate = 'Fix Image Rotation';
$lang->about_image_autorotate = 'correct images that are rotated by mobile devices.'; $lang->about_image_autorotate = 'Correct images that are rotated by mobile devices.';
$lang->image_remove_exif_data = 'Remove EXIF'; $lang->image_remove_exif_data = 'Remove EXIF';
$lang->about_image_remove_exif_data = 'remove EXIF data including camera, GPS information, and more in image file for privacy.<br />Even if this option is not used, EXIF data may be removed when the image is converted by other settings.'; $lang->about_image_remove_exif_data = 'Remove EXIF data including camera, GPS information, and more in image file for privacy.<br />Even if this option is not used, EXIF data may be removed when the image is converted by other settings.';
$lang->image_always_reencode = 'Always Reencode'; $lang->image_always_reencode = 'Always Reencode';
$lang->about_image_always_reencode = 'Reencode images to a constant quality even if they do not meet one of the conditions above. This may help save disk space and traffic.'; $lang->about_image_always_reencode = 'Reencode images to a constant quality even if they do not meet one of the conditions above. This may help save disk space and traffic.';
$lang->image_autoconv_gif2mp4 = 'Convert GIF to MP4'; $lang->image_autoconv_gif2mp4 = 'Convert GIF to MP4';
$lang->about_image_autoconv_gif2mp4 = 'convert animated GIF images into MP4 videos to save storage and bandwidth.<br />This requires ffmpeg settings below. Videos may not play properly in older browsers.'; $lang->about_image_autoconv_gif2mp4 = 'Convert animated GIF images into MP4 videos to save storage and bandwidth.<br />This requires ffmpeg settings below. Videos may not play properly in older browsers.';
$lang->max_video_size = 'Limit Video Size'; $lang->max_video_size = 'Limit Video Size';
$lang->about_max_video_size = 'Limit the dimensions of uploaded videos. Note that this is only indirectly related to file size.'; $lang->about_max_video_size = 'Limit the dimensions of uploaded videos. Note that this is only indirectly related to file size.';
$lang->max_video_duration = 'Limit Video Duration'; $lang->max_video_duration = 'Limit Video Duration';
@ -120,15 +122,19 @@ $lang->about_video_autoconv_any2mp4 = 'Convert all other types of videos to MP4
$lang->video_always_reencode = 'Always Reencode'; $lang->video_always_reencode = 'Always Reencode';
$lang->about_video_always_reencode = 'Reencode videos to a constant quality even if they do not meet one of the conditions above. This may help save disk space and traffic.'; $lang->about_video_always_reencode = 'Reencode videos to a constant quality even if they do not meet one of the conditions above. This may help save disk space and traffic.';
$lang->video_thumbnail = 'Video Thumbnail'; $lang->video_thumbnail = 'Video Thumbnail';
$lang->about_video_thumbnail = 'extract a thumbnail image from uploaded video.'; $lang->about_video_thumbnail = 'Extract a thumbnail image from uploaded video.';
$lang->video_mp4_gif_time = 'Play Like GIF'; $lang->video_mp4_gif_time = 'Play Like GIF';
$lang->about_video_mp4_gif_time = 'treat silent MP4 videos with duration less than the set time as GIF images, and play with auto and loop.'; $lang->about_video_mp4_gif_time = 'Treat silent MP4 videos with duration less than the set time as GIF images, and play with auto and loop.';
$lang->external_program_paths = 'Paths to External Programs'; $lang->external_program_paths = 'Paths to External Programs';
$lang->ffmpeg_path = 'Absolute Path to ffmpeg'; $lang->ffmpeg_path = 'Absolute Path to ffmpeg';
$lang->ffprobe_path = 'Absolute Path to ffprobe'; $lang->ffprobe_path = 'Absolute Path to ffprobe';
$lang->ffmpeg_timeout = 'ffmpeg Timeout';
$lang->magick_path = 'Absolute Path to magick'; $lang->magick_path = 'Absolute Path to magick';
$lang->magick_timeout = 'magick Timeout';
$lang->about_ffmpeg_path = 'Rhymix uses ffmpeg to convert video files.'; $lang->about_ffmpeg_path = 'Rhymix uses ffmpeg to convert video files.';
$lang->about_ffmpeg_timeout = 'If the video conversion task is not completed within a certain time, it will be terminated.<br />Proper timeout settings can help manage server load.<br />However, if set longer than the PHP execution time limit (%d seconds), the conversion result will not be saved.';
$lang->about_magick_path = 'Rhymix uses magick to convert newer image formats such as AVIF and HEIC.<br />Note that the \'convert\' command from previous versions of ImageMagick doesn\'t support these formats.<br />The latest version can be downloaded from their <a href="https://imagemagick.org/script/download.php" target="_blank">official site</a>.'; $lang->about_magick_path = 'Rhymix uses magick to convert newer image formats such as AVIF and HEIC.<br />Note that the \'convert\' command from previous versions of ImageMagick doesn\'t support these formats.<br />The latest version can be downloaded from their <a href="https://imagemagick.org/script/download.php" target="_blank">official site</a>.';
$lang->about_magick_timeout = 'If the image conversion task is not completed within a certain time, it will be terminated.<br />Proper timeout settings can help manage server load.<br />However, if set longer than the PHP execution time limit (%d seconds), the conversion result will not be saved.';
$lang->msg_cannot_use_exec = 'The exec() function is disabled on this server.'; $lang->msg_cannot_use_exec = 'The exec() function is disabled on this server.';
$lang->msg_cannot_use_ffmpeg = 'In order to use this feature, PHP must be able to execute \'ffmpeg\' and \'ffprobe\' commands.'; $lang->msg_cannot_use_ffmpeg = 'In order to use this feature, PHP must be able to execute \'ffmpeg\' and \'ffprobe\' commands.';
$lang->msg_cannot_use_exif = 'In order to use this feature, PHP must be installed with the \'exif\' extension.'; $lang->msg_cannot_use_exif = 'In order to use this feature, PHP must be installed with the \'exif\' extension.';

View file

@ -21,6 +21,7 @@ $lang->allowed_filesize_exceeded = '파일이 너무 큽니다. 용량 제한은
$lang->allowed_attach_size = '문서 첨부 제한'; $lang->allowed_attach_size = '문서 첨부 제한';
$lang->allowed_filetypes = '허용 확장자'; $lang->allowed_filetypes = '허용 확장자';
$lang->allow_multimedia_direct_download = '멀티미디어 파일 직접 접근 허용'; $lang->allow_multimedia_direct_download = '멀티미디어 파일 직접 접근 허용';
$lang->pre_conversion_filesize = '변환 전 유예 용량';
$lang->download_short_url = '다운로드시 짧은주소 사용'; $lang->download_short_url = '다운로드시 짧은주소 사용';
$lang->inline_download_format = '다운로드시 현재 창 사용'; $lang->inline_download_format = '다운로드시 현재 창 사용';
$lang->inline_download_image = '이미지'; $lang->inline_download_image = '이미지';
@ -45,6 +46,7 @@ $lang->about_allowed_filesize_global = '관리자를 포함하여 사이트 전
$lang->about_allowed_attach_size_global = '관리자를 포함하여 사이트 전체에 적용되는 문서당 총 첨부 용량 제한입니다.'; $lang->about_allowed_attach_size_global = '관리자를 포함하여 사이트 전체에 적용되는 문서당 총 첨부 용량 제한입니다.';
$lang->about_allowed_size_limits = 'IE9 이하, 구버전 안드로이드 등에서는 php.ini에서 지정한 %sB로 제한됩니다.'; $lang->about_allowed_size_limits = 'IE9 이하, 구버전 안드로이드 등에서는 php.ini에서 지정한 %sB로 제한됩니다.';
$lang->about_allowed_filetypes = '업로드를 허용할 확장자 목록입니다. 구 버전의 *.* 문법은 사용하지 않습니다.<br />여러 개 입력시 쉼표(,)을 이용해서 구분해 주세요. 예) doc, zip, pdf'; $lang->about_allowed_filetypes = '업로드를 허용할 확장자 목록입니다. 구 버전의 *.* 문법은 사용하지 않습니다.<br />여러 개 입력시 쉼표(,)을 이용해서 구분해 주세요. 예) doc, zip, pdf';
$lang->about_pre_conversion_filesize = '변환 대상인 이미지 또는 동영상은 설정한 용량만큼 우선 업로드를 허용하고, 변환 후 용량을 기준으로 다시 확인합니다.<br>설정을 비워 둘 경우, 변환 전후 용량을 동일하게 제한합니다.';
$lang->about_save_changelog = '파일 저장 및 삭제 내역을 DB에 기록합니다.'; $lang->about_save_changelog = '파일 저장 및 삭제 내역을 DB에 기록합니다.';
$lang->cmd_delete_checked_file = '선택항목 삭제'; $lang->cmd_delete_checked_file = '선택항목 삭제';
$lang->cmd_move_to_document = '문서로 이동'; $lang->cmd_move_to_document = '문서로 이동';
@ -126,9 +128,13 @@ $lang->about_video_mp4_gif_time = '설정된 시간 이하의 길이를 가진
$lang->external_program_paths = '외부 프로그램 경로'; $lang->external_program_paths = '외부 프로그램 경로';
$lang->ffmpeg_path = 'ffmpeg 절대경로'; $lang->ffmpeg_path = 'ffmpeg 절대경로';
$lang->ffprobe_path = 'ffprobe 절대경로'; $lang->ffprobe_path = 'ffprobe 절대경로';
$lang->ffmpeg_timeout = 'ffmpeg 타임아웃';
$lang->magick_path = 'magick 절대경로'; $lang->magick_path = 'magick 절대경로';
$lang->magick_timeout = 'magick 타임아웃';
$lang->about_ffmpeg_path = '동영상 변환에 사용합니다.'; $lang->about_ffmpeg_path = '동영상 변환에 사용합니다.';
$lang->about_ffmpeg_timeout = '동영상 변환 작업이 일정 시간 안에 완료되지 않으면 강제로 종료합니다.<br />적절한 타임아웃 설정은 서버 부하 관리에 도움이 됩니다.<br />단, PHP 실행 제한 시간(%d초)보다 길게 설정할 경우 변환 결과가 저장되지 않습니다.';
$lang->about_magick_path = 'AVIF, HEIC 등 일부 이미지 변환에 사용합니다.<br />구 버전 ImageMagick의 convert 명령은 이러한 포맷을 지원하지 않습니다.<br />새 버전은 <a href="https://imagemagick.org/script/download.php" target="_blank">공식 사이트</a>에서 다운받을 수 있습니다.'; $lang->about_magick_path = 'AVIF, HEIC 등 일부 이미지 변환에 사용합니다.<br />구 버전 ImageMagick의 convert 명령은 이러한 포맷을 지원하지 않습니다.<br />새 버전은 <a href="https://imagemagick.org/script/download.php" target="_blank">공식 사이트</a>에서 다운받을 수 있습니다.';
$lang->about_magick_timeout = '이미지 변환 작업이 일정 시간 안에 완료되지 않으면 강제로 종료합니다.<br />적절한 타임아웃 설정은 서버 부하 관리에 도움이 됩니다.<br />단, PHP 실행 제한 시간(%d초)보다 길게 설정할 경우 변환 결과가 저장되지 않습니다.';
$lang->msg_cannot_use_exec = '이 서버에서 exec() 함수를 사용할 수 없습니다.'; $lang->msg_cannot_use_exec = '이 서버에서 exec() 함수를 사용할 수 없습니다.';
$lang->msg_cannot_use_ffmpeg = '이 기능을 사용하려면 PHP에서 ffmpeg 및 ffprobe 명령을 실행할 수 있어야 합니다.'; $lang->msg_cannot_use_ffmpeg = '이 기능을 사용하려면 PHP에서 ffmpeg 및 ffprobe 명령을 실행할 수 있어야 합니다.';
$lang->msg_cannot_use_exif = '이 기능을 사용하려면 PHP exif 확장모듈이 필요합니다.'; $lang->msg_cannot_use_exif = '이 기능을 사용하려면 PHP exif 확장모듈이 필요합니다.';

View file

@ -34,6 +34,13 @@
<p class="x_help-block">{sprintf($lang->about_allowed_attach_size, getUrl('', 'module', 'admin', 'act', 'dispFileAdminUploadConfig'))}<br />{sprintf($lang->about_allowed_size_limits, ini_get('upload_max_filesize'))}</p> <p class="x_help-block">{sprintf($lang->about_allowed_attach_size, getUrl('', 'module', 'admin', 'act', 'dispFileAdminUploadConfig'))}<br />{sprintf($lang->about_allowed_size_limits, ini_get('upload_max_filesize'))}</p>
</div> </div>
</div> </div>
<div class="x_control-group">
<label for="allowedFiletypes" class="x_control-label">{$lang->pre_conversion_filesize}</label>
<div class="x_controls">
<input type="number" min="0" name="pre_conversion_filesize" id="pre_conversion_filesize" value="{$config->pre_conversion_filesize ?? ''}" size="7" style="min-width:80px" /> MB
<p class="x_help-block">{$lang->about_pre_conversion_filesize}</p>
</div>
</div>
<div class="x_control-group"> <div class="x_control-group">
<label for="allowed_filetypes" class="x_control-label">{$lang->allowed_filetypes}</label> <label for="allowed_filetypes" class="x_control-label">{$lang->allowed_filetypes}</label>
<div class="x_controls"> <div class="x_controls">

View file

@ -23,6 +23,13 @@
<p class="x_help-block">{$lang->about_allowed_attach_size_global}<br />{sprintf($lang->about_allowed_size_limits, ini_get('upload_max_filesize'))}</p> <p class="x_help-block">{$lang->about_allowed_attach_size_global}<br />{sprintf($lang->about_allowed_size_limits, ini_get('upload_max_filesize'))}</p>
</div> </div>
</div> </div>
<div class="x_control-group">
<label for="allowedFiletypes" class="x_control-label">{$lang->pre_conversion_filesize}</label>
<div class="x_controls">
<input type="number" min="0" name="pre_conversion_filesize" id="pre_conversion_filesize" value="{$config->pre_conversion_filesize ?? ''}" size="7" style="min-width:80px" /> MB
<p class="x_help-block">{$lang->about_pre_conversion_filesize}</p>
</div>
</div>
<div class="x_control-group"> <div class="x_control-group">
<label for="allowedFiletypes" class="x_control-label">{$lang->allowed_filetypes}</label> <label for="allowedFiletypes" class="x_control-label">{$lang->allowed_filetypes}</label>
<div class="x_controls"> <div class="x_controls">
@ -276,6 +283,14 @@
<p class="x_text-info" cond="!$is_exec_available">{$lang->msg_cannot_use_exec}</p> <p class="x_text-info" cond="!$is_exec_available">{$lang->msg_cannot_use_exec}</p>
</div> </div>
</div> </div>
<div class="x_control-group">
<label class="x_control-label">{$lang->ffmpeg_timeout}</label>
<div class="x_controls">
<input type="number" min="0" name="ffmpeg_timeout" value="{$config->ffmpeg_timeout}" style="min-width:80px" /> {$lang->unit_sec}
<p class="x_help-block">{sprintf($lang->about_ffmpeg_timeout, ini_get('max_execution_time'))}</p>
<p class="x_text-info" cond="!$is_exec_available">{$lang->msg_cannot_use_exec}</p>
</div>
</div>
<div class="x_control-group"> <div class="x_control-group">
<label class="x_control-label">{$lang->magick_path}</label> <label class="x_control-label">{$lang->magick_path}</label>
<div class="x_controls"> <div class="x_controls">
@ -284,6 +299,14 @@
<p class="x_text-info" cond="!$is_exec_available">{$lang->msg_cannot_use_exec}</p> <p class="x_text-info" cond="!$is_exec_available">{$lang->msg_cannot_use_exec}</p>
</div> </div>
</div> </div>
<div class="x_control-group">
<label class="x_control-label">{$lang->magick_timeout}</label>
<div class="x_controls">
<input type="number" min="0" name="magick_timeout" value="{$config->magick_timeout}" style="min-width:80px" /> {$lang->unit_sec}
<p class="x_help-block">{sprintf($lang->about_magick_timeout, ini_get('max_execution_time'))}</p>
<p class="x_text-info" cond="!$is_exec_available">{$lang->msg_cannot_use_exec}</p>
</div>
</div>
</section> </section>
<div class="x_clearfix btnArea"> <div class="x_clearfix btnArea">

View file

@ -311,14 +311,22 @@ class LayoutAdminController extends Layout
$layout_srl = Context::get('layout_srl'); $layout_srl = Context::get('layout_srl');
$code = Context::get('code'); $code = Context::get('code');
$code_css = Context::get('code_css'); $code_css = Context::get('code_css');
$is_post = ($_SERVER['REQUEST_METHOD'] == 'POST'); if (!$layout_srl || !$code || !\RX_POST)
if(!$layout_srl || !$code || !$is_post)
{ {
throw new Rhymix\Framework\Exceptions\InvalidRequest; throw new Rhymix\Framework\Exceptions\InvalidRequest;
} }
$layout_info = LayoutModel::getLayout($layout_srl);
if (!$layout_info)
{
throw new Rhymix\Framework\Exceptions\TargetNotFound;
}
if (!$layout_info->is_edited)
{
return new BaseObject(-1, 'layout.layout_editing_deprecated_p1');
}
$oLayoutModel = getModel('layout'); $oLayoutModel = getModel('layout');
$layout_file = $oLayoutModel->getUserLayoutHtml($layout_srl); $layout_file = $oLayoutModel->getUserLayoutHtml($layout_srl);
FileHandler::writeFile($layout_file, $code); FileHandler::writeFile($layout_file, $code);

View file

@ -40,10 +40,10 @@
<action name="procMemberInsert" type="controller" route="signup" /> <action name="procMemberInsert" type="controller" route="signup" />
<action name="procMemberCheckValue" type="controller" /> <action name="procMemberCheckValue" type="controller" />
<action name="procMemberLogin" type="controller" route="login" /> <action name="procMemberLogin" type="controller" route="login" />
<action name="procMemberFindAccount" type="controller" method="GET|POST" ruleset="findAccount" /> <action name="procMemberFindAccount" type="controller" ruleset="findAccount" />
<action name="procMemberFindAccountByQuestion" type="controller" method="GET|POST" /> <action name="procMemberFindAccountByQuestion" type="controller" />
<action name="procMemberAuthAccount" type="controller" method="GET|POST" route="auth/$member_srl/$auth_key" /> <action name="procMemberAuthAccount" type="controller" method="GET|POST" route="auth/$member_srl/$auth_key" />
<action name="procMemberAuthEmailAddress" type="controller" method="GET|POST" /> <action name="procMemberAuthEmailAddress" type="controller" method="GET|POST" route="authEmail/$member_srl/$auth_key" />
<action name="procMemberResendAuthMail" type="controller" ruleset="resendAuthMail" /> <action name="procMemberResendAuthMail" type="controller" ruleset="resendAuthMail" />
<action name="procMemberSendVerificationSMS" type="controller" /> <action name="procMemberSendVerificationSMS" type="controller" />
<action name="procMemberConfirmVerificationSMS" type="controller" /> <action name="procMemberConfirmVerificationSMS" type="controller" />

View file

@ -944,6 +944,8 @@ class MemberAdminController extends Member
if(!$output->toBool()) return $output; if(!$output->toBool()) return $output;
// memberConfig update // memberConfig update
$config = MemberModel::getMemberConfig();
$signupItem = new stdClass(); $signupItem = new stdClass();
$signupItem->name = $args->column_name; $signupItem->name = $args->column_name;
$signupItem->title = $args->column_title; $signupItem->title = $args->column_title;
@ -954,9 +956,6 @@ class MemberAdminController extends Member
$signupItem->description = $args->description; $signupItem->description = $args->description;
$signupItem->isPublic = 'Y'; $signupItem->isPublic = 'Y';
$oMemberModel = getModel('member');
$config = $oMemberModel->getMemberConfig();
if($isInsert) if($isInsert)
{ {
$config->signupForm[] = $signupItem; $config->signupForm[] = $signupItem;
@ -967,6 +966,7 @@ class MemberAdminController extends Member
{ {
if($val->member_join_form_srl == $signupItem->member_join_form_srl) if($val->member_join_form_srl == $signupItem->member_join_form_srl)
{ {
$signupItem->isPublic = $val->isPublic ?? 'Y';
$config->signupForm[$key] = $signupItem; $config->signupForm[$key] = $signupItem;
} }
} }
@ -1838,7 +1838,7 @@ class MemberAdminController extends Member
// Perform login as the target member // Perform login as the target member
// Session::login() sets the basic session variables, and setSessionInfo() populates Context with member details // Session::login() sets the basic session variables, and setSessionInfo() populates Context with member details
Rhymix\Framework\Session::login($member_info->member_srl); Rhymix\Framework\Session::login($member_info->member_srl);
$oMemberController = getController('member'); $oMemberController = MemberController::getInstance();
$oMemberController->setSessionInfo(); $oMemberController->setSessionInfo();
$this->setRedirectUrl(getNotEncodedUrl('')); $this->setRedirectUrl(getNotEncodedUrl(''));

View file

@ -44,7 +44,7 @@ class MemberAdminModel extends Member
$args = new stdClass(); $args = new stdClass();
$args->is_admin = Context::get('is_admin') === 'Y' ? 'Y' : null; $args->is_admin = Context::get('is_admin') === 'Y' ? 'Y' : null;
$args->status = Context::get('is_denied') === 'Y' ? 'DENIED' : null; $args->status = Context::get('is_denied') === 'Y' ? 'DENIED' : null;
$args->selected_group_srl = Context::get('selected_group_srl'); $args->selected_group_srl = intval(Context::get('selected_group_srl')) ?: null;
$filter = Context::get('filter_type'); $filter = Context::get('filter_type');
switch($filter) switch($filter)
@ -128,21 +128,11 @@ class MemberAdminModel extends Member
} }
// Change the query id if selected_group_srl exists (for table join) // Change the query id if selected_group_srl exists (for table join)
$sort_order = Context::get('sort_order'); $sort_order = Context::get('sort_order') === 'desc' ? 'desc' : 'asc';
$sort_index = Context::get('sort_index'); $sort_index = Context::get('sort_index');
if(!$sort_index || !in_array($sort_index, ['user_id', 'email_address', 'phone_number', 'user_name', 'nick_name', 'regdate', 'last_login'])) if(!$sort_index || !in_array($sort_index, ['user_id', 'email_address', 'phone_number', 'user_name', 'nick_name', 'regdate', 'last_login']))
{ {
$sort_index = "list_order"; $sort_index = 'list_order';
}
if(!$sort_order)
{
$sort_order = 'asc';
}
if($sort_order != 'asc')
{
$sort_order = 'desc';
} }
if($args->selected_group_srl) if($args->selected_group_srl)
@ -158,13 +148,13 @@ class MemberAdminModel extends Member
$args->sort_order = $sort_order; $args->sort_order = $sort_order;
Context::set('sort_order', $sort_order); Context::set('sort_order', $sort_order);
// Other variables
$args->page = Context::get('page');
$args->list_count = 40;
$args->page_count = 10;
$output = executeQuery($query_id, $args);
return $output; // Other variables
$args->list_count = intval(Context::get('list_count')) ?: 30;
$args->page_count = 10;
$args->page = max(1, intval(Context::get('page')));
return executeQueryArray($query_id, $args);
} }
/** /**

View file

@ -1872,7 +1872,7 @@ class MemberController extends Member
$tpl_path = sprintf('%sskins/%s', $this->module_path, $member_config->skin); $tpl_path = sprintf('%sskins/%s', $this->module_path, $member_config->skin);
if(!is_dir($tpl_path)) $tpl_path = sprintf('%sskins/%s', $this->module_path, 'default'); if(!is_dir($tpl_path)) $tpl_path = sprintf('%sskins/%s', $this->module_path, 'default');
$find_url = getFullUrl ('', 'module', 'member', 'act', 'procMemberAuthAccount', 'member_srl', $member_info->member_srl, 'auth_key', $args->auth_key); $find_url = self::generateSafeAuthUrl('procMemberAuthAccount', $member_info->member_srl, $args->auth_key);
Context::set('find_url', $find_url); Context::set('find_url', $find_url);
$oTemplate = new Rhymix\Framework\Template($tpl_path, 'find_member_account_mail'); $oTemplate = new Rhymix\Framework\Template($tpl_path, 'find_member_account_mail');
@ -2104,7 +2104,7 @@ class MemberController extends Member
$tpl_path = sprintf('%sskins/%s', $this->module_path, $member_config->skin); $tpl_path = sprintf('%sskins/%s', $this->module_path, $member_config->skin);
if(!is_dir($tpl_path)) $tpl_path = sprintf('%sskins/%s', $this->module_path, 'default'); if(!is_dir($tpl_path)) $tpl_path = sprintf('%sskins/%s', $this->module_path, 'default');
$auth_url = getFullUrl('','module','member','act','procMemberAuthAccount','member_srl',$member_info->member_srl, 'auth_key',$auth_info->auth_key); $auth_url = self::generateSafeAuthUrl('procMemberAuthAccount', $member_info->member_srl, $auth_info->auth_key);
Context::set('auth_url', $auth_url); Context::set('auth_url', $auth_url);
$oTemplate = new Rhymix\Framework\Template($tpl_path, 'confirm_member_account_mail'); $oTemplate = new Rhymix\Framework\Template($tpl_path, 'confirm_member_account_mail');
@ -2161,7 +2161,7 @@ class MemberController extends Member
$tpl_path = sprintf('%sskins/%s', $this->module_path, $member_config->skin); $tpl_path = sprintf('%sskins/%s', $this->module_path, $member_config->skin);
if(!is_dir($tpl_path)) $tpl_path = sprintf('%sskins/%s', $this->module_path, 'default'); if(!is_dir($tpl_path)) $tpl_path = sprintf('%sskins/%s', $this->module_path, 'default');
$auth_url = getFullUrl('','module','member','act','procMemberAuthAccount','member_srl',$member_info->member_srl, 'auth_key',$auth_args->auth_key); $auth_url = self::generateSafeAuthUrl('procMemberAuthAccount', $member_info->member_srl, $auth_args->auth_key);
Context::set('auth_url', $auth_url); Context::set('auth_url', $auth_url);
$oTemplate = new Rhymix\Framework\Template($tpl_path, 'confirm_member_account_mail'); $oTemplate = new Rhymix\Framework\Template($tpl_path, 'confirm_member_account_mail');
@ -3721,7 +3721,7 @@ class MemberController extends Member
Context::set('memberInfo', $memberInfo); Context::set('memberInfo', $memberInfo);
Context::set('newEmail', $newEmail); Context::set('newEmail', $newEmail);
$auth_url = getFullUrl('','module','member','act','procMemberAuthEmailAddress','member_srl',$member_info->member_srl, 'auth_key',$auth_args->auth_key); $auth_url = self::generateSafeAuthUrl('procMemberAuthEmailAddress', $member_info->member_srl, $auth_args->auth_key);
Context::set('auth_url', $auth_url); Context::set('auth_url', $auth_url);
$oTemplate = new Rhymix\Framework\Template($tpl_path, 'confirm_member_new_email'); $oTemplate = new Rhymix\Framework\Template($tpl_path, 'confirm_member_new_email');
@ -4054,6 +4054,43 @@ class MemberController extends Member
return new BaseObject(0); return new BaseObject(0);
} }
/**
* Generate a URL pointing to the main page of a properly configured domain.
*
* @return string
*/
public static function generateSafeLink(string $target = '_blank'): string
{
$domain_info = ModuleModel::getSiteInfoByDomain($_SERVER['HTTP_HOST']) ?: ModuleModel::getDefaultDomainInfo();
$base_url = Context::getRequestUri(0, $domain_info->domain);
$title = Context::replaceUserLang($domain_info->settings->title ?? '');
if ($title === '')
{
$title = $base_url;
}
return sprintf('<a href="%s" target="%s">%s</a>', escape($base_url, false), escape($target, false), escape($title, false));
}
/**
* Generate a URL for account auth.
*
* @param string $act
* @param int $member_srl
* @param string $auth_key
* @return string
*/
public static function generateSafeAuthUrl(string $act, int $member_srl, string $auth_key): string
{
$domain_info = ModuleModel::getSiteInfoByDomain($_SERVER['HTTP_HOST']) ?: ModuleModel::getDefaultDomainInfo();
$base_url = Context::getRequestUri(0, $domain_info->domain);
return $base_url . substr(getUrl([
'module' => 'member',
'act' => $act,
'member_srl' => $member_srl,
'auth_key' => $auth_key,
]), strlen(\RX_BASEURL));
}
/** /**
* Denied user login and write description * Denied user login and write description
* *

View file

@ -1,7 +1,7 @@
{$lang->msg_confirm_account_info}<br /> {$lang->msg_confirm_account_info}<br />
<hr noshade="noshade" /> <hr noshade="noshade" />
<ul> <ul>
<li>Site : <a href="{getUrl()}" target="_blank">{getUrl()}</a></li> <li>{$lang->site} : {MemberController::generateSafeLink()}</li>
<li loop="$memberInfo=>$name,$value" cond="!is_object($value)&&!is_array($value)">{$name} : {$value}</li> <li loop="$memberInfo=>$name,$value" cond="!is_object($value)&&!is_array($value)">{$name} : {$value}</li>
</ul> </ul>
<hr noshade="noshade" /> <hr noshade="noshade" />

View file

@ -1,7 +1,7 @@
{$lang->msg_confirm_account_info}<br /> {$lang->msg_confirm_account_info}<br />
<hr noshade="noshade" /> <hr noshade="noshade" />
<ul> <ul>
<li>Site : <a href="{getUrl()}" target="_blank">{getUrl()}</a></li> <li>{$lang->site} : {MemberController::generateSafeLink()}</li>
<li loop="$memberInfo=>$name,$value" cond="!is_object($value)&&!is_array($value)">{$name} : {$value}</li> <li loop="$memberInfo=>$name,$value" cond="!is_object($value)&&!is_array($value)">{$name} : {$value}</li>
</ul> </ul>
<hr noshade="noshade" /> <hr noshade="noshade" />

View file

@ -1,7 +1,7 @@
{$lang->msg_find_account_info}<br /> {$lang->msg_find_account_info}<br />
<hr noshade="noshade" /> <hr noshade="noshade" />
<ul> <ul>
<li>{$lang->site} : <a href="{getUrl()}" target="_blank">{getUrl()}</a></li> <li>{$lang->site} : {MemberController::generateSafeLink()}</li>
<!--@if($memberInfo[$lang->user_id])--> <!--@if($memberInfo[$lang->user_id])-->
<li>{$lang->user_id} : {$memberInfo[$lang->user_id]}</li> <li>{$lang->user_id} : {$memberInfo[$lang->user_id]}</li>
<!--@elseif($memberInfo[$lang->email_address])--> <!--@elseif($memberInfo[$lang->email_address])-->

View file

@ -24,8 +24,6 @@
<action name="procMenuAdminCopyItem" type="controller" /> <action name="procMenuAdminCopyItem" type="controller" />
<action name="procMenuAdminUpdateAuth" type="controller" /> <action name="procMenuAdminUpdateAuth" type="controller" />
<action name="procMenuAdminButtonUpload" type="controller" /> <action name="procMenuAdminButtonUpload" type="controller" />
<action name="procMenuAdminUploadButton" type="controller" />
<action name="procMenuAdminDeleteButton" type="controller" />
<action name="procMenuAdminInsertItemForAdminMenu" type="controller" /> <action name="procMenuAdminInsertItemForAdminMenu" type="controller" />
<action name="procMenuAdminMakeXmlFile" type="controller" /> <action name="procMenuAdminMakeXmlFile" type="controller" />
<action name="procMenuAdminArrangeItem" type="controller" /> <action name="procMenuAdminArrangeItem" type="controller" />

View file

@ -1560,61 +1560,6 @@ class MenuAdminController extends Menu
$this->add('xml_file',$xml_file); $this->add('xml_file',$xml_file);
} }
/**
* Register a menu image button
* @return void
*/
function procMenuAdminUploadButton()
{
$menu_srl = Context::get('menu_srl');
$menu_item_srl = Context::get('menu_item_srl');
$target = Context::get('target');
$target_file = Context::get($target);
// Error occurs when the target is neither a uploaded file nor a valid file
if(!$menu_srl || !$menu_item_srl)
{
Context::set('error_messge', lang('msg_invalid_request'));
}
else if(!$target_file || !is_uploaded_file($target_file['tmp_name']) || !preg_match('/\.(jpe?g|gif|png|svg|webp)$/i',$target_file['name']))
{
Context::set('error_messge', lang('msg_invalid_request'));
}
// Move the file to a specific director if the uploaded file meets requirement
else
{
$tmp_arr = explode('.',$target_file['name']);
$ext = $tmp_arr[count($tmp_arr)-1];
$path = sprintf('./files/attach/menu_button/%d/', $menu_srl);
$filename = sprintf('%s%d.%s.%s', $path, $menu_item_srl, $target, $ext);
if(!is_dir($path)) FileHandler::makeDir($path);
move_uploaded_file($target_file['tmp_name'], $filename);
Context::set('filename', $filename);
}
$this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('menu_file_uploaded');
}
/**
* Remove the menu image button
* @return void
*/
function procMenuAdminDeleteButton()
{
$menu_srl = Context::get('menu_srl');
$menu_item_srl = Context::get('menu_item_srl');
$target = Context::get('target');
$filename = Context::get('filename');
FileHandler::removeFile($filename);
$this->add('target', $target);
}
/** /**
* Get all act list for admin menu * Get all act list for admin menu
* @return void * @return void

View file

@ -25,6 +25,17 @@ class SecurityTest extends \Codeception\Test\Unit
$source = '<svg><rect></rect><script></script></svg>'; $source = '<svg><rect></rect><script></script></svg>';
$target = '<?xml version="1.0" encoding="UTF-8"?>' . "\n<svg>\n <rect></rect>\n</svg>\n"; $target = '<?xml version="1.0" encoding="UTF-8"?>' . "\n<svg>\n <rect></rect>\n</svg>\n";
$this->assertEquals($target, Rhymix\Framework\Security::sanitize($source, 'svg')); $this->assertEquals($target, Rhymix\Framework\Security::sanitize($source, 'svg'));
// Command
if (!\RX_WINDOWS)
{
$source = '/usr/bin/ffmpeg';
$target = '/usr/bin/ffmpeg';
$this->assertEquals($target, Rhymix\Framework\Security::sanitize($source, 'command'));
$source = '/usr/bin/path with space/ffmpeg';
$target = '\'/usr/bin/path with space/ffmpeg\'';
$this->assertEquals($target, Rhymix\Framework\Security::sanitize($source, 'command'));
}
} }
public function testEncryption() public function testEncryption()