diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 938956a87..cd44c0340 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: PHP Lint & Codeception on: [ push, pull_request ] jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php index 1d5dcc37c..e1edbf63c 100644 --- a/classes/context/Context.class.php +++ b/classes/context/Context.class.php @@ -2879,14 +2879,10 @@ class Context { return isset(self::$_instance->meta_tags[$name]) ? self::$_instance->meta_tags[$name]['content'] : null; } - - $ret = array(); - foreach(self::$_instance->meta_tags as $name => $content) + else { - $ret[] = array('name' => $name, 'is_http_equiv' => $content['is_http_equiv'], 'content' => escape($content['content'], false)); + return array_values(self::$_instance->meta_tags); } - - return $ret; } /** @@ -2894,14 +2890,17 @@ class Context * * @param string $name name of meta tag * @param string $content content of meta tag - * @param mixed $is_http_equiv value of http_equiv + * @param bool $is_http_equiv + * @param bool $is_before_title * @return void */ - public static function addMetaTag($name, $content, $is_http_equiv = false) + public static function addMetaTag($name, $content, $is_http_equiv = false, $is_before_title = true) { self::$_instance->meta_tags[$name] = array( + 'name' => $name, + 'content' => escape(self::replaceUserLang($content, true), false), 'is_http_equiv' => (bool)$is_http_equiv, - 'content' => self::replaceUserLang($content, true), + 'is_before_title' => (bool)$is_before_title, ); } diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index 52e5f697d..4622d803f 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -196,6 +196,12 @@ class HTMLDisplayHandler Context::set('favicon_url', $favicon_url); Context::set('mobicon_url', $mobicon_url); + // Only print the X-UA-Compatible meta tag if somebody is still using IE + if (preg_match('!Trident/7\.0!', $_SERVER['HTTP_USER_AGENT'] ?? '')) + { + Context::addMetaTag('X-UA-Compatible', 'IE=edge', true); + } + return $output; } @@ -641,7 +647,7 @@ class HTMLDisplayHandler { if ($tag !== '') { - Context::addOpenGraphData('og:article:tag', $tag, false); + Context::addOpenGraphData('og:article:tag', $tag); } } @@ -683,21 +689,21 @@ class HTMLDisplayHandler function _addTwitterMetadata() { $card_type = $this->_image_type === 'document' ? 'summary_large_image' : 'summary'; - Context::addMetaTag('twitter:card', $card_type); + Context::addMetaTag('twitter:card', $card_type, false, false); foreach(Context::getOpenGraphData() as $val) { if ($val['property'] === 'og:title') { - Context::addMetaTag('twitter:title', $val['content']); + Context::addMetaTag('twitter:title', $val['content'], false, false); } if ($val['property'] === 'og:description') { - Context::addMetaTag('twitter:description', $val['content']); + Context::addMetaTag('twitter:description', $val['content'], false, false); } if ($val['property'] === 'og:image' && $this->_image_type === 'document') { - Context::addMetaTag('twitter:image', $val['content']); + Context::addMetaTag('twitter:image', $val['content'], false, false); } } } diff --git a/classes/module/ModuleHandler.class.php b/classes/module/ModuleHandler.class.php index 7d518befa..95c535c3d 100644 --- a/classes/module/ModuleHandler.class.php +++ b/classes/module/ModuleHandler.class.php @@ -693,7 +693,8 @@ class ModuleHandler extends Handler } } - if ($kind === 'admin') { + if ($kind === 'admin') + { Context::addMetaTag('robots', 'noindex'); } diff --git a/common/defaults/config.php b/common/defaults/config.php index f28768d7c..8351d0056 100644 --- a/common/defaults/config.php +++ b/common/defaults/config.php @@ -151,5 +151,7 @@ return array( ], 'use_rewrite' => true, 'use_sso' => false, - 'other' => array(), + 'other' => [ + 'proxy' => null, + ], ); diff --git a/common/framework/HTTP.php b/common/framework/HTTP.php index 51e0b79ce..1948dee49 100644 --- a/common/framework/HTTP.php +++ b/common/framework/HTTP.php @@ -209,9 +209,10 @@ class HTTP ]; // Add proxy settings. - if (defined('__PROXY_SERVER__')) + $proxy = config('other.proxy') ?: (defined('__PROXY_SERVER__') ? constant('__PROXY_SERVER__') : ''); + if ($proxy !== '') { - $proxy = parse_url(constant('__PROXY_SERVER__')); + $proxy = parse_url($proxy); $proxy_scheme = preg_match('/^(https|socks)/', $proxy['scheme'] ?? '') ? ($proxy['scheme'] . '://') : 'http://'; $proxy_auth = (!empty($proxy['user']) && !empty($proxy['pass'])) ? ($proxy['user'] . ':' . $proxy['pass'] . '@') : ''; $settings['proxy'] = sprintf('%s%s%s:%s', $proxy_scheme, $proxy_auth, $proxy['host'], $proxy['port']); diff --git a/common/framework/drivers/mail/mailfunction.php b/common/framework/drivers/mail/mailfunction.php index 6e11d03c2..545031106 100644 --- a/common/framework/drivers/mail/mailfunction.php +++ b/common/framework/drivers/mail/mailfunction.php @@ -7,15 +7,6 @@ namespace Rhymix\Framework\Drivers\Mail; */ class MailFunction extends Base implements \Rhymix\Framework\Drivers\MailInterface { - /** - * Direct invocation of the constructor is not permitted. - */ - protected function __construct() - { - include_once \RX_BASEDIR . 'common/libraries/swift_mail.php'; - $this->_mailer = new \Swift_Mailer(new \Swift_MailTransport); - } - /** * Get the human-readable name of this mail driver. * @@ -58,6 +49,12 @@ class MailFunction extends Base implements \Rhymix\Framework\Drivers\MailInterfa */ public function send(\Rhymix\Framework\Mail $message) { + if ($this->_mailer === null) + { + include_once \RX_BASEDIR . 'common/libraries/swift_mail.php'; + $this->_mailer = new \Swift_Mailer(new \Swift_MailTransport); + } + try { $errors = []; diff --git a/common/framework/drivers/mail/smtp.php b/common/framework/drivers/mail/smtp.php index a15f05f68..7a762b2a9 100644 --- a/common/framework/drivers/mail/smtp.php +++ b/common/framework/drivers/mail/smtp.php @@ -7,23 +7,6 @@ namespace Rhymix\Framework\Drivers\Mail; */ class SMTP extends Base implements \Rhymix\Framework\Drivers\MailInterface { - /** - * Direct invocation of the constructor is not permitted. - */ - protected function __construct(array $config) - { - $security = in_array($config['smtp_security'], ['ssl', 'tls']) ? $config['smtp_security'] : null; - $transport = new \Swift_SmtpTransport($config['smtp_host'], $config['smtp_port'], $security); - $transport->setUsername($config['smtp_user']); - $transport->setPassword($config['smtp_pass']); - $local_domain = $transport->getLocalDomain(); - if (preg_match('/^\*\.(.+)$/', $local_domain, $matches)) - { - $transport->setLocalDomain($matches[1]); - } - $this->mailer = new \Swift_Mailer($transport); - } - /** * Get the list of configuration fields required by this mail driver. * @@ -56,9 +39,32 @@ class SMTP extends Base implements \Rhymix\Framework\Drivers\MailInterface */ public function send(\Rhymix\Framework\Mail $message) { + if ($this->_mailer === null) + { + if (isset($this->_config['smtp_security']) && in_array($this->_config['smtp_security'], ['ssl', 'tls'])) + { + $security = $this->_config['smtp_security']; + } + else + { + $security = null; + } + + $transport = new \Swift_SmtpTransport($this->_config['smtp_host'], $this->_config['smtp_port'], $security); + $transport->setUsername($this->_config['smtp_user']); + $transport->setPassword($this->_config['smtp_pass']); + $local_domain = $transport->getLocalDomain(); + if (preg_match('/^\*\.(.+)$/', $local_domain, $matches)) + { + $transport->setLocalDomain($matches[1]); + } + $this->_mailer = new \Swift_Mailer($transport); + } + try { - $result = $this->mailer->send($message->message, $errors); + $errors = []; + $result = $this->_mailer->send($message->message, $errors); } catch(\Exception $e) { diff --git a/common/framework/drivers/sms/apistore.php b/common/framework/drivers/sms/apistore.php deleted file mode 100644 index 2b294a0a5..000000000 --- a/common/framework/drivers/sms/apistore.php +++ /dev/null @@ -1,141 +0,0 @@ - 500, - 'sms_max_length' => 90, - 'sms_max_length_in_charset' => 'CP949', - 'lms_supported' => true, - 'lms_supported_country_codes' => array(82), - 'lms_max_length' => 2000, - 'lms_max_length_in_charset' => 'CP949', - 'lms_subject_supported' => true, - 'lms_subject_max_length' => 60, - 'mms_supported' => false, - 'delay_supported' => true, - ); - - /** - * Config keys used by this driver are stored here. - */ - protected static $_required_config = array('api_user', 'api_key'); - - /** - * Check if the current SMS driver is supported on this server. - * - * This method returns true on success and false on failure. - * - * @return bool - */ - public static function isSupported() - { - return true; - } - - /** - * Store the last response. - */ - protected $_last_response = ''; - - /** - * Send a message. - * - * This method returns true on success and false on failure. - * - * @param array $messages - * @param object $original - * @return bool - */ - public function send(array $messages, \Rhymix\Framework\SMS $original) - { - $status = true; - - foreach ($messages as $i => $message) - { - $data = array(); - $data['send_phone'] = $message->from; - $data['dest_phone'] = implode(',', $message->to); - $data['msg_body'] = strval($message->content); - if ($message->type !== 'SMS' && $message->subject) - { - $data['subject'] = $message->subject; - } - - $result = $this->_apiCall(sprintf('message/%s', strtolower($message->type)), $data); - if (!$result) - { - $message->errors[] = 'ApiStore API returned invalid response: ' . $this->_getLastResponse(); - $status = false; - } - if ($result->result_message !== 'OK') - { - $message->errors[] = 'ApiStore API error: ' . $result->result_code . ' ' . $result->result_message; - } - } - - return $status; - } - - /** - * API call. - * - * @param string $url - * @param array $data - * @param string $method (optional) - * @return object|false - */ - protected function _apiCall(string $url, array $data, string $method = 'POST') - { - // Build the request URL. - if ($data['version']) - { - $version = $data['version']; - unset($data['version']); - } - else - { - $version = 1; - } - $url = sprintf('http://api.apistore.co.kr/ppurio/%d/%s/%s', $version, trim($url, '/'), $this->_config['api_user']); - - // Set the API key in the header. - $headers = array( - 'x-waple-authorization' => $this->_config['api_key'], - ); - - // Send the API reqeust. - if ($method === 'GET') - { - if ($data) - { - $url .= '?' . http_build_query($data); - } - $this->_last_response = \FileHandler::getRemoteResource($url, null, 5, $method, null, $headers) ?: ''; - } - else - { - $this->_last_response = \FileHandler::getRemoteResource($url, $data, 5, $method, null, $headers) ?: ''; - } - $result = @json_decode($this->_last_response); - return $result ?: false; - } - - /** - * Fetch the last API response. - * - * @return string - */ - protected function _getLastResponse() - { - return $this->_last_response; - } -} diff --git a/common/scripts/cron.php b/common/scripts/cron.php index 563f71c70..19ea7b1c9 100644 --- a/common/scripts/cron.php +++ b/common/scripts/cron.php @@ -33,6 +33,7 @@ else } // Get queue configuration set by the administrator. +$display_errors = config('queue.display_errors') === false ? false : true; $timeout = (config('queue.interval') ?? 1) * 60; $process_count = config('queue.process_count') ?? 1; @@ -41,6 +42,14 @@ if (PHP_SAPI !== 'cli') { ignore_user_abort(true); set_time_limit(max(60, $timeout)); + if ($display_errors) + { + ini_set('display_errors', true); + } + if (Rhymix\Framework\Session::checkStart()) + { + Rhymix\Framework\Session::close(); + } } // Create multiple processes if configured. @@ -89,3 +98,9 @@ else { Rhymix\Framework\Queue::process($timeout); } + +// If called over the network, display a simple OK message to indicate success. +if (PHP_SAPI !== 'cli') +{ + echo "OK\n"; +} diff --git a/common/tpl/common_layout.html b/common/tpl/common_layout.html index 04a7e326d..b4d64176c 100644 --- a/common/tpl/common_layout.html +++ b/common/tpl/common_layout.html @@ -7,11 +7,12 @@ - -@foreach (Context::getMetaTag() as $val) - -@endforeach +@foreach (Context::getMetaTag() as $val) +@if ($val['is_before_title']) + +@endif +@endforeach {{ Context::getBrowserTitle() }} @@ -55,6 +56,11 @@ @endforeach +@foreach (Context::getMetaTag() as $val) +@if (!$val['is_before_title']) + +@endif +@endforeach @foreach (Context::getOpenGraphData() as $og_metadata) @endforeach diff --git a/modules/admin/controllers/systemconfig/Advanced.php b/modules/admin/controllers/systemconfig/Advanced.php index 4135a0a0f..8622d1599 100644 --- a/modules/admin/controllers/systemconfig/Advanced.php +++ b/modules/admin/controllers/systemconfig/Advanced.php @@ -109,6 +109,7 @@ class Advanced extends Base Context::set('minify_scripts', Config::get('view.minify_scripts')); Context::set('concat_scripts', Config::get('view.concat_scripts')); Context::set('jquery_version', Config::get('view.jquery_version')); + Context::set('outgoing_proxy', Config::get('other.proxy')); $this->setTemplateFile('config_advanced'); } @@ -215,6 +216,13 @@ class Advanced extends Base Config::set('locale.auto_select_lang', $vars->auto_select_lang === 'Y'); Config::set('locale.default_timezone', $vars->default_timezone); + // Proxy + $proxy = trim($vars->outgoing_proxy ?? ''); + if ($proxy !== '' && !preg_match('!^(https?|socks)://.+!', $proxy)) + { + throw new Exception('msg_invalid_outgoing_proxy'); + } + // Other settings Config::set('url.rewrite', intval($vars->use_rewrite)); Config::set('use_rewrite', $vars->use_rewrite > 0); @@ -226,6 +234,7 @@ class Advanced extends Base Config::set('view.concat_scripts', $vars->concat_scripts ?: 'none'); Config::set('view.delay_compile', intval($vars->delay_template_compile)); Config::set('view.jquery_version', $vars->jquery_version == 3 ? 3 : 2); + Config::set('other.proxy', $proxy); // Save if (!Config::save()) diff --git a/modules/admin/controllers/systemconfig/Queue.php b/modules/admin/controllers/systemconfig/Queue.php index 1422dbbc5..6ccc67d71 100644 --- a/modules/admin/controllers/systemconfig/Queue.php +++ b/modules/admin/controllers/systemconfig/Queue.php @@ -84,6 +84,9 @@ class Queue extends Base $driver_config[$conf_name] = $conf_value === '' ? null : $conf_value; } + // Validate error display setting. + $display_errors = Context::get('webcron_display_errors') === 'Y' ? true : false; + // Validate the interval. $interval = intval($vars->queue_interval ?? 1); if ($interval < 1 || $interval > 10) @@ -116,10 +119,10 @@ class Queue extends Base throw new Exception('msg_queue_driver_not_usable'); } - // Save system config. Config::set("queue.enabled", $enabled); Config::set("queue.driver", $driver); + Config::set("queue.display_errors", $display_errors); Config::set("queue.interval", $interval); Config::set("queue.process_count", $process_count); Config::set("queue.key", $key); diff --git a/modules/admin/lang/en.php b/modules/admin/lang/en.php index a2ff0abbb..1e54bc591 100644 --- a/modules/admin/lang/en.php +++ b/modules/admin/lang/en.php @@ -203,6 +203,9 @@ $lang->cache_truncate_method_empty = 'Delete content of cache folder'; $lang->about_cache_truncate_method = 'It is faster and more reliable to delete the cache folder itself.
Choose the option to delete content only if the cache folder cannot be deleted, e.g. it is a mountpoint.'; $lang->cache_control_header = 'Cache-Control header'; $lang->about_cache_control_header = 'Select the Cache-Control header to apply to HTML pages that generally should not be cached.
Deselecting some of these options may help in certain circumstances, but at the cost of displaying outdated information.'; +$lang->outgoing_proxy = 'Proxy Outgoing Requests'; +$lang->about_outgoing_proxy = 'Use a proxy to hide the server\'s IP when making requests to other sites.
This setting does not apply to modules that implement their own HTTP clients.'; +$lang->msg_invalid_outgoing_proxy = 'Proxy URL must begin with http://, https:// or socks://'; $lang->msg_cache_handler_not_supported = 'Your server does not support the selected cache method, or Rhymix is unable to use the cache with the given settings.'; $lang->msg_invalid_default_url = 'The default URL is invalid.'; $lang->msg_default_url_ssl_inconsistent = 'In order to use SSL always, the default URL must also begin with https://'; @@ -288,6 +291,8 @@ $lang->cmd_queue_enabled = 'Use Task Queue'; $lang->cmd_queue_enabled_help = 'The task queue will stop accepting new tasks if you uncheck the above.'; $lang->cmd_queue_driver = 'Queue Driver'; $lang->cmd_queue_driver_help = 'Select the driver for the task queue that suits your hosting environment and website needs.
Some drivers such as Redis will need the corresponding program to be installed on the server.'; +$lang->cmd_queue_webcron_display_errors = 'Display Webcron Errors'; +$lang->cmd_queue_webcron_display_errors_help = 'Show webcron errors to help with debugging, if you have difficulty accessing server error logs.'; $lang->cmd_queue_interval = 'Calling Interval'; $lang->cmd_queue_interval_help = 'Use a scheduler such as crontab or systemd timer to call the script on a set interval.
All tasks are processed as soon as possible regardless of the interval, but a short interval means quick recovery from any error.
For web-based cron, this should not exceed the max_execution_time setting in php.ini.
The max_execution_time on this server is %d seconds.'; $lang->cmd_queue_process_count = 'Process Count'; @@ -300,8 +305,8 @@ $lang->cmd_queue_config_keys['user'] = 'User'; $lang->cmd_queue_config_keys['pass'] = 'Password'; $lang->cmd_queue_config_keys['dbnum'] = 'DB Number'; $lang->msg_queue_instructions['same_as_php'] = '(same as PHP)'; -$lang->msg_queue_instructions['crontab1'] = 'Log into the server as the %s account and run crontab -e to paste the following content into your crontab. (DO NOT run it as root!)
The %s directory in the example should be replaced with a path where logs can be recorded.'; -$lang->msg_queue_instructions['crontab2'] = 'If you change the calling interval below, the crontab interval must be adjusted accordingly.'; +$lang->msg_queue_instructions['crontab1'] = 'Log into the server as the %s account and run crontab -e to paste the following content into your crontab. (DO NOT run it as root!)
If the account shown above cannot be logged into, such as apache or www-data, try running sudo crontab -e -u %s from a different account.'; +$lang->msg_queue_instructions['crontab2'] = 'The %s directory in the example should be replaced with a path where logs can be recorded.
If you change the calling interval below, the crontab interval must be adjusted accordingly.'; $lang->msg_queue_instructions['webcron'] = 'Configure an external cron service to make a GET request to the following URL every minute, or following the interval set below.
Check your logs to make sure that the cron service is reaching your website.'; $lang->msg_queue_instructions['systemd1'] = 'Put the following content in /etc/systemd/system/rhymix-queue.service'; $lang->msg_queue_instructions['systemd2'] = 'Put the following content in /etc/systemd/system/rhymix-queue.timer'; diff --git a/modules/admin/lang/ko.php b/modules/admin/lang/ko.php index 7fd335612..3cb78e0e1 100644 --- a/modules/admin/lang/ko.php +++ b/modules/admin/lang/ko.php @@ -204,6 +204,9 @@ $lang->cache_truncate_method_empty = '캐시 내용만 삭제'; $lang->about_cache_truncate_method = '캐시 폴더를 삭제하는 방법이 더 빠르고 안정적입니다.
내용만 삭제하는 방법은 램디스크를 캐시 폴더로 사용하는 등 폴더 자체를 삭제해서는 안 되는 경우에만 선택하십시오.'; $lang->cache_control_header = '캐시 컨트롤 헤더'; $lang->about_cache_control_header = '브라우저 캐시를 적용하지 않을 일반 HTML 페이지에 적용할 Cache-Control 헤더 내용을 선택할 수 있습니다.
선택을 해제하면 뒤로가기 등 특정한 상황에서 성능이 개선될 수도 있지만, 오래된 정보가 노출되는 등 부작용이 발생할 수도 있습니다.'; +$lang->outgoing_proxy = '외부 요청 프록시'; +$lang->about_outgoing_proxy = '외부 요청시 프록시를 사용하여 서버 IP 노출을 방지합니다.
코어에서 제공하는 클래스와 함수를 사용하지 않고 외부 요청을 자체 구현한 서드파티 자료에는 적용되지 않습니다.'; +$lang->msg_invalid_outgoing_proxy = '프록시 주소는 http://, https:// 또는 socks://로 시작해야 합니다.'; $lang->msg_cache_handler_not_supported = '선택하신 캐시 방식을 서버에서 지원하지 않거나, 주어진 정보로 캐시에 접속할 수 없습니다.'; $lang->msg_invalid_default_url = '기본 URL이 올바르지 않습니다.'; $lang->msg_default_url_ssl_inconsistent = 'SSL을 항상 사용하실 경우 기본 URL도 https://로 시작해야 합니다.'; @@ -284,6 +287,8 @@ $lang->cmd_queue_enabled = '비동기 작업 사용'; $lang->cmd_queue_enabled_help = '체크를 해제하면 더이상 작업을 접수하지 않습니다.'; $lang->cmd_queue_driver = '비동기 드라이버'; $lang->cmd_queue_driver_help = '비동기 작업을 관리할 방법을 설정합니다. 호스팅 환경과 사이트의 필요에 맞추어 선택하세요.
Redis 등 일부 드라이버는 서버에 해당 기능이 설치되어 있어야 사용할 수 있습니다.'; +$lang->cmd_queue_webcron_display_errors = '웹크론 오류 표시'; +$lang->cmd_queue_webcron_display_errors_help = '에러 로그를 확인하기 어려운 서버 환경인 경우, 웹크론 에러를 화면에 표시하여 문제 파악을 돕습니다.'; $lang->cmd_queue_interval = '호출 간격'; $lang->cmd_queue_interval_help = 'crontab, systemd timer, 웹크론 등을 사용하여 일정한 주기로 스크립트를 호출해 주십시오.
모든 비동기 작업은 호출 간격과 무관하게 실시간으로 처리되나, 호출 간격이 짧으면 장애 발생시 신속하게 복구됩니다.
웹크론 사용시에는 php.ini의 실행 시간 제한을 초과하지 않는 것이 좋습니다.
이 서버의 max_execution_time은 %d초로 설정되어 있습니다.'; $lang->cmd_queue_process_count = '프로세스 갯수'; @@ -296,8 +301,8 @@ $lang->cmd_queue_config_keys['user'] = '아이디'; $lang->cmd_queue_config_keys['pass'] = '암호'; $lang->cmd_queue_config_keys['dbnum'] = 'DB 번호'; $lang->msg_queue_instructions['same_as_php'] = 'PHP를 실행하는 계정과 동일한'; -$lang->msg_queue_instructions['crontab1'] = '%s 계정으로 서버에 로그인하여 crontab -e 명령을 실행한 후, 아래의 내용을 붙여넣으십시오. (root 권한으로 실행하지 마십시오.)
예제의 %s 디렉토리는 로그를 기록할 수 있는 경로로 변경하여 사용하십시오.'; -$lang->msg_queue_instructions['crontab2'] = '스크립트 호출 간격을 변경할 경우, 설정에 맞추어 crontab 실행 간격도 조절하여야 합니다.'; +$lang->msg_queue_instructions['crontab1'] = '%s 계정으로 서버에 로그인하여 crontab -e 명령을 실행한 후, 아래의 내용을 붙여넣으십시오. (root 권한으로 실행하지 마십시오!)
만약 apachewww-data처럼 로그인할 수 없는 계정이라면, 다른 계정에서 sudo crontab -e -u %s 명령을 실행해 볼 수 있습니다.'; +$lang->msg_queue_instructions['crontab2'] = '예제의 %s 디렉토리는 로그를 기록할 권한이 있는 경로로 변경하여 사용하십시오.
스크립트 호출 간격을 변경할 경우, 설정에 맞추어 crontab 실행 간격도 조절하여야 합니다.'; $lang->msg_queue_instructions['webcron'] = '아래의 URL을 1분 간격 또는 아래에서 설정한 호출 간격에 맞추어 GET으로 호출하도록 합니다.
웹크론 서비스가 방화벽이나 CDN 등에 의해 차단되지 않도록 주의하고, 정상적으로 호출되는지 서버 로그를 확인하십시오.'; $lang->msg_queue_instructions['systemd1'] = '/etc/systemd/system/rhymix-queue.service 파일에 아래와 같은 내용을 넣습니다.'; $lang->msg_queue_instructions['systemd2'] = '/etc/systemd/system/rhymix-queue.timer 파일에 아래와 같은 내용을 넣습니다.'; diff --git a/modules/admin/tpl/config_advanced.html b/modules/admin/tpl/config_advanced.html index 3378ea501..6373337fe 100644 --- a/modules/admin/tpl/config_advanced.html +++ b/modules/admin/tpl/config_advanced.html @@ -201,6 +201,13 @@

