Merge branch 'develop' into pr/1851

This commit is contained in:
Kijin Sung 2022-02-07 21:31:45 +09:00
commit 4eced6daf0
71 changed files with 1045 additions and 508 deletions

View file

@ -127,7 +127,7 @@
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_smtp_pass">{$lang->cmd_advanced_mailer_smtp_pass}</label>
<div class="x_controls">
<input type="password" name="mail_{$driver_name}_smtp_pass" id="mail_{$driver_name}_smtp_pass" value="{$conf_value}" />
<input type="password" name="mail_{$driver_name}_smtp_pass" id="mail_{$driver_name}_smtp_pass" value="{$conf_value}" autocomplete="new-password" />
</div>
</div>
<!--@end-->
@ -167,7 +167,7 @@
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_{$driver_name}_api_pass">{$lang->cmd_advanced_mailer_api_pass}</label>
<div class="x_controls full-width">
<input type="password" name="mail_{$driver_name}_api_pass" id="mail_{$driver_name}_api_pass" value="{$conf_value}" />
<input type="password" name="mail_{$driver_name}_api_pass" id="mail_{$driver_name}_api_pass" value="{$conf_value}" autocomplete="new-password" />
</div>
</div>
<!--@end-->
@ -194,7 +194,7 @@
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_{$driver_name}_api_secret">{$lang->cmd_advanced_mailer_api_secret}</label>
<div class="x_controls">
<input type="password" name="mail_{$driver_name}_api_secret" id="mail_{$driver_name}_api_secret" value="{$conf_value}" />
<input type="password" name="mail_{$driver_name}_api_secret" id="mail_{$driver_name}_api_secret" value="{$conf_value}" autocomplete="new-password" />
</div>
</div>
<!--@end-->
@ -258,7 +258,7 @@
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="sms_{$driver_name}_api_pass">{$lang->cmd_advanced_mailer_api_pass}</label>
<div class="x_controls full-width">
<input type="password" name="sms_{$driver_name}_api_pass" id="sms_{$driver_name}_api_pass" value="{$conf_value}" />
<input type="password" name="sms_{$driver_name}_api_pass" id="sms_{$driver_name}_api_pass" value="{$conf_value}" autocomplete="new-password" />
</div>
</div>
<!--@end-->
@ -285,7 +285,7 @@
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="sms_{$driver_name}_api_secret">{$lang->cmd_advanced_mailer_api_secret}</label>
<div class="x_controls">
<input type="password" name="sms_{$driver_name}_api_secret" id="sms_{$driver_name}_api_secret" value="{$conf_value}" />
<input type="password" name="sms_{$driver_name}_api_secret" id="sms_{$driver_name}_api_secret" value="{$conf_value}" autocomplete="new-password" />
</div>
</div>
<!--@end-->
@ -294,7 +294,7 @@
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="sms_{$driver_name}_sender_key">{$lang->cmd_advanced_mailer_sender_key}</label>
<div class="x_controls">
<input type="password" name="sms_{$driver_name}_sender_key" id="sms_{$driver_name}_sender_key" value="{$conf_value}" />
<input type="password" name="sms_{$driver_name}_sender_key" id="sms_{$driver_name}_sender_key" value="{$conf_value}" autocomplete="new-password" />
<p class="x_help-block">{$lang->cmd_admin_sms_sender_key_help}</p>
</div>
</div>
@ -363,7 +363,7 @@
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="push_{$driver_name}_api_key">{$lang->cmd_advanced_mailer_fcm_api_key}</label>
<div class="x_controls">
<input type="password" name="push_{$driver_name}_api_key" id="push_{$driver_name}_api_key" value="{$conf_value|escape}" />
<input type="password" name="push_{$driver_name}_api_key" id="push_{$driver_name}_api_key" value="{$conf_value|escape}" autocomplete="new-password" />
</div>
</div>
<!--@end-->
@ -381,7 +381,7 @@
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="push_{$driver_name}_passphrase">{$lang->cmd_advanced_mailer_apns_passphrase}</label>
<div class="x_controls">
<input type="password" name="push_{$driver_name}_passphrase" id="push_{$driver_name}_passphrase" value="{$conf_value|escape}" />
<input type="password" name="push_{$driver_name}_passphrase" id="push_{$driver_name}_passphrase" value="{$conf_value|escape}" autocomplete="new-password" />
</div>
</div>
<!--@end-->

View file

@ -45,11 +45,11 @@ class boardView extends board
$count_category = count(DocumentModel::getCategoryList($this->module_info->module_srl));
if($count_category)
{
if($this->module_info->hide_category)
if(isset($this->module_info->hide_category))
{
$this->module_info->use_category = ($this->module_info->hide_category == 'Y') ? 'N' : 'Y';
}
else if($this->module_info->use_category)
elseif(isset($this->module_info->use_category))
{
$this->module_info->hide_category = ($this->module_info->use_category == 'Y') ? 'N' : 'Y';
}
@ -655,7 +655,7 @@ class boardView extends board
// List of columns that should always be selected
$defaultColumnList = array(
'document_srl', 'module_srl', 'category_srl', 'lang_code', 'is_notice',
'title', 'title_bold', 'title_color', 'member_srl', 'nick_name', 'extra_vars',
'title', 'title_bold', 'title_color', 'member_srl', 'nick_name', 'tags', 'extra_vars',
'comment_count', 'trackback_count', 'uploaded_count', 'status', 'regdate', 'last_update',
);

View file

@ -1,9 +1,15 @@
<load target="css/mboard.css" />
<div class="bd">
<!--@if(isset($module_info->mobile_header_text) && $module_info->mobile_header_text)-->
<div class="bd_header_text">{$module_info->mobile_header_text}</div>
<!--@endif-->
<!--@if($oDocument->isExists())-->
<include target="read.html" />
<!--@else-->
<include target="_list.html" />
<!--@end-->
<!--@if(isset($module_info->mobile_footer_text) && $module_info->mobile_footer_text)-->
<div class="bd_footer_text">{$module_info->mobile_footer_text}</div>
<!--@endif-->
</div>

View file

@ -2,6 +2,9 @@
<load target="css/mboard.css" />
<div class="bd">
<!--@if(isset($module_info->mobile_header_text) && $module_info->mobile_header_text)-->
<div class="bd_header_text">{$module_info->mobile_header_text}</div>
<!--@endif-->
<div class="hx h2">
<h2><a href="{getUrl('','vid',$vid,'mid',$mid)}">{$module_info->browser_title}</a></h2>
</div>
@ -86,5 +89,8 @@
<div class="bna">
<button type="submit" class="bn dark">{$lang->cmd_registration}</button>
</div>
</form>
</form>
<!--@if(isset($module_info->mobile_footer_text) && $module_info->mobile_footer_text)-->
<div class="bd_footer_text">{$module_info->mobile_footer_text}</div>
<!--@endif-->
</div>

View file

@ -1,9 +1,15 @@
<load target="css/mboard.css" />
<div class="bd">
<!--@if(isset($module_info->mobile_header_text) && $module_info->mobile_header_text)-->
<div class="bd_header_text">{$module_info->mobile_header_text}</div>
<!--@endif-->
<!--@if($oDocument->isExists())-->
<!--#include("read.html")-->
<include target="read.html" />
<!--@else-->
<!--#include("_list.html")-->
<include target="_list.html" />
<!--@end-->
<!--@if(isset($module_info->mobile_footer_text) && $module_info->mobile_footer_text)-->
<div class="bd_footer_text">{$module_info->mobile_footer_text}</div>
<!--@endif-->
</div>

View file

@ -2,7 +2,10 @@
<load target="css/mboard.css" />
<div class="bd">
<h2 class="h2"><a href="{getUrl('','vid',$vid,'mid',$mid)}">{$module_info->browser_title}</a> &rsaquo; {$lang->cmd_write}</h2>
<!--@if(isset($module_info->mobile_header_text) && $module_info->mobile_header_text)-->
<div class="bd_header_text">{$module_info->mobile_header_text}</div>
<!--@endif-->
<h2 class="h2"><a href="{getUrl('','vid',$vid,'mid',$mid)}">{$module_info->browser_title}</a> &rsaquo; {$lang->cmd_write}</h2>
<form action="./" method="POST" onsubmit="return procFilter(this, insert)">
<input type="hidden" name="mid" value="{$mid}" />
<input type="hidden" name="document_srl" value="{$document_srl}" />
@ -81,4 +84,7 @@
<button type="submit" class="bn">{$lang->cmd_registration}</button>
</div>
</form>
<!--@if(isset($module_info->mobile_footer_text) && $module_info->mobile_footer_text)-->
<div class="bd_footer_text">{$module_info->mobile_footer_text}</div>
<!--@endif-->
</div>

View file

@ -78,6 +78,9 @@
<input cond="$module_info->secret=='Y'" type="checkbox" name="is_secret" value="Y" id="is_secret" class="iCheck" />
<label cond="$module_info->secret=='Y'" for="is_secret">{$lang->secret}</label>
</div>
<div class="write_captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('comment')">
{$captcha}
</div>
<div class="btnArea">
<button type="submit" class="btn">{$lang->cmd_comment_registration}</button>
</div>

View file

@ -32,6 +32,9 @@
<input cond="$module_info->secret=='Y'" type="checkbox" name="is_secret" value="Y" id="is_secret" checked="checked"|cond="$oComment->get('is_secret')=='Y'" class="iCheck" />
<label cond="$module_info->secret=='Y'" for="is_secret">{$lang->secret}</label>
</div>
<div class="write_captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('comment')">
{$captcha}
</div>
<div class="btnArea">
<button type="submit" class="btn">{$lang->cmd_comment_registration}</button>
</div>

View file

@ -73,6 +73,9 @@
<input type="text" name="tags" id="tags" value="{htmlspecialchars($oDocument->get('tags'))}" class="iText" style="width:300px" title="Tag" />
</span>
</div>
<div class="write_captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('document')">
{$captcha}
</div>
<div class="btnArea">
<input type="submit" value="{$lang->cmd_registration}" class="btn" />
<block cond="!$oDocument->isExists() || $oDocument->get('status') == 'TEMP'">

View file

@ -86,6 +86,9 @@
<input cond="$module_info->secret=='Y'" type="checkbox" name="is_secret" value="Y" id="is_secret" class="iCheck" />
<label cond="$module_info->secret=='Y'" for="is_secret">{$lang->secret}</label>
</div>
<div class="write_captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('comment')">
{$captcha}
</div>
<div style="float:right">
<button type="submit" class="btn_insert"><i class="xi-message"></i> {$lang->cmd_comment_registration}</button>
</div>

View file

@ -32,7 +32,7 @@
<input cond="$module_info->secret=='Y'" type="checkbox" name="is_secret" value="Y" id="is_secret" checked="checked"|cond="$oComment->get('is_secret')=='Y'" class="iCheck" />
<label cond="$module_info->secret=='Y'" for="is_secret">{$lang->secret}</label>
</div>
<div class="write_captcha" cond="$captcha">
<div class="write_captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('comment')">
{$captcha}
</div>
<div style="float:right">

View file

@ -76,7 +76,7 @@
<input type="text" name="reason_update" id="reason_update" value="" class="iText" style="width:300px" title="reason update" />
</span>
</div>
<div class="write_captcha" cond="isset($captcha) && $captcha">
<div class="write_captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('document')">
{$captcha}
</div>
<div style="float:right">

View file

@ -1334,7 +1334,7 @@ class documentController extends document
);
$config = DocumentModel::getDocumentConfig();
if (!$config->view_count_option || !isset($valid_options[$config->view_count_option]))
if (!isset($config->view_count_option) || !isset($valid_options[$config->view_count_option]))
{
$config->view_count_option = 'once';
}
@ -1351,7 +1351,7 @@ class documentController extends document
$logged_info = Context::get('logged_info');
// Option 'some': only count once per session.
if ($config->view_count_option != 'all' && $_SESSION['readed_document'][$document_srl])
if ($config->view_count_option != 'all' && isset($_SESSION['readed_document'][$document_srl]))
{
return false;
}
@ -1370,7 +1370,7 @@ class documentController extends document
}
// Pass if the author's member_srl is the same as the visitor's.
if($member_srl && $logged_info->member_srl && $logged_info->member_srl == $member_srl)
if($member_srl && $logged_info && $logged_info->member_srl && $logged_info->member_srl == $member_srl)
{
$_SESSION['readed_document'][$document_srl] = true;
return false;

View file

@ -204,7 +204,7 @@ class documentItem extends BaseObject
}
$logged_info = Context::get('logged_info');
if (!$logged_info->member_srl)
if (!$logged_info || !$logged_info->member_srl)
{
return $this->grant_cache = false;
}

