From 9e1772285af5752ef27952c5ced911c9889bf383 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 19 Oct 2024 01:55:10 +0900 Subject: [PATCH 01/15] Try Ubuntu 24.04 for github CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From 8473eeabc3759edc8fcde69e9fad47eaadacde7b Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 25 Oct 2024 01:46:42 +0900 Subject: [PATCH 02/15] Fix serialization error when sending email by SMTP in background task https://xetown.com/questions/1842571 --- .../framework/drivers/mail/mailfunction.php | 15 +++---- common/framework/drivers/mail/smtp.php | 42 +++++++++++-------- 2 files changed, 30 insertions(+), 27 deletions(-) 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) { From f859b705372b71cd803a5e37b235da53d79dfc2c Mon Sep 17 00:00:00 2001 From: "Jin Hu, Baek" Date: Fri, 25 Oct 2024 02:05:29 +0900 Subject: [PATCH 03/15] delete ApiStore SMS Driver Apistore.co.kr service terminated in Nov, 2022 --- common/framework/drivers/sms/apistore.php | 141 ---------------------- tests/unit/framework/SMSTest.php | 2 - 2 files changed, 143 deletions(-) delete mode 100644 common/framework/drivers/sms/apistore.php 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/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'])); From 62ce762b4a8e57ae82a370d7cce0b9626f1879aa Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 25 Oct 2024 02:20:37 +0900 Subject: [PATCH 04/15] Fix #2422 remove APISTORE SMS API --- common/framework/drivers/sms/apistore.php | 141 ---------------------- 1 file changed, 141 deletions(-) delete mode 100644 common/framework/drivers/sms/apistore.php 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; - } -} From c7d7cdda8b72cb6a4fa4141841cd9872d17d081f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 25 Oct 2024 02:21:55 +0900 Subject: [PATCH 05/15] Remove APISTORE from unit tests --- tests/unit/framework/SMSTest.php | 2 -- 1 file changed, 2 deletions(-) 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'])); From e8c03318e47914f37e438221a5128db32f3d07a4 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 26 Oct 2024 16:40:31 +0900 Subject: [PATCH 06/15] Close session before webcron processing --- common/scripts/cron.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/scripts/cron.php b/common/scripts/cron.php index 563f71c70..f138a8be0 100644 --- a/common/scripts/cron.php +++ b/common/scripts/cron.php @@ -41,6 +41,10 @@ if (PHP_SAPI !== 'cli') { ignore_user_abort(true); set_time_limit(max(60, $timeout)); + if (Rhymix\Framework\Session::checkStart()) + { + Rhymix\Framework\Session::close(); + } } // Create multiple processes if configured. From 4ebaa6b0cbaa06df73011d845d27b6c5667c01f7 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 26 Oct 2024 16:50:14 +0900 Subject: [PATCH 07/15] Display errors and OK message in webcron result page --- common/scripts/cron.php | 11 +++++++++++ modules/admin/controllers/systemconfig/Queue.php | 5 ++++- modules/admin/lang/en.php | 2 ++ modules/admin/lang/ko.php | 2 ++ modules/admin/tpl/config_queue.html | 15 +++++++++++++++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/common/scripts/cron.php b/common/scripts/cron.php index f138a8be0..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,10 @@ 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(); @@ -93,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/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..44d44bf8b 100644 --- a/modules/admin/lang/en.php +++ b/modules/admin/lang/en.php @@ -288,6 +288,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'; diff --git a/modules/admin/lang/ko.php b/modules/admin/lang/ko.php index 7fd335612..06e3bfbd3 100644 --- a/modules/admin/lang/ko.php +++ b/modules/admin/lang/ko.php @@ -284,6 +284,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 = '프로세스 갯수'; diff --git a/modules/admin/tpl/config_queue.html b/modules/admin/tpl/config_queue.html index eda100a42..b82d902a9 100644 --- a/modules/admin/tpl/config_queue.html +++ b/modules/admin/tpl/config_queue.html @@ -167,6 +167,21 @@ systemctl enable rhymix-queue.timer +
+ +
+ + +

{$lang->cmd_queue_webcron_display_errors_help}

+
+
+
From ccf8806bb4f0d0f3e4d136a35d3290466c39e21c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 27 Oct 2024 17:05:10 +0900 Subject: [PATCH 08/15] Add outgoing proxy setting --- common/defaults/config.php | 4 +++- modules/admin/controllers/systemconfig/Advanced.php | 9 +++++++++ modules/admin/lang/en.php | 3 +++ modules/admin/lang/ko.php | 3 +++ modules/admin/tpl/config_advanced.html | 7 +++++++ 5 files changed, 25 insertions(+), 1 deletion(-) 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/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/lang/en.php b/modules/admin/lang/en.php index 44d44bf8b..78a478e17 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://'; diff --git a/modules/admin/lang/ko.php b/modules/admin/lang/ko.php index 06e3bfbd3..8ca26bb44 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://로 시작해야 합니다.'; 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}