{$lang->about_cache_control_header}

+
+ +
+ +

{$lang->about_outgoing_proxy}

+
+
diff --git a/modules/admin/tpl/config_queue.html b/modules/admin/tpl/config_queue.html index eda100a42..4838a80f7 100644 --- a/modules/admin/tpl/config_queue.html +++ b/modules/admin/tpl/config_queue.html @@ -116,11 +116,11 @@ endif; }

- {sprintf($lang->msg_queue_instructions['crontab1'], $user_info['name'] ?? 'PHP', $user_info['dir'] . 'logs')|noescape} + {sprintf($lang->msg_queue_instructions['crontab1'], $user_info['name'] ?? 'PHP', $user_info['name'] ?? 'PHP')|noescape}

-
* * * * * php {\RX_BASEDIR}index.php common.cron >> {$user_info['dir']}logs{\DIRECTORY_SEPARATOR}queue.log 2>&1
+
* * * * * /usr/bin/php {\RX_BASEDIR}index.php common.cron >> {$user_info['dir']}logs{\DIRECTORY_SEPARATOR}queue.log 2>&1

- {$lang->msg_queue_instructions['crontab2']|noescape} + {sprintf($lang->msg_queue_instructions['crontab2'], $user_info['dir'] . 'logs')|noescape}