View file

@ -530,7 +530,7 @@ class installController extends install
// Create a table if the schema xml exists in the "schemas" directory of the module
$schema_dir = sprintf('%s/schemas/', $module_path);
$schema_files = FileHandler::readDir($schema_dir, NULL, false, true);
$schema_sorted = [];
foreach ($schema_files as $filename)
{
if (!preg_match('/\/([a-zA-Z0-9_]+)\.xml$/', $filename, $matches))
@ -543,16 +543,24 @@ class installController extends install
}
$table_name = $matches[1];
if($oDB->isTableExists($table_name))
if(isset($schema_sorted[$table_name]) || $oDB->isTableExists($table_name))
{
continue;
}
$schema_sorted[$table_name] = $filename;
}
$schema_sorted = Rhymix\Framework\Parsers\DBTableParser::resolveDependency($schema_sorted);
foreach ($schema_sorted as $table_name => $filename)
{
$output = $oDB->createTable($filename);
if(!$output->toBool())
{
throw new Exception(lang('msg_create_table_failed') . ': ' . $table_name . ': ' . $oDB->getError()->getMessage());
}
}
// Create a table and module instance and then execute install() method
unset($oModule);
$oModule = ModuleModel::getModuleInstallClass($module);

View file

@ -505,7 +505,7 @@ class layoutModel extends layout
$cache_file = $this->getUserLayoutCache($layout_srl, Context::getLangType());
}
if(file_exists($cache_file)&&filemtime($cache_file)>filemtime($xml_file))
if(file_exists($cache_file) && filemtime($cache_file) > filemtime($xml_file))
{
include($cache_file);
@ -513,7 +513,7 @@ class layoutModel extends layout
{
foreach($vars as $key => $value)
{
if(!$layout_info->extra_var->{$key} && !$layout_info->{$key})
if(!isset($layout_info->extra_var->{$key}) && !isset($layout_info->{$key}))
{
$layout_info->{$key} = $value;
}

View file

@ -38,8 +38,6 @@
<action name="procMemberInsert" type="controller" route="signup" />
<action name="procMemberCheckValue" type="controller" />
<action name="procMemberLogin" type="controller" route="login" />
<action name="procMemberRegisterDevice" type="controller" route="device/register" />
<action name="procMemberLoginWithDevice" type="controller" route="device/login" />
<action name="procMemberFindAccount" type="controller" method="GET|POST" ruleset="findAccount" />
<action name="procMemberFindAccountByQuestion" type="controller" method="GET|POST" />
<action name="procMemberAuthAccount" type="controller" method="GET|POST" />
@ -51,7 +49,6 @@
<action name="procMemberModifyInfo" type="controller" permission="member" />
<action name="procMemberModifyPassword" type="controller" permission="member" ruleset="modifyPassword" />
<action name="procMemberModifyEmailAddress" type="controller" permission="member" ruleset="modifyEmailAddress" />
<action name="procMemberDeleteDevice" type="controller" permission="member" />
<action name="procMemberLeave" type="controller" permission="member" ruleset="leaveMember" />
<action name="procMemberInsertProfileImage" type="controller" permission="member" ruleset="insertProfileImage" />
<action name="procMemberDeleteProfileImage" type="controller" permission="member" />
@ -73,6 +70,11 @@
<action name="procMemberLogout" type="controller" permission="member" />
<action name="procMemberSpammerManage" type="controller" permission="manager" check_var="module_srl" />
<action name="procMemberRegisterDevice" class="Controllers\Device" route="device/register" />
<action name="procMemberLoginWithDevice" class="Controllers\Device" route="device/login" />
<action name="procMemberUnregisterDevice" class="Controllers\Device" route="device/unregister" />
<action name="procMemberDeleteDevice" class="Controllers\Device" permission="member" />
<action name="dispMemberAdminList" type="view" admin_index="true" menu_name="userList" menu_index="true"/>
<action name="dispMemberAdminInfo" type="view" menu_name="userList" />
<action name="dispMemberAdminInsert" type="view" menu_name="userList" />

View file

@ -0,0 +1,376 @@
<?php
namespace Rhymix\Modules\Member\Controllers;
class Device extends \member
{
/**
* Automatically recognize device token from header or cookie and register it.
*
* If the device is already registered, just update its last active date.
*
* @return \BaseObject
*/
public function autoRegisterDevice(int $member_srl): \BaseObject
{
$device_token = $this->_getDeviceToken();
if ($device_token)
{
$output = executeQuery('member.getMemberDevice', ['device_token' => $device_token]);
if (!$output->data || $output->data->member_srl != $member_srl)
{
$output = $this->procMemberRegisterDevice($member_srl, $device_token);
if ($output instanceof \BaseObject && !$output->toBool())
{
return $output;
}
$this->_setDeviceKey();
}
else
{
executeQuery('member.updateMemberDeviceLastActiveDate', ['device_token' => $device_token]);
}
}
return new \BaseObject;
}
/**
* Register device
*/
public function procMemberRegisterDevice($member_srl = null, $device_token = null)
{
// Set the response method to JSON, but only if this method was called directly.
// The response method will remain unchanged, for example, when this method was called by autoRegisterDevice()
if (\Context::get('act') === 'procMemberRegisterDevice')
{
\Context::setResponseMethod('JSON');
}
// Check user_id, password, device_token
$allow_guest_device = config('push.allow_guest_device');
$user_id = \Context::get('user_id');
$password = \Context::get('password');
$device_token = $device_token ?? \Context::get('device_token');
$device_model = escape(\Context::get('device_model'));
// Return an error when id and password doesn't exist
if (!$member_srl && $this->user->member_srl)
{
$member_srl = $this->user->member_srl;
}
if (!$member_srl && !$user_id && !$allow_guest_device)
{
return new \BaseObject(-1, 'NULL_USER_ID');
}
if (!$member_srl && !$password && !$allow_guest_device)
{
return new \BaseObject(-1, 'NULL_PASSWORD');
}
if (!$device_token)
{
return new \BaseObject(-1, 'NULL_DEVICE_TOKEN');
}
// Get device information
$browserInfo = \Rhymix\Framework\UA::getBrowserInfo();
$device_type = escape(strtolower($browserInfo->os));
$device_version = $browserInfo->os_version;
if (!$device_model)
{
$device_model = escape($browserInfo->device);
}
// Detect device token type
if (preg_match('/^[0-9a-z]{64}$/', $device_token))
{
$device_token_type = 'apns';
}
elseif (preg_match('/^[0-9a-zA-Z:_-]+$/', $device_token) && strlen($device_token) > 64)
{
$device_token_type = 'fcm';
}
else
{
return new \BaseObject(-1, 'INVALID_DEVICE_TOKEN');
}
if ($member_srl)
{
$member_srl = intval($member_srl);
}
elseif ($user_id && $password)
{
$output = \memberController::getInstance()->procMemberLogin($user_id, $password);
if(!$output->toBool())
{
return new \BaseObject(-1, 'LOGIN_FAILED');
}
$logged_info = \Context::get('logged_info');
$member_srl = intval($logged_info->member_srl);
}
else
{
$logged_info = null;
$member_srl = 0;
}
// Generate keys
$random_key = \Rhymix\Framework\Security::getRandom();
$device_key = hash_hmac('sha256', $random_key, $member_srl . ':' . config('crypto.authentication_key'));
// Prepare query arguments
$args = new \stdClass;
$args->device_srl = getNextSequence();
$args->member_srl = $member_srl;
$args->device_token = $device_token;
$args->device_token_type = $device_token_type;
$args->device_key = $device_key;
$args->device_type = $device_type;
$args->device_version = $device_version;
$args->device_model = $device_model;
// Call trigger (before)
$trigger_output = \ModuleHandler::triggerCall('member.insertMemberDevice', 'before', $args);
if(!$trigger_output->toBool()) return $trigger_output;
// Start transaction
$oDB = \DB::getInstance();
$oDB->begin();
// Remove duplicated token key
executeQuery('member.deleteMemberDevice', ['device_token' => $device_token]);
// Create member_device
$output = executeQuery('member.insertMemberDevice', $args);
if(!$output->toBool())
{
$oDB->rollback();
return $output;
}
// Call trigger (after)
\ModuleHandler::triggerCall('member.insertMemberDevice', 'after', $args);
$oDB->commit();
// Set parameters
$this->add('member_srl', $member_srl);
$this->add('user_id', $logged_info ? $logged_info->user_id : null);
$this->add('user_name', $logged_info ? $logged_info->user_name : null);
$this->add('nick_name', $logged_info ? $logged_info->nick_name : null);
$this->add('device_key', $random_key);
}
/**
* Automatically log-in to registered device
*/
public function procMemberLoginWithDevice()
{
\Context::setResponseMethod('JSON');
// Check member_srl, device_token, device_key
$allow_guest_device = config('push.allow_guest_device');
$member_srl = abs(\Context::get('member_srl'));
$device_token = strval(\Context::get('device_token'));
$random_key = strval(\Context::get('device_key'));
// Return an error when id, password and device_key doesn't exist
if (!$member_srl && !$allow_guest_device)
{
return new \BaseObject(-1, 'NULL_MEMBER_SRL');
}
if (!$device_token)
{
return new \BaseObject(-1, 'NULL_DEVICE_TOKEN');
}
if (!$random_key)
{
return new \BaseObject(-1, 'NULL_DEVICE_KEY');
}
// Check the device token and key.
$args = new \stdClass;
$args->member_srl = $member_srl;
$args->device_token = $device_token;
$args->device_key = hash_hmac('sha256', $random_key, $member_srl . ':' . config('crypto.authentication_key'));
$output = executeQuery('member.getMemberDevice', $args);
if (!$output->toBool())
{
return new \BaseObject(-1, 'DEVICE_RETRIEVE_FAILED');
}
if (!$output->data || !is_object($output->data))
{
return new \BaseObject(-1, 'UNREGISTERED_DEVICE');
}
// Log-in
if($member_srl)
{
$member_info = \MemberModel::getMemberInfoByMemberSrl($member_srl);
$output = \memberController::getInstance()->doLogin($member_info->user_id);
if(!$output->toBool())
{
return new \BaseObject(-1, 'LOGIN_FAILED');
}
}
else
{
$member_info = null;
}
// Update last active date
executeQuery('member.updateMemberDeviceLastActiveDate', ['device_token' => $device_token]);
$this->add('member_srl', $member_srl);
$this->add('user_id', $member_info ? $member_info->user_id : null);
$this->add('user_name', $member_info ? $member_info->user_name : null);
$this->add('nick_name', $member_info ? $member_info->nick_name : null);
}
/**
* Unregister a registered device.
*
* This action requires a device token and matching device key.
* It is intended to be called by mobile applications.
*/
public function procMemberUnregisterDevice()
{
\Context::setResponseMethod('JSON');
// Check member_srl, device_token, device_key
$allow_guest_device = config('push.allow_guest_device');
$member_srl = abs(\Context::get('member_srl'));
$device_token = strval(\Context::get('device_token'));
$random_key = strval(\Context::get('device_key'));
// Return an error when id, password and device_key doesn't exist
if (!$member_srl && !$allow_guest_device)
{
return new \BaseObject(-1, 'NULL_MEMBER_SRL');
}
if (!$device_token)
{
return new \BaseObject(-1, 'NULL_DEVICE_TOKEN');
}
if (!$random_key)
{
return new \BaseObject(-1, 'NULL_DEVICE_KEY');
}
// Check the device token and key.
$args = new \stdClass;
$args->member_srl = $member_srl;
$args->device_token = $device_token;
$args->device_key = hash_hmac('sha256', $random_key, $member_srl . ':' . config('crypto.authentication_key'));
$output = executeQuery('member.getMemberDevice', $args);
if (!$output->toBool())
{
return new \BaseObject(-1, 'DEVICE_RETRIEVE_FAILED');
}
if (!$output->data || !is_object($output->data))
{
return new \BaseObject(-1, 'UNREGISTERED_DEVICE');
}
// Delete the device.
$args = new \stdClass;
$args->device_token = $device_token;
$output = executeQuery('member.deleteMemberDevice', $args);
if (!$output->toBool())
{
return new \BaseObject(-1, 'DELETE_FAILED');
}
}
/**
* Delete a registered device.
*
* This action requires only the device_srl, but it must belong to the currently logged in member.
* It is intended to be called from the web frontend.
*/
public function procMemberDeleteDevice()
{
// Check the device_srl and member_srl of the currently logged in member.
$device_srl = intval(\Context::get('device_srl'));
if (!$device_srl)
{
throw new \Rhymix\Framework\Exceptions\InvalidRequest;
}
$member_srl = $this->user->member_srl;
if (!$member_srl)
{
throw new \Rhymix\Framework\Exceptions\NotPermitted;
}
// Check that the device_srl matches the member.
$args = new \stdClass;
$args->device_srl = $device_srl;
$args->member_srl = $member_srl;
$output = executeQuery('member.getMemberDevice', $args);
if (!$output->data || !is_object($output->data))
{
throw new \Rhymix\Framework\Exceptions\TargetNotFound;
}
// Delete the device.
$args = new \stdClass;
$args->device_token = $output->data->device_token;
$output = executeQuery('member.deleteMemberDevice', $args);
return $output;
}
/**
* Get device token from POST parameter, HTTP header or cookie
*
* @return string|null
*/
protected function _getDeviceToken()
{
// POST parameter named device_token
$device_token = $_POST['device_token'] ?? null;
if ($device_token && is_string($device_token) && $device_token !== '')
{
return $device_token;
}
// HTTP header named X-Device-Token
$device_token = $_SERVER['HTTP_X_DEVICE_TOKEN'] ?? null;
if ($device_token)
{
return $device_token;
}
// Cookie named device_token
$device_token = $_COOKIE['device_token'] ?? null;
if ($device_token)
{
return $device_token;
}
}
/**
* Set device key via header or cookie
*
* @return void
*/
protected function _setDeviceKey()
{
$member_srl = $this->get('member_srl');
$device_key = $this->get('device_key');
if (!$member_srl || !$device_key)
{
return;
}
// Set header if header was given, or cookie otherwise
if (isset($_SERVER['HTTP_X_DEVICE_TOKEN']))
{
header('X-Device-Key: ' . urlencode($member_srl . ':' . $device_key));
}
else
{
setcookie('device_key', $member_srl . ':' . $device_key, time() + 60, \RX_BASEURL, null, !!config('session.use_ssl_cookies'), true);
}
}
}

View file

@ -303,6 +303,7 @@ $lang->cmd_optional = 'Optional';
$lang->cmd_disabled = 'Disabled';
$lang->cmd_image_max_width = 'Max Width';
$lang->cmd_image_max_height = 'Max Height';
$lang->cmd_image_force_ratio = 'Fixed Aspect Ratio';
$lang->cmd_input_extend_form = 'Add Signup Form Field';
$lang->about_multi_type = 'Enter the value of multi-or single-item selection.(separated by line breaks)';
$lang->msg_delete_extend_form = 'Delete the selected item.';

View file

@ -317,6 +317,7 @@ $lang->cmd_optional = '선택';
$lang->cmd_disabled = '사용 안 함';
$lang->cmd_image_max_width = '너비 제한';
$lang->cmd_image_max_height = '높이 제한';
$lang->cmd_image_force_ratio = '가로세로 비율 고정';
$lang->cmd_input_extend_form = '회원가입 추가 항목 생성';
$lang->about_multi_type = '다중 또는 단일 항목의 선택 값을 입력하세요.(줄 바꿈으로 구분)';
$lang->msg_delete_extend_form = '선택한 항목을 삭제합니다.';

View file

@ -17,7 +17,10 @@
<input id="email_address1" type="email" name="email_address" />
</li>
</ul>
<div class="captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('recovery')">
{$captcha}
</div>
<div class="bna">
<input type="submit" class="bn dark" value="{$lang->cmd_send_mail}" />
<input type="submit" class="bn dark" value="{$lang->cmd_send_mail}" />
</div>
</form>

View file

@ -15,7 +15,7 @@
<li><label for="id"><!--@if($identifier == 'user_id')-->{$lang->user_id}<!--@else-->{$lang->email_address}<!--@end--></label><input name="user_id" type="<!--@if($identifier == 'user_id')-->text<!--@else-->email<!--@end-->" id="id" value="" /></li>
<li><label for="pw">{$lang->password}</label><input name="password" type="password" id="pw" value="" /></li>
</ul>
<div class="captcha" cond="$captcha">
<div class="captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('login')">
{$captcha}
</div>
<div class="hp" id="keep_msg" style="display:none;">

View file

@ -60,9 +60,12 @@
</block>
</li>
</ul>
<div class="bna">
<input type="submit" class="bn dark" value="{$lang->cmd_registration}" />
</div>
<div class="captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('signup')">
{$captcha}
</div>
<div class="bna">
<input type="submit" class="bn dark" value="{$lang->cmd_registration}" />
</div>
</form>
</div>
<script>
@ -95,4 +98,4 @@
return false;});
});
})(jQuery);
</script>
</script>