+
+
From a23308b728bb3c44e8037115fcd2f34510ca20f3 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 27 Oct 2024 17:07:18 +0900 Subject: [PATCH 09/15] Use proxy setting when making outgoing HTTP requests --- common/framework/HTTP.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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']); From 8049d5a8c92b9b4333a484a4f5c766365cd1ee60 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 27 Oct 2024 22:25:41 +0900 Subject: [PATCH 10/15] Use absolute path of the php executable in crontab example --- modules/admin/tpl/config_queue.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/admin/tpl/config_queue.html b/modules/admin/tpl/config_queue.html index b82d902a9..440d29367 100644 --- a/modules/admin/tpl/config_queue.html +++ b/modules/admin/tpl/config_queue.html @@ -118,7 +118,7 @@

{sprintf($lang->msg_queue_instructions['crontab1'], $user_info['name'] ?? 'PHP', $user_info['dir'] . 'logs')|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}

@@ -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} From bf3d920a1d3121b35ef7e23eef47726461c0e3a0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 27 Oct 2024 22:32:58 +0900 Subject: [PATCH 11/15] Improve crontab instructions for queue --- modules/admin/lang/en.php | 4 ++-- modules/admin/lang/ko.php | 4 ++-- modules/admin/tpl/config_queue.html | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/admin/lang/en.php b/modules/admin/lang/en.php index 78a478e17..1e54bc591 100644 --- a/modules/admin/lang/en.php +++ b/modules/admin/lang/en.php @@ -305,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 8ca26bb44..3cb78e0e1 100644 --- a/modules/admin/lang/ko.php +++ b/modules/admin/lang/ko.php @@ -301,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_queue.html b/modules/admin/tpl/config_queue.html index 440d29367..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}

* * * * * /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}

