Compare commits

...

24 commits

Author SHA1 Message Date
Lastorder
2fd8df9de6
Merge branch 'rhymix:master' into master 2025-09-06 15:48:53 +09:00
Kijin Sung
173bed7c2a Version 2.1.27 2025-09-06 13:45:10 +09:00
Kijin Sung
893b08a742 Clarify message sending options when managing documents 2025-09-06 13:31:00 +09:00
Kijin Sung
87a3256fa0 Fix incorrect method and act 2025-09-06 13:08:04 +09:00
Kijin Sung
0e0f14d8e7 Clean up help text and format of admin_mail 2025-09-05 21:39:26 +09:00
Kijin Sung
0b33bb39e5 Clean up help text 2025-09-05 21:00:59 +09:00
Kijin Sung
4e6591af13 Fix most links pointing to default domain even when unregistered domain action is set to "display" 2025-09-05 18:03:54 +09:00
Kijin Sung
cafddbfc67 Fix undefined variable $layout_info in widget skin 2025-09-05 18:02:55 +09:00
Kijin Sung
c643e80447 Fix implicitly nullable type declarations in SwiftMailer
SwiftMailer는 지원이 종료되었으므로 차기 버전에서 Symfony Mailer로 교체 예정
2025-09-05 15:46:49 +09:00
Kijin Sung
629c2ab73a Don't generate warning if admin_mail setting does not exist 2025-09-04 22:00:20 +09:00
Kijin Sung
7a6400f39d Allow point reversion policies to be customized for each module 2025-09-04 21:47:28 +09:00
Kijin Sung
5f8586ec8f Fix empty values being set to 0 when module point config is modified 2025-09-04 21:31:09 +09:00
Kijin Sung
4a84f52edb Optimize index structure of counter_log table for quick querying when a new visitor arrives 2025-09-04 01:14:43 +09:00
Kijin Sung
9b18c48256 Fix types and sizes in counter module tables 2025-09-04 01:03:37 +09:00
Kijin Sung
fdb19f2e39 Fix broken link to document and comment when module belongs to a different domain 2025-09-04 00:58:56 +09:00
Kijin Sung
87e5a9e32a Delete AI-generated garbage 2025-09-04 00:45:11 +09:00
Kijin Sung
e3fea89dbc Fix broken link in to module in document, comment, file list when module belongs to a different domain 2025-09-04 00:42:54 +09:00
Kijin Sung
ae8a8662b1 Exclude admin actions from redirect based on document_srl 2025-09-03 23:34:29 +09:00
Kijin Sung
f4f81ba258 Fix type error when menu_srl or menu_item_srl does not exist 2025-09-02 23:18:14 +09:00
Kijin Sung
ee13ce551e Fix fatal error when preg_split() fails on a legacy template 2025-09-02 23:18:01 +09:00
Kijin Sung
51762315de Support "if" in <group> inside <groups> #2595 2025-09-01 00:31:20 +09:00
Kijin Sung
386f93fc6c Fix vote_log_view nad update_view display in permission panel in sitemap not showing currently selected groups 2025-09-01 00:23:15 +09:00
Kijin Sung
ba7a91b150 Fix incorrect redirect if a member action is added to a document URL
일부 레거시 스킨에서 글읽기 화면 -> 로그인 화면으로 전환하는 링크를
getUrl('act', 'dispMemberLoginForm') 이렇게 해 놓아서
mid=board&document_srl=123&act=dispMemberLoginForm 이런 형태의 URL이 생성됨.
이런 주소로 접속하면 document_srl을 기준으로 글읽기 화면의 짧은주소로
리다이렉트되는데, 이것은 원래의 의도에 어긋나므로
act를 기준으로 로그인 화면으로 리다이렉트하도록 변경함.
회원가입, 회원정보 보기, 쪽지함 보기 등 회원 메뉴에 소속된 대부분의 act 포함.
2025-09-01 00:05:13 +09:00
Kijin Sung
b79af31d0e Fix domain restriction not being enforced when $document_srl is set 2025-08-31 23:55:17 +09:00
57 changed files with 384 additions and 162 deletions

View file