View file

@ -156,7 +156,13 @@ class memberAdminController extends member
$validity_info = Rhymix\Framework\Session::getValidityInfo($args->member_srl);
$validity_info->invalid_before = time();
Rhymix\Framework\Session::setValidityInfo($args->member_srl, $validity_info);
executeQuery('member.deleteAutologin', (object)array('member_srl' => $args->member_srl));
executeQuery('member.deleteAutologin', ['member_srl' => $args->member_srl]);
}
// Invalidate auth mail if denied or limited
if ($args->denied === 'Y' || $args->limited >= date('Ymd'))
{
executeQuery('member.deleteAuthMail', ['member_srl' => $args->member_srl]);
}
// Save Signature
@ -371,7 +377,7 @@ class memberAdminController extends member
'emailhost_check',
'special_phone_number', 'special_phone_code', 'max_auth_sms_count', 'max_auth_sms_count_time', 'redirect_url',
'phone_number_default_country', 'phone_number_hide_country', 'phone_number_allow_duplicate', 'phone_number_verify_by_sms',
'profile_image_max_width', 'profile_image_max_height', 'profile_image_max_filesize',
'profile_image_max_width', 'profile_image_max_height', 'profile_image_max_filesize', 'profile_image_force_ratio',
'image_name_max_width', 'image_name_max_height', 'image_name_max_filesize',
'image_mark_max_width', 'image_mark_max_height', 'image_mark_max_filesize',
'signature_editor_skin', 'sel_editor_colorset', 'signature_html', 'signature_html_retroact', 'member_allow_fileupload'
@ -469,6 +475,7 @@ class memberAdminController extends member
$signupItem->max_width = $all_args->{$key.'_max_width'};
$signupItem->max_height = $all_args->{$key.'_max_height'};
$signupItem->max_filesize = $all_args->{$key.'_max_filesize'};
$signupItem->force_ratio = $all_args->{$key.'_force_ratio'} === 'N' ? 'N' : 'Y';
}
// set extends form