From bf0093b56ac160c54d32f5c325aeadc4f76417e1 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 27 Oct 2024 22:50:02 +0900 Subject: [PATCH 12/15] Move most meta tags above the to below it #2419 --- classes/context/Context.class.php | 17 ++++++++--------- classes/display/HTMLDisplayHandler.php | 14 +++++++------- classes/module/ModuleHandler.class.php | 15 ++++++++------- common/tpl/common_layout.html | 9 ++++++++- 4 files changed, 31 insertions(+), 24 deletions(-) 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..33dc2fd83 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -506,7 +506,7 @@ class HTMLDisplayHandler $description = Context::getMetaTag('description'); } Context::addOpenGraphData('og:description', $description); - Context::addMetaTag('description', $description); + Context::addMetaTag('description', $description, false, false); } // Add metadata about this page. @@ -641,7 +641,7 @@ class HTMLDisplayHandler { if ($tag !== '') { - Context::addOpenGraphData('og:article:tag', $tag, false); + Context::addOpenGraphData('og:article:tag', $tag); } } @@ -663,7 +663,7 @@ class HTMLDisplayHandler // Add author name for articles. if ($page_type === 'article' && $permitted && config('seo.og_use_nick_name')) { - Context::addMetaTag('author', $oDocument->getNickName()); + Context::addMetaTag('author', $oDocument->getNickName(), false, false); Context::addOpenGraphData('og:article:author', $oDocument->getNickName()); } @@ -683,21 +683,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..42615a800 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'); } @@ -865,29 +866,29 @@ class ModuleHandler extends Handler $module_config = ModuleModel::getModuleConfig('module'); if (!empty($module_info->meta_keywords)) { - Context::addMetaTag('keywords', $module_info->meta_keywords); + Context::addMetaTag('keywords', $module_info->meta_keywords, false, false); } elseif (!empty($site_module_info->settings->meta_keywords)) { - Context::addMetaTag('keywords', $site_module_info->settings->meta_keywords); + Context::addMetaTag('keywords', $site_module_info->settings->meta_keywords, false, false); } elseif (!empty($module_config->meta_keywords)) { - Context::addMetaTag('keywords', $module_config->meta_keywords); + Context::addMetaTag('keywords', $module_config->meta_keywords, false, false); } // Set meta description. if (!empty($module_info->meta_description)) { - Context::addMetaTag('description', $module_info->meta_description); + Context::addMetaTag('description', $module_info->meta_description, false, false); } elseif (!empty($site_module_info->settings->meta_description)) { - Context::addMetaTag('description', $site_module_info->settings->meta_description); + Context::addMetaTag('description', $site_module_info->settings->meta_description, false, false); } elseif (!empty($module_config->meta_description)) { - Context::addMetaTag('description', $module_config->meta_description); + Context::addMetaTag('description', $module_config->meta_description, false, false); } } diff --git a/common/tpl/common_layout.html b/common/tpl/common_layout.html index 04a7e326d..2e5c093dd 100644 --- a/common/tpl/common_layout.html +++ b/common/tpl/common_layout.html @@ -7,11 +7,13 @@ <meta charset="utf-8"> <meta name="generator" content="Rhymix"> <meta name="viewport" content="{{ config('mobile.viewport') ?? HTMLDisplayHandler::DEFAULT_VIEWPORT }}" /> +<meta name="csrf-token" content="{!! \Rhymix\Framework\Session::getGenericToken() !!}" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> @foreach (Context::getMetaTag() as $val) +@if ($val['is_before_title']) <meta http-equiv="{{ $val['name'] }}"|if="$val['is_http_equiv']" name="{{ $val['name'] }}"|if="!$val['is_http_equiv']" content="{{ $val['content'] }}" /> +@endif @endforeach -<meta name="csrf-token" content="{!! \Rhymix\Framework\Session::getGenericToken() !!}" /> <!-- TITLE --> <title>{{ Context::getBrowserTitle() }} @@ -55,6 +57,11 @@ @endforeach +@foreach (Context::getMetaTag() as $val) +@if (!$val['is_before_title']) + +@endif +@endforeach @foreach (Context::getOpenGraphData() as $og_metadata) @endforeach From 9c92ad1f0573a9c1cbfdd5aaff97e976bbbb25f4 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 27 Oct 2024 22:59:12 +0900 Subject: [PATCH 13/15] Remove X-UA-Compatible meta tag in all browsers except IE 11 --- classes/display/HTMLDisplayHandler.php | 6 ++++++ common/tpl/common_layout.html | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index 33dc2fd83..b088f3d86 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; } diff --git a/common/tpl/common_layout.html b/common/tpl/common_layout.html index 2e5c093dd..25638bc49 100644 --- a/common/tpl/common_layout.html +++ b/common/tpl/common_layout.html @@ -8,7 +8,6 @@ - @foreach (Context::getMetaTag() as $val) @if ($val['is_before_title']) From e6bd94855e7f67b78fbd1035df4663b9f504aca0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 27 Oct 2024 23:01:07 +0900 Subject: [PATCH 14/15] Remove double escape of meta tag content --- common/tpl/common_layout.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/tpl/common_layout.html b/common/tpl/common_layout.html index 25638bc49..b4d64176c 100644 --- a/common/tpl/common_layout.html +++ b/common/tpl/common_layout.html @@ -10,7 +10,7 @@ @foreach (Context::getMetaTag() as $val) @if ($val['is_before_title']) - + @endif @endforeach @@ -58,7 +58,7 @@ @foreach (Context::getMetaTag() as $val) @if (!$val['is_before_title']) - + @endif @endforeach @foreach (Context::getOpenGraphData() as $og_metadata) From 76bb57ad80c0c33d91a1542000e29484f112bbd1 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 27 Oct 2024 23:07:23 +0900 Subject: [PATCH 15/15] Move some meta tags back above the MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 원칙적으로 순서는 관계가 없으나, description이 title보다 먼저 나와야 한다고 주장하는 변태들이 있으므로 빌미를 주지 않기 위해 상단으로 다시 옮김. --- classes/display/HTMLDisplayHandler.php | 4 ++-- classes/module/ModuleHandler.class.php | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index b088f3d86..4622d803f 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -512,7 +512,7 @@ class HTMLDisplayHandler $description = Context::getMetaTag('description'); } Context::addOpenGraphData('og:description', $description); - Context::addMetaTag('description', $description, false, false); + Context::addMetaTag('description', $description); } // Add metadata about this page. @@ -669,7 +669,7 @@ class HTMLDisplayHandler // Add author name for articles. if ($page_type === 'article' && $permitted && config('seo.og_use_nick_name')) { - Context::addMetaTag('author', $oDocument->getNickName(), false, false); + Context::addMetaTag('author', $oDocument->getNickName()); Context::addOpenGraphData('og:article:author', $oDocument->getNickName()); } diff --git a/classes/module/ModuleHandler.class.php b/classes/module/ModuleHandler.class.php index 42615a800..95c535c3d 100644 --- a/classes/module/ModuleHandler.class.php +++ b/classes/module/ModuleHandler.class.php @@ -866,29 +866,29 @@ class ModuleHandler extends Handler $module_config = ModuleModel::getModuleConfig('module'); if (!empty($module_info->meta_keywords)) { - Context::addMetaTag('keywords', $module_info->meta_keywords, false, false); + Context::addMetaTag('keywords', $module_info->meta_keywords); } elseif (!empty($site_module_info->settings->meta_keywords)) { - Context::addMetaTag('keywords', $site_module_info->settings->meta_keywords, false, false); + Context::addMetaTag('keywords', $site_module_info->settings->meta_keywords); } elseif (!empty($module_config->meta_keywords)) { - Context::addMetaTag('keywords', $module_config->meta_keywords, false, false); + Context::addMetaTag('keywords', $module_config->meta_keywords); } // Set meta description. if (!empty($module_info->meta_description)) { - Context::addMetaTag('description', $module_info->meta_description, false, false); + Context::addMetaTag('description', $module_info->meta_description); } elseif (!empty($site_module_info->settings->meta_description)) { - Context::addMetaTag('description', $site_module_info->settings->meta_description, false, false); + Context::addMetaTag('description', $site_module_info->settings->meta_description); } elseif (!empty($module_config->meta_description)) { - Context::addMetaTag('description', $module_config->meta_description, false, false); + Context::addMetaTag('description', $module_config->meta_description); } }