@@ -137,7 +137,7 @@ Description=Rhymix Queue Service [Service] -ExecStart=php {\RX_BASEDIR}index.php common.cron +ExecStart=/usr/bin/php {\RX_BASEDIR}index.php common.cron User={$user_info['name']}

{$lang->msg_queue_instructions['systemd2']|noescape} @@ -167,6 +167,21 @@ systemctl enable rhymix-queue.timer

+
+ +
+ + +

{$lang->cmd_queue_webcron_display_errors_help}

+
+
+
diff --git a/tests/unit/framework/SMSTest.php b/tests/unit/framework/SMSTest.php index 4c51e2e42..df947740a 100644 --- a/tests/unit/framework/SMSTest.php +++ b/tests/unit/framework/SMSTest.php @@ -16,13 +16,11 @@ class SMSTest extends \Codeception\Test\Unit { $drivers = Rhymix\Framework\SMS::getSupportedDrivers(); $this->assertTrue(isset($drivers['dummy'])); - $this->assertTrue(isset($drivers['apistore'])); $this->assertTrue(isset($drivers['coolsms'])); $this->assertTrue(isset($drivers['iwinv'])); $this->assertTrue(isset($drivers['solapi'])); $this->assertTrue(isset($drivers['ppurio'])); $this->assertEquals('Dummy', $drivers['dummy']['name']); - $this->assertTrue(in_array('api_user', $drivers['apistore']['required'])); $this->assertTrue(in_array('api_key', $drivers['coolsms']['required'])); $this->assertTrue(in_array('api_url', $drivers['iwinv']['required'])); $this->assertTrue(in_array('api_user', $drivers['ppurio']['required']));