View file

@ -33,22 +33,50 @@ class memberController extends member
throw new Rhymix\Framework\Exception('null_user_id');
}
// Variables
if(!$user_id) $user_id = Context::get('user_id');
$user_id = trim($user_id);
$config = MemberModel::getMemberConfig();
if(!$password) $password = Context::get('password');
$password = trim($password);
// User ID, email address or phone number
if (!$user_id)
{
$user_id = trim(Context::get('user_id'));
}
if (!$user_id && $config->identifiers && in_array('email_address', $config->identifiers))
{
$user_id = trim(Context::get('email_address'));
}
if (!$user_id && $config->identifiers && in_array('phone_number', $config->identifiers))
{
$user_id = trim(Context::get('phone_number'));
}
if (!$user_id)
{
throw new Rhymix\Framework\Exception('null_user_id');
}
if(!$keep_signed) $keep_signed = Context::get('keep_signed');
// Return an error when id and password doesn't exist
if(!$user_id) throw new Rhymix\Framework\Exception('null_user_id');
if(!$password) throw new Rhymix\Framework\Exception('null_password');
// Password
if (!$password)
{
$password = trim(Context::get('password'));
}
if (!$password)
{
throw new Rhymix\Framework\Exception('null_password');
}
$output = $this->doLogin($user_id, $password, $keep_signed=='Y'?true:false);
if (!$output->toBool()) return $output;
// Autologin option
if (!$keep_signed)
{
$keep_signed = Context::get('keep_signed');
}
$config = ModuleModel::getModuleConfig('member');
// Attempt login
$output = $this->doLogin($user_id, $password, $keep_signed === 'Y' ? true : false);
if (!$output->toBool())
{
return $output;
}
// Get info of member who just logged in
$member_info = Context::get('logged_info');
// Check change_password_date
@ -71,24 +99,7 @@ class memberController extends member
executeQuery('member.deleteAuthMail', $args);
// If a device token is supplied, attempt to register it.
$device_token = $this->_getDeviceToken();
if ($device_token)
{
$output = executeQuery('member.getMemberDevice', ['device_token' => $device_token]);
if (!$output->data || $output->data->member_srl != $member_info->member_srl)
{
$output = $this->procMemberRegisterDevice($member_info->member_srl, $device_token);
if ($output instanceof BaseObject && !$output->toBool())
{
return $output;
}
$this->_setDeviceKey();
}
else
{
executeQuery('member.updateMemberDeviceLastActiveDate', ['device_token' => $device_token]);
}
}
Rhymix\Modules\Member\Controllers\Device::getInstance()->autoRegisterDevice($member_info->member_srl);
if(!$config->after_login_url)
{
@ -101,242 +112,6 @@ class memberController extends member
return $this->setRedirectUrl($returnUrl, $output);
}
/**
* Register device
*/
function procMemberRegisterDevice($member_srl = null, $device_token = null)
{
if (Context::get('act') === 'procMemberRegisterDevice')
{
Context::setResponseMethod('JSON');
}
// Check user_id, password, device_token
$allow_guest_device = config('push.allow_guest_device');
$user_id = Context::get('user_id');
$password = Context::get('password');
$device_token = $device_token ?? Context::get('device_token');
$device_model = escape(Context::get('device_model'));
// Return an error when id and password doesn't exist
if(!$member_srl && $this->user->member_srl)
{
$member_srl = $this->user->member_srl;
}
if(!$member_srl && !$user_id && !$allow_guest_device)
{
return new BaseObject(-1, 'NULL_USER_ID');
}
if(!$member_srl && !$password && !$allow_guest_device)
{
return new BaseObject(-1, 'NULL_PASSWORD');
}
if(!$device_token)
{
return new BaseObject(-1, 'NULL_DEVICE_TOKEN');
}
// Get device information
$browserInfo = Rhymix\Framework\UA::getBrowserInfo();
$device_type = escape(strtolower($browserInfo->os));
$device_version = $browserInfo->os_version;
if(!$device_model)
{
$device_model = escape($browserInfo->device);
}
// Detect device token type
if (preg_match('/^[0-9a-z]{64}$/', $device_token))
{
$device_token_type = 'apns';
}
elseif (preg_match('/^[0-9a-zA-Z:_-]+$/', $device_token) && strlen($device_token) > 64)
{
$device_token_type = 'fcm';
}
else
{
return new BaseObject(-1, 'INVALID_DEVICE_TOKEN');
}
if ($member_srl)
{
$member_srl = intval($member_srl);
}
elseif ($user_id && $password)
{
$output = $this->procMemberLogin($user_id, $password);
if(!$output->toBool())
{
return new BaseObject(-1, 'LOGIN_FAILED');
}
$logged_info = Context::get('logged_info');
$member_srl = intval($logged_info->member_srl);
}
else
{
$logged_info = null;
$member_srl = 0;
}
// Generate keys
$random_key = Rhymix\Framework\Security::getRandom();
$device_key = hash_hmac('sha256', $random_key, $member_srl . ':' . config('crypto.authentication_key'));
// Prepare query arguments
$args = new stdClass;
$args->device_srl = getNextSequence();
$args->member_srl = $member_srl;
$args->device_token = $device_token;
$args->device_token_type = $device_token_type;
$args->device_key = $device_key;
$args->device_type = $device_type;
$args->device_version = $device_version;
$args->device_model = $device_model;
// Call trigger (before)
$trigger_output = ModuleHandler::triggerCall('member.insertMemberDevice', 'before', $args);
if(!$trigger_output->toBool()) return $trigger_output;
// Start transaction
$oDB = DB::getInstance();
$oDB->begin();
// Remove duplicated token key
executeQuery('member.deleteMemberDevice', ['device_token' => $device_token]);
// Create member_device
$output = executeQuery('member.insertMemberDevice', $args);
if(!$output->toBool())
{
$oDB->rollback();
return $output;
}
// Call trigger (after)
ModuleHandler::triggerCall('member.insertMemberDevice', 'after', $args);
$oDB->commit();
// Set parameters
$this->add('member_srl', $member_srl);
$this->add('user_id', $logged_info ? $logged_info->user_id : null);
$this->add('user_name', $logged_info ? $logged_info->user_name : null);
$this->add('nick_name', $logged_info ? $logged_info->nick_name : null);
$this->add('device_key', $random_key);
}
/**
* Automatically log-in to registered device
*/
function procMemberLoginWithDevice()
{
Context::setResponseMethod('JSON');
// Check member_srl, device_token, device_key
$allow_guest_device = config('push.allow_guest_device');
$member_srl = intval(Context::get('member_srl'));
$device_token = Context::get('device_token');
$random_key = Context::get('device_key');
// Return an error when id, password and device_key doesn't exist
if(!$member_srl && !$allow_guest_device) return new BaseObject(-1, 'NULL_MEMBER_SRL');
if(!$device_token) return new BaseObject(-1, 'NULL_DEVICE_TOKEN');
if(!$random_key) return new BaseObject(-1, 'NULL_DEVICE_KEY');
$args = new stdClass;
$args->member_srl = $member_srl;
$args->device_token = $device_token;
$args->device_key = hash_hmac('sha256', $random_key, $member_srl . ':' . config('crypto.authentication_key'));
$output = executeQueryArray('member.getMemberDevice', $args);
if(!$output->toBool())
{
return new BaseObject(-1, 'DEVICE_RETRIEVE_FAILED');
}
if(!$output->data)
{
return new BaseObject(-1, 'UNREGISTERED_DEVICE');
}
// Log-in
if($member_srl)
{
$member_info = MemberModel::getMemberInfoByMemberSrl($member_srl);
$output = $this->doLogin($member_info->user_id);
if(!$output->toBool())
{
return new BaseObject(-1, 'LOGIN_FAILED');
}
}
else
{
$member_info = null;
}
// Update last active date
executeQuery('member.updateMemberDeviceLastActiveDate', ['device_token' => $device_token]);
$this->add('member_srl', $member_srl);
$this->add('user_id', $member_info ? $member_info->user_id : null);
$this->add('user_name', $member_info ? $member_info->user_name : null);
$this->add('nick_name', $member_info ? $member_info->nick_name : null);
}
/**
* Get device token from POST parameter, HTTP header or cookie
*
* @return string|null
*/
protected function _getDeviceToken()
{
// POST parameter named device_token
$device_token = Context::get('device_token');
if ($device_token && $_SERVER['REQUEST_METHOD'] === 'POST')
{
return $device_token;
}
// HTTP header named X-Device-Token
$device_token = $_SERVER['HTTP_X_DEVICE_TOKEN'] ?? null;
if ($device_token)
{
return $device_token;
}
// Cookie named device_token
$device_token = $_COOKIE['device_token'] ?? null;
if ($device_token)
{
return $device_token;
}
}
/**
* Set device key via header or cookie
*
* @return void
*/
protected function _setDeviceKey()
{
$member_srl = $this->get('member_srl');
$device_key = $this->get('device_key');
if (!$member_srl || !$device_key)
{
return;
}
// Set header if header was given, or cookie otherwise
if (isset($_SERVER['HTTP_X_DEVICE_TOKEN']))
{
header('X-Device-Key: ' . urlencode($member_srl . ':' . $device_key));
}
else
{
setcookie('device_key', $member_srl . ':' . $device_key, time() + 60, \RX_BASEURL, null, !!config('session.use_ssl_cookies'), true);
}
}
/**
* Log-out
*
@ -1050,16 +825,7 @@ class memberController extends member
}
// Register device
$device_token = $this->_getDeviceToken();
if ($device_token)
{
$output = $this->procMemberRegisterDevice($args->member_srl, $device_token);
if ($output instanceof BaseObject && !$output->toBool())
{
return $output;
}
$this->_setDeviceKey();
}
Rhymix\Modules\Member\Controllers\Device::getInstance()->autoRegisterDevice($args->member_srl, false);
// Results
$this->add('member_srl', $args->member_srl);
@ -1431,7 +1197,7 @@ class memberController extends member
* Insert a profile image
*
* @param int $member_srl
* @param object $target_file
* @param string $target_file
*
* @return void
*/
@ -1458,14 +1224,27 @@ class memberController extends member
}
$target_path = sprintf('files/member_extra_info/profile_image/%s', getNumberingPath($member_srl));
$target_filename = sprintf('%s%d.%s', $target_path, $member_srl, $ext);
FileHandler::makeDir($target_path);
$target_filename = sprintf('%s%d.%s', $target_path, $member_srl, $ext);
// Convert if the image size is larger than a given size
if($width > $max_width || $height > $max_height)
if ($width > $max_width || $height > $max_height)
{
$resize = true;
}
elseif ($config->profile_image_force_ratio !== 'N' && ($width / $height !== $max_width / $max_height))
{
$resize = true;
}
else
{
$resize = false;
}
if ($resize)
{
$temp_filename = sprintf('files/cache/tmp/profile_image_%d.%s', $member_srl, $ext);
FileHandler::createImageFile($target_file, $temp_filename, $max_width, $max_height, $ext);
FileHandler::createImageFile($target_file, $temp_filename, $max_width, $max_height, $ext, 'fill', 75);
// 파일 용량 제한
FileHandler::clearStatCache($temp_filename);
@ -3686,35 +3465,6 @@ class memberController extends member
$_SESSION['verify_by_sms']['status'] = true;
return new BaseObject(0, 'verify_by_sms_code_confirmed');
}
/**
* Delete a registered device.
*/
public function procMemberDeleteDevice()
{
$device_srl = intval(Context::get('device_srl'));
$logged_info = Context::get('logged_info');
$args = new stdClass;
$args->device_srl = $device_srl;
$output = executeQuery('member.getMemberDevice', $args);
if (!$output->data || !is_object($output->data))
{
throw new Rhymix\Framework\Exceptions\TargetNotFound;
}
if (!$output->data->member_srl || $output->data->member_srl != $logged_info->member_srl)
{
throw new Rhymix\Framework\Exceptions\TargetNotFound;
}
$args = new stdClass;
$args->device_token = $output->data->device_token;
$output = executeQuery('member.deleteMemberDevice', $args);
if (!$output->toBool())
{
return $output;
}
}
/**
* trigger for document.getDocumentMenu. Append to popup menu a button for procMemberSpammerManage()

View file

@ -481,8 +481,17 @@ class memberModel extends member
$oSecurity = new Security($info);
$oSecurity->encodeHTML('user_id', 'user_name', 'nick_name', 'find_account_answer', 'description', 'address.', 'group_list..');
$info->homepage = strip_tags($info->homepage);
$info->blog = strip_tags($info->blog);
// Validate URLs
$info->homepage = escape(strip_tags($info->homepage));
if ($info->homepage !== '' && !preg_match('!^https?://[^\\\\/]+!', $info->homepage))
{
$info->homepage = '';
}
$info->blog = escape(strip_tags($info->blog));
if ($info->blog !== '' && !preg_match('!^https?://[^\\\\/]+!', $info->blog))
{
$info->blog = '';
}
if($extra_vars)
{
@ -499,18 +508,6 @@ class memberModel extends member
}
}
// Check format.
$oValidator = new Validator();
if(!$oValidator->applyRule('url', $info->homepage))
{
$info->homepage = '';
}
if(!$oValidator->applyRule('url', $info->blog))
{
$info->blog = '';
}
$GLOBALS['__member_info__'][$info->member_srl] = $info;
}

View file

@ -607,9 +607,18 @@ class memberView extends member
*/
function dispMemberLoginForm()
{
// Get referer URL
$referer_url = Context::get('referer_url') ?: ($_SERVER['HTTP_REFERER'] ?? '');
if (!$referer_url || !Rhymix\Framework\URL::isInternalURL($referer_url) || contains('procMember', $referer_url))
{
$referer_url = getNotEncodedUrl('act', '');
}
Context::set('referer_url', $referer_url);
// Return to previous screen if already logged in.
if(Context::get('is_logged'))
{
$this->setRedirectUrl(getNotEncodedUrl('act',''));
$this->setRedirectUrl($referer_url);
return;
}
@ -617,6 +626,7 @@ class memberView extends member
$config = $this->member_config;
Context::set('identifier', $config->identifier);
// Get validator status
$XE_VALIDATOR_MESSAGE = Context::get('XE_VALIDATOR_MESSAGE');
$XE_VALIDATOR_ERROR = Context::get('XE_VALIDATOR_ERROR');
if($XE_VALIDATOR_ERROR == -11)
@ -624,15 +634,6 @@ class memberView extends member
Context::set('XE_VALIDATOR_MESSAGE', $XE_VALIDATOR_MESSAGE . $config->limit_day_description);
}
if(strpos(Context::get('referer_url'), 'procMember') !== false || ($XE_VALIDATOR_ERROR < -10 && $XE_VALIDATOR_ERROR > -21))
{
Context::set('referer_url', getUrl(''));
}
else
{
Context::set('referer_url', escape($_SERVER['HTTP_REFERER']));
}
// Set a template file
$this->setTemplateFile('login_form');
}