@ -242,7 +242,7 @@ class Context
else
{
$site_module_info = new stdClass;
$site_module_info->domain = $_SERVER['HTTP_HOST'];
$site_module_info->domain = Rhymix\Framework\URL::getCurrentDomain();
$site_module_info->security = RX_SSL ? 'always' : 'none';
$site_module_info->settings = new stdClass;
$site_module_info->is_default_replaced = true;

View file

@ -147,7 +147,11 @@ class ModuleHandler extends Handler
return true;
case 'display':
// pass
$site_module_info->domain_srl = -1;
$site_module_info->domain = Rhymix\Framework\URL::getCurrentDomain();
$site_module_info->is_default_domain = 'N';
$site_module_info->is_default_replaced = true;
Context::set('site_module_info', $site_module_info);
}
}
}
@ -182,8 +186,11 @@ class ModuleHandler extends Handler
}
}
// Initialize module info.
$module_info = null;
// Get module info from document_srl.
if($this->document_srl)
if($this->document_srl && !preg_match('/Admin/', $this->act))
{
$module_info = $this->_checkDocumentSrl();
if ($module_info === false)
@ -191,15 +198,14 @@ class ModuleHandler extends Handler
return false;
}
}
else
{
$module_info = null;
}
// Get module info from mid.
if(!$module_info && $this->mid)
{
$module_info = ModuleModel::getModuleInfoByMid($this->mid);
}
// If the module does not belong to the current domain, throw a 404.
if($module_info && isset($module_info->domain_srl) && $module_info->domain_srl > -1)
{
if($module_info->domain_srl != $site_module_info->domain_srl)
@ -209,7 +215,6 @@ class ModuleHandler extends Handler
return true;
}
}
}
// Set module info as the default module for the domain.
if(!$module_info && !$this->module && !$this->mid)
@ -783,6 +788,14 @@ class ModuleHandler extends Handler
}
}
// Redirect if a member action is requested with an unnecessary document_srl. (For backward compatibility)
if(preg_match('/^disp(Member|Communication)/', $this->act))
{
Context::setCacheControl(0);
header('Location: ' . getNotEncodedUrl('document_srl', null), true, 301);
return false;
}
// Remove module info if a different module has already been selected for the current request.
if($this->module && $module_info->module !== $this->module)
{

View file

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

View file

@ -175,9 +175,10 @@ class DBQueryParser extends BaseParser
foreach ($xml->groups->children() as $tag)
{
$name = $tag->getName();
$ifvar = trim($tag['if'] ?? '') ?: null;
if ($name === 'group')
{
$query->groupby->columns[] = trim($tag['column'] ?? '');
$query->groupby->columns[] = [trim($tag['column'] ?? ''), $ifvar];
}
elseif ($name === 'having')
{

View file

@ -222,6 +222,14 @@ class Query extends VariableBase
$columns = array();
foreach ($this->groupby->columns as $column_name)
{
if (is_array($column_name))
{
list($column_name, $ifvar) = $column_name;
if ($ifvar && empty($this->_args[$ifvar]))
{
continue;
}
}
if (self::isValidColumnName($column_name))
{
$columns[] = self::quoteName($column_name);

View file

@ -278,7 +278,7 @@ class TemplateParser_v1
$skip = sprintf('(?!%s)', implode('|', ['marquee']));
$split_regex = "@(</?{$skip}[a-zA-Z](?>[^<>{}\"]+|<!--.*?-->.*?<!--.*?end-->|{[^}]*}|\"(?>'.*?'|.)*?\"|.)*?>)@s";
$nodes = preg_split($split_regex, $content, -1, PREG_SPLIT_DELIM_CAPTURE);
$nodes = preg_split($split_regex, $content, -1, PREG_SPLIT_DELIM_CAPTURE) ?: [];
for($idx = 1, $node_len = count($nodes); $idx < $node_len; $idx+=2)
{

View file

@ -106,7 +106,7 @@ class Swift_Encoder_QpEncoder implements Swift_Encoder
* @param Swift_CharacterStream $charStream to use for reading characters
* @param Swift_StreamFilter $filter if input should be canonicalized
*/
public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null)
public function __construct(Swift_CharacterStream $charStream, ?Swift_StreamFilter $filter = null)
{
$this->charStream = $charStream;
if (!isset(self::$safeMapShare[$this->getSafeMapShareId()])) {

View file

@ -56,7 +56,7 @@ interface Swift_KeyCache
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $is = null);
public function getInputByteStream($nsKey, $itemKey, ?Swift_InputByteStream $is = null);
/**
* Get data back out of the cache as a string.

View file

@ -105,7 +105,7 @@ class Swift_KeyCache_ArrayKeyCache implements Swift_KeyCache
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
public function getInputByteStream($nsKey, $itemKey, ?Swift_InputByteStream $writeThrough = null)
{
$is = clone $this->stream;
$is->setKeyCache($this);

View file

@ -128,7 +128,7 @@ class Swift_KeyCache_DiskKeyCache implements Swift_KeyCache
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
public function getInputByteStream($nsKey, $itemKey, ?Swift_InputByteStream $writeThrough = null)
{
$is = clone $this->stream;
$is->setKeyCache($this);

View file

@ -52,7 +52,7 @@ class Swift_KeyCache_NullKeyCache implements Swift_KeyCache
*
* @return Swift_InputByteStream
*/
public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null)
public function getInputByteStream($nsKey, $itemKey, ?Swift_InputByteStream $writeThrough = null)
{
}

View file

@ -49,7 +49,7 @@ class Swift_KeyCache_SimpleKeyCacheInputStream implements Swift_KeyCache_KeyCach
* @param string $bytes
* @param Swift_InputByteStream $is optional
*/
public function write($bytes, Swift_InputByteStream $is = null)
public function write($bytes, ?Swift_InputByteStream $is = null)
{
$this->keyCache->setString(
$this->nsKey, $this->itemKey, $bytes, Swift_KeyCache::MODE_APPEND

View file

@ -24,7 +24,7 @@ class Swift_Mime_ContentEncoder_QpContentEncoder extends Swift_Encoder_QpEncoder
* @param Swift_StreamFilter $filter if canonicalization should occur
* @param bool $dotEscape if dot stuffing workaround must be enabled
*/
public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null, $dotEscape = false)
public function __construct(Swift_CharacterStream $charStream, ?Swift_StreamFilter $filter = null, $dotEscape = false)
{
$this->dotEscape = $dotEscape;
parent::__construct($charStream, $filter);

View file

@ -200,7 +200,7 @@ abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header
*
* @return string
*/
protected function createPhrase(Swift_Mime_Header $header, $string, $charset, Swift_Mime_HeaderEncoder $encoder = null, $shorten = false)
protected function createPhrase(Swift_Mime_Header $header, $string, $charset, ?Swift_Mime_HeaderEncoder $encoder = null, $shorten = false)
{
// Treat token as exactly what was given
$phraseStr = $string;

View file

@ -42,7 +42,7 @@ class Swift_Mime_Headers_IdentificationHeader extends Swift_Mime_Headers_Abstrac
*
* @param string $name
*/
public function __construct($name, EmailValidator $emailValidator, Swift_AddressEncoder $addressEncoder = null)
public function __construct($name, EmailValidator $emailValidator, ?Swift_AddressEncoder $addressEncoder = null)
{
$this->setFieldName($name);
$this->emailValidator = $emailValidator;

View file

@ -39,7 +39,7 @@ class Swift_Mime_Headers_MailboxHeader extends Swift_Mime_Headers_AbstractHeader
*
* @param string $name of Header
*/
public function __construct($name, Swift_Mime_HeaderEncoder $encoder, EmailValidator $emailValidator, Swift_AddressEncoder $addressEncoder = null)
public function __construct($name, Swift_Mime_HeaderEncoder $encoder, EmailValidator $emailValidator, ?Swift_AddressEncoder $addressEncoder = null)
{
$this->setFieldName($name);
$this->setEncoder($encoder);

View file

@ -41,7 +41,7 @@ class Swift_Mime_Headers_ParameterizedHeader extends Swift_Mime_Headers_Unstruct
*
* @param string $name
*/
public function __construct($name, Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder = null)
public function __construct($name, Swift_Mime_HeaderEncoder $encoder, ?Swift_Encoder $paramEncoder = null)
{
parent::__construct($name, $encoder);
$this->paramEncoder = $paramEncoder;

View file

@ -39,7 +39,7 @@ class Swift_Mime_Headers_PathHeader extends Swift_Mime_Headers_AbstractHeader
*
* @param string $name
*/
public function __construct($name, EmailValidator $emailValidator, Swift_AddressEncoder $addressEncoder = null)
public function __construct($name, EmailValidator $emailValidator, ?Swift_AddressEncoder $addressEncoder = null)
{
$this->setFieldName($name);
$this->emailValidator = $emailValidator;

View file

@ -37,7 +37,7 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_CharsetObserver
*
* @param string|null $charset
*/
public function __construct(Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder, EmailValidator $emailValidator, $charset = null, Swift_AddressEncoder $addressEncoder = null)
public function __construct(Swift_Mime_HeaderEncoder $encoder, Swift_Encoder $paramEncoder, EmailValidator $emailValidator, $charset = null, ?Swift_AddressEncoder $addressEncoder = null)
{
$this->encoder = $encoder;
$this->paramEncoder = $paramEncoder;
@ -72,7 +72,7 @@ class Swift_Mime_SimpleHeaderFactory implements Swift_Mime_CharsetObserver
*
* @return Swift_Mime_Header
*/
public function createDateHeader($name, DateTimeInterface $dateTime = null)
public function createDateHeader($name, ?DateTimeInterface $dateTime = null)
{
$header = new Swift_Mime_Headers_DateHeader($name);
if (isset($dateTime)) {

View file

@ -76,7 +76,7 @@ class Swift_Mime_SimpleHeaderSet implements Swift_Mime_CharsetObserver
*
* @param string $name
*/
public function addDateHeader($name, DateTimeInterface $dateTime = null)
public function addDateHeader($name, ?DateTimeInterface $dateTime = null)
{
$this->storeHeader($name, $this->factory->createDateHeader($name, $dateTime));
}

View file

@ -50,7 +50,7 @@ class Swift_Plugins_AntiFloodPlugin implements Swift_Events_SendListener, Swift_
* @param int $sleep time
* @param Swift_Plugins_Sleeper $sleeper (not needed really)
*/
public function __construct($threshold = 99, $sleep = 0, Swift_Plugins_Sleeper $sleeper = null)
public function __construct($threshold = 99, $sleep = 0, ?Swift_Plugins_Sleeper $sleeper = null)
{
$this->setThreshold($threshold);
$this->setSleepTime($sleep);

View file

@ -76,7 +76,7 @@ class Swift_Plugins_ThrottlerPlugin extends Swift_Plugins_BandwidthMonitorPlugin
* @param Swift_Plugins_Sleeper $sleeper (only needed in testing)
* @param Swift_Plugins_Timer $timer (only needed in testing)
*/
public function __construct($rate, $mode = self::BYTES_PER_MINUTE, Swift_Plugins_Sleeper $sleeper = null, Swift_Plugins_Timer $timer = null)
public function __construct($rate, $mode = self::BYTES_PER_MINUTE, ?Swift_Plugins_Sleeper $sleeper = null, ?Swift_Plugins_Timer $timer = null)
{
$this->rate = $rate;
$this->mode = $mode;

View file

@ -46,7 +46,7 @@ abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport
*
* @param string $localDomain
*/
public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1', Swift_AddressEncoder $addressEncoder = null)
public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1', ?Swift_AddressEncoder $addressEncoder = null)
{
$this->buffer = $buf;
$this->eventDispatcher = $dispatcher;

View file

@ -51,7 +51,7 @@ class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTranspo
* @param Swift_Transport_EsmtpHandler[] $extensionHandlers
* @param string $localDomain
*/
public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1', Swift_AddressEncoder $addressEncoder = null)
public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1', ?Swift_AddressEncoder $addressEncoder = null)
{
parent::__construct($buf, $dispatcher, $localDomain, $addressEncoder);
$this->setExtensionHandlers($extensionHandlers);

View file

@ -36,7 +36,7 @@ class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTran
*
* @param string $localDomain
*/
public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1', Swift_AddressEncoder $addressEncoder = null)
public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher, $localDomain = '127.0.0.1', ?Swift_AddressEncoder $addressEncoder = null)
{
parent::__construct($buf, $dispatcher, $localDomain, $addressEncoder);
}

View file

@ -24,7 +24,7 @@ class Swift_Transport_SpoolTransport implements Swift_Transport
/**
* Constructor.
*/
public function __construct(Swift_Events_EventDispatcher $eventDispatcher, Swift_Spool $spool = null)
public function __construct(Swift_Events_EventDispatcher $eventDispatcher, ?Swift_Spool $spool = null)
{
$this->eventDispatcher = $eventDispatcher;
$this->spool = $spool;

View file

@ -88,6 +88,7 @@ class BoardAdminController extends Board {
$args->meta_description = trim(utf8_normalize_spaces($args->meta_description));
$args->header_text = Rhymix\Modules\Admin\Models\Utility::cleanHeaderAndFooterScripts($args->header_text ?? '');
$args->footer_text = Rhymix\Modules\Admin\Models\Utility::cleanHeaderAndFooterScripts($args->footer_text ?? '');
$args->admin_mail = implode(', ', array_map('trim', explode(',', $args->admin_mail ?? '')));
// if there is an existed module
if ($args->module_srl && $module_info->module_srl != $args->module_srl)

View file

@ -258,7 +258,7 @@ class BoardController extends Board
$oDocument->setGrantForSession();
// send an email to admin user
if ($this->module_info->admin_mail && config('mail.default_from'))
if (isset($this->module_info->admin_mail) && $this->module_info->admin_mail && config('mail.default_from'))
{
$browser_title = Context::replaceUserLang($this->module_info->browser_title);
$mail_title = sprintf(lang('msg_document_notify_mail'), $browser_title, cut_str($obj->title, 20, '...'));

View file

@ -18,7 +18,7 @@ $lang->consultation = 'Consultation';
$lang->use_consultation = 'Use as Consultation Board';
$lang->secret = 'Secret';
$lang->thisissecret = 'This is a secret post.';
$lang->admin_mail = 'Administrator\'s Mail';
$lang->admin_mail = 'Administrator Mail';
$lang->update_log = 'Update Log';
$lang->last_updater = 'Latest Update by';
$lang->cmd_board_list = 'Boards List';
@ -40,7 +40,7 @@ $lang->about_anonymous_name = 'You can customize the anonymous name that will be
$lang->about_board = 'This module is for creating and managing boards.';
$lang->about_consultation = 'Members who are not managers will only see their own posts.<br>When this feature is enabled, non-members cannot read or write any posts on this board.';
$lang->about_secret = 'Users will be able to write secret posts or comments.';
$lang->about_admin_mail = 'A mail will be sent when a post or comment is submitted. Mails can be sent to mutiple mail addresses if connecting addresses with commas(,).';
$lang->about_admin_mail = 'Send an e-mail when a new post or comment is submitted. Separate multiple recipients with a comma.';
$lang->about_list_config = 'If using list-style skin, you may arrange items to display. However, this feature might not be availble for non-official skins. If you double-click target items and display items, then you can add / remove them';
$lang->about_use_status = 'Please select status that can be selected when you write a post.';
$lang->about_protect_comment = 'Prevent updating or deleting a comment if it has children.';

View file

@ -40,7 +40,7 @@ $lang->about_anonymous_name = '익명 기능을 사용할 때 표시할 익명
$lang->about_board = '게시판을 생성하고 관리할 수 있습니다.';
$lang->about_consultation = '관리 권한이 없는 회원은 자신이 쓴 글만 보이도록 합니다.<br>상담 기능 사용시 비회원 글쓰기는 금지됩니다.';
$lang->about_secret = '게시판 및 댓글의 비밀글 기능을 사용할 수 있도록 합니다.';
$lang->about_admin_mail = '글이나 댓글이 등록될때 등록된 메일주소로 메일이 발송됩니다. 콤마(,)로 연결시 다수의 메일주소로 발송할 수 있습니다.';
$lang->about_admin_mail = '새 글이나 댓글이 등록되면 지정된 메일 주소로 알림을 받습니다. 여러 메일 주소를 지정하려면 쉼표(,)로 구분하세요.';
$lang->about_list_config = '게시판의 목록형식 사용시 원하는 항목들로 배치를 할 수 있습니다. 단 스킨에서 지원하지 않는 경우 불가능합니다. 대상항목/ 표시항목의 항목을 더블클릭하면 추가/ 제거가 됩니다.';
$lang->about_use_status = '글 작성 시 선택할 수 있는 상태를 지정해주세요.';
$lang->about_protect_comment = '댓글의 댓글이 있을경우 해당댓글을 삭제 및 수정을 할 수 없도록 합니다.';

View file

@ -68,8 +68,6 @@
<option value="0">{$lang->notuse}</option>
<option loop="$layout_list => $key, $val" value="{$val->layout_srl}" selected="selected"|cond="$module_info->layout_srl == $val->layout_srl">{$val->title} ({$val->layout})</option>
</select>
<a href="#layout_help" class="x_icon-question-sign" data-toggle>{$lang->help}</a>
<p id="layout_help" class="x_help-block x_hide">{$lang->about_layout}</p>
</div>
</div>
<div class="x_control-group">
@ -423,8 +421,7 @@
<label class="x_control-label" for="admin_mail">{$lang->admin_mail}</label>
<div class="x_controls">
<input type="text" name="admin_mail" id="admin_mail" value="{$module_info->admin_mail}" />
<a href="#admin_mail_help" class="x_icon-question-sign" data-toggle>{$lang->help}</a>
<p id="admin_mail_help" class="x_help-block x_hide">{$lang->about_admin_mail}</p>
<p class="x_help-block">{$lang->about_admin_mail}</p>
</div>
</div>
<div class="x_control-group">
@ -434,16 +431,13 @@
<option value="0">{$lang->notuse}</option>
<option loop="$module_category => $key, $val" value="{$key}" selected="selected"|cond="$module_info->module_category_srl == $key">{$val->title}</option>
</select>
<a href="#module_category_help" class="x_icon-question-sign" data-toggle>{$lang->help}</a>
<p id="module_category_help" class="x_help-block x_hide">{$lang->about_module_category}</p>
<p class="x_help-block">{$lang->about_module_category}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="description">{$lang->description}</label>
<div class="x_controls">
<textarea name="description" id="description" rows="4" cols="42" placeholder="{$lang->about_description}" style="vertical-align:top">{$module_info->description}</textarea>
<a href="#description_help" class="x_icon-question-sign" data-toggle>{$lang->help}</a>
<p id="description_help" class="x_help-block x_hide">{$lang->about_description}</p>
</div>
</div>
</section>

View file

@ -3,6 +3,7 @@
<table name="modules" />
</tables>
<columns>
<column name="domain_srl" />
<column name="module_srl" />
<column name="mid" />
<column name="browser_title" />

View file

@ -49,10 +49,16 @@ xe.lang.msg_empty_search_keyword = '{$lang->msg_empty_search_keyword}';
<tr loop="$comment_list => $no, $val">
{@ $comment = $val->getContentText(200)}
<td>
<block cond="isset($module_list[$val->module_srl])">
{@ $comment_url = getUrl(['mid' => $module_list[$val->module_srl]->mid ?? null, 'document_srl' => $val->document_srl, 'comment_srl' => $val->comment_srl]) . '#comment_' . $val->comment_srl}
<!--@if(isset($module_list[$val->module_srl]))-->
<!--@if($module_list[$val->module_srl]->domain_srl == -1 || $module_list[$val->module_srl]->domain_srl == $site_module_info->domain_srl)-->
<a href="{getUrl('', 'mid', $module_list[$val->module_srl]->mid)}" target="_blank">{$module_list[$val->module_srl]->browser_title}</a> -
</block>
<a href="{getUrl('','document_srl',$val->document_srl,'comment_srl',$val->comment_srl)}#comment_{$val->comment_srl}" target="_blank"><!--@if(strlen($comment))-->{$comment}<!--@else--><em>{$lang->no_text_comment}</em><!--@end--></a>
<!--@else-->
{@ $module_url = ModuleModel::getDomainByModuleSrl($val->module_srl); $comment_url = $module_url . $comment_url; }
<a href="{$module_url}{getUrl('', 'mid', $module_list[$val->module_srl]->mid)}" target="_blank">{$module_list[$val->module_srl]->browser_title}</a> -
<!--@endif-->
<!--@endif-->
<a href="{$comment_url}" target="_blank"><!--@if(strlen($comment))-->{$comment}<!--@else--><em>{$lang->no_text_comment}</em><!--@end--></a>
</td>
<td class="nowr">
<span cond="$val->get('member_srl') <= 0">{$val->getNickName()}</span>

View file

@ -31,6 +31,20 @@ class counter extends ModuleObject
return true;
}
// Index optimization
if (!$oDB->isIndexExists('counter_log', 'idx_regdate_ipaddress'))
{
return true;
}
if ($oDB->isIndexExists('counter_log', 'idx_site_counter_log'))
{
return true;
}
if ($oDB->isIndexExists('counter_log', 'idx_counter_log'))
{
return true;
}
return false;
}
@ -46,6 +60,20 @@ class counter extends ModuleObject
{
$oDB->dropTable('counter_site_status');
}
// Index optimization
if (!$oDB->isIndexExists('counter_log', 'idx_regdate_ipaddress'))
{
$oDB->addIndex('counter_log', 'idx_regdate_ipaddress', ['regdate(8)', 'ipaddress']);
}
if ($oDB->isIndexExists('counter_log', 'idx_site_counter_log'))
{
$oDB->dropIndex('counter_log', 'idx_site_counter_log');
}
if ($oDB->isIndexExists('counter_log', 'idx_counter_log'))
{
$oDB->dropIndex('counter_log', 'idx_counter_log');
}
}
/**

View file

@ -1,6 +1,8 @@
<table name="counter_log">
<column name="site_srl" type="number" size="11" notnull="notnull" default="0" index="idx_site_counter_log" />
<column name="ipaddress" type="varchar" size="60" notnull="notnull" index="idx_site_counter_log" />
<column name="regdate" type="date" index="idx_counter_log" />
<column name="id" type="bigint" notnull="notnull" primary_key="primary_key" auto_increment="auto_increment" />
<column name="site_srl" type="bigint" notnull="notnull" default="0" />
<column name="regdate" type="date" notnull="notnull" />
<column name="ipaddress" type="varchar" size="60" notnull="notnull" />
<column name="user_agent" type="varchar" size="250" />
<index name="idx_regdate_ipaddress" columns="regdate(8),ipaddress" />
</table>

View file

@ -1,5 +1,5 @@
<table name="counter_status">
<column name="regdate" type="number" size="11" notnull="notnull" primary_key="primary_key" />
<column name="unique_visitor" type="number" size="11" default="0" />
<column name="pageview" type="number" size="11" default="0" />
<column name="regdate" type="int" notnull="notnull" primary_key="primary_key" />
<column name="unique_visitor" type="bigint" notnull="notnull" default="0" />
<column name="pageview" type="bigint" notnull="notnull" default="0" />
</table>

View file

@ -3408,8 +3408,12 @@ class DocumentController extends Document
$obj->target_module_srl = intval(Context::get('module_srl') ?: Context::get('target_module_srl'));
$obj->target_category_srl = intval(Context::get('target_category_srl'));
$obj->manager_message = Context::get('message_content') ? nl2br(escape(strip_tags(Context::get('message_content')))) : '';
$obj->send_message = $obj->manager_message || Context::get('send_default_message') == 'Y';
$obj->send_message = Context::get('send_message') ?? 'default';
$obj->return_message = '';
if (Context::get('send_default_message') === 'Y')
{
$obj->send_message = 'default';
}
// Check permission of target module
if($obj->target_module_srl)
@ -3540,40 +3544,42 @@ class DocumentController extends Document
// Send a message
$actions = lang('default_message_verbs');
if(isset($actions[$obj->type]) && $obj->send_message)
if(isset($actions[$obj->type]) && $obj->send_message !== 'none')
{
// Set message
$title = sprintf(lang('default_message_format'), $actions[$obj->type]);
$content = <<< Content
$content = <<<EOT
<div style="padding:10px 0;"><strong>{$title}</strong></div>
<p>{$obj->manager_message}</p>
<hr>
<ul>%1\$s</ul>
Content;
$document_item = '<li><a href="%1$s">%2$s</a></li>';
EOT;
// Set recipient
$recipients = array();
foreach ($obj->document_list as $document_srl => $oDocument)
{
if(!($member_srl = abs($oDocument->get('member_srl'))) || $logged_info->member_srl == $member_srl)
$member_srl = abs($oDocument->get('member_srl'));
if(!$member_srl || $logged_info->member_srl == $member_srl)
{
continue;
}
if(!isset($recipients[$member_srl]))
{
$recipients[$member_srl] = array();
}
$recipients[$member_srl][] = sprintf($document_item, $oDocument->getPermanentUrl(), $oDocument->getTitleText());
$recipients[$member_srl][] = vsprintf('<li><a href="%1$s">%2$s</a></li>', [
escape($oDocument->getPermanentUrl()),
$oDocument->getTitleText(),
]);
}
// Send
$oCommunicationController = CommunicationController::getInstance();
foreach ($recipients as $member_srl => $items)
{
$oCommunicationController->sendMessage($this->user->member_srl, $member_srl, $title, sprintf($content, implode('', $items)), true, null, false);
$content = sprintf($content, implode('', $items));
$oCommunicationController->sendMessage($this->user->member_srl, $member_srl, $title, $content, true, null, false);
}
}

View file

@ -104,7 +104,9 @@ $lang->select_module_id = 'Select a module ID.';
$lang->select_category = 'Select a category.';
$lang->category_description = 'Category Description';
$lang->no_title_document = 'No title in this document.';
$lang->send_default_message = 'Send the default message';
$lang->send_default_message = 'Send default message';
$lang->send_custom_message = 'Send message with content below';
$lang->send_no_message = 'Don\'t send message';
$lang->msg_admin_document_no_move_to_trash = 'You are not allowed to move the superadmin\'s article to the trash bin.';
$lang->default_message_format = '%1$s the document by manager.';
$lang->default_message_verbs['move'] = 'moved';

View file

@ -96,7 +96,9 @@ $lang->select_category = '분류를 선택하세요.';
$lang->category_description = '분류 설명';
$lang->no_title_document = '제목이 없는 문서입니다.';
$lang->msg_admin_document_no_move_to_trash = '최고 관리자의 게시물을 휴지통으로 이동시킬 권한이 없습니다.';
$lang->send_default_message = '기본 내용으로만 쪽지 보내기';
$lang->send_default_message = '기본 내용으로 쪽지 보내기';
$lang->send_custom_message = '아래 내용으로 쪽지 보내기';
$lang->send_no_message = '쪽지 보내지 않음';
$lang->default_message_format = '관리자에 의해 게시물이 %1$s되었습니다.';
$lang->default_message_verbs['move'] = '다른 게시판으로 이동';
$lang->default_message_verbs['copy'] = '다른 게시판에 복사';

View file

@ -3,6 +3,7 @@
<table name="modules" />
</tables>
<columns>
<column name="domain_srl" />
<column name="module_srl" />
<column name="mid" />
<column name="browser_title" />

View file

@ -1,8 +1,9 @@
<!--%import("filter/manage_checked_document.xml")-->
<load target="js/document_admin.js" />
{@Context::addMetaTag('viewport', 'width=device-width', FALSE);}
<form action="./" method="get" id="fo_management">
<form action="./" method="post" id="fo_management">
<input type="hidden" name="module" value="document" />
<input type="hidden" name="act" value="procDocumentManageCheckedDocument" />
<input type="hidden" name="type" value="" />
<div class="x_modal-header">
<h1>{$lang->cmd_manage_document}</h1>
@ -40,9 +41,21 @@
</div>
<div class="x_control-group">
<label class="x_control-label" for="message_content">{$lang->cmd_send_message}</label>
<div class="x_controls" style="margin-right:14px">
<textarea name="message_content" id="message_content" rows="4" cols="42" style="width:100%"></textarea>
<label for="send_default_message" class="x_inline"><input type="checkbox" name="send_default_message" id="send_default_message" value="Y" checked="checked" /> {$lang->send_default_message}</label>
<div class="x_controls" id="message_options">
<label for="send_default_message" class="x_inline">
<input type="radio" name="send_message" id="send_default_message" value="default" checked="checked" />
{$lang->send_default_message}
</label>
<label for="send_custom_message" class="x_inline">
<input type="radio" name="send_message" id="send_custom_message" value="custom" />
{$lang->send_custom_message}
</label>
<label for="send_no_message" class="x_inline">
<input type="radio" name="send_message" id="send_no_message" value="none" />
{$lang->send_no_message}
</label>
<br />
<textarea name="message_content" id="message_content" rows="4" cols="42" class="x_full-width"></textarea>
</div>
</div>
</div>
@ -60,20 +73,19 @@
</form>
<script>
jQuery(function($){
var message_content_area = $('#message_content');
if($('#send_default_message').is(':checked'))
{
message_content_area.prop("disabled", true);
}
$('#send_default_message').change(function(){
if($(this).is(':checked')){
message_content_area.prop("disabled", true);
} else {
const message_options = $('#message_options');
const message_content_area = $('#message_content');
message_content_area.prop('disabled', true);
message_options.on('change', 'input[name="send_message"]', function(){
if($('#send_custom_message').is(':checked')) {
message_content_area.prop("disabled", false);
} else {
message_content_area.prop("disabled", true);
}
});
<!--@if($module_srl > 0)-->
doGetCategoryFromModule({$module_srl});
<!--@end-->
const module_srl = {intval($module_srl)};
if (module_srl > 0) {
doGetCategoryFromModule(module_srl);
}
});
</script>

View file

@ -50,11 +50,18 @@ xe.lang.msg_empty_search_keyword = '{$lang->msg_empty_search_keyword}';
<tr loop="$document_list => $no, $oDocument">
<td class="title">
<!--@if(isset($module_list[$oDocument->get('module_srl')]))-->
<!--@if($module_list[$oDocument->get('module_srl')]->domain_srl == -1 || $module_list[$oDocument->get('module_srl')]->domain_srl == $site_module_info->domain_srl)-->
<a href="{$oDocument->getPermanentUrl()}" target="_blank"><!--@if(trim($oDocument->getTitleText()) !== '')-->{escape($oDocument->getTitleText(), false)}<!--@else--><em>{$lang->no_title_document}</em><!--@end--></a>
<span cond="isset($module_list[$oDocument->get('module_srl')])">
- <a href="{getUrl('', 'mid', $module_list[$oDocument->get('module_srl')]->mid)}" target="_blank">{$module_list[$oDocument->get('module_srl')]->browser_title}</a>
</span></td>
<!--@else-->
<a href="{ModuleModel::getDomainByModuleSrl($oDocument->get('module_srl'))}{getUrl('', 'mid', $module_list[$oDocument->get('module_srl')]->mid, 'document_srl', $oDocument->document_srl)}" target="_blank"><!--@if(trim($oDocument->getTitleText()) !== '')-->{escape($oDocument->getTitleText(), false)}<!--@else--><em>{$lang->no_title_document}</em><!--@end--></a>
- <a href="{ModuleModel::getDomainByModuleSrl($oDocument->get('module_srl'))}{getUrl('', 'mid', $module_list[$oDocument->get('module_srl')]->mid)}" target="_blank">{$module_list[$oDocument->get('module_srl')]->browser_title}</a>
<!--@endif-->
<!--@else-->
<a href="{$oDocument->getPermanentUrl()}" target="_blank"><!--@if(trim($oDocument->getTitleText()) !== '')-->{escape($oDocument->getTitleText(), false)}<!--@else--><em>{$lang->no_title_document}</em><!--@end--></a>
<!--@endif-->
</td>
<td class="nowr">
<span cond="$oDocument->get('member_srl') <= 0">{$oDocument->getNickName()}</span>
<a href="#popup_menu_area" class="member_{abs($oDocument->get('member_srl'))}" cond="$oDocument->get('member_srl') < 0">({$member_nick_name[abs($oDocument->get('member_srl'))]})</a>

View file

@ -3,6 +3,7 @@
<table name="modules" />
</tables>
<columns>
<column name="domain_srl" />
<column name="module_srl" />
<column name="mid" />
<column name="browser_title" />

View file

@ -84,11 +84,18 @@ xe.lang.msg_empty_search_keyword = '{$lang->msg_empty_search_keyword}';
<!--@if(preg_match('/^(?:ev:)?doc$/', $val->upload_target_type) && $document_list[$document_srl] && $document_list[$document_srl]->get('module_srl') == $document_list[$document_srl]->get('member_srl'))-->[{$lang->cmd_temp_save}] <!--@end-->
<!--@if(preg_match('/^(?:ev:)?doc$/', $val->upload_target_type) && $document_list[$document_srl] && $document_list[$document_srl]->get('module_srl') == 0)-->[{$lang->cmd_trash}] <!--@end-->
<!--@if($val->module_srl && isset($module_list[$val->module_srl]))-->
<a href="{getUrl('', 'mid', $module_list[$val->module_srl]->mid)}" target="_blank">{$module_list[$val->module_srl]->browser_title}</a>
<!--@end-->
<!--@if(isset($module_list[$val->module_srl]))-->
<!--@if($module_list[$val->module_srl]->domain_srl == -1 || $module_list[$val->module_srl]->domain_srl == $site_module_info->domain_srl)-->
<a href="{getUrl('', 'mid', $module_list[$val->module_srl]->mid)}" target="_blank">{$module_list[$val->module_srl]->browser_title}</a> -
<!--@else-->
{@ $module_url = ModuleModel::getDomainByModuleSrl($val->module_srl); $move_uri = $module_url . $move_uri; }
<a href="{$module_url}{getUrl('', 'mid', $module_list[$val->module_srl]->mid)}" target="_blank">{$module_list[$val->module_srl]->browser_title}</a> -
<!--@endif-->
<!--@endif-->
<!--@if($document_list[$document_srl] && preg_match('/^(?:ev:)?(?:doc|com)$/', $val->upload_target_type))-->
- <!--@if($document_list[$document_srl]->get('module_srl') != $document_list[$document_srl]->get('member_srl'))--><a href="{$move_uri}" target="_blank">{$document_list[$document_srl]->getTitle()}</a><!--@else-->{$document_list[$document_srl]->getTitle()}<!--@end-->
<!--@if($document_list[$document_srl]->get('module_srl') != $document_list[$document_srl]->get('member_srl'))--><a href="{$move_uri}" target="_blank">{$document_list[$document_srl]->getTitle()}</a><!--@else-->{$document_list[$document_srl]->getTitle()}<!--@end-->
<!--@end-->
<!--@end-->
</th>

View file

@ -2374,7 +2374,7 @@ jQuery(function($){
case 'member':
case 'not_member':
case 'site':
$node.find('._group_loggedin').prop('selected', true);
//$node.find('._group_loggedin').prop('selected', true);
break;
case 'manager':
case 'root':

View file

@ -80,8 +80,7 @@ $lang->msg_extra_name_exists = 'Already registered field ID. Please input anothe
$lang->about_browser_title = 'It will be shown in the browser title. It will be also used in a RSS/Trackback.';
$lang->about_mid = 'The module name must begin with a Latin alphabet, and only consist of Latin alphabets, numerals, and underscores.';
$lang->about_default = 'If checked, this module will be shown when users access the site without mid value (mid=NoValue).';
$lang->about_module_category = 'It enables you to manage it through a module category.
The URL for the module manager is <a href="./?module=admin&amp;act=dispModuleAdminCategory">Module category </a>.';
$lang->about_module_category = 'Module categories can be edited in <a href="./?module=admin&amp;act=dispModuleAdminCategory" target="_blank">Module category</a>.';
$lang->about_description = 'This description is only for a manager.';
$lang->about_header_text = 'The contents will be shown on the top of the module. (html tags available)';
$lang->about_footer_text = 'The contents will be shown on the bottom of the module. (html tags available)';

View file

@ -80,7 +80,7 @@ $lang->msg_extra_name_exists = '이미 존재하는 확장 변수 ID입니다.
$lang->about_browser_title = '브라우저 제목에 나타나는 값입니다. RSS/Trackback에서도 사용됩니다.';
$lang->about_mid = 'URL상의 모듈 이름은 영문, 숫자, _ 만으로 이루어져야 하며, 첫 글자는 반드시 영문 알파벳이어야 합니다.';
$lang->about_default = '선택하면 사이트에 mid값 없이 접속하였을 경우 기본으로 보여줍니다.';
$lang->about_module_category = '분류를 통한 관리를 할 수 있도록 합니다. 모듈 분류 관리는 <a href="./?module=admin&amp;act=dispModuleAdminCategory">모듈분류</a>에서 할 수 있습니다.';
$lang->about_module_category = '모듈 분류 관리는 <a href="./?module=admin&amp;act=dispModuleAdminCategory" target="_blank">모듈 분류</a>에서 할 수 있습니다.';
$lang->about_description = '관리용으로 사용되는 설명입니다.';
$lang->about_header_text = '콘텐츠 상단에 표시되는 내용입니다. (HTML 태그 사용 가능)';
$lang->about_footer_text = '콘텐츠 하단에 표시되는 내용입니다. (HTML 태그 사용 가능)';

View file

@ -656,8 +656,8 @@ class ModuleController extends Module
{
unset($args);
$args = new stdClass;
$args->menu_srl = $output->data->menu_srl;
$args->menu_item_srl = $output->data->menu_item_srl;
$args->menu_srl = $output->data->menu_srl ?: 0;
$args->menu_item_srl = $output->data->menu_item_srl ?: 0;
$args->is_force = 'N';
$oMenuAdminController = getAdminController('menu');

View file

@ -12,6 +12,7 @@ class ModuleModel extends Module
*/
public static $_mid_map = [];
public static $_module_srl_map = [];
public static $_domain_map = [];
/**
* @brief Initialization
@ -708,13 +709,13 @@ class ModuleModel extends Module
$mid = Rhymix\Framework\Cache::get('site_and_module:module_srl_mid:' . $module_srl);
if (isset($mid))
{
return $mid;
return self::$_module_srl_map[$module_srl] = $mid;
}
$args = new stdClass;
$args->module_srls = $module_srl;
$output = executeQuery('module.getModuleInfoByModuleSrl', $args, ['mid']);
if ($output->data)
if (is_object($output->data))
{
$mid = self::$_module_srl_map[$module_srl] = $output->data->mid;
Rhymix\Framework\Cache::set('site_and_module:module_srl_mid:' . $module_srl, $mid, 0, true);
@ -726,6 +727,60 @@ class ModuleModel extends Module
}
}
/**
* Return the domain (including scheme and port) by module_srl
*
* @param int $module_srl
* @return ?string
*/
public static function getDomainByModuleSrl(int $module_srl): ?string
{
$module_srl = intval($module_srl);
if (isset(self::$_domain_map[$module_srl]))
{
return self::$_domain_map[$module_srl];
}
$prefix = Rhymix\Framework\Cache::get('site_and_module:module_srl_prefix:' . $module_srl);
if (isset($prefix))
{
self::$_domain_map[$module_srl] = $prefix;
return $prefix;
}
$args = new stdClass;
$args->module_srls = $module_srl;
$args->include_domain_info = true;
$output = executeQuery('module.getModuleInfoByModuleSrl', $args);
if (is_object($output->data))
{
$info = self::$_module_srl_map[$module_srl] = $output->data;
if (!$info->domain_srl || $info->domain_srl == -1 || !isset($info->domain))
{
$prefix = '';
}
else
{
$prefix = $info->security === 'always' ? 'https://' : 'http://';
$prefix .= $info->domain;
if ($info->security === 'always' && $info->https_port)
{
$prefix .= ':' . $info->https_port;
}
if ($info->security !== 'always' && $info->http_port)
{
$prefix .= ':' . $info->http_port;
}
}
Rhymix\Framework\Cache::set('site_and_module:module_srl_prefix:' . $module_srl, $prefix, 0, true);
return $prefix;
}
else
{
return null;
}
}
/**
* @brief Get forward value by the value of act
*/

View file

@ -1,8 +1,20 @@
<query id="getModuleInfoByModuleSrl" action="select">
<tables>
<table name="modules" />
</tables>
<table name="domains" type="left join" if="include_domain_info">
<conditions>
<condition operation="in" column="module_srl" var="module_srls" notnull="notnull" />
<condition operation="equal" column="domains.domain_srl" var="modules.domain_srl" />
</conditions>
</table>
</tables>
<columns>
<column name="modules.*" />
<column name="domains.domain" if="include_domain_info" />
<column name="domains.security" if="include_domain_info" />
<column name="domains.http_port" if="include_domain_info" />
<column name="domains.https_port" if="include_domain_info" />
</columns>
<conditions>
<condition operation="in" column="modules.module_srl" var="module_srls" notnull="notnull" />
</conditions>
</query>

View file

@ -231,32 +231,72 @@ class PointAdminController extends Point
function procPointAdminInsertPointModuleConfig()
{
$module_srl = Context::get('target_module_srl');
if(!$module_srl) throw new Rhymix\Framework\Exceptions\InvalidRequest;
if (!$module_srl)
{
throw new Rhymix\Framework\Exceptions\InvalidRequest;
}
// In case of batch configuration of several modules
if(preg_match('/^([0-9,]+)$/',$module_srl)) $module_srl = explode(',',$module_srl);
else $module_srl = array($module_srl);
if (preg_match('/^([0-9,]+)$/',$module_srl))
{
$module_srl = explode(',',$module_srl);
}
else
{
$module_srl = array($module_srl);
}
// Save configurations
$oModuleController = getController('module');
for($i=0;$i<count($module_srl);$i++)
{
$srl = trim($module_srl[$i]);
if(!$srl) continue;
unset($config);
$config['insert_document'] = (int)Context::get('insert_document');
$config['insert_comment'] = (int)Context::get('insert_comment');
$config['upload_file'] = (int)Context::get('upload_file');
$config['download_file'] = (int)Context::get('download_file');
$config['read_document'] = (int)Context::get('read_document');
$config['voter'] = (int)Context::get('voter');
$config['blamer'] = (int)Context::get('blamer');
$config['voter_comment'] = (int)Context::get('voter_comment');
$config['blamer_comment'] = (int)Context::get('blamer_comment');
$config['download_file_author'] = (int)Context::get('download_file_author');
$config['read_document_author'] = (int)Context::get('read_document_author');
$config['voted'] = (int)Context::get('voted');
$config['blamed'] = (int)Context::get('blamed');
$config['voted_comment'] = (int)Context::get('voted_comment');
$config['blamed_comment'] = (int)Context::get('blamed_comment');
$srl = trim($module_srl[$i] ?? '');
if (!$srl)
{
continue;
}
$config = [];
$numeric_keys = [
'insert_document',
'insert_comment',
'upload_file',
'download_file',
'read_document',
'voter',
'blamer',
'voter_comment',
'blamer_comment',
'download_file_author',
'read_document_author',
'voted',
'blamed',
'voted_comment',
'blamed_comment'
];
$boolean_keys = [
'insert_document_revert_on_delete',
'insert_comment_revert_on_delete',
'upload_file_revert_on_delete'
];
foreach ($numeric_keys as $key)
{
$value = trim(Context::get($key) ?? '');
if ($value !== '')
{
$config[$key] = (int)$value;
}
}
foreach ($boolean_keys as $key)
{
$value = trim(Context::get($key) ?? '');
if ($value !== '')
{
$config[$key] = ($value === 'Y');
}
}
$oModuleController->insertModulePartConfig('point', $srl, $config);
}

View file

@ -211,8 +211,8 @@ class PointController extends Point
}
// Return if disabled
$config = $this->getConfig();
if ($config->insert_document_revert_on_delete === false)
$revert = PointModel::getModulePointConfig($module_srl, 'insert_document_revert_on_delete');
if ($revert === false)
{
return;
}
@ -319,8 +319,8 @@ class PointController extends Point
}
// Return if disabled
$config = $this->getConfig();
if ($config->insert_comment_revert_on_delete === false)
$revert = PointModel::getModulePointConfig($module_srl, 'insert_comment_revert_on_delete');
if ($revert === false)
{
return;
}
@ -333,6 +333,7 @@ class PointController extends Point
}
// Abort if the document is older than a configured limit.
$config = $this->getConfig();
$time_limit = $config->insert_comment_limit ?: $config->no_point_date;
if ($time_limit > 0 && ztime($oDocument->get('regdate')) < RX_TIME - ($time_limit * 86400))
{
@ -381,14 +382,12 @@ class PointController extends Point
}
// Return if disabled
$config = $this->getConfig();
if ($config->upload_file_revert_on_delete === false)
$revert = PointModel::getModulePointConfig($module_srl, 'upload_file_revert_on_delete');
if ($revert === false)
{
return;
}
// Get the points of the member
// Subtract points for the file.
$diff = PointModel::getModulePointConfig($module_srl, 'upload_file');

View file

@ -271,7 +271,7 @@ class PointModel extends Point
*
* @param int $module_srl
* @param string $config_key
* @return int
* @return int|bool
*/
public static function getModulePointConfig($module_srl, $config_key)
{
@ -306,10 +306,10 @@ class PointModel extends Point
else
{
$default_config = self::getConfig();
$point = $default_config->{$config_key};
$point = $default_config->{$config_key} ?? 0;
}
return intval($point);
return is_bool($point) ? $point : intval($point);
}
}
/* End of file point.model.php */

View file

@ -1,7 +1,6 @@
<load target="js/point_admin.js" />
<section class="section">
<h1>{$lang->point}</h1>
<form action="./" method="post" id="fo_point" class="x_form-horizontal">
<input type="hidden" name="module" value="point" />
<input type="hidden" name="act" value="procPointAdminInsertPointModuleConfig" />
@ -11,91 +10,109 @@
<div class="x_control-group">
<label for="insert_document" class="x_control-label">{$lang->point_insert_document}</label>
<div class="x_controls">
<input type="number" name="insert_document" id="insert_document" value="{$module_config['insert_document'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="insert_document" id="insert_document" value="{$module_config['insert_document'] ?? ''}" /> {$module_config['point_name']}
&nbsp; {$lang->cmd_point_revert_on_delete}
<select name="insert_document_revert_on_delete">
<option value="" selected="selected"|cond="!isset($module_config['insert_document_revert_on_delete'])">{$lang->default_value}</option>
<option value="Y" selected="selected"|cond="($module_config['insert_document_revert_on_delete'] ?? null) === true">{$lang->cmd_yes}</option>
<option value="N" selected="selected"|cond="($module_config['insert_document_revert_on_delete'] ?? null) === false">{$lang->cmd_no}</option>
</select>
</div>
</div>
<div class="x_control-group">
<label for="insert_comment" class="x_control-label">{$lang->point_insert_comment}</label>
<div class="x_controls">
<input type="number" name="insert_comment" id="insert_comment" value="{$module_config['insert_comment'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="insert_comment" id="insert_comment" value="{$module_config['insert_comment'] ?? ''}" /> {$module_config['point_name']}
&nbsp; {$lang->cmd_point_revert_on_delete}
<select name="insert_comment_revert_on_delete">
<option value="" selected="selected"|cond="!isset($module_config['insert_comment_revert_on_delete'])">{$lang->default_value}</option>
<option value="Y" selected="selected"|cond="($module_config['insert_comment_revert_on_delete'] ?? null) === true">{$lang->cmd_yes}</option>
<option value="N" selected="selected"|cond="($module_config['insert_comment_revert_on_delete'] ?? null) === false">{$lang->cmd_no}</option>
</select>
</div>
</div>
<div class="x_control-group">
<label for="upload_file" class="x_control-label">{$lang->point_upload_file}</label>
<div class="x_controls">
<input type="number" name="upload_file" id="upload_file" value="{$module_config['upload_file'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="upload_file" id="upload_file" value="{$module_config['upload_file'] ?? ''}" /> {$module_config['point_name']}
&nbsp; {$lang->cmd_point_revert_on_delete}
<select name="upload_file_revert_on_delete">
<option value="" selected="selected"|cond="!isset($module_config['upload_file_revert_on_delete'])">{$lang->default_value}</option>
<option value="Y" selected="selected"|cond="($module_config['upload_file_revert_on_delete'] ?? null) === true">{$lang->cmd_yes}</option>
<option value="N" selected="selected"|cond="($module_config['upload_file_revert_on_delete'] ?? null) === false">{$lang->cmd_no}</option>
</select>
</div>
</div>
<div class="x_control-group">
<label for="download_file" class="x_control-label">{$lang->point_download_file}</label>
<div class="x_controls">
<input type="number" name="download_file" id="download_file" value="{$module_config['download_file'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="download_file" id="download_file" value="{$module_config['download_file'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="read_document" class="x_control-label">{$lang->point_read_document}</label>
<div class="x_controls">
<input type="number" name="read_document" id="read_document" value="{$module_config['read_document'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="read_document" id="read_document" value="{$module_config['read_document'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="voter" class="x_control-label">{$lang->point_voter}</label>
<div class="x_controls">
<input type="number" name="voter" id="voter" value="{$module_config['voter'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="voter" id="voter" value="{$module_config['voter'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="blamer" class="x_control-label">{$lang->point_blamer}</label>
<div class="x_controls">
<input type="number" name="blamer" id="blamer" value="{$module_config['blamer'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="blamer" id="blamer" value="{$module_config['blamer'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="voter_comment" class="x_control-label">{$lang->point_voter_comment}</label>
<div class="x_controls">
<input type="number" name="voter_comment" id="voter_comment" value="{$module_config['voter_comment'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="voter_comment" id="voter_comment" value="{$module_config['voter_comment'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="blamer_comment" class="x_control-label">{$lang->point_blamer_comment}</label>
<div class="x_controls">
<input type="number" name="blamer_comment" id="blamer_comment" value="{$module_config['blamer_comment'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="blamer_comment" id="blamer_comment" value="{$module_config['blamer_comment'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="download_file_author" class="x_control-label">{$lang->point_download_file_author}</label>
<div class="x_controls">
<input type="number" name="download_file_author" id="download_file_author" value="{$module_config['download_file_author'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="download_file_author" id="download_file_author" value="{$module_config['download_file_author'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="read_document_author" class="x_control-label">{$lang->point_read_document_author}</label>
<div class="x_controls">
<input type="number" name="read_document_author" id="read_document_author" value="{$module_config['read_document_author'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="read_document_author" id="read_document_author" value="{$module_config['read_document_author'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="voted" class="x_control-label">{$lang->point_voted}</label>
<div class="x_controls">
<input type="number" name="voted" id="voted" value="{$module_config['voted'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="voted" id="voted" value="{$module_config['voted'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="blamed" class="x_control-label">{$lang->point_blamed}</label>
<div class="x_controls">
<input type="number" name="blamed" id="blamed" value="{$module_config['blamed'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="blamed" id="blamed" value="{$module_config['blamed'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="voted_comment" class="x_control-label">{$lang->point_voted_comment}</label>
<div class="x_controls">
<input type="number" name="voted_comment" id="voted_comment" value="{$module_config['voted_comment'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="voted_comment" id="voted_comment" value="{$module_config['voted_comment'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_control-group">
<label for="blamed_comment" class="x_control-label">{$lang->point_blamed_comment}</label>
<div class="x_controls">
<input type="number" name="blamed_comment" id="blamed_comment" value="{$module_config['blamed_comment'] ?? null}" /> {$module_config['point_name']}
<input type="number" name="blamed_comment" id="blamed_comment" value="{$module_config['blamed_comment'] ?? ''}" /> {$module_config['point_name']}
</div>
</div>
<div class="x_clearfix btnArea">

View file

@ -15,6 +15,7 @@
</conditions>
<groups if="if_groupby">
<group column="member.member_srl" />
<group column="member.nick_name" if="if_groupby_nick_name" />
<having>
<condition operation="notequal" column="member.member_srl" var="exclude_member_srl" notnull="notnull" />
</having>

View file

@ -178,7 +178,8 @@ class DBQueryParserTest extends \Codeception\Test\Unit
$this->assertEquals('AND', $query->conditions[1]->pipe);
$this->assertTrue($query->groupby instanceof Rhymix\Framework\Parsers\DBQuery\GroupBy);
$this->assertEquals('member.member_srl', $query->groupby->columns[0]);
$this->assertEquals(['member.member_srl', null], $query->groupby->columns[0]);
$this->assertEquals(['member.nick_name', 'if_groupby_nick_name'], $query->groupby->columns[1]);
$this->assertEquals(1, count($query->groupby->having));
$this->assertTrue($query->groupby->having[0] instanceof Rhymix\Framework\Parsers\DBQuery\Condition);
$this->assertEquals('member.member_srl', $query->groupby->having[0]->column);
@ -196,6 +197,12 @@ class DBQueryParserTest extends \Codeception\Test\Unit
'AND `documents`.`document_srl` IN (?, ?, ?) GROUP BY `member`.`member_srl` HAVING `member`.`member_srl` != ?', $sql);
$this->assertEquals(['12', '34', '56', '4'], $params);
$args['if_groupby_nick_name'] = true;
$sql = $query->getQueryString('rx_', $args);
$this->assertEquals('SELECT `member`.`member_srl`, COUNT(*) AS `count` FROM `rx_documents` AS `documents`, `rx_member` AS `member` ' .
'WHERE `documents`.`member_srl` = `member`.`member_srl` AND `documents`.`member_srl` = `member`.`member_srl` ' .
'AND `documents`.`document_srl` IN (?, ?, ?) GROUP BY `member`.`member_srl`, `member`.`nick_name` HAVING `member`.`member_srl` != ?', $sql);
$args = array(
'document_srl_list' => [12, 34, 56], 'exclude_member_srl' => 4, 'exclude_document_srl_list' => '78,90',
'if_table' => true, 'if_column' => true, 'if_condition2' => true,

View file

@ -1,6 +1,6 @@
<load target="./js/content_widget.js" cond="$widget_info->page_count || (is_countable($widget_info->tab) && count($widget_info->tab))" />
<!--// Check layout. If it is Simple World, select layout primary colors as a primary color of this skin -->
{@$layout_info = Context::get('layout_info')}
{@$layout_info = Context::get('layout_info') ?? new stdClass();}
<block cond="$layout_info->extra_var->primary_color->type === 'select' || $layout_info->extra_var->customized_primary_color->type === 'colorpicker'">
{@
if(!$layout_info->primary_color)