View file

@ -14,7 +14,9 @@
<input type="hidden" name="xe_validator_id" value="modules/member/skin/default/find_member_account/1" />
<div>
<input type="email" name="email_address" required placeholder="{$lang->email_address}" title="{$lang->email_address}" /><br />
<block cond="$captcha">{$captcha}<br /></block>
<block cond="isset($captcha) && $captcha && $captcha->isTargetAction('recovery')">
{$captcha}<br />
</block>
<input type="submit" class="btn btn-inverse" value="{$lang->cmd_find_member_account}" />
</div>
</form>
@ -33,7 +35,9 @@
<input type="hidden" name="xe_validator_id" value="modules/member/skin/default/find_member_account/3" />
<div>
<input type="email" id="email_address" name="email_address" value="" required placeholder="{$lang->email_address}" title="{$lang->email_address}" /><br />
<block cond="$captcha">{$captcha}<br /></block>
<block cond="isset($captcha) && $captcha && $captcha->isTargetAction('recovery')">
{$captcha}<br />
</block>
<input type="submit" value="{$lang->cmd_resend_auth_mail}" class="btn btn-inverse" />
</div>
</form>

View file

@ -18,7 +18,7 @@
<input type="email" cond="$identifier != 'user_id'" name="user_id" id="uid" required placeholder="{$lang->email_address}" title="{$lang->email_address}" />
<input type="password" name="password" id="upw" required placeholder="{$lang->password}" title="{$lang->password}" />
</div>
<div class="control-group captcha" cond="$captcha">
<div class="control-group captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('login')">
{$captcha}
</div>
<div class="control-group">

View file

@ -79,7 +79,7 @@
<label for="allow_{$key}" loop="$lang->allow_message_type=>$key,$val"><input type="radio" name="allow_message" value="{$key}" checked="checked"|cond="$member_info->allow_message == $key || (!$member_info && $key == 'Y')" id="allow_{$key}" /> {$val}</label>
</div>
</div>
<div class="control-group" cond="$captcha">
<div class="control-group captcha" cond="isset($captcha) && $captcha && $captcha->isTargetAction('signup')">
<div class="control-label">{$lang->captcha}</div>
<div class="controls">{$captcha}</div>
</div>

View file

@ -152,6 +152,13 @@
<p class="x_help-block">
<label class="x_inline" for="{$item->name}_max_filesize">{$lang->allowed_filesize}</label> <input type="number" min="1" name="{$item->name}_max_filesize" id="{$item->name}_max_filesize" value="{$item->max_filesize}" /> KB
</p>
<!--@if($item->name === 'profile_image')-->
<p class="x_help-block">
<label class="x_inline">{$lang->cmd_image_force_ratio}</label>
<label class="x_inline"><input type="radio" name="profile_image_force_ratio" value="Y" checked="checked"|cond="$config->profile_image_force_ratio !== 'N'" /> {$lang->cmd_yes}</label>
<label class="x_inline"><input type="radio" name="profile_image_force_ratio" value="N" checked="checked"|cond="$config->profile_image_force_ratio === 'N'" /> {$lang->cmd_no}</label>
</p>
<!--@endif-->
</div>
<div cond="$item->name == 'phone_number'" class="_subItem" style="display:none"|cond="!$item->isUse">

View file

@ -1939,9 +1939,9 @@ class moduleModel extends module
$module_info->module = $module_info->module_srl = 0;
}
if (isset($GLOBALS['__MODULE_GRANT__'][$module_info->module][intval($module_info->module_srl ?? 0)][intval($member_info->member_srl)]))
if (isset($GLOBALS['__MODULE_GRANT__'][$module_info->module][intval($module_info->module_srl ?? 0)][intval($member_info->member_srl ?? 0)]))
{
$__cache = &$GLOBALS['__MODULE_GRANT__'][$module_info->module][intval($module_info->module_srl ?? 0)][intval($member_info->member_srl)];
$__cache = &$GLOBALS['__MODULE_GRANT__'][$module_info->module][intval($module_info->module_srl ?? 0)][intval($member_info->member_srl ?? 0)];
if (is_object($__cache) && !$xml_info)
{
return $__cache;
@ -1975,7 +1975,7 @@ class moduleModel extends module
foreach($privilege_list as $val)
{
// If an administrator, grant all
if($member_info->is_admin == 'Y')
if($member_info && $member_info->is_admin == 'Y')
{
$grant->{$val} = true;
}
@ -2018,7 +2018,7 @@ class moduleModel extends module
}
// Log-in member only
if($member_info->member_srl)
if($member_info && $member_info->member_srl)
{
if($val->group_srl == -1 || $val->group_srl == -2)
{
@ -2063,7 +2063,7 @@ class moduleModel extends module
}
// Log-in member only
if($member_info->member_srl)
if($member_info && $member_info->member_srl)
{
if($item->default == 'member' || $item->default == 'site')
{

View file

@ -35,6 +35,7 @@ class ncenterliteController extends ncenterlite
$args->target_url = $url;
$args->target_browser = '';
$args->target_summary = '';
$args->target_content = null;
if (is_object($message))
{
@ -388,6 +389,7 @@ class ncenterliteController extends ncenterlite
$args->target_type = $this->_TYPE_ADMIN_DOCUMENT;
$args->target_url = getNotEncodedUrl('', 'mid', $module_info->mid, 'document_srl', $obj->document_srl);
$args->target_summary = cut_str(strip_tags($obj->title), 50);
$args->target_content = mb_substr(trim(utf8_normalize_spaces(strip_tags($obj->content))), 0, 200, 'UTF-8') ?: (strpos($obj->content, '<img') !== false ? lang('ncenterlite_content_image') : lang('ncenterlite_content_empty'));
$args->regdate = date('YmdHis');
$args->target_browser = $module_info->browser_title;
$args->module_srl = $obj->module_srl;
@ -446,6 +448,7 @@ class ncenterliteController extends ncenterlite
$args->target_type = $this->_TYPE_COMMENT_ALL;
$args->target_url = getNotEncodedUrl('', 'mid', $module_info->mid, 'document_srl', $document_srl, 'comment_srl', $comment_srl) . '#comment_' . $comment_srl;
$args->target_summary = cut_str(trim(utf8_normalize_spaces(strip_tags($content))), 50) ?: (strpos($content, '<img') !== false ? lang('ncenterlite_content_image') : lang('ncenterlite_content_empty'));
$args->target_content = null;
$args->target_nick_name = $obj->nick_name;
$args->target_email_address = $obj->email_address;
$args->regdate = date('YmdHis');
@ -482,6 +485,7 @@ class ncenterliteController extends ncenterlite
$args->target_type = $this->_TYPE_ADMIN_COMMENT;
$args->target_url = getNotEncodedUrl('', 'mid', $module_info->mid, 'document_srl', $document_srl, 'comment_srl', $comment_srl) . '#comment_' . $comment_srl;
$args->target_summary = cut_str(trim(utf8_normalize_spaces(strip_tags($content))), 50) ?: (strpos($content, '<img') !== false ? lang('ncenterlite_content_image') : lang('ncenterlite_content_empty'));
$args->target_content = null;
$args->target_nick_name = $obj->nick_name;
$args->target_email_address = $obj->email_address;
$args->regdate = date('YmdHis');
@ -560,6 +564,7 @@ class ncenterliteController extends ncenterlite
$args->target_type = $this->_TYPE_COMMENT;
$args->target_url = getNotEncodedUrl('', 'mid', $module_info->mid, 'document_srl', $document_srl, 'comment_srl', $comment_srl) . '#comment_' . $comment_srl;
$args->target_summary = cut_str(trim(utf8_normalize_spaces(strip_tags($content))), 50) ?: (strpos($content, '<img') !== false ? lang('ncenterlite_content_image') : lang('ncenterlite_content_empty'));
$args->target_content = null;
$args->target_nick_name = $obj->nick_name;
$args->target_email_address = $obj->email_address;
$args->regdate = $regdate;
@ -609,6 +614,7 @@ class ncenterliteController extends ncenterlite
$args->target_type = $this->_TYPE_COMMENT;
$args->target_url = getNotEncodedUrl('', 'mid', $module_info->mid, 'document_srl', $document_srl, 'comment_srl', $comment_srl) . '#comment_' . $comment_srl;
$args->target_summary = cut_str(trim(utf8_normalize_spaces(strip_tags($content))), 50) ?: (strpos($content, '<img') !== false ? lang('ncenterlite_content_image') : lang('ncenterlite_content_empty'));
$args->target_content = null;
$args->target_nick_name = $obj->nick_name;
$args->target_email_address = $obj->email_address;
$args->regdate = $regdate;
@ -658,6 +664,7 @@ class ncenterliteController extends ncenterlite
$args->type = $this->_TYPE_MESSAGE;
$args->target_type = $this->_TYPE_MESSAGE;
$args->target_summary = $obj->title;
$args->target_content = mb_substr(trim(utf8_normalize_spaces(strip_tags($obj->content))), 0, 200, 'UTF-8');
$args->regdate = date('YmdHis');
$args->notify = $this->_getNotifyId($args);
$args->target_url = getNotEncodedUrl('', 'act', 'dispCommunicationMessages', 'message_srl', $obj->related_srl);
@ -698,6 +705,7 @@ class ncenterliteController extends ncenterlite
$args->type = $this->_TYPE_DOCUMENT;
$args->target_type = $this->_TYPE_SCRAPPED;
$args->target_summary = $obj->title;
$args->target_content = null;
$args->regdate = date('YmdHis');
$args->notify = $this->_getNotifyId($args);
$args->target_url = getNotEncodedUrl('', 'mid', $module_info->mid, 'document_srl', $obj->document_srl);
@ -741,6 +749,7 @@ class ncenterliteController extends ncenterlite
$args->type = $this->_TYPE_DOCUMENT;
$args->target_type = $this->_TYPE_VOTED;
$args->target_summary = $oDocument->get('title');
$args->target_content = null;
$args->regdate = date('YmdHis');
$args->notify = $this->_getNotifyId($args);
$args->target_url = getNotEncodedUrl('', 'mid', $module_info->mid, 'document_srl', $obj->document_srl);
@ -821,6 +830,7 @@ class ncenterliteController extends ncenterlite
$args->type = $this->_TYPE_COMMENT;
$args->target_type = $this->_TYPE_VOTED;
$args->target_summary = cut_str(trim(utf8_normalize_spaces(strip_tags($content))), 50);
$args->target_content = null;
$args->regdate = date('YmdHis');
$args->module_srl = $obj->module_srl;
$args->notify = $this->_getNotifyId($args);
@ -958,7 +968,7 @@ class ncenterliteController extends ncenterlite
{
$comment_srl = Context::get('comment_srl');
$logged_info = Context::get('logged_info');
if($comment_srl && $logged_info)
if($comment_srl && $logged_info && $logged_info->member_srl)
{
$args->target_srl = $comment_srl;
$args->member_srl = $logged_info->member_srl;
@ -975,7 +985,7 @@ class ncenterliteController extends ncenterlite
$document_srl = Context::get('document_srl');
$logged_info = Context::get('logged_info');
if($document_srl && $config->document_read == 'Y' && $logged_info->member_srl)
if($document_srl && $config->document_read == 'Y' && $logged_info && $logged_info->member_srl)
{
$args->srl = $document_srl;
$args->member_srl = $logged_info->member_srl;
@ -1562,10 +1572,28 @@ class ncenterliteController extends ncenterlite
$target_url = Rhymix\Framework\URL::getCurrentDomainUrl($target_url);
}
if (!isset($args->extra_data) || !$args->extra_data)
{
$args->extra_data = [];
$args->extra_data['sender'] = strval($args->target_nick_name);
$args->extra_data['profile_image'] = '';
if ($args->target_member_srl > 0)
{
$profile_image = MemberModel::getProfileImage($args->target_member_srl);
if ($profile_image && $profile_image->src)
{
$args->extra_data['profile_image'] = Rhymix\Framework\URL::getCurrentDomainUrl($profile_image->src);
}
}
$args->extra_data['type'] = strval($args->target_type);
$args->extra_data['subject'] = strval($args->target_summary);
$args->extra_data['content'] = isset($args->target_content) ? mb_substr($args->target_content, 0, 200, 'UTF-8') : '';
}
$oPush = new \Rhymix\Framework\Push();
$oPush->setSubject($content);
$oPush->setContent(strval($args->extra_content));
$oPush->setData($args->extra_data ?: []);
$oPush->setData($args->extra_data);
$oPush->setURL(strval($target_url));
$oPush->addTo(intval($args->member_srl));
$oPush->send();

View file

@ -66,7 +66,7 @@ $lang->point_recal_finished = 'Point recalculation is finished.';
$lang->point_update_desc = 'Insert + in front of the number to increase the point or - to decrease, and update the point. If you don\'t insert + or -, the point will be set as the value you entered.';
$lang->give_point = 'Give or Take Points';
$lang->point_given_prefix = '';
$lang->point_given_suffix = 'points';
$lang->point_given_suffix = '$point';
$lang->point_time_limit_prefix = 'until';
$lang->point_time_limit_suffix = 'days after submission';
$lang->search_target_list['nick_name'] = 'Nick Name';

View file

@ -67,7 +67,7 @@ $lang->point_recal_finished = '포인트 재계산이 완료되었습니다.';
$lang->point_update_desc = '포인트를 증가시키려면 +를 감소시키려면 -를 숫자앞에 표기한 후 업데이트해 주세요. + 또는 - 표시가 없으면 입력한 값으로 설정됩니다.';
$lang->give_point = '포인트 부여/차감';
$lang->point_given_prefix = '';
$lang->point_given_suffix = '포인트';
$lang->point_given_suffix = '$point';
$lang->point_time_limit_prefix = '작성 후';
$lang->point_time_limit_suffix = '일까지';
$lang->search_target_list['nick_name'] = '닉네임';

View file

@ -410,7 +410,7 @@ class pointController extends point
public function triggerBeforeDownloadFile($obj)
{
$logged_info = Context::get('logged_info');
$logged_member_srl = $logged_info->member_srl;
$logged_member_srl = $logged_info ? $logged_info->member_srl : 0;
$author_member_srl = abs($obj->member_srl);
$module_srl = $obj->module_srl;
if ($logged_member_srl && $logged_member_srl == $author_member_srl)
@ -443,7 +443,7 @@ class pointController extends point
public function triggerDownloadFile($obj)
{
$logged_info = Context::get('logged_info');
$logged_member_srl = $logged_info->member_srl;
$logged_member_srl = $logged_info ? $logged_info->member_srl : 0;
$author_member_srl = abs($obj->member_srl);
$module_srl = $obj->module_srl;
if ($logged_member_srl && $logged_member_srl == $author_member_srl)
@ -481,7 +481,7 @@ class pointController extends point
public function triggerUpdateReadedCount($obj)
{
$logged_info = Context::get('logged_info');
$logged_member_srl = $logged_info->member_srl;
$logged_member_srl = $logged_info ? $logged_info->member_srl : 0;
$author_member_srl = abs($obj->get('member_srl'));
$module_srl = $obj->get('module_srl');
if ($logged_member_srl && $logged_member_srl == $author_member_srl)
@ -587,7 +587,7 @@ class pointController extends point
public function triggerUpdateVotedCount($obj)
{
$logged_info = Context::get('logged_info');
$logged_member_srl = $logged_info->member_srl;
$logged_member_srl = $logged_info ? $logged_info->member_srl : 0;
$target_member_srl = abs($obj->member_srl);
if ($logged_member_srl && $logged_member_srl == $target_member_srl)
{

View file

@ -89,18 +89,18 @@
<tr>
<th class="nowr">{$lang->cmd_signup}</td>
<td class="nowr">
&nbsp;{$lang->point_given_prefix}
{strtr($lang->point_given_prefix, ['$'.'point' => $config->point_name])}
<input type="number" value="{$config->signup_point ?: '0'}" name="signup_point" id="signup_point" />
&nbsp;{$lang->point_given_suffix}
&nbsp;{strtr($lang->point_given_suffix, ['$'.'point' => $config->point_name])}
</td>
<td class="nowr"></td>
</tr>
<tr>
<th class="nowr">{$lang->cmd_login}</td>
<td class="nowr">
&nbsp;{$lang->point_given_prefix}
{strtr($lang->point_given_prefix, ['$'.'point' => $config->point_name])}
<input type="number" value="{$config->login_point ?: '0'}" name="login_point" id="login_point" />
&nbsp;{$lang->point_given_suffix}
&nbsp;{strtr($lang->point_given_suffix, ['$'.'point' => $config->point_name])}
</td>
<td class="nowr"></td>
</tr>
@ -108,9 +108,9 @@
<tr>
<th class="nowr">{lang('point_' . $action_type)}</td>
<td class="nowr">
&nbsp;{$lang->point_given_prefix}
{strtr($lang->point_given_prefix, ['$'.'point' => $config->point_name])}
<input type="number" value="{$config_array[$action_type] ?: '0'}" name="{$action_type}" id="{$action_type}" />
&nbsp;{$lang->point_given_suffix}
&nbsp;{strtr($lang->point_given_suffix, ['$'.'point' => $config->point_name])}
<block cond="$action_config['except_notice']">
<label class="x_inline" for="{$action_type}_except_notice" style="margin-left:10px">
<input type="checkbox" value="Y" name="{$action_type}_except_notice" id="{$action_type}_except_notice" checked="checked"|cond="$config_array[$action_type . '_except_notice']" />

View file

@ -30,9 +30,7 @@ class pollController extends poll
$stop_date = date('YmdHis', $_SERVER['REQUEST_TIME']+60*60*24*30);
}
$logged_info = Context::get('logged_info');
$vars = Context::getRequestVars();
$args = new stdClass;
$tmp_args = array();
@ -70,9 +68,9 @@ class pollController extends poll
$tmp_args[$poll_index]->item = array();
}
if($logged_info->is_admin != 'Y')
if(!$this->user->isAdmin())
{
$val = htmlspecialchars($val, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
$val = escape($val, false);
}
switch($tmp_arr[0])
@ -108,15 +106,14 @@ class pollController extends poll
// Configure the variables
$poll_srl = getNextSequence();
$member_srl = $logged_info->member_srl?$logged_info->member_srl:0;
$oDB = &DB::getInstance();
$oDB = DB::getInstance();
$oDB->begin();
// Register the poll
$poll_args = new stdClass;
$poll_args->poll_srl = $poll_srl;
$poll_args->member_srl = $member_srl;
$poll_args->member_srl = $this->user->member_srl;
$poll_args->list_order = $poll_srl*-1;
$poll_args->stop_date = $args->stop_date;
$poll_args->poll_count = 0;
@ -180,8 +177,10 @@ class pollController extends poll
if($poll_item_title=='') throw new Rhymix\Framework\Exception('msg_item_title_cannot_empty');
$logged_info = Context::get('logged_info');
if(!$logged_info) throw new Rhymix\Framework\Exception('msg_cannot_add_item');
if(!$this->user->member_srl)
{
throw new Rhymix\Framework\Exception('msg_cannot_add_item');
}
if(!$poll_srl || !$poll_index_srl) throw new Rhymix\Framework\Exceptions\InvalidRequest;
@ -196,12 +195,12 @@ class pollController extends poll
if(!$this->isAbletoAddItem($type)) throw new Rhymix\Framework\Exception('msg_cannot_add_item');
if($logged_info->is_admin != 'Y')
if(!$this->user->isAdmin())
{
$poll_item_title = htmlspecialchars($poll_item_title, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
$poll_item_title = escape($poll_item_title, false);
}
$oDB = &DB::getInstance();
$oDB = DB::getInstance();
$oDB->begin();
$item_args = new stdClass;
@ -210,7 +209,7 @@ class pollController extends poll
$item_args->title = $poll_item_title;
$item_args->poll_count = 0;
$item_args->upload_target_srl = 0;
$item_args->add_user_srl = $logged_info->member_srl;
$item_args->add_user_srl = $this->user->member_srl;
$output = executeQuery('poll.insertPollItem', $item_args);
if(!$output->toBool())
{
@ -226,8 +225,10 @@ class pollController extends poll
$poll_index_srl = (int) Context::get('index_srl');
$poll_item_srl = Context::get('item_srl');
$logged_info = Context::get('logged_info');
if(!$logged_info) throw new Rhymix\Framework\Exception('msg_cannot_delete_item');
if(!$this->user->member_srl)
{
throw new Rhymix\Framework\Exception('msg_cannot_delete_item');
}
if(!$poll_srl || !$poll_index_srl || !$poll_item_srl) throw new Rhymix\Framework\Exceptions\InvalidRequest;
@ -248,8 +249,14 @@ class pollController extends poll
if(!$output->data) throw new Rhymix\Framework\Exception('poll_no_poll_or_deleted_poll');
$poll_member_srl = $output->data->member_srl;
if($add_user_srl!=$logged_info->member_srl && $poll_member_srl!=$logged_info->member_srl) throw new Rhymix\Framework\Exception('msg_cannot_delete_item');
if($poll_count>0) throw new Rhymix\Framework\Exception('msg_cannot_delete_item_poll_exist');
if($add_user_srl != $this->user->member_srl && $poll_member_srl != $this->user->member_srl)
{
throw new Rhymix\Framework\Exception('msg_cannot_delete_item');
}
if($poll_count > 0)
{
throw new Rhymix\Framework\Exception('msg_cannot_delete_item_poll_exist');
}
$oDB = &DB::getInstance();
$oDB->begin();
@ -330,11 +337,7 @@ class pollController extends poll
$log_args = new stdClass;
$log_args->poll_srl = $poll_srl;
$log_args->poll_item = $args->poll_item_srl;
$logged_info = Context::get('logged_info');
$member_srl = $logged_info->member_srl?$logged_info->member_srl:0;
$log_args->member_srl = $member_srl;
$log_args->member_srl = $this->user->member_srl;
$log_args->ipaddress = \RX_CLIENT_IP;
$output = executeQuery('poll.insertPollLog', $log_args);

View file

@ -239,19 +239,19 @@ class spamfilterController extends spamfilter
return;
}
$enable = false;
$target_actions = [];
foreach (['signup', 'login', 'recovery', 'document', 'comment'] as $action)
{
if ($config->captcha->target_actions[$action])
{
if (preg_match(self::$_captcha_actions[$action], $obj->act) || ($action === 'comment' && !$obj->act && Context::get('document_srl')))
if (preg_match(self::$_captcha_actions[$action], $obj->act) || ($action === 'comment' && (!$obj->act || $obj->act === 'dispBoardContent') && Context::get('document_srl')))
{
$enable = true;
$target_actions[$action] = true;
}
}
}
if ($enable)
if (count($target_actions))
{
include_once __DIR__ . '/spamfilter.lib.php';
spamfilter_reCAPTCHA::init($config->captcha);
@ -262,7 +262,10 @@ class spamfilterController extends spamfilter
}
else
{
Context::set('captcha', new spamfilter_reCAPTCHA());
$captcha = new spamfilter_reCAPTCHA();
$captcha->setTargetActions($target_actions);
$captcha->addScripts();
Context::set('captcha', $captcha);
}
}
}

View file

@ -7,6 +7,7 @@ class spamfilter_reCAPTCHA
protected static $scripts_added = false;
protected static $instances_inserted = 0;
protected static $sequence = 1;
protected $_target_actions = [];
public static function init($config)
{
@ -47,19 +48,29 @@ class spamfilter_reCAPTCHA
$_SESSION['recaptcha_authenticated'] = true;
}
public function __construct()
public function addScripts()
{
if (!self::$scripts_added)
{
self::$scripts_added = true;
Context::loadFile(array('./modules/spamfilter/tpl/js/recaptcha.js', 'body'));
Context::addHtmlFooter('<script src="https://www.google.com/recaptcha/api.js?render=explicit&amp;onload=reCaptchaCallback" async defer></script>');
$html = '<div id="recaptcha-config" data-sitekey="%s" data-theme="%s" data-size="%s"></div>';
$html = sprintf($html, escape(self::$config->site_key), self::$config->theme ?: 'light', self::$config->size ?: 'normal');
$html = '<div id="recaptcha-config" data-sitekey="%s" data-theme="%s" data-size="%s" data-targets="%s"></div>';
$html = sprintf($html, escape(self::$config->site_key), self::$config->theme ?: 'light', self::$config->size ?: 'normal', implode(',', array_keys($this->_target_actions)));
Context::addHtmlFooter($html);
}
}
public function setTargetActions(array $target_actions)
{
$this->_target_actions = $target_actions;
}
public function isTargetAction(string $action): bool
{
return isset($this->_target_actions[$action]);
}
public function __toString()
{
return sprintf('<div id="recaptcha-instance-%d" class="g-recaptcha"></div>', self::$instances_inserted++);

View file

@ -3,15 +3,31 @@ function reCaptchaCallback() {
var recaptcha_config = $("#recaptcha-config");
var recaptcha_instances = $(".g-recaptcha");
var recaptcha_instance_id = 1;
var recaptcha_targets = String(recaptcha_config.data("targets")).split(",");
if (recaptcha_instances.size() === 0) {
if (recaptcha_instances.length === 0) {
var autoinsert_candidates = $("form").filter(function() {
var actinput = $("input[name='act']", this);
if (actinput.size() && actinput.val() && actinput.val().match(/^proc.+(Insert(Document|Comment|)|Login|FindAccount|ResendAuthMail)/i)) {
return true;
if (actinput.length && actinput.val()) {
var act = String(actinput.val());
if (act.match(/^procMemberInsert$/i) && recaptcha_targets.indexOf("signup") > -1) {
return true;
}
if (act.match(/^procMemberLogin$/i) && recaptcha_targets.indexOf("login") > -1) {
return true;
}
if (act.match(/^procMember(FindAccount|ResendAuthMail)$/i) && recaptcha_targets.indexOf("recovery") > -1) {
return true;
}
if (act.match(/^proc[A-Z][a-zA-Z0-9_]+InsertDocument$/i) && recaptcha_targets.indexOf("document") > -1) {
return true;
}
if (act.match(/^proc[A-Z][a-zA-Z0-9_]+InsertComment$/i) && recaptcha_targets.indexOf("comment") > -1) {
return true;
}
}
var procfilter = $(this).attr("onsubmit");
if (procfilter && procfilter.match(/procFilter\b.+\binsert/i)) {
if (procfilter && procfilter.match(/procFilter\b.+\binsert/i) && (recaptcha_targets.indexOf("document") > -1 || recaptcha_targets.indexOf("comment") > -1)) {
return true;
}
return false;