Merge branch 'develop' into pr/session-class

This commit is contained in:
Kijin Sung 2017-02-07 22:13:08 +09:00
commit 483ac84796
454 changed files with 10659 additions and 30145 deletions

View file

@ -1,23 +1,21 @@
language: php language: php
php: php:
- 5.4
- 5.5 - 5.5
- 5.6 - 5.6
- 7.0 - 7.0
- 7.1
- hhvm - hhvm
sudo: false sudo: false
before_script: before_script:
- npm install grunt grunt-cli grunt-contrib-jshint grunt-contrib-csslint grunt-phplint --save-dev - npm install grunt grunt-cli grunt-contrib-jshint grunt-contrib-csslint grunt-phplint --save-dev
- mysql -u root -e "CREATE DATABASE rhymix" - mysql -u root -e "CREATE DATABASE rhymix"
- mysql -u root -e "SET PASSWORD FOR 'travis'@'localhost' = PASSWORD('travis')" - mysql -u root -e "SET PASSWORD FOR 'travis'@'localhost' = PASSWORD('travis')"
- if [[ $TRAVIS_PHP_VERSION != "5.4" && $TRAVIS_PHP_VERSION != "hhvm" ]]; then php -S localhost:8000 & fi - if [[ $TRAVIS_PHP_VERSION != "hhvm" ]]; then php -S localhost:8000 & fi
- if [[ $TRAVIS_PHP_VERSION == "5.4" ]]; then wget http://codeception.com/releases/2.0.16/codecept.phar; fi - wget http://codeception.com/releases/2.1.11/codecept.phar
- if [[ ! -f codecept.phar ]]; then wget http://codeception.com/releases/2.1.6/codecept.phar; fi
script: script:
- if [[ -s codecept.phar ]]; then php codecept.phar build; fi - php codecept.phar build
- if [[ -s codecept.phar && $TRAVIS_PHP_VERSION == "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis --skip install; fi - if [[ $TRAVIS_PHP_VERSION == "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis --skip install; fi
- if [[ -s codecept.phar && $TRAVIS_PHP_VERSION == "5.4" ]]; then php codecept.phar run -d --fail-fast --env travis --skip install; fi - if [[ $TRAVIS_PHP_VERSION != "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis; fi
- if [[ -s codecept.phar && $TRAVIS_PHP_VERSION != "5.4" && $TRAVIS_PHP_VERSION != "hhvm" ]]; then php codecept.phar run -d --fail-fast --env travis; fi
- grunt lint - grunt lint
notifications: notifications:
email: false email: false

View file

@ -42,6 +42,7 @@ module.exports = function(grunt) {
'box-sizing' : false, 'box-sizing' : false,
'font-sizes' : false, 'font-sizes' : false,
'duplicate-background-images' : false, 'duplicate-background-images' : false,
'order-alphabetical' : false,
'ids' : false, 'ids' : false,
'important' : false, 'important' : false,
'overqualified-elements' : false, 'overqualified-elements' : false,

View file

@ -3,7 +3,7 @@
Rhymix(라이믹스)는 누구든지 쉽고 자유롭게 독립적인 홈페이지를 만들어 Rhymix(라이믹스)는 누구든지 쉽고 자유롭게 독립적인 홈페이지를 만들어
자신을 표현하고 커뮤니티를 키워나갈 수 있도록 돕기 위한 CMS(content management system)입니다. 자신을 표현하고 커뮤니티를 키워나갈 수 있도록 돕기 위한 CMS(content management system)입니다.
XETOWN 커뮤니티에서 [XE](https://www.xpressengine.com)를 fork(가지치기)하여 진행하는 프로젝트로, [XpressEngine](https://www.xpressengine.com) 1.8 버전을 fork(가지치기)하여 진행하는 프로젝트로,
누구나 무료로 사용할 수 있고 개발에 참여할 수도 있는 자유 소프트웨어(free software)입니다. 누구나 무료로 사용할 수 있고 개발에 참여할 수도 있는 자유 소프트웨어(free software)입니다.
Rhymix는 "시를 짓다, 운을 맞추다"라는 의미의 "rhyme"과 Rhymix는 "시를 짓다, 운을 맞추다"라는 의미의 "rhyme"과
@ -28,7 +28,7 @@ Rhymix는 개발자와 사용자가 서로의 권리와 책임을 존중하는
- 초보자도 쉽게 클릭 몇 번으로 웹사이트를 완성할 수 있을 만큼 편리한 CMS - 초보자도 쉽게 클릭 몇 번으로 웹사이트를 완성할 수 있을 만큼 편리한 CMS
- 최신 기술을 적극적으로 사용하고 속도가 빠르며 보안이 우수한 CMS - 최신 기술을 적극적으로 사용하고 속도가 빠르며 보안이 우수한 CMS
- 커뮤니티를 통해 사용자와 개발자의 건전한 의사소통을 돕는 CMS - 커뮤니티를 통해 사용자와 개발자의 건전한 의사소통을 돕는 CMS
- **애드온, 모듈, 위젯 등 기존 XE 서드파티 자료와의 호환성 100% 목표!** - 애드온, 모듈, 위젯 등 기존 XE 서드파티 자료들과의 호환성을 최대한 보장하려고 노력합니다.
### 설치 환경 ### 설치 환경
@ -46,9 +46,6 @@ Rhymix를 사용하려면 아래의 조건을 충족하는 웹호스팅이나
- simplexml - simplexml
- php.ini에서 session.auto_start = Off로 설정되어 있어야 합니다. - php.ini에서 session.auto_start = Off로 설정되어 있어야 합니다.
- 설치 폴더 또는 files 폴더에 쓰기 권한이 주어져야 합니다. - 설치 폴더 또는 files 폴더에 쓰기 권한이 주어져야 합니다.
- MySQL/MariaDB 외에도 아래의 DB를 사용할 수 있습니다.
- CUBRID 9.0 이상
- Microsoft SQL Server 2008 이상
### 개발 참여 ### 개발 참여
@ -61,9 +58,12 @@ devops@rhymix.org로 알려 주시면 감사하겠습니다.
### 공식 홈페이지 ### 공식 홈페이지
- XETOWN : https://www.xetown.com
- Rhymix : https://www.rhymix.org - Rhymix : https://www.rhymix.org
### 커뮤니티
- XETOWN : https://www.xetown.com
### 저작권 및 라이선스 ### 저작권 및 라이선스
Rhymix는 [GNU GPL v2](http://korea.gnu.org/documents/copyleft/gpl.ko.html) Rhymix는 [GNU GPL v2](http://korea.gnu.org/documents/copyleft/gpl.ko.html)

View file

@ -4,14 +4,14 @@
* @author NAVER (developers@xpressengine.com) * @author NAVER (developers@xpressengine.com)
*/ */
(function($){ (function($){
var protocol_re = '(https?|ftp|news|telnet|irc|mms)://'; var protocol_re = '(?:(?:https?|ftp|news|telnet|irc|mms)://)';
var domain_re = '(?:[^\\s./]+\\.)+[^\\s./]+'; var domain_re = '(?:[^\\s./)>]+\\.)+[^\\s./)>]+';
var max_255_re = '(?:1[0-9]{2}|2[0-4][0-9]|25[0-5]|[1-9]?[0-9])'; var max_255_re = '(?:1[0-9]{2}|2[0-4][0-9]|25[0-5]|[1-9]?[0-9])';
var ip_re = '(?:'+max_255_re+'\\.){3}'+max_255_re; var ip_re = '(?:'+max_255_re+'\\.){3}'+max_255_re;
var port_re = '(?::([0-9]+))?'; var port_re = '(?::([0-9]+))?';
var user_re = '(?:/~\\w+)?'; var user_re = '(?:/~\\w+)?';
var path_re = '(?:/[\\w!@$%&!?+=_~"/.,:;-]*)?'; var path_re = '(?:/[^\\s]*)?';
var hash_re = '(?:#[\\w!@$%&!?+=_~"/.,:;-]*)?'; var hash_re = '(?:#[^\\s]*)?';
var url_regex = new RegExp('('+protocol_re+'('+domain_re+'|'+ip_re+'|localhost'+')'+port_re+user_re+path_re+hash_re+')', 'ig'); var url_regex = new RegExp('('+protocol_re+'('+domain_re+'|'+ip_re+'|localhost'+')'+port_re+user_re+path_re+hash_re+')', 'ig');
@ -36,8 +36,25 @@
var content = textNode.nodeValue; var content = textNode.nodeValue;
var dummy = $('<span>'); var dummy = $('<span>');
//content = content.replace(/</g, '&lt;').replace(/>/g, '&gt;'); content = content.replace(/</g, '&lt;').replace(/>/g, '&gt;');
content = content.replace(url_regex, '<a href="$1" target="_blank">$1</a>'); content = content.replace(url_regex, function(match, p1, offset, string) {
var match;
var suffix = '';
if (p1.indexOf('(') < 0 && p1.match(/\)$/)) {
p1 = p1.replace(/\)$/, '');
suffix = ')';
} else if (p1.indexOf('[') < 0 && p1.match(/\]$/)) {
p1 = p1.replace(/\]$/, '');
suffix = ']';
} else if (p1.indexOf('&lt;') < 0 && p1.match(/&gt;$/)) {
p1 = p1.replace(/&gt;$/, '');
suffix = '&gt;';
} else if (match = /^([\x21-\x7E]+\.[a-z]+)([가-힣]{1,3})$/.exec(p1)) {
p1 = match[1];
suffix = match[2];
}
return '<a href="' + p1 + '" target="_blank">' + p1 + '</a>' + suffix;
});
$(textNode).before(dummy); $(textNode).before(dummy);
$(textNode).replaceWith(content); $(textNode).replaceWith(content);
@ -75,4 +92,11 @@
}); });
xe.registerPlugin(new AutoLink()); xe.registerPlugin(new AutoLink());
$(document).on('click', '.xe_content a', function() {
if (!$(this).attr("target")) {
$(this).attr("target", "_blank");
}
});
})(jQuery); })(jQuery);

View file

@ -99,8 +99,10 @@ if($called_position == 'before_module_proc')
$category_list = $oDocumentModel->getCategoryList($this->module_srl); $category_list = $oDocumentModel->getCategoryList($this->module_srl);
// Specifies a temporary file storage // Specifies a temporary file storage
$tmp_uploaded_path = sprintf(_XE_PATH_ . 'files/cache/blogapi/%s/%s/', $this->mid, $user_id); $logged_info = Context::get('logged_info');
$uploaded_target_path = sprintf(_XE_PATH_ . 'files/cache/blogapi/%s/%s/', $this->mid, $user_id); $mediaPath = sprintf('files/cache/blogapi/%s/%s/', $this->mid, $logged_info->member_srl);
$mediaAbsPath = _XE_PATH_ . $mediaPath;
$mediaUrlPath = Context::getRequestUri() . $mediaPath;
switch($method_name) switch($method_name)
{ {
@ -167,22 +169,56 @@ if($called_position == 'before_module_proc')
foreach($fileinfo as $key => $val) foreach($fileinfo as $key => $val)
{ {
$nodename = (string)$val->name; $nodename = (string)$val->name;
if($nodename == 'bits') if($nodename === 'bits')
{
$filedata = base64_decode((string)$val->value->base64); $filedata = base64_decode((string)$val->value->base64);
elseif($nodename == 'name') }
$filename = (string)$val->value->string; else if($nodename === 'name')
{
$filename = pathinfo((string)$val->value->string, PATHINFO_BASENAME);
}
} }
$tmp_arr = explode('/', $filename); if($logged_info->is_admin != 'Y')
$filename = array_pop($tmp_arr); {
// check file type
if(isset($file_module_config->allowed_filetypes) && $file_module_config->allowed_filetypes !== '*.*')
{
$filetypes = explode(';', $file_module_config->allowed_filetypes);
$ext = array();
FileHandler::makeDir($tmp_uploaded_path); foreach($filetypes as $item)
{
$item = explode('.', $item);
$ext[] = strtolower(array_pop($item));
}
$target_filename = sprintf('%s%s', $tmp_uploaded_path, $filename); $uploaded_ext = explode('.', $filename);
$uploaded_ext = strtolower(array_pop($uploaded_ext));
if(!in_array($uploaded_ext, $ext))
{
printContent(getXmlRpcFailure(1, 'Not allowed file type'));
break;
}
}
$allowed_filesize = $file_module_config->allowed_filesize * 1024 * 1024;
if($allowed_filesize < strlen($filedata))
{
printContent(getXmlRpcFailure(1, 'This file exceeds the attachment limit'));
break;
}
}
$temp_filename = Password::createSecureSalt(12, 'alnum');
$target_filename = sprintf('%s%s', $mediaAbsPath, $temp_filename);
FileHandler::makeDir($mediaAbsPath);
FileHandler::writeFile($target_filename, $filedata); FileHandler::writeFile($target_filename, $filedata);
$obj = new stdClass(); FileHandler::writeFile($target_filename . '_source_filename', $filename);
$obj->url = Context::getRequestUri() . $target_filename;
$obj = new stdClass();
$obj->url = Context::getRequestUri() . $mediaPath . $temp_filename;
$content = getXmlRpcResponse($obj); $content = getXmlRpcResponse($obj);
printContent($content); printContent($content);
break; break;
@ -291,21 +327,34 @@ if($called_position == 'before_module_proc')
$obj->module_srl = $this->module_srl; $obj->module_srl = $this->module_srl;
// Attachment // Attachment
if(is_dir($tmp_uploaded_path)) if(is_dir($mediaAbsPath))
{ {
$file_list = FileHandler::readDir($tmp_uploaded_path); $file_list = FileHandler::readDir($mediaAbsPath, '/(_source_filename)$/is');
$file_count = count($file_list); $file_count = count($file_list);
if($file_count) if($file_count)
{ {
$oFileController = getController('file'); $oFileController = getController('file');
for($i = 0; $i < $file_count; $i++) $oFileModel = getModel('file');
foreach($file_list as $file)
{ {
$file_info['tmp_name'] = sprintf('%s%s', $tmp_uploaded_path, $file_list[$i]); $filename = FileHandler::readFile($mediaAbsPath . $file);
$file_info['name'] = $file_list[$i]; $temp_filename = str_replace('_source_filename', '', $file);
$file_info = array();
$file_info['tmp_name'] = sprintf('%s%s', $mediaAbsPath, $temp_filename);
$file_info['name'] = $filename;
$fileOutput = $oFileController->insertFile($file_info, $this->module_srl, $document_srl, 0, true); $fileOutput = $oFileController->insertFile($file_info, $this->module_srl, $document_srl, 0, true);
$uploaded_filename = $fileOutput->get('uploaded_filename');
$source_filename = $fileOutput->get('source_filename'); if($fileOutput->get('direct_download') === 'N')
$obj->content = str_replace($uploaded_target_path . $source_filename, sprintf('/files/attach/images/%s/%s%s', $this->module_srl, getNumberingPath($document_srl, 3), $uploaded_filename), $obj->content); {
$replace_url = Context::getRequestUri() . $oFileModel->getDownloadUrl($fileOutput->file_srl, $fileOutput->sid, $this->module_srl);
}
else
{
$replace_url = Context::getRequestUri() . $fileOutput->get('uploaded_filename');
}
$obj->content = str_replace($mediaUrlPath . $temp_filename, $replace_url, $obj->content);
} }
$obj->uploaded_count = $file_count; $obj->uploaded_count = $file_count;
} }
@ -332,7 +381,7 @@ if($called_position == 'before_module_proc')
{ {
$content = getXmlRpcResponse(strval($document_srl)); $content = getXmlRpcResponse(strval($document_srl));
} }
FileHandler::removeDir($tmp_uploaded_path); FileHandler::removeDir($mediaAbsPath);
printContent($content); printContent($content);
break; break;
@ -404,27 +453,36 @@ if($called_position == 'before_module_proc')
// Document srl // Document srl
$obj->document_srl = $document_srl; $obj->document_srl = $document_srl;
$obj->module_srl = $this->module_srl; $obj->module_srl = $this->module_srl;
// Attachment // Attachment
if(is_dir($tmp_uploaded_path)) if(is_dir($mediaAbsPath))
{ {
$file_list = FileHandler::readDir($tmp_uploaded_path); $file_list = FileHandler::readDir($mediaAbsPath, '/(_source_filename)$/is');
$file_count = count($file_list); $file_count = count($file_list);
if($file_count) if($file_count)
{ {
$oFileController = getController('file'); $oFileController = getController('file');
for($i = 0; $i < $file_count; $i++) $oFileModel = getModel('file');
foreach($file_list as $file)
{ {
$file_info['tmp_name'] = sprintf('%s%s', $tmp_uploaded_path, $file_list[$i]); $filename = FileHandler::readFile($mediaAbsPath . $file);
$file_info['name'] = $file_list[$i]; $temp_filename = str_replace('_source_filename', '', $file);
$moved_filename = sprintf('./files/attach/images/%s/%s/%s', $this->module_srl, $document_srl, $file_info['name']);
if(file_exists($moved_filename))
continue;
$file_info = array();
$file_info['tmp_name'] = sprintf('%s%s', $mediaAbsPath, $temp_filename);
$file_info['name'] = $filename;
$fileOutput = $oFileController->insertFile($file_info, $this->module_srl, $document_srl, 0, true); $fileOutput = $oFileController->insertFile($file_info, $this->module_srl, $document_srl, 0, true);
$uploaded_filename = $fileOutput->get('uploaded_filename');
$source_filename = $fileOutput->get('source_filename'); if($fileOutput->get('direct_download') === 'N')
$obj->content = str_replace($uploaded_target_path . $source_filename, sprintf('/files/attach/images/%s/%s%s', $this->module_srl, getNumberingPath($document_srl, 3), $uploaded_filename), $obj->content); {
$replace_url = Context::getRequestUri() . $oFileModel->getDownloadUrl($fileOutput->file_srl, $fileOutput->sid, $this->module_srl);
}
else
{
$replace_url = Context::getRequestUri() . $fileOutput->get('uploaded_filename');
}
$obj->content = str_replace($mediaUrlPath . $temp_filename, $replace_url, $obj->content);
} }
$obj->uploaded_count += $file_count; $obj->uploaded_count += $file_count;
} }
@ -440,7 +498,7 @@ if($called_position == 'before_module_proc')
else else
{ {
$content = getXmlRpcResponse(true); $content = getXmlRpcResponse(true);
FileHandler::removeDir($tmp_uploaded_path); FileHandler::removeDir($mediaAbsPath);
} }
printContent($content); printContent($content);

View file

@ -1425,6 +1425,14 @@ class Context
{ {
$result[$k] = urlencode($v); $result[$k] = urlencode($v);
} }
elseif($key === 'xe_validator_id')
{
$result[$k] = htmlspecialchars($v, ENT_COMPAT | ENT_HTML401, 'UTF-8', FALSE);
}
elseif(starts_with('XE_VALIDATOR_', $key, false))
{
unset($result[$k]);
}
else else
{ {
$result[$k] = $v; $result[$k] = $v;
@ -1513,6 +1521,10 @@ class Context
} }
// Allow if the current user is in the list of allowed IPs. // Allow if the current user is in the list of allowed IPs.
if (PHP_SAPI === 'cli')
{
return;
}
if (Rhymix\Framework\Filters\IpFilter::inRanges(RX_CLIENT_IP, config('lock.allow'))) if (Rhymix\Framework\Filters\IpFilter::inRanges(RX_CLIENT_IP, config('lock.allow')))
{ {
return; return;

View file

@ -369,7 +369,7 @@ class DB
$log['time'] = date('Y-m-d H:i:s'); $log['time'] = date('Y-m-d H:i:s');
$log['backtrace'] = array(); $log['backtrace'] = array();
if (config('debug.enabled') && in_array('queries', config('debug.display_content'))) if (config('debug.enabled') && ($this->isError() || in_array('queries', config('debug.display_content'))))
{ {
$bt = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); $bt = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
foreach($bt as $no => $call) foreach($bt as $no => $call)

View file

@ -201,7 +201,7 @@ class HTMLDisplayHandler
if(is_array(Context::get('INPUT_ERROR'))) if(is_array(Context::get('INPUT_ERROR')))
{ {
$INPUT_ERROR = Context::get('INPUT_ERROR'); $INPUT_ERROR = Context::get('INPUT_ERROR');
$keys = array_keys($INPUT_ERROR); $keys = array_map(function($str) { return preg_quote($str, '@'); }, array_keys($INPUT_ERROR));
$keys = '(' . implode('|', $keys) . ')'; $keys = '(' . implode('|', $keys) . ')';
$output = preg_replace_callback('@(<input)([^>]*?)\sname="' . $keys . '"([^>]*?)/?>@is', array(&$this, '_preserveValue'), $output); $output = preg_replace_callback('@(<input)([^>]*?)\sname="' . $keys . '"([^>]*?)/?>@is', array(&$this, '_preserveValue'), $output);
@ -259,42 +259,28 @@ class HTMLDisplayHandler
// get type // get type
$type = 'text'; $type = 'text';
if(preg_match('/\stype="([a-z]+)"/i', $str, $m)) if(preg_match('/\stype="([^"]+)"/i', $str, $m))
{ {
$type = strtolower($m[1]); $type = strtolower($m[1]);
} }
switch($type) switch($type)
{ {
case 'text':
case 'hidden':
case 'email':
case 'search':
case 'tel':
case 'url':
case 'email':
case 'datetime':
case 'date':
case 'month':
case 'week':
case 'time':
case 'datetime-local':
case 'number':
case 'range':
case 'color':
$str = preg_replace('@\svalue="[^"]*?"@', ' ', $str) . ' value="' . htmlspecialchars($INPUT_ERROR[$match[3]], ENT_COMPAT | ENT_HTML401, 'UTF-8', false) . '"';
break;
case 'password':
$str = preg_replace('@\svalue="[^"]*?"@', ' ', $str);
break;
case 'radio': case 'radio':
case 'checkbox': case 'checkbox':
$str = preg_replace('@\schecked(="[^"]*?")?@', ' ', $str); if(preg_match('@\s(?i:value)="' . preg_quote($INPUT_ERROR[$match[3]], '@') . '"@', $str))
if(@preg_match('@\s(?i:value)="' . $INPUT_ERROR[$match[3]] . '"@', $str))
{ {
$str .= ' checked="checked"'; $str = preg_replace('@\schecked(="[^"]*?")?@', ' checked="checked"', $str);
} }
break; break;
default:
if (!preg_match('@\svalue="([^"]*?)"@', $str))
{
$str = $str . ' value=""';
}
$str = preg_replace_callback('@\svalue="([^"]*?)"@', function() use($INPUT_ERROR, $match) {
return ' value="' . escape($INPUT_ERROR[$match[3]], true) . '"';
}, $str);
} }
return $str . ' />'; return $str . ' />';
@ -333,7 +319,7 @@ class HTMLDisplayHandler
{ {
$INPUT_ERROR = Context::get('INPUT_ERROR'); $INPUT_ERROR = Context::get('INPUT_ERROR');
preg_match('@<textarea.*?>@is', $matches[0], $mm); preg_match('@<textarea.*?>@is', $matches[0], $mm);
return $mm[0] . $INPUT_ERROR[$matches[1]] . '</textarea>'; return $mm[0] . escape($INPUT_ERROR[$matches[1]], true) . '</textarea>';
} }
/** /**
@ -403,7 +389,7 @@ class HTMLDisplayHandler
if ($document_srl) if ($document_srl)
{ {
$oDocument = Context::get('oDocument') ?: getModel('document')->getDocument($document_srl, false, false); $oDocument = Context::get('oDocument') ?: getModel('document')->getDocument($document_srl, false, false);
if ($oDocument instanceof documentItem && $oDocument->document_srl == $document_srl && !$oDocument->isSecret()) if (is_object($oDocument) && $oDocument->document_srl == $document_srl && (!method_exists($oDocument, 'isSecret') || !$oDocument->isSecret()))
{ {
$page_type = 'article'; $page_type = 'article';
} }

View file

@ -208,15 +208,7 @@ class FileHandler
*/ */
public static function removeBlankDir($path) public static function removeBlankDir($path)
{ {
$path = self::getRealPath($path); return Rhymix\Framework\Storage::deleteEmptyDirectory(self::getRealPath($path), false);
if (Rhymix\Framework\Storage::isEmptyDirectory($path))
{
return Rhymix\Framework\Storage::deleteDirectory($path);
}
else
{
return false;
}
} }
/** /**
@ -256,12 +248,22 @@ class FileHandler
return $size . 'Bytes'; return $size . 'Bytes';
} }
if($size >= 1024 && $size < 1024 * 1024) if($size >= 1024 && $size < (1024 * 1024))
{ {
return sprintf("%0.1fKB", $size / 1024); return sprintf("%0.1fKB", $size / 1024);
} }
return sprintf("%0.2fMB", $size / (1024 * 1024)); if($size >= (1024 * 1024) && $size < (1024 * 1024 * 1024))
{
return sprintf("%0.2fMB", $size / (1024 * 1024));
}
if($size >= (1024 * 1024 * 1024) && $size < (1024 * 1024 * 1024 * 1024))
{
return sprintf("%0.2fGB", $size / (1024 * 1024 * 1024));
}
return sprintf("%0.2fTB", $size / (1024 * 1024 * 1024 * 1024));
} }
/** /**
@ -426,11 +428,14 @@ class FileHandler
*/ */
public static function returnBytes($val) public static function returnBytes($val)
{ {
$val = preg_replace('/[^0-9\.PTGMK]/', '', $val);
$unit = strtoupper(substr($val, -1)); $unit = strtoupper(substr($val, -1));
$val = (float)$val; $val = (float)$val;
switch ($unit) switch ($unit)
{ {
case 'P': $val *= 1024;
case 'T': $val *= 1024;
case 'G': $val *= 1024; case 'G': $val *= 1024;
case 'M': $val *= 1024; case 'M': $val *= 1024;
case 'K': $val *= 1024; case 'K': $val *= 1024;

View file

@ -909,7 +909,7 @@ class ModuleHandler extends Handler
public static function _setInputValueToSession() public static function _setInputValueToSession()
{ {
$requestVars = Context::getRequestVars(); $requestVars = Context::getRequestVars();
unset($requestVars->act, $requestVars->mid, $requestVars->vid, $requestVars->success_return_url, $requestVars->error_return_url); unset($requestVars->act, $requestVars->mid, $requestVars->vid, $requestVars->success_return_url, $requestVars->error_return_url, $requestVars->xe_validator_id);
foreach($requestVars AS $key => $value) foreach($requestVars AS $key => $value)
{ {
$_SESSION['INPUT_ERROR'][$key] = $value; $_SESSION['INPUT_ERROR'][$key] = $value;

View file

@ -3,7 +3,7 @@
/** /**
* RX_VERSION is the version number of the Rhymix CMS. * RX_VERSION is the version number of the Rhymix CMS.
*/ */
define('RX_VERSION', '1.8.25'); define('RX_VERSION', '1.8.29');
/** /**
* RX_MICROTIME is the startup time of the current script, in microseconds since the Unix epoch. * RX_MICROTIME is the startup time of the current script, in microseconds since the Unix epoch.
@ -27,9 +27,9 @@ if (isset($_SERVER['DOCUMENT_ROOT']) && !strncmp(RX_BASEDIR, str_replace('\\',
{ {
define('RX_BASEURL', str_replace('//', '/', '/' . trim(substr(RX_BASEDIR, strlen($_SERVER['DOCUMENT_ROOT'])), '/') . '/')); define('RX_BASEURL', str_replace('//', '/', '/' . trim(substr(RX_BASEDIR, strlen($_SERVER['DOCUMENT_ROOT'])), '/') . '/'));
} }
elseif (isset($_SERVER['PHP_SELF']) && ($len = strlen($_SERVER['PHP_SELF'])) && $len >= 10 && substr($_SERVER['PHP_SELF'], $len - 10) === '/index.php') elseif (isset($_SERVER['PHP_SELF']) && ($pos = strpos($_SERVER['PHP_SELF'], '/index.php')) !== false)
{ {
define('RX_BASEURL', str_replace('//', '/', '/' . trim(str_replace('\\', '/', substr($_SERVER['PHP_SELF'], 0, $len - 10)), '/') . '/')); define('RX_BASEURL', str_replace('//', '/', '/' . trim(str_replace('\\', '/', substr($_SERVER['PHP_SELF'], 0, $pos)), '/') . '/'));
} }
else else
{ {
@ -66,6 +66,11 @@ elseif (isset($_SERVER['REMOTE_ADDR']) && @inet_pton($_SERVER['REMOTE_ADDR']) !=
define('RX_CLIENT_IP_VERSION', 6); define('RX_CLIENT_IP_VERSION', 6);
define('RX_CLIENT_IP', $_SERVER['REMOTE_ADDR']); define('RX_CLIENT_IP', $_SERVER['REMOTE_ADDR']);
} }
elseif (PHP_SAPI === 'cli')
{
define('RX_CLIENT_IP_VERSION', 4);
define('RX_CLIENT_IP', '127.0.0.1');
}
else else
{ {
define('RX_CLIENT_IP_VERSION', 4); define('RX_CLIENT_IP_VERSION', 4);
@ -135,6 +140,20 @@ define('_XE_LOCATION_SITE_', 'https://www.xpressengine.com/');
define('_XE_DOWNLOAD_SERVER_', 'https://download.xpressengine.com/'); define('_XE_DOWNLOAD_SERVER_', 'https://download.xpressengine.com/');
define('__DEBUG__', 0); define('__DEBUG__', 0);
/**
* Status constants for various content types.
*/
define('RX_STATUS_TEMP', 0);
define('RX_STATUS_PUBLIC', 1);
define('RX_STATUS_SECRET', 2);
define('RX_STATUS_EMBARGO', 3);
define('RX_STATUS_TRASH', 4);
define('RX_STATUS_CENSORED', 5);
define('RX_STATUS_CENSORED_BY_ADMIN', 6);
define('RX_STATUS_DELETED', 7);
define('RX_STATUS_DELETED_BY_ADMIN', 8);
define('RX_STATUS_OTHER', 9);
/** /**
* Other useful constants. * Other useful constants.
*/ */

View file

@ -103,6 +103,7 @@ return array(
'mediafilter' => array( 'mediafilter' => array(
'iframe' => array(), 'iframe' => array(),
'object' => array(), 'object' => array(),
'classes' => array(),
), ),
'mobile' => array( 'mobile' => array(
'enabled' => true, 'enabled' => true,

View file

@ -136,7 +136,6 @@ class Config
// Save the main config file. // Save the main config file.
$buff = '<?php' . "\n" . '// Rhymix System Configuration' . "\n" . 'return ' . self::serialize(self::$_config) . ';' . "\n"; $buff = '<?php' . "\n" . '// Rhymix System Configuration' . "\n" . 'return ' . self::serialize(self::$_config) . ';' . "\n";
$result = Storage::write(\RX_BASEDIR . self::$config_filename, $buff) ? true : false; $result = Storage::write(\RX_BASEDIR . self::$config_filename, $buff) ? true : false;
//if (!$result) return false;
// Save XE-compatible config files. // Save XE-compatible config files.
$db_info = \Context::convertDBInfo(self::$_config); $db_info = \Context::convertDBInfo(self::$_config);
@ -148,7 +147,7 @@ class Config
Storage::write(\RX_BASEDIR . self::$old_db_config_filename, $buff); Storage::write(\RX_BASEDIR . self::$old_db_config_filename, $buff);
$buff = '<?php' . "\n\n" . $warning . "\n\n" . '$ftp_info = ' . self::serialize($ftp_info) . ';' . "\n"; $buff = '<?php' . "\n\n" . $warning . "\n\n" . '$ftp_info = ' . self::serialize($ftp_info) . ';' . "\n";
Storage::write(\RX_BASEDIR . self::$old_ftp_config_filename, $buff); Storage::write(\RX_BASEDIR . self::$old_ftp_config_filename, $buff);
return true; return $result;
} }
/** /**

View file

@ -236,10 +236,33 @@ class Debug
'file' => $query['called_file'], 'file' => $query['called_file'],
'line' => $query['called_line'], 'line' => $query['called_line'],
'method' => $query['called_method'], 'method' => $query['called_method'],
'backtrace' => $query['backtrace'], 'backtrace' => $query['backtrace'] ?: array(),
); );
self::$_queries[] = $query_object; self::$_queries[] = $query_object;
// Add the entry to the error log if the result wasn't successful.
if ($query['result'] === 'error')
{
$error_object = (object)array(
'type' => 'Query Error',
'time' => $query_object->time,
'message' => $query['errstr'] . ' (code ' . intval($query['errno']) . ')',
'file' => $query_object->file,
'line' => $query_object->line,
'backtrace' => $query_object->backtrace ?: array(),
);
self::$_errors[] = $error_object;
if (config('debug.write_error_log') === 'all')
{
$log_entry = strtr(sprintf('Query Error: %s in %s on line %d', $error_object->message, $error_object->file, intval($error_object->line)), "\0\r\n\t\v\e\f", ' ');
error_log($log_entry . \PHP_EOL . self::formatBacktrace($error_object->backtrace));
}
}
// Add the entry to the slow query log.
if ($query_object->query_time && $query_object->query_time >= config('debug.log_slow_queries')) if ($query_object->query_time && $query_object->query_time >= config('debug.log_slow_queries'))
{ {
self::$_slow_queries[] = $query_object; self::$_slow_queries[] = $query_object;

View file

@ -143,6 +143,10 @@ class Redis implements \Rhymix\Framework\Drivers\CacheInterface
{ {
return null; return null;
} }
if (ctype_digit($value))
{
return $value;
}
$value = unserialize($value); $value = unserialize($value);
if ($value === false) if ($value === false)
@ -168,7 +172,8 @@ class Redis implements \Rhymix\Framework\Drivers\CacheInterface
{ {
try try
{ {
return $this->_conn->setex($key, $ttl, serialize($value)) ? true : false; $value = (is_scalar($value) && ctype_digit($value)) ? $value : serialize($value);
return $this->_conn->setex($key, $ttl, $value) ? true : false;
} }
catch (\RedisException $e) catch (\RedisException $e)
{ {

View file

@ -61,6 +61,7 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
protected function _connect($filename) protected function _connect($filename)
{ {
$this->_dbh = new \SQLite3($filename); $this->_dbh = new \SQLite3($filename);
$this->_dbh->busyTimeout(250);
$this->_dbh->exec('PRAGMA journal_mode = MEMORY'); $this->_dbh->exec('PRAGMA journal_mode = MEMORY');
$this->_dbh->exec('PRAGMA synchronous = OFF'); $this->_dbh->exec('PRAGMA synchronous = OFF');
} }
@ -89,7 +90,7 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
*/ */
public static function isSupported() public static function isSupported()
{ {
return class_exists('\\SQLite3', false) && config('crypto.authentication_key') !== null; return class_exists('\\SQLite3', false) && config('crypto.authentication_key') !== null && stripos(\PHP_SAPI, 'win') === false;
} }
/** /**
@ -117,8 +118,18 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
{ {
$table = 'cache_' . (crc32($key) % 32); $table = 'cache_' . (crc32($key) % 32);
$stmt = $this->_dbh->prepare('SELECT v, exp FROM ' . $table . ' WHERE k = :key'); $stmt = $this->_dbh->prepare('SELECT v, exp FROM ' . $table . ' WHERE k = :key');
if (!$stmt)
{
return null;
}
$stmt->bindValue(':key', $key, \SQLITE3_TEXT); $stmt->bindValue(':key', $key, \SQLITE3_TEXT);
$result = $stmt->execute(); $result = $stmt->execute();
if (!$result)
{
return null;
}
$row = $result->fetchArray(\SQLITE3_NUM); $row = $result->fetchArray(\SQLITE3_NUM);
if ($row) if ($row)
{ {
@ -154,6 +165,11 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
{ {
$table = 'cache_' . (crc32($key) % 32); $table = 'cache_' . (crc32($key) % 32);
$stmt = $this->_dbh->prepare('INSERT OR REPLACE INTO ' . $table . ' (k, v, exp) VALUES (:key, :val, :exp)'); $stmt = $this->_dbh->prepare('INSERT OR REPLACE INTO ' . $table . ' (k, v, exp) VALUES (:key, :val, :exp)');
if (!$stmt)
{
return false;
}
$stmt->bindValue(':key', $key, \SQLITE3_TEXT); $stmt->bindValue(':key', $key, \SQLITE3_TEXT);
$stmt->bindValue(':val', serialize($value), \SQLITE3_TEXT); $stmt->bindValue(':val', serialize($value), \SQLITE3_TEXT);
$stmt->bindValue(':exp', $ttl ? (time() + $ttl) : 0, \SQLITE3_INTEGER); $stmt->bindValue(':exp', $ttl ? (time() + $ttl) : 0, \SQLITE3_INTEGER);
@ -173,6 +189,11 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
{ {
$table = 'cache_' . (crc32($key) % 32); $table = 'cache_' . (crc32($key) % 32);
$stmt = $this->_dbh->prepare('DELETE FROM ' . $table . ' WHERE k = :key'); $stmt = $this->_dbh->prepare('DELETE FROM ' . $table . ' WHERE k = :key');
if (!$stmt)
{
return false;
}
$stmt->bindValue(':key', $key, \SQLITE3_TEXT); $stmt->bindValue(':key', $key, \SQLITE3_TEXT);
return $stmt->execute() ? true : false; return $stmt->execute() ? true : false;
} }
@ -189,9 +210,19 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
{ {
$table = 'cache_' . (crc32($key) % 32); $table = 'cache_' . (crc32($key) % 32);
$stmt = $this->_dbh->prepare('SELECT 1 FROM ' . $table . ' WHERE k = :key AND (exp = 0 OR exp >= :exp)'); $stmt = $this->_dbh->prepare('SELECT 1 FROM ' . $table . ' WHERE k = :key AND (exp = 0 OR exp >= :exp)');
if (!$stmt)
{
return false;
}
$stmt->bindValue(':key', $key, \SQLITE3_TEXT); $stmt->bindValue(':key', $key, \SQLITE3_TEXT);
$stmt->bindValue(':exp', time(), \SQLITE3_INTEGER); $stmt->bindValue(':exp', time(), \SQLITE3_INTEGER);
$result = $stmt->execute(); $result = $stmt->execute();
if (!$result)
{
return false;
}
$row = $result->fetchArray(\SQLITE3_NUM); $row = $result->fetchArray(\SQLITE3_NUM);
if ($row) if ($row)
{ {
@ -215,14 +246,17 @@ class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
*/ */
public function incr($key, $amount) public function incr($key, $amount)
{ {
$this->_dbh->exec('BEGIN');
$current_value = $this->get($key); $current_value = $this->get($key);
$new_value = intval($current_value) + $amount; $new_value = intval($current_value) + $amount;
if ($this->set($key, $new_value)) if ($this->set($key, $new_value))
{ {
$this->_dbh->exec('COMMIT');
return $new_value; return $new_value;
} }
else else
{ {
$this->_dbh->exec('ROLLBACK');
return false; return false;
} }
} }

View file

@ -29,7 +29,7 @@ abstract class Base implements \Rhymix\Framework\Drivers\MailInterface
* Create a new instance of the current mail driver, using the given settings. * Create a new instance of the current mail driver, using the given settings.
* *
* @param array $config * @param array $config
* @return void * @return object
*/ */
public static function getInstance(array $config) public static function getInstance(array $config)
{ {

View file

@ -160,13 +160,16 @@ class Woorimail extends Base implements \Rhymix\Framework\Drivers\MailInterface
$data['receiver_nickname'] = implode(',', $data['receiver_nickname']); $data['receiver_nickname'] = implode(',', $data['receiver_nickname']);
// Define connection options. // Define connection options.
$headers = array(
'Accept' => 'application/json, text/javascript, */*; q=0.1',
);
$options = array( $options = array(
'timeout' => 5, 'timeout' => 5,
'useragent' => 'PHP', 'useragent' => 'PHP',
); );
// Send the API request. // Send the API request.
$request = \Requests::post(self::$_url, array(), $data, $options); $request = \Requests::post(self::$_url, $headers, $data, $options);
$result = @json_decode($request->body); $result = @json_decode($request->body);
// Parse the result. // Parse the result.

View file

@ -0,0 +1,120 @@
<?php
namespace Rhymix\Framework\Drivers\SMS;
/**
* The base class for other SMS drivers.
*/
abstract class Base implements \Rhymix\Framework\Drivers\SMSInterface
{
/**
* The configuration is stored here.
*/
protected $_config = null;
/**
* The driver specification is stored here.
*/
protected static $_spec = array();
/**
* Config keys used by this driver are stored here.
*/
protected static $_required_config = array();
protected static $_optional_config = array();
/**
* Direct invocation of the constructor is not permitted.
*/
protected function __construct(array $config)
{
$this->_config = $config;
}
/**
* Create a new instance of the current SMS driver, using the given settings.
*
* @param array $config
* @return object
*/
public static function getInstance(array $config)
{
return new static($config);
}
/**
* Get the human-readable name of this SMS driver.
*
* @return string
*/
public static function getName()
{
return class_basename(get_called_class());
}
/**
* Get the list of configuration fields required by this SMS driver.
*
* @return array
*/
public static function getRequiredConfig()
{
return static::$_required_config;
}
/**
* Get the list of configuration fields optionally used by this SMS driver.
*
* @return array
*/
public static function getOptionalConfig()
{
return static::$_optional_config;
}
/**
* Get the list of API types supported by this SMS driver.
*
* @return array
*/
public static function getAPITypes()
{
return array();
}
/**
* Get the spec for this SMS driver.
*
* @return array
*/
public static function getAPISpec()
{
return static::$_spec;
}
/**
* Check if the current SMS driver is supported on this server.
*
* This method returns true on success and false on failure.
*
* @return bool
*/
public static function isSupported()
{
return false;
}
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param array $messages
* @param object $original
* @return bool
*/
public function send(array $messages, \Rhymix\Framework\SMS $original)
{
return false;
}
}

View file

@ -0,0 +1,123 @@
<?php
namespace Rhymix\Framework\Drivers\SMS;
/**
* The CoolSMS SMS driver.
*/
class CoolSMS extends Base implements \Rhymix\Framework\Drivers\SMSInterface
{
/**
* API specifications.
*/
protected static $_spec = array(
'max_recipients' => 1000,
'sms_max_length' => 90,
'sms_max_length_in_charset' => 'CP949',
'lms_supported' => true,
'lms_supported_country_codes' => array(82),
'lms_max_length' => 2000,
'lms_max_length_in_charset' => 'CP949',
'lms_subject_supported' => true,
'lms_subject_max_length' => 40,
'mms_supported' => true,
'mms_supported_country_codes' => array(82),
'mms_max_length' => 2000,
'mms_max_length_in_charset' => 'CP949',
'mms_subject_supported' => true,
'mms_subject_max_length' => 40,
'image_allowed_types' => array('jpg', 'gif', 'png'),
'image_max_dimensions' => array(2048, 2048),
'image_max_filesize' => 300000,
'delay_supported' => true,
);
/**
* Config keys used by this driver are stored here.
*/
protected static $_required_config = array('api_key', 'api_secret');
protected static $_optional_config = array('sender_key');
/**
* Check if the current SMS driver is supported on this server.
*
* This method returns true on success and false on failure.
*
* @return bool
*/
public static function isSupported()
{
return true;
}
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param array $messages
* @param object $original
* @return bool
*/
public function send(array $messages, \Rhymix\Framework\SMS $original)
{
try
{
$sender = new \Nurigo\Api\Message($this->_config['api_key'], $this->_config['api_secret']);
$status = true;
foreach ($messages as $i => $message)
{
$options = new \stdClass;
if ($this->_config['sender_key'])
{
$options->sender_key = $this->_config['sender_key'];
$options->type = 'CTA';
}
else
{
$options->type = $message->type;
}
$options->from = $message->from;
$options->to = implode(',', $message->to);
$options->text = $message->content ?: $message->type;
$options->charset = 'utf8';
$options->srk = 'K0009334574';
if ($message->delay && $message->delay > time())
{
$options->datetime = gmdate('YmdHis', $message->delay + (3600 * 9));
}
if ($message->country && $message->country != 82)
{
$options->country = $message->country;
}
if ($message->subject)
{
$options->subject = $message->subject;
}
if ($message->image)
{
$options->image = $message->image;
}
foreach ($original->getExtraVars() as $key => $value)
{
$options->$key = $value;
}
$result = $sender->send($options);
if (!$result->success_count)
{
$error_codes = implode(', ', $result->error_list ?: array('Unknown'));
$original->addError('Error (' . $error_codes . ') while sending message ' . ($i + 1) . ' of ' . count($messages) . ' to ' . $options->to);
$status = false;
}
}
return $status;
}
catch (\Nurigo\Exceptions\CoolsmsException $e)
{
$message->errors[] = class_basename($e) . ': ' . $e->getMessage();
return false;
}
}
}

View file

@ -0,0 +1,89 @@
<?php
namespace Rhymix\Framework\Drivers\SMS;
/**
* The dummy SMS driver.
*/
class Dummy extends Base implements \Rhymix\Framework\Drivers\SMSInterface
{
/**
* API specifications.
*/
protected static $_spec = array(
'max_recipients' => 100,
'sms_max_length' => 90,
'sms_max_length_in_charset' => 'CP949',
'lms_supported' => true,
'lms_supported_country_codes' => array(82),
'lms_max_length' => 2000,
'lms_max_length_in_charset' => 'CP949',
'lms_subject_supported' => true,
'lms_subject_max_length' => 40,
'mms_supported' => true,
'mms_supported_country_codes' => array(82),
'mms_max_length' => 2000,
'mms_max_length_in_charset' => 'CP949',
'mms_subject_supported' => false,
'mms_subject_max_length' => 40,
'image_allowed_types' => array(),
'image_max_dimensions' => array(1024, 1024),
'image_max_filesize' => 300000,
'delay_supported' => true,
);
/**
* Sent messages are stored here for debugging and testing.
*/
protected $_sent_messages = array();
/**
* Check if the current SMS driver is supported on this server.
*
* This method returns true on success and false on failure.
*
* @return bool
*/
public static function isSupported()
{
return true;
}
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param array $messages
* @param object $original
* @return bool
*/
public function send(array $messages, \Rhymix\Framework\SMS $original)
{
foreach ($messages as $message)
{
$this->_sent_messages[] = $message;
}
return true;
}
/**
* Get sent messages.
*
* @return array
*/
public function getSentMessages()
{
return $this->_sent_messages;
}
/**
* Reset sent messages.
*
* @return void
*/
public function resetSentMessages()
{
$this->_sent_messages = array();
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace Rhymix\Framework\Drivers;
/**
* The SMS driver interface.
*/
interface SMSInterface
{
/**
* Create a new instance of the current SMS driver, using the given settings.
*
* @param array $config
* @return void
*/
public static function getInstance(array $config);
/**
* Get the human-readable name of this SMS driver.
*
* @return string
*/
public static function getName();
/**
* Get the list of configuration fields required by this SMS driver.
*
* @return array
*/
public static function getRequiredConfig();
/**
* Get the list of configuration fields optionally used by this SMS driver.
*
* @return array
*/
public static function getOptionalConfig();
/**
* Get the list of API types supported by this SMS driver.
*
* @return array
*/
public static function getAPITypes();
/**
* Get the spec for this SMS driver.
*
* @return array
*/
public static function getAPISpec();
/**
* Check if the current SMS driver is supported on this server.
*
* This method returns true on success and false on failure.
*
* @return bool
*/
public static function isSupported();
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param array $messages
* @param object $original
* @return bool
*/
public function send(array $messages, \Rhymix\Framework\SMS $original);
}

View file

@ -16,8 +16,8 @@ class FilenameFilter
public static function clean($filename) public static function clean($filename)
{ {
// Replace dangerous characters with safe alternatives, maintaining meaning as much as possible. // Replace dangerous characters with safe alternatives, maintaining meaning as much as possible.
$illegal = array('\\', '/', '<', '>', '{', '}', ':', ';', '|', '"', '~', '`', '@', '#', '$', '%', '^', '&', '*', '?'); $illegal = array('\\', '/', '<', '>', '{', '}', ':', ';', '|', '"', '~', '`', '$', '%', '^', '*', '?');
$replace = array('', '', '(', ')', '(', ')', '_', ',', '_', '', '_', '\'', '_', '_', '_', '_', '_', '_', '', ''); $replace = array('', '', '(', ')', '(', ')', '_', ',', '_', '', '_', '\'', '_', '_', '_', '', '');
$filename = str_replace($illegal, $replace, $filename); $filename = str_replace($illegal, $replace, $filename);
// Remove control characters. // Remove control characters.
@ -85,4 +85,22 @@ class FilenameFilter
// Trim trailing slashes. // Trim trailing slashes.
return rtrim($path, '/'); return rtrim($path, '/');
} }
/**
* Check if a file has an extension that would allow direct download.
*
* @param string $filename
* @return bool
*/
public static function isDirectDownload($filename)
{
if (preg_match('/\.(as[fx]|avi|flac|flv|gif|jpe?g|m4[av]|midi?|mkv|moov|mov|mp[1234]|mpe?g|ogg|png|qt|ram?|rmm?|swf|wav|web[mp]|wm[av])$/i', $filename))
{
return true;
}
else
{
return false;
}
}
} }

View file

@ -2,6 +2,7 @@
namespace Rhymix\Framework\Filters; namespace Rhymix\Framework\Filters;
use Rhymix\Framework\Config;
use Rhymix\Framework\Security; use Rhymix\Framework\Security;
use Rhymix\Framework\Storage; use Rhymix\Framework\Storage;
@ -11,9 +12,9 @@ use Rhymix\Framework\Storage;
class HTMLFilter class HTMLFilter
{ {
/** /**
* HTMLPurifier instance is cached here. * HTMLPurifier instances are cached here.
*/ */
protected static $_htmlpurifier; protected static $_instances = array();
/** /**
* Pre-processing and post-processing filters are stored here. * Pre-processing and post-processing filters are stored here.
@ -69,18 +70,42 @@ class HTMLFilter
* Filter HTML content to block XSS attacks. * Filter HTML content to block XSS attacks.
* *
* @param string $input * @param string $input
* @param array|bool $allow_classes (optional)
* @param bool $allow_editor_components (optional)
* @param bool $allow_widgets (optional)
* @return string * @return string
*/ */
public static function clean($input) public static function clean($input, $allow_classes = false, $allow_editor_components = true, $allow_widgets = false)
{ {
foreach (self::$_preproc as $callback) foreach (self::$_preproc as $callback)
{ {
$input = $callback($input); $input = $callback($input);
} }
$input = self::_preprocess($input); if ($allow_classes === true)
$output = self::getHTMLPurifier()->purify($input); {
$output = self::_postprocess($output); $allowed_classes = null;
}
else
{
if (is_array($allow_classes))
{
$allowed_classes = array_values($allow_classes);
}
else
{
$allowed_classes = Config::get('mediafilter.classes') ?: array();
}
if ($allow_widgets)
{
$allowed_classes[] = 'zbxe_widget_output';
}
}
$input = self::_preprocess($input, $allow_editor_components, $allow_widgets);
$output = self::getHTMLPurifier($allowed_classes)->purify($input);
$output = self::_postprocess($output, $allow_editor_components, $allow_widgets);
foreach (self::$_postproc as $callback) foreach (self::$_postproc as $callback)
{ {
@ -93,17 +118,27 @@ class HTMLFilter
/** /**
* Get an instance of HTMLPurifier. * Get an instance of HTMLPurifier.
* *
* @param array|null $allowed_classes (optional)
* @return object * @return object
*/ */
public static function getHTMLPurifier() public static function getHTMLPurifier($allowed_classes = null)
{ {
// Keep separate instances for different sets of allowed classes.
if ($allowed_classes !== null)
{
$allowed_classes = array_unique($allowed_classes);
sort($allowed_classes);
}
$key = sha1(serialize($allowed_classes));
// Create an instance with reasonable defaults. // Create an instance with reasonable defaults.
if (self::$_htmlpurifier === null) if (!isset(self::$_instances[$key]))
{ {
// Get the default configuration. // Get the default configuration.
$config = \HTMLPurifier_Config::createDefault(); $config = \HTMLPurifier_Config::createDefault();
// Customize the default configuration. // Customize the default configuration.
$config->set('Attr.AllowedClasses', $allowed_classes);
$config->set('Attr.AllowedFrameTargets', array('_blank')); $config->set('Attr.AllowedFrameTargets', array('_blank'));
$config->set('Attr.DefaultImageAlt', ''); $config->set('Attr.DefaultImageAlt', '');
$config->set('Attr.EnableID', true); $config->set('Attr.EnableID', true);
@ -143,11 +178,11 @@ class HTMLFilter
self::_supportCSS3($config); self::_supportCSS3($config);
// Cache our instance of HTMLPurifier. // Cache our instance of HTMLPurifier.
self::$_htmlpurifier = new \HTMLPurifier($config); self::$_instances[$key] = new \HTMLPurifier($config);
} }
// Return the cached instance. // Return the cached instance.
return self::$_htmlpurifier; return self::$_instances[$key];
} }
/** /**
@ -226,6 +261,7 @@ class HTMLFilter
)); ));
// Support additional properties. // Support additional properties.
$def->addAttribute('i', 'aria-hidden', 'Text');
$def->addAttribute('img', 'srcset', 'Text'); $def->addAttribute('img', 'srcset', 'Text');
$def->addAttribute('iframe', 'allowfullscreen', 'Bool'); $def->addAttribute('iframe', 'allowfullscreen', 'Bool');
} }
@ -378,12 +414,17 @@ class HTMLFilter
* Rhymix-specific preprocessing method. * Rhymix-specific preprocessing method.
* *
* @param string $content * @param string $content
* @param bool $allow_editor_components (optional)
* @param bool $allow_widgets (optional)
* @return string * @return string
*/ */
protected static function _preprocess($content) protected static function _preprocess($content, $allow_editor_components = true, $allow_widgets = false)
{ {
// Encode widget and editor component properties so that they are not removed by HTMLPurifier. // Encode widget and editor component properties so that they are not removed by HTMLPurifier.
$content = self::_encodeWidgetsAndEditorComponents($content); if ($allow_editor_components || $allow_widgets)
{
$content = self::_encodeWidgetsAndEditorComponents($content, $allow_editor_components, $allow_widgets);
}
return $content; return $content;
} }
@ -391,9 +432,11 @@ class HTMLFilter
* Rhymix-specific postprocessing method. * Rhymix-specific postprocessing method.
* *
* @param string $content * @param string $content
* @param bool $allow_editor_components (optional)
* @param bool $allow_widgets (optional)
* @return string * @return string
*/ */
protected static function _postprocess($content) protected static function _postprocess($content, $allow_editor_components = true, $allow_widgets = false)
{ {
// Define acts to allow and deny. // Define acts to allow and deny.
$allow_acts = array('procFileDownload'); $allow_acts = array('procFileDownload');
@ -435,7 +478,7 @@ class HTMLFilter
}, $content); }, $content);
// Restore widget and editor component properties. // Restore widget and editor component properties.
$content = self::_decodeWidgetsAndEditorComponents($content); $content = self::_decodeWidgetsAndEditorComponents($content, $allow_editor_components, $allow_widgets);
return $content; return $content;
} }
@ -443,11 +486,27 @@ class HTMLFilter
* Encode widgets and editor components before processing. * Encode widgets and editor components before processing.
* *
* @param string $content * @param string $content
* @param bool $allow_editor_components (optional)
* @param bool $allow_widgets (optional)
* @return string * @return string
*/ */
protected static function _encodeWidgetsAndEditorComponents($content) protected static function _encodeWidgetsAndEditorComponents($content, $allow_editor_components = true, $allow_widgets = false)
{ {
return preg_replace_callback('!<(div|img)([^>]*)(editor_component="[^"]+"|class="zbxe_widget_output")([^>]*)>!i', function($match) { $regexp = array();
if ($allow_editor_components)
{
$regexp[] = 'editor_component="[^"]+"';
}
if ($allow_widgets)
{
$regexp[] = 'class="zbxe_widget_output"';
}
if (!count($regexp))
{
return $content;
}
return preg_replace_callback('!<(div|img)([^>]*)(' . implode('|', $regexp) . ')([^>]*)>!i', function($match) {
$tag = strtolower($match[1]); $tag = strtolower($match[1]);
$attrs = array(); $attrs = array();
$html = preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($tag, &$attrs) { $html = preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($tag, &$attrs) {
@ -476,10 +535,25 @@ class HTMLFilter
* Decode widgets and editor components after processing. * Decode widgets and editor components after processing.
* *
* @param string $content * @param string $content
* @param bool $allow_editor_components (optional)
* @param bool $allow_widgets (optional)
* @return string * @return string
*/ */
protected static function _decodeWidgetsAndEditorComponents($content) protected static function _decodeWidgetsAndEditorComponents($content, $allow_editor_components = true, $allow_widgets = false)
{ {
if (!$allow_editor_components)
{
$content = preg_replace('!(<(?:div|img)[^>]*)\s(editor_component="(?:[^"]+)")!i', '$1', $content);
}
if (!$allow_widgets)
{
$content = preg_replace('!(<(?:div|img)[^>]*)\s(widget="(?:[^"]+)")!i', '$1blocked-$2', $content);
}
if (!$allow_editor_components && !$allow_widgets)
{
return $content;
}
return preg_replace_callback('!<(div|img)([^>]*)(\srx_encoded_properties="([^"]+)")!i', function($match) { return preg_replace_callback('!<(div|img)([^>]*)(\srx_encoded_properties="([^"]+)")!i', function($match) {
$attrs = array(); $attrs = array();
$decoded_properties = Security::decrypt($match[4]); $decoded_properties = Security::decrypt($match[4]);

820
common/framework/sms.php Normal file
View file

@ -0,0 +1,820 @@
<?php
namespace Rhymix\Framework;
/**
* The SMS class.
*/
class SMS
{
/**
* Instance properties.
*/
public $driver = null;
protected $caller = '';
protected $from = null;
protected $to = array();
protected $subject = '';
protected $content = '';
protected $attachments = array();
protected $extra_vars = array();
protected $delay_timestamp = 0;
protected $force_sms = false;
protected $allow_split_sms = true;
protected $allow_split_lms = true;
protected $errors = array();
protected $sent = false;
/**
* Static properties.
*/
public static $default_driver = null;
public static $custom_drivers = array();
/**
* Set the default driver.
*
* @param object $driver
* @return void
*/
public static function setDefaultDriver(Drivers\SMSInterface $driver)
{
self::$default_driver = $driver;
}
/**
* Get the default driver.
*
* @return object
*/
public static function getDefaultDriver()
{
if (!self::$default_driver)
{
$default_driver = config('sms.type');
$default_driver_class = '\Rhymix\Framework\Drivers\SMS\\' . $default_driver;
if (class_exists($default_driver_class))
{
$default_driver_config = config('sms.' . $default_driver) ?: array();
self::$default_driver = $default_driver_class::getInstance($default_driver_config);
}
else
{
self::$default_driver = Drivers\SMS\Dummy::getInstance(array());
}
}
return self::$default_driver;
}
/**
* Add a custom mail driver.
*/
public static function addDriver(Drivers\SMSInterface $driver)
{
self::$custom_drivers[] = $driver;
}
/**
* Get the list of supported mail drivers.
*
* @return array
*/
public static function getSupportedDrivers()
{
$result = array();
foreach (Storage::readDirectory(__DIR__ . '/drivers/sms', false) as $filename)
{
$driver_name = substr($filename, 0, -4);
$class_name = '\Rhymix\Framework\Drivers\SMS\\' . $driver_name;
if ($class_name::isSupported())
{
$result[$driver_name] = array(
'name' => $class_name::getName(),
'required' => $class_name::getRequiredConfig(),
'optional' => $class_name::getOptionalConfig(),
'api_types' => $class_name::getAPITypes(),
'api_spec' => $class_name::getAPISpec(),
);
}
}
foreach (self::$custom_drivers as $driver)
{
if ($driver->isSupported())
{
$result[strtolower(class_basename($driver))] = array(
'name' => $driver->getName(),
'required' => $driver->getRequiredConfig(),
'optional' => $driver->getOptionalConfig(),
'api_types' => $driver->getAPITypes(),
'api_spec' => $class_name::getAPISpec(),
);
}
}
ksort($result);
return $result;
}
/**
* The constructor.
*/
public function __construct()
{
$this->driver = self::getDefaultDriver();
$this->from = trim(preg_replace('/[^0-9]/', '', config('sms.default_from'))) ?: null;
$this->allow_split_sms = (config('sms.allow_split.sms') !== false);
$this->allow_split_lms = (config('sms.allow_split.lms') !== false);
}
/**
* Set the sender's phone number.
*
* @param string $number Phone number
* @return bool
*/
public function setFrom($number)
{
$this->from = preg_replace('/[^0-9]/', '', $number);
return true;
}
/**
* Get the sender's phone number.
*
* @return string|null
*/
public function getFrom()
{
return $this->from;
}
/**
* Add a recipient.
*
* @param string $number Phone number
* @param string $country Country code (optional)
* @return bool
*/
public function addTo($number, $country = 0)
{
$this->to[] = (object)array(
'number' => preg_replace('/[^0-9]/', '', $number),
'country' => intval(preg_replace('/[^0-9]/', '', $country)),
);
return true;
}
/**
* Get the list of recipients without country codes.
*
* @return array
*/
public function getRecipients()
{
return array_map(function($recipient) {
return $recipient->number;
}, $this->to);
}
/**
* Get the list of recipients with country codes.
*
* @return array
*/
public function getRecipientsWithCountry()
{
return $this->to;
}
/**
* Get the list of recipients grouped by country code.
*
* @return array
*/
public function getRecipientsGroupedByCountry()
{
$result = array();
foreach ($this->to as $recipient)
{
$result[$recipient->country][] = $recipient->number;
}
return $result;
}
/**
* Set the subject.
*
* @param string $subject
* @return bool
*/
public function setSubject($subject)
{
$this->subject = utf8_trim(utf8_clean($subject));
return true;
}
/**
* Get the subject.
*
* @return string
*/
public function getSubject()
{
return $this->subject;
}
/**
* Set the subject (alias to setSubject).
*
* @param string $subject
* @return bool
*/
public function setTitle($subject)
{
return $this->setSubject($subject);
}
/**
* Get the subject (alias to getSubject).
*
* @return string
*/
public function getTitle()
{
return $this->getSubject();
}
/**
* Set the content.
*
* @param string $content
* @return bool
*/
public function setBody($content)
{
$this->content = utf8_trim(utf8_clean($content));
$this->content = strtr($this->content, array("\r\n" => "\n"));
return true;
}
/**
* Get the content.
*
* @return string
*/
public function getBody()
{
return $this->content;
}
/**
* Set the content (alias to setBody).
*
* @param string $content
* @return void
*/
public function setContent($content)
{
return $this->setBody($content);
}
/**
* Get the content (alias to getBody).
*
* @return string
*/
public function getContent()
{
return $this->getBody();
}
/**
* Attach a file.
*
* @param string $local_filename
* @param string $display_filename (optional)
* @return bool
*/
public function attach($local_filename, $display_filename = null)
{
if ($display_filename === null)
{
$display_filename = basename($local_filename);
}
if (!Storage::exists($local_filename))
{
return false;
}
$this->attachments[] = (object)array(
'type' => 'mms',
'local_filename' => $local_filename,
'display_filename' => $display_filename,
'cid' => null,
);
return true;
}
/**
* Get the list of attachments to this message.
*
* @return array
*/
public function getAttachments()
{
return $this->attachments;
}
/**
* Set an extra variable.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function setExtraVar($key, $value)
{
$this->extra_vars[$key] = $value;
}
/**
* Get an extra variable.
*
* @param string $key
* @return mixed
*/
public function getExtraVar($key)
{
return isset($this->extra_vars[$key]) ? $this->extra_vars[$key] : null;
}
/**
* Get all extra variables.
*
* @param string $key
* @return mixed
*/
public function getExtraVars()
{
return $this->extra_vars;
}
/**
* Set all extra variables.
*
* @param array $vars
* @return void
*/
public function setExtraVars(array $vars)
{
$this->extra_vars = $vars;
}
/**
* Delay sending the message.
*
* Delays (in seconds) less than 1 year will be treated as relative to the
* current time. Greater values will be interpreted as a Unix timestamp.
*
* This feature may not be implemented by all drivers.
*
* @param int $when Unix timestamp
* @return bool
*/
public function setDelay($when)
{
if ($when <= (86400 * 365))
{
$when = time() + $when;
}
if ($when <= time())
{
$when = 0;
}
$this->delay_timestamp = intval($when);
return true;
}
/**
* Get the Unix timestamp of when to send the message.
*
* This method always returns a Unix timestamp, even if the original value
* was given as a relative delay.
*
* This feature may not be implemented by all drivers.
*
* @return int
*/
public function getDelay()
{
return $this->delay_timestamp;
}
/**
* Force this message to use SMS (not LMS or MMS).
*
* @return void
*/
public function forceSMS()
{
$this->force_sms = true;
}
/**
* Unforce this message to use SMS (not LMS or MMS).
*
* @return void
*/
public function unforceSMS()
{
$this->force_sms = false;
}
/**
* Check if this message is forced to use SMS.
*
* @return bool
*/
public function isForceSMS()
{
return $this->force_sms;
}
/**
* Allow this message to be split into multiple SMS.
*
* @return void
*/
public function allowSplitSMS()
{
$this->allow_split_sms = true;
}
/**
* Allow this message to be split into multiple LMS.
*
* @return void
*/
public function allowSplitLMS()
{
$this->allow_split_lms = true;
}
/**
* Disallow this message to be split into multiple SMS.
*
* @return void
*/
public function disallowSplitSMS()
{
$this->allow_split_sms = false;
}
/**
* Disallow this message to be split into multiple LMS.
*
* @return void
*/
public function disallowSplitLMS()
{
$this->allow_split_lms = false;
}
/**
* Check if splitting this message into multiple SMS is allowed.
*
* @return bool
*/
public function isSplitSMSAllowed()
{
return $this->allow_split_sms;
}
/**
* Check if splitting this message into multiple LMS is allowed.
*
* @return bool
*/
public function isSplitLMSAllowed()
{
return $this->allow_split_lms;
}
/**
* Send the message.
*
* @return bool
*/
public function send()
{
// Get caller information.
$backtrace = debug_backtrace(0);
if(count($backtrace) && isset($backtrace[0]['file']))
{
$this->caller = $backtrace[0]['file'] . ($backtrace[0]['line'] ? (' line ' . $backtrace[0]['line']) : '');
}
$output = \ModuleHandler::triggerCall('sms.send', 'before', $this);
if(!$output->toBool())
{
$this->errors[] = $output->getMessage();
return false;
}
if (config('sms.default_force') && config('sms.default_from'))
{
$this->setFrom(config('sms.default_from'));
}
try
{
if ($this->driver)
{
$messages = $this->_formatSpec($this->driver->getAPISpec());
if (count($messages))
{
$this->sent = $this->driver->send($messages, $this) ? true : false;
}
else
{
$this->errors[] = 'No recipients selected';
$this->sent = false;
}
}
else
{
$this->errors[] = 'No SMS driver selected';
$this->sent = false;
}
}
catch(\Exception $e)
{
$this->errors[] = class_basename($e) . ': ' . $e->getMessage();
$this->sent = false;
}
$output = \ModuleHandler::triggerCall('sms.send', 'after', $this);
if(!$output->toBool())
{
$this->errors[] = $output->getMessage();
}
return $this->sent;
}
/**
* Check if the message was sent.
*
* @return bool
*/
public function isSent()
{
return $this->sent;
}
/**
* Get caller information.
*
* @return string
*/
public function getCaller()
{
return $this->caller;
}
/**
* Get errors.
*
* @return array
*/
public function getErrors()
{
return $this->errors;
}
/**
* Add an error message.
*
* @param string $message
* @return void
*/
public function addError($message)
{
$this->errors[] = $message;
}
/**
* Format the current message according to an API spec.
*
* @param array $spec API specifications
* @return array
*/
protected function _formatSpec(array $spec)
{
// Initialize the return array.
$result = array();
// Get the list of recipients.
$recipients = $this->getRecipientsGroupedByCountry();
// Group the recipients by country code.
foreach ($recipients as $country_code => $country_recipients)
{
// Merge recipients into groups.
if ($spec['max_recipients'] > 1)
{
$country_recipients = array_chunk($country_recipients, $spec['max_recipients']);
}
// Send to each set of merged recipients.
foreach ($country_recipients as $recipient_numbers)
{
// Populate the item.
$item = new \stdClass;
$item->type = 'SMS';
$item->from = $this->getFrom();
$item->to = $recipient_numbers;
$item->country = $country_code;
if ($spec['delay_supported'])
{
$item->delay = $this->getDelay() ?: 0;
}
// Get message content.
$subject = $this->getSubject();
$content = $this->getContent();
$attachments = $attachments = $this->getAttachments();
// Determine the message type.
if (!$this->isForceSMS() && ($spec['lms_supported'] || $spec['mms_supported']))
{
// Check attachments, subject, and message length.
if ($spec['mms_supported'] && count($attachments))
{
$item->type = 'MMS';
}
elseif ($spec['lms_supported'] && $subject)
{
$item->subject = $subject;
$item->type = 'LMS';
}
elseif ($spec['lms_supported'] && $this->_getLengthInCharset($content, $spec['sms_max_length_in_charset']) > $spec['sms_max_length'])
{
$item->type = 'LMS';
}
else
{
$item->type = 'SMS';
}
// Check the country code.
if ($item->type === 'MMS' && $country_code && is_array($spec['mms_supported_country_codes']) && !in_array($country_code, $spec['mms_supported_country_codes']))
{
$item->type = 'LMS';
}
if ($item->type === 'LMS' && $country_code && is_array($spec['lms_supported_country_codes']) && !in_array($country_code, $spec['lms_supported_country_codes']))
{
$item->type = 'SMS';
}
}
// Remove subject and attachments if the message type is SMS.
if ($item->type === 'SMS')
{
if ($subject)
{
$content = $subject . "\n" . $content;
unset($item->subject);
}
$attachments = array();
}
// If message subject is not supported, prepend it to the content instead.
if ($item->subject && !$spec[strtolower($item->type) . '_subject_supported'])
{
$content = $item->subject . "\n" . $content;
unset($item->subject);
}
elseif ($item->subject && $this->_getLengthInCharset($item->subject, $spec[strtolower($item->type) . '_max_length_in_charset']) > $spec[strtolower($item->type) . '_subject_max_length'])
{
$subject_parts = $this->_splitString($item->subject, $spec[strtolower($item->type) . '_subject_max_length'], $spec[strtolower($item->type) . '_max_length_in_charset']);
$subject_short = array_shift($subject_parts);
$subject_remainder = utf8_trim(substr($item->subject, strlen($subject_short)));
$item->subject = $subject_short;
$content = $subject_remainder . "\n" . $content;
}
// Split the content if necessary.
if (($item->type === 'SMS' && $this->allow_split_sms) || ($item->type !== 'SMS' && $this->allow_split_lms))
{
if ($this->_getLengthInCharset($content, $spec[strtolower($item->type) . '_max_length_in_charset']) > $spec[strtolower($item->type) . '_max_length'])
{
$content_parts = $this->_splitString($content, $spec[strtolower($item->type) . '_max_length'], $spec[strtolower($item->type) . '_max_length_in_charset']);
}
else
{
$content_parts = array($content);
}
}
else
{
$content_parts = array($content);
}
// Generate a message for each part of the content and attachments.
$message_count = max(count($content_parts), count($attachments));
$last_content = $item->type;
for ($i = 1; $i <= $message_count; $i++)
{
// Get the message content.
if ($content_part = array_shift($content_parts))
{
$item->content = $last_content = $content_part;
}
else
{
$item->content = $last_content ?: $item->type;
}
// Get the attachment.
if ($attachment = array_shift($attachments))
{
$item->image = $attachment->local_filename;
}
else
{
unset($item->image);
}
// Clone the item to make a part.
$cloneitem = clone $item;
// Determine the best message type for this part.
if ($cloneitem->type !== 'SMS' && !$cloneitem->subject)
{
$cloneitem->type = $attachment ? 'MMS' : ($this->_getLengthInCharset($content_part, $spec['sms_max_length_in_charset']) > $spec['sms_max_length'] ? 'LMS' : 'SMS');
}
// Add the cloned part to the result array.
$result[] = $cloneitem;
}
}
}
// Return the message parts.
return $result;
}
/**
* Get the length of a string in another character set.
*
* @param string $str String to measure
* @param string $charset Character set to measure length
* @return
*/
protected function _getLengthInCharset($str, $charset)
{
$str = @iconv('UTF-8', $charset . '//IGNORE', $str);
return strlen($str);
}
/**
* Split a string into several short chunks.
*
* @param string $str String to split
* @param int $max_length Maximum length of a chunk
* @param string $charset Character set to measure length
* @return array
*/
protected function _splitString($str, $max_length, $charset)
{
$str = utf8_trim(utf8_normalize_spaces($str, true));
$chars = preg_split('//u', $str, -1, \PREG_SPLIT_NO_EMPTY);
$result = array();
$current_entry = '';
$current_length = 0;
foreach ($chars as $char)
{
$char_length = strlen(@iconv('UTF-8', $charset . '//IGNORE', $char));
if (($current_length + $char_length > $max_length) || ($current_length + $char_length > $max_length - 7 && ctype_space($char)))
{
$result[] = trim($current_entry);
$current_entry = $char;
$current_length = $char_length;
}
else
{
$current_entry .= $char;
$current_length += $char_length;
}
}
if ($current_entry !== '')
{
$result[] = trim($current_entry);
}
return $result;
}
}

View file

@ -714,6 +714,36 @@ class Storage
} }
} }
/**
* Delete a directory only if it is empty.
*
* @param string $dirname
* @param bool $delete_empty_parents (optional)
* @return bool
*/
public static function deleteEmptyDirectory($dirname, $delete_empty_parents = false)
{
$dirname = rtrim($dirname, '/\\');
if (!self::isDirectory($dirname) || !self::isEmptyDirectory($dirname))
{
return false;
}
$result = @rmdir($dirname);
if (!$result)
{
return false;
}
else
{
if ($delete_empty_parents)
{
self::deleteEmptyDirectory(dirname($dirname), true);
}
return true;
}
}
/** /**
* Get the current umask. * Get the current umask.
* *
@ -742,7 +772,7 @@ class Storage
/** /**
* Determine the best umask for this installation of Rhymix. * Determine the best umask for this installation of Rhymix.
* *
* @return int * @return string
*/ */
public static function recommendUmask() public static function recommendUmask()
{ {
@ -756,27 +786,7 @@ class Storage
$file_uid = fileowner(__FILE__); $file_uid = fileowner(__FILE__);
// Get the UID of the current PHP process. // Get the UID of the current PHP process.
if (function_exists('posix_geteuid')) $php_uid = self::getServerUID();
{
$php_uid = posix_geteuid();
}
else
{
$testfile = \RX_BASEDIR . 'files/cache/uidcheck';
if (self::exists($testfile))
{
self::delete($testfile);
}
if (self::write($testfile, 'TEST'))
{
$php_uid = fileowner($testfile);
self::delete($testfile);
}
else
{
$php_uid = -1;
}
}
// If both UIDs are the same, set the umask to 0022. // If both UIDs are the same, set the umask to 0022.
if ($file_uid == $php_uid) if ($file_uid == $php_uid)
@ -790,4 +800,36 @@ class Storage
return '0000'; return '0000';
} }
} }
/**
* Get the UID of the server process.
*
* @return int|false
*/
public static function getServerUID()
{
if (function_exists('posix_geteuid'))
{
return posix_geteuid();
}
else
{
$testfile = \RX_BASEDIR . 'files/cache/uidcheck_' . time();
if (self::exists($testfile))
{
self::delete($testfile);
}
if (self::write($testfile, 'TEST'))
{
$uid = fileowner($testfile);
self::delete($testfile);
return $uid;
}
else
{
return false;
}
}
}
} }

View file

@ -96,6 +96,35 @@ function array_last_key(array $array)
return key($array); return key($array);
} }
/**
* Escape all keys and values in a multi-dimensional array.
*
* @param array $array The array to escape
* @param bool $double_escape Set this to false to skip symbols that are already escaped (default: true)
* @return array
*/
function array_escape(array $array, $double_escape = true)
{
$flags = ENT_QUOTES | ENT_SUBSTITUTE;
$result = array();
foreach ($array as $key => $value)
{
if (is_array($value))
{
$result[htmlspecialchars($key, $flags, 'UTF-8', $double_escape)] = array_escape($value, $double_escape, $flags);
}
elseif (is_object($value))
{
$result[htmlspecialchars($key, $flags, 'UTF-8', $double_escape)] = (object)array_escape(get_object_vars($value), $double_escape, $flags);
}
else
{
$result[htmlspecialchars($key, $flags, 'UTF-8', $double_escape)] = htmlspecialchars($value, $flags, 'UTF-8', $double_escape);
}
}
return $result;
}
/** /**
* Flatten a multi-dimensional array into a one-dimensional array. * Flatten a multi-dimensional array into a one-dimensional array.
* Based on util.php <https://github.com/brandonwamboldt/utilphp> * Based on util.php <https://github.com/brandonwamboldt/utilphp>
@ -155,7 +184,7 @@ function clean_path($path)
*/ */
function escape($str, $double_escape = true) function escape($str, $double_escape = true)
{ {
$flags = defined('ENT_SUBSTITUTE') ? (ENT_QUOTES | ENT_SUBSTITUTE) : (ENT_QUOTES | ENT_IGNORE); $flags = ENT_QUOTES | ENT_SUBSTITUTE;
return htmlspecialchars($str, $flags, 'UTF-8', $double_escape); return htmlspecialchars($str, $flags, 'UTF-8', $double_escape);
} }
@ -178,8 +207,7 @@ function escape_css($str)
*/ */
function escape_js($str) function escape_js($str)
{ {
$flags = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT; $flags = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE;
if (defined('JSON_UNESCAPED_UNICODE')) $flags = $flags | JSON_UNESCAPED_UNICODE;
$str = json_encode((string)$str, $flags); $str = json_encode((string)$str, $flags);
return substr($str, 1, strlen($str) - 2); return substr($str, 1, strlen($str) - 2);
} }

View file

@ -313,12 +313,27 @@ jQuery(function($) {
}; };
/** /**
* @brief string prototype으로 trim 함수 추가 * @brief string prototype으로 escape 함수 추가
**/ **/
String.prototype.trim = function() { String.prototype.escape = function(double_escape) {
return this.replace(/(^\s*)|(\s*$)/g, ""); var map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' };
var revmap = { '&amp;amp;': '&amp;', '&amp;lt;': '&lt;', '&amp;gt;': '&gt;', '&amp;quot;': '&quot;', "&amp;#039;": '&#039;' };
var result = String(this).replace(/[&<>"']/g, function(m) { return map[m]; });
if (double_escape === false) {
return result.replace(/&amp;(amp|lt|gt|quot|#039);/g, function(m) { return revmap[m]; });
} else {
return result;
}
}; };
/**
* @brief string prototype으로 trim 함수 추가
**/
if (!String.prototype.trim) {
String.prototype.trim = function() {
return String(this).replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
}
})(); })();
/** /**
@ -511,17 +526,17 @@ function setFixedPopupSize() {
if(w < 800) w = 800 + offset.left*2; if(w < 800) w = 800 + offset.left*2;
dw = $win.width(); dw = $win.width();
dh = $win.height(); dh = $win.height();
// Window 의 너비나 높이는 스크린의 너비나 높이보다 클 수 없다. 스크린의 너비나 높이와 내용의 너비나 높이를 비교해서 최소값을 이용한다. // Window 의 너비나 높이는 스크린의 너비나 높이보다 클 수 없다. 스크린의 너비나 높이와 내용의 너비나 높이를 비교해서 최소값을 이용한다.
if(Math.min(w, window.screen.availWidth) != dw) window.resizeBy(Math.min(w, window.screen.availWidth) - dw, 0); w = Math.min(w, window.screen.availWidth);
if(Math.min(h, window.screen.availHeight-100) != dh) window.resizeBy(0, Math.min(h, window.screen.availHeight-100) - dh); h = Math.min(h, window.screen.availHeight - 100);
window.resizeBy(w - dw, h - dh);
$pc.width(Math.min(w, window.screen.availWidth)-offset.left*2).css({overflow:'',height:''}); $pc.width(w - offset.left*2).css({overflow:'',height:''});
if(Math.min(h, window.screen.availHeight-100) === window.screen.availHeight-100) { if(h === window.screen.availHeight - 100) {
$pc.width(Math.min(w, window.screen.availWidth)-offset.left*2-scbw).css({overflow:'',height:''}); $pc.width(w - offset.left*2-scbw).css({overflow:'',height:''});
} }
} }

View file

@ -62,24 +62,24 @@
var currentEnforce_ssl = window.enforce_ssl; var currentEnforce_ssl = window.enforce_ssl;
if(location.protocol == 'https:') { window.enforce_ssl = true; } if(location.protocol == 'https:') { window.enforce_ssl = true; }
var chunkStatus = true;
var defaultFormData = {
"editor_sequence": data.editorSequence,
"upload_target_srl" : data.uploadTargetSrl,
"mid" : window.current_mid,
"act": 'procFileUpload'
};
var settings = { var settings = {
url: request_uri url: request_uri,
.setQuery('module', 'file') formData: defaultFormData,
.setQuery('act', 'procFileUpload')
.setQuery('mid', window.current_mid),
formData: {
"editor_sequence": data.editorSequence,
"upload_target_srl" : data.uploadTargetSrl,
"mid" : window.current_mid,
"act": 'procFileUpload'
},
dropZone: $container, dropZone: $container,
add: function(e, d) { add: function(e, d) {
var dfd = jQuery.Deferred(); var dfd = jQuery.Deferred();
$.each(d.files, function(index, file) { $.each(d.files, function(index, file) {
if(data.settings.maxFileSize <= file.size) { if(data.settings.maxFileSize > 0 && data.settings.maxFileSize < file.size) {
dfd.reject(); dfd.reject();
alert(window.xe.msg_exceeds_limit_size); alert(window.xe.msg_exceeds_limit_size);
return false; return false;
@ -91,25 +91,79 @@
d.submit(); d.submit();
}); });
}, },
submit: function(e, data) {
data.formData = defaultFormData;
data.formData.nonce = "T" + new Date().getTime() + "." + Math.random();
chunkStatus = true;
},
chunksend: function(e, data) {
if (!chunkStatus) {
return false;
}
},
chunkdone: function(e, res) {
if (res.result) {
if (res.result.error != 0) {
if (res.result.message) {
alert(res.result.message);
} else {
alert(window.xe.msg_file_upload_error + " (Type 1)");
}
return chunkStatus = false;
}
} else {
alert(window.xe.msg_file_upload_error + " (Type 2)");
return chunkStatus = false;
}
},
chunkfail: function(e, data) {
if (chunkStatus) {
alert(window.xe.msg_file_upload_error + " (Type 3)" + "<br>\n" + data.errorThrown + "<br>\n" + data.textStatus);
return chunkStatus = false;
}
},
done: function(e, res) { done: function(e, res) {
data.settings.progressbarGraph.width('100%');
data.settings.progressPercent.text('100%');
data.settings.progressbar.delay(1000).slideUp();
data.settings.progressStatus.delay(1000).slideUp();
var result = res.response().result; var result = res.response().result;
var temp_code = ''; var temp_code = '';
if (!result) {
if(!result) return; alert(window.xe.msg_file_upload_error + " (Type 4)");
return false;
if(!jQuery.isPlainObject(result)) result = jQuery.parseJSON(result); }
if (!jQuery.isPlainObject(result)) {
if(!result) return; result = jQuery.parseJSON(result);
}
if (!result) {
alert(window.xe.msg_file_upload_error + " (Type 5)" + "<br>\n" + res.response().result);
return false;
}
if(result.error == 0) { if(result.error == 0) {
if(/\.(jpe?g|png|gif)$/i.test(result.source_filename)) { if(/\.(jpe?g|png|gif)$/i.test(result.source_filename)) {
temp_code += '<img src="' + result.download_url + '" alt="' + result.source_filename + '" editor_component="image_link" data-file-srl="' + result.file_srl + '" />'; temp_code += '<img src="' + result.download_url + '" alt="' + result.source_filename + '" editor_component="image_link" data-file-srl="' + result.file_srl + '" />';
temp_code += "\r\n<p><br></p>\r\n"; if (opt.autoinsertImage === 'paragraph') {
_getCkeInstance(settings.formData.editor_sequence).insertHtml("<p>" + temp_code + "</p>\n", "unfiltered_html");
} else if (opt.autoinsertImage === 'inline') {
_getCkeInstance(settings.formData.editor_sequence).insertHtml(temp_code, "unfiltered_html");
}
} }
} else if (result.message) {
_getCkeInstance(settings.formData.editor_sequence).insertHtml(temp_code, "unfiltered_html");
} else {
alert(result.message); alert(result.message);
return false;
} else {
alert(window.xe.msg_file_upload_error + " (Type 6)" + "<br>\n" + res.response().result);
return false;
}
},
fail: function(e, data) {
data.settings.progressbar.delay(1000).slideUp();
data.settings.progressStatus.delay(1000).slideUp();
if (chunkStatus) {
alert(window.xe.msg_file_upload_error + " (Type 7)" + "<br>\n" + data.errorThrown + "<br>\n" + data.textStatus);
return false;
} }
}, },
stop: function() { stop: function() {
@ -121,14 +175,9 @@
data.settings.progressbar.show(); data.settings.progressbar.show();
}, },
progressall: function (e, d) { progressall: function (e, d) {
var progress = parseInt(d.loaded / d.total * 100, 10); var progress = Math.round(d.loaded / d.total * 999) / 10;
data.settings.progressbarGraph.width(progress+'%'); data.settings.progressbarGraph.width(progress+'%');
data.settings.progressPercent.text(progress+'%'); data.settings.progressPercent.text(progress+'%');
if(progress >= 100) {
data.settings.progressbar.delay(3000).slideUp();
data.settings.progressStatus.delay(3000).slideUp();
}
} }
}; };
window.enforce_ssl = currentEnforce_ssl; window.enforce_ssl = currentEnforce_ssl;
@ -277,13 +326,13 @@
file_srls.push(file_srl); file_srls.push(file_srl);
} }
file_srls = file_srls.join(','); exec_json('file.procFileDelete', {'file_srls': file_srls.join(','), 'editor_sequence': data.editorSequence}, function() {
exec_json('file.procFileDelete', {'file_srls': file_srls, 'editor_sequence': data.editorSequence}, function() {
file_srls = file_srls.split(',');
$.each(file_srls, function(idx, srl){ $.each(file_srls, function(idx, srl){
data.settings.fileList.find('ul').find('li[data-file-srl=' + srl + ']').remove(); data.settings.fileList.find('ul').find('li[data-file-srl=' + srl + ']').remove();
}); });
var ckeditor = _getCkeInstance(data.editorSequence);
var regexp = new RegExp('<(img) [^>]*data-file-srl="(' + file_srls.join('|') + ')"[^>]*>', 'g');
ckeditor.setData(ckeditor.getData().replace(regexp, ''));
self.loadFilelist($container); self.loadFilelist($container);
}); });
}, },
@ -296,6 +345,7 @@
var obj = {}; var obj = {};
obj.mid = window.current_mid; obj.mid = window.current_mid;
obj.editor_sequence = data.editorSequence; obj.editor_sequence = data.editorSequence;
obj.allow_chunks = 'Y';
$.exec_json('file.getFileList', obj, function(res){ $.exec_json('file.getFileList', obj, function(res){
data.uploadTargetSrl = res.upload_target_srl; data.uploadTargetSrl = res.upload_target_srl;

View file

@ -1 +0,0 @@
main.js

View file

@ -0,0 +1 @@
This file is not used in Rhymix.

View file

@ -168,7 +168,7 @@
// Convert params to object and fill in the module and act. // Convert params to object and fill in the module and act.
params = params ? ($.isArray(params) ? arr2obj(params) : params) : {}; params = params ? ($.isArray(params) ? arr2obj(params) : params) : {};
action = action.split("."); action = action.split(".");
if (action.length != 2) return; //if (action.length != 2) return;
params.module = action[0]; params.module = action[0];
params.act = action[1]; params.act = action[1];
params._rx_ajax_compat = 'JSON'; params._rx_ajax_compat = 'JSON';
@ -275,7 +275,7 @@
// Convert params to object and fill in the module and act. // Convert params to object and fill in the module and act.
params = params ? ($.isArray(params) ? arr2obj(params) : params) : {}; params = params ? ($.isArray(params) ? arr2obj(params) : params) : {};
action = action.split("."); action = action.split(".");
if (action.length != 2) return; //if (action.length != 2) return;
params.module = action[0]; params.module = action[0];
params.act = action[1]; params.act = action[1];

View file

@ -110,6 +110,9 @@ $lang->comment = 'Comment';
$lang->description = 'Description'; $lang->description = 'Description';
$lang->trackback = 'Trackback'; $lang->trackback = 'Trackback';
$lang->tag = 'Tag'; $lang->tag = 'Tag';
$lang->mail = 'Mail';
$lang->email = 'E-mail';
$lang->sms = 'SMS';
$lang->allow_comment = 'Allow Comments'; $lang->allow_comment = 'Allow Comments';
$lang->lock_comment = 'Block Comments'; $lang->lock_comment = 'Block Comments';
$lang->allow_trackback = 'Allow Trackbacks'; $lang->allow_trackback = 'Allow Trackbacks';

View file

@ -110,6 +110,9 @@ $lang->comment = '댓글';
$lang->description = '설명'; $lang->description = '설명';
$lang->trackback = '엮인글'; $lang->trackback = '엮인글';
$lang->tag = '태그'; $lang->tag = '태그';
$lang->mail = '메일';
$lang->email = '이메일';
$lang->sms = 'SMS';
$lang->allow_comment = '댓글 허용'; $lang->allow_comment = '댓글 허용';
$lang->lock_comment = '댓글 잠금'; $lang->lock_comment = '댓글 잠금';
$lang->allow_trackback = '엮인글 허용'; $lang->allow_trackback = '엮인글 허용';

View file

@ -0,0 +1,58 @@
<?php
/**
* This script deletes empty directories under the 'files' directory.
*
* It may be useful when your web host imposes a hard limit on the number of
* inodes, or when your backups take too long due to the large number of
* unused directories.
*
* This script only works on Unix-like operating systems where the 'find'
* command is available.
*/
require_once __DIR__ . '/common.php';
// Initialize the exit status.
$exit_status = 0;
// Delete empty directories in the attachment directory.
passthru(sprintf('find %s -type d -empty -delete', escapeshellarg(RX_BASEDIR . 'files/attach')), $result);
if ($result == 0)
{
echo "Successfully deleted all empty directories under files/attach.\n";
}
else
{
echo "Error while deleting empty directories under files/attach.\n";
$exit_status = $result;
}
// Delete empty directories in the member extra info directory.
passthru(sprintf('find %s -type d -empty -delete', escapeshellarg(RX_BASEDIR . 'files/member_extra_info')), $result);
if ($result == 0)
{
echo "Successfully deleted all empty directories under files/member_extra_info.\n";
}
else
{
echo "Error while deleting empty directories under files/member_extra_info.\n";
$exit_status = $result;
}
// Delete empty directories in the thumbnails directory.
passthru(sprintf('find %s -type d -empty -delete', escapeshellarg(RX_BASEDIR . 'files/thumbnails')), $result);
if ($result == 0)
{
echo "Successfully deleted all empty directories under files/thumbnails.\n";
}
else
{
echo "Error while deleting empty directories under files/thumbnails.\n";
$exit_status = $result;
}
// Set the exit status if there were any errors.
if ($exit_status != 0)
{
exit($exit_status);
}

View file

@ -0,0 +1,98 @@
<?php
/**
* This script deletes files that were not properly uploaded.
*
* Files can remain in an invalid status for two reasons: 1) a user abandons
* a document or comment after uploading files; or 2) a chunked upload is
* aborted without the server having any opportunity to clean it up.
* These files can obviously take up a lot of disk space. In order to prevent
* them from accumulating too much, you should run this script at least once
* every few days.
*/
require_once __DIR__ . '/common.php';
// Delete garbage files older than this number of days.
$days = 10;
// Initialize the exit status.
$exit_status = 0;
// Initialize objects.
$oDB = DB::getInstance();
$oFileController = getController('file');
// Find and delete files where isvalid = N.
$args = new stdClass;
$args->isvalid = 'N';
$args->list_count = 50;
$args->regdate_before = date('YmdHis', time() - ($days * 86400));
while (true)
{
$output = executeQueryArray('file.getFileList', $args);
if ($output->toBool())
{
if ($output->data)
{
$oDB->begin();
foreach ($output->data as $file_info)
{
$oFileController->deleteFile($file_info->file_srl);
}
$oDB->commit();
if ($output->page_navigation && $output->page_navigation->total_count == count($output->data))
{
break;
}
}
else
{
break;
}
}
else
{
echo "Error while deleting garbage files older than $days days.\n";
echo $output->getMessage() . "\n";
$exit_status = 11;
break;
}
}
if ($exit_status == 0)
{
echo "Successfully deleted all garbage files older than $days days.\n";
}
// Find and delete temporary chunks.
$dirname = RX_BASEDIR . 'files/attach/chunks';
$threshold = time() - ($days * 86400);
$chunks = Rhymix\Framework\Storage::readDirectory($dirname);
if ($chunks)
{
foreach ($chunks as $chunk)
{
if (@filemtime($chunk) < $threshold)
{
$result = Rhymix\Framework\Storage::delete($chunk);
if (!$result)
{
$exit_status = 12;
}
}
}
}
if ($exit_status == 0)
{
echo "Successfully deleted aborted file chunks older than $days days.\n";
}
else
{
echo "Error while deleting aborted file chunks older than $days days.\n";
}
// Set the exit status if there were any errors.
if ($exit_status != 0)
{
exit($exit_status);
}

View file

@ -0,0 +1,37 @@
<?php
/**
* This script deletes old notifications.
*
* Notifications must be dismissed as quickly as possible in order to prevent
* the ncenterlite_notify table from becoming too large. For best performance,
* you should run this script at least once every few days.
*/
require_once __DIR__ . '/common.php';
// Delete notifications older than this number of days.
$days = 30;
// Initialize the exit status.
$exit_status = 0;
// Execute the query.
$args = new stdClass;
$args->old_date = date('YmdHis', time() - ($days * 86400));
$output = executeQuery('ncenterlite.deleteNotifyAll', $args);
if ($output->toBool())
{
echo "Successfully deleted all notifications older than $days days.\n";
}
else
{
echo "Error while deleting notifications older than $days days.\n";
echo $output->getMessage() . "\n";
$exit_status = 11;
}
// Set the exit status if there were any errors.
if ($exit_status != 0)
{
exit($exit_status);
}

View file

@ -0,0 +1,41 @@
<?php
/**
* This script deletes old thumbnails.
*
* Thumbnails can take up a large amount of disk space and inodes if they are
* allowed to accumulate. Since most websites only need thumbnails for recent
* posts, it is okay to delete old thumbnails.
*
* Do not run this script if you have a gallery-style module where visitors
* regularly view old posts. This will force thumbnails to be regenerated,
* increasing the server load and making your pages load slower.
*
* This script only works on Unix-like operating systems where the 'find'
* command is available.
*/
require_once __DIR__ . '/common.php';
// Delete thumbnails older than this number of days.
$days = 90;
// Initialize the exit status.
$exit_status = 0;
// Delete old thumbnails.
passthru(sprintf('find %s -type f -mtime +%d -delete', escapeshellarg(RX_BASEDIR . 'files/thumbnails'), abs($days)), $result);
if ($result == 0)
{
echo "Successfully deleted all thumbnails older than $days days.\n";
}
else
{
echo "Error while deleting thumbnails older than $days days.\n";
$exit_status = $result;
}
// Set the exit status if there were any errors.
if ($exit_status != 0)
{
exit($exit_status);
}

42
common/scripts/common.php Normal file
View file

@ -0,0 +1,42 @@
<?php
/**
* This file must be included at the top of all shell scripts (cron jobs).
*
* HERE BE DRAGONS.
*
* Failure to perform the checks listed in this file at the top of a cron job,
* or any attempt to work around the limitations deliberately placed in this
* file, may result in errors or degradation of service.
*
* Please be warned that errors may not show up immediately, especially if you
* screw up the permissions inside deeply nested directory trees. You may find
* it difficult and/or costly to undo the damages when errors begin to show up
* several months later.
*/
// Abort if not CLI.
if (PHP_SAPI !== 'cli')
{
echo "This script must be executed on the command line interface.\n";
exit(1);
}
// Load Rhymix.
chdir(dirname(dirname(__DIR__)));
require_once dirname(__DIR__) . '/autoload.php';
// Abort if the UID does not match.
$uid = Rhymix\Framework\Storage::getServerUID();
if ($uid === 0)
{
echo "This script must not be executed by the root user.\n";
exit(2);
}
$web_server_uid = fileowner(RX_BASEDIR . 'files/config/config.php');
if ($uid !== $web_server_uid)
{
$web_server_uid = posix_getpwuid($web_server_uid);
echo "This script must be executed by the same user as the usual web server process ({$web_server_uid['name']}).\n";
exit(3);
}

View file

@ -4,6 +4,8 @@
{$content} {$content}
</div> </div>
<script> <script>
jQuery(window).load(setFixedPopupSize); jQuery(function() {
setTimeout(setFixedPopupSize, 500);
});
var _isPoped = true; var _isPoped = true;
</script> </script>

View file

@ -19,6 +19,7 @@
"ext-openssl": "*", "ext-openssl": "*",
"ext-pcre": "*", "ext-pcre": "*",
"ext-xml": "*", "ext-xml": "*",
"coolsms/php-sdk": "2.0.*",
"defuse/php-encryption": "1.2.1", "defuse/php-encryption": "1.2.1",
"ezyang/htmlpurifier": "4.7.*", "ezyang/htmlpurifier": "4.7.*",
"hautelook/phpass": "0.3.*", "hautelook/phpass": "0.3.*",

126
composer.lock generated
View file

@ -4,9 +4,63 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "64d8278cd705dec01a94208e378716f8", "hash": "66af49066c9b92d6708080f3d32860f4",
"content-hash": "fa544ace96d8d80c11db9dafb2c49753", "content-hash": "51acda30c4d45b4c956743d4bd1ac16e",
"packages": [ "packages": [
{
"name": "coolsms/php-sdk",
"version": "v2.0",
"source": {
"type": "git",
"url": "https://github.com/coolsms/php-sdk.git",
"reference": "ce00fea155169dcf2a3759abd41ad20ff80ca0b1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/coolsms/php-sdk/zipball/ce00fea155169dcf2a3759abd41ad20ff80ca0b1",
"reference": "ce00fea155169dcf2a3759abd41ad20ff80ca0b1",
"shasum": ""
},
"require": {
"php": ">=5.5.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Nurigo\\": "app/Nurigo"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "nurigo",
"email": "contact@nurigo.net",
"homepage": "http://coolsms.co.kr",
"role": "Developer"
}
],
"description": "Send message using PHP and RestAPI[TEST]",
"homepage": "http://coolsms.co.kr",
"keywords": [
"TextMessage",
"cellphone",
"cool",
"coolsms",
"global",
"lms",
"message",
"messages",
"mms",
"mobile",
"nurigo",
"phone",
"sms"
],
"time": "2016-09-02 03:28:39"
},
{ {
"name": "defuse/php-encryption", "name": "defuse/php-encryption",
"version": "v1.2.1", "version": "v1.2.1",
@ -272,16 +326,16 @@
}, },
{ {
"name": "leafo/scssphp", "name": "leafo/scssphp",
"version": "v0.6.3", "version": "v0.6.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/leafo/scssphp.git", "url": "https://github.com/leafo/scssphp.git",
"reference": "a27edad3d16635a222d7204706572e24c338aa17" "reference": "6fdfe19d2b13a3f12ba0792227f0718809ce4e4d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/leafo/scssphp/zipball/a27edad3d16635a222d7204706572e24c338aa17", "url": "https://api.github.com/repos/leafo/scssphp/zipball/6fdfe19d2b13a3f12ba0792227f0718809ce4e4d",
"reference": "a27edad3d16635a222d7204706572e24c338aa17", "reference": "6fdfe19d2b13a3f12ba0792227f0718809ce4e4d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -321,20 +375,20 @@
"scss", "scss",
"stylesheet" "stylesheet"
], ],
"time": "2016-01-15 02:50:06" "time": "2016-09-11 01:34:11"
}, },
{ {
"name": "league/html-to-markdown", "name": "league/html-to-markdown",
"version": "4.2.0", "version": "4.2.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/html-to-markdown.git", "url": "https://github.com/thephpleague/html-to-markdown.git",
"reference": "9a5becc8c6b520920fb846afefcfd7faf4c31712" "reference": "8dfe3b1e6d459b320bec1a4b5499cd9d62796ac0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/9a5becc8c6b520920fb846afefcfd7faf4c31712", "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/8dfe3b1e6d459b320bec1a4b5499cd9d62796ac0",
"reference": "9a5becc8c6b520920fb846afefcfd7faf4c31712", "reference": "8dfe3b1e6d459b320bec1a4b5499cd9d62796ac0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -385,20 +439,20 @@
"html", "html",
"markdown" "markdown"
], ],
"time": "2016-02-01 16:49:02" "time": "2016-09-27 12:38:24"
}, },
{ {
"name": "matthiasmullie/minify", "name": "matthiasmullie/minify",
"version": "1.3.34", "version": "1.3.39",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/matthiasmullie/minify.git", "url": "https://github.com/matthiasmullie/minify.git",
"reference": "272e46113404f66ced256659552a0cc074a7810f" "reference": "1a6cb6b457690034bde461593edb510949bdd6e7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/272e46113404f66ced256659552a0cc074a7810f", "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/1a6cb6b457690034bde461593edb510949bdd6e7",
"reference": "272e46113404f66ced256659552a0cc074a7810f", "reference": "1a6cb6b457690034bde461593edb510949bdd6e7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -441,7 +495,7 @@
"minifier", "minifier",
"minify" "minify"
], ],
"time": "2016-03-01 08:00:27" "time": "2016-10-27 22:32:49"
}, },
{ {
"name": "matthiasmullie/path-converter", "name": "matthiasmullie/path-converter",
@ -553,7 +607,7 @@
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/michelf/php-smartypants/zipball/c0465c6d4c5ab853c2fa45df6c10bce7e35cc137", "url": "https://api.github.com/repos/michelf/php-smartypants/zipball/171a3a2552f33340bf3636bdb4b05eb4e406fbda",
"reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137", "reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137",
"shasum": "" "shasum": ""
}, },
@ -649,16 +703,16 @@
}, },
{ {
"name": "sunra/php-simple-html-dom-parser", "name": "sunra/php-simple-html-dom-parser",
"version": "v1.5.0", "version": "v1.5.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sunra/php-simple-html-dom-parser.git", "url": "https://github.com/sunra/php-simple-html-dom-parser.git",
"reference": "a0b80ace086c7e09085669205e1b3c2c9c7a453c" "reference": "f910346ce47513a49ed5b8de197cde26c3f0b193"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sunra/php-simple-html-dom-parser/zipball/a0b80ace086c7e09085669205e1b3c2c9c7a453c", "url": "https://api.github.com/repos/sunra/php-simple-html-dom-parser/zipball/f910346ce47513a49ed5b8de197cde26c3f0b193",
"reference": "a0b80ace086c7e09085669205e1b3c2c9c7a453c", "reference": "f910346ce47513a49ed5b8de197cde26c3f0b193",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -676,9 +730,9 @@
], ],
"authors": [ "authors": [
{ {
"name": "sunra", "name": "Sunra",
"email": "sunra@yandex.ru", "email": "sunra@yandex.ru",
"homepage": "http://github.com/sunra" "homepage": "https://github.com/sunra"
} }
], ],
"description": "Composer adaptation of: A HTML DOM parser written in PHP5+ let you manipulate HTML in a very easy way! Require PHP 5+. Supports invalid HTML. Find tags on an HTML page with selectors just like jQuery. Extract contents from HTML in a single line.", "description": "Composer adaptation of: A HTML DOM parser written in PHP5+ let you manipulate HTML in a very easy way! Require PHP 5+. Supports invalid HTML. Find tags on an HTML page with selectors just like jQuery. Extract contents from HTML in a single line.",
@ -688,27 +742,27 @@
"html", "html",
"parser" "parser"
], ],
"time": "2013-05-04 14:32:03" "time": "2016-05-20 11:21:15"
}, },
{ {
"name": "swiftmailer/swiftmailer", "name": "swiftmailer/swiftmailer",
"version": "v5.4.2", "version": "v5.4.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git", "url": "https://github.com/swiftmailer/swiftmailer.git",
"reference": "d8db871a54619458a805229a057ea2af33c753e8" "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d8db871a54619458a805229a057ea2af33c753e8", "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
"reference": "d8db871a54619458a805229a057ea2af33c753e8", "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": { "require-dev": {
"mockery/mockery": "~0.9.1,<0.9.4" "mockery/mockery": "~0.9.1"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -741,20 +795,20 @@
"mail", "mail",
"mailer" "mailer"
], ],
"time": "2016-05-01 08:45:47" "time": "2016-07-08 11:51:25"
}, },
{ {
"name": "true/punycode", "name": "true/punycode",
"version": "v2.0.2", "version": "v2.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/true/php-punycode.git", "url": "https://github.com/true/php-punycode.git",
"reference": "74fa01d4de396c40e239794123b3874cb594a30c" "reference": "74033cbe9fdd3eba597f8af501947a125b3b8087"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/true/php-punycode/zipball/74fa01d4de396c40e239794123b3874cb594a30c", "url": "https://api.github.com/repos/true/php-punycode/zipball/74033cbe9fdd3eba597f8af501947a125b3b8087",
"reference": "74fa01d4de396c40e239794123b3874cb594a30c", "reference": "74033cbe9fdd3eba597f8af501947a125b3b8087",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -787,7 +841,7 @@
"idna", "idna",
"punycode" "punycode"
], ],
"time": "2016-01-07 17:12:58" "time": "2016-08-09 14:50:44"
} }
], ],
"packages-dev": null, "packages-dev": null,

View file

@ -13,7 +13,7 @@
$material_colors = array( $material_colors = array(
'red' => '#f44336', 'red' => '#f44336',
'crimson' => '#66001f', 'crimson' => '#aa0000',
'pink' => '#e91e63', 'pink' => '#e91e63',
'purple' => '#9c27b0', 'purple' => '#9c27b0',
'deep-purple' => '#673ab7', 'deep-purple' => '#673ab7',
@ -52,7 +52,7 @@
<load target="layout.scss" vars="$layout_scss_value" /> <load target="layout.scss" vars="$layout_scss_value" />
<load target="layout.js" /> <load target="layout.js" />
<div class="skip"><a href="#content">{$lang->skip_to_content}</a></div> <div class="skip"><a href="#content">{lang('skip_to_content')}</a></div>
<header class="layout_frame"> <header class="layout_frame">
<div class="layout_header layout_canvas"> <div class="layout_header layout_canvas">
<h1> <h1>
@ -65,7 +65,7 @@
</h1> </h1>
<div id="layout_menu_toggle"> <div id="layout_menu_toggle">
<button class="layout_mobile_menu layout_mobile_menu--htx" data-target="layout_gnb"> <button class="layout_mobile_menu layout_mobile_menu--htx" data-target="layout_gnb">
<span>{$lang->menu}</span> <span>{lang('common.menu')}</span>
</button> </button>
</div> </div>
<div class="hside layout_pc"> <div class="hside layout_pc">
@ -75,8 +75,8 @@
<input type="hidden" name="vid" value="{$vid}" /> <input type="hidden" name="vid" value="{$vid}" />
<input type="hidden" name="mid" value="{$mid}" /> <input type="hidden" name="mid" value="{$mid}" />
<input type="hidden" name="act" value="IS" /> <input type="hidden" name="act" value="IS" />
<input type="text" name="is_keyword" value="{$is_keyword}" required placeholder="{$lang->cmd_search}" title="{$lang->cmd_search}" /> <input type="text" name="is_keyword" value="{$is_keyword}" required placeholder="{lang('common.cmd_search')}" title="{lang('common.cmd_search')}" />
<input type="submit" value="{$lang->cmd_search}" /> <input type="submit" value="{lang('common.cmd_search')}" />
</form> </form>
<!--// Search --> <!--// Search -->
</div> </div>
@ -86,18 +86,18 @@
<nav class="layout_frame layout_menu" id="layout_gnb"> <nav class="layout_frame layout_menu" id="layout_gnb">
<ul> <ul>
<li class="layout_dropdown"> <li class="layout_dropdown">
<a href="{getUrl('act', 'dispMemberLoginForm')}" cond="!$is_logged">{sprintf($lang->simple_hello, $lang->simple_guest)}</a> <a href="{getUrl('act', 'dispMemberLoginForm')}" cond="!$is_logged">{sprintf(lang('simple_hello'), lang('simple_guest'))}</a>
<ul class="layout_dropdown-content" cond="!$is_logged"> <ul class="layout_dropdown-content" cond="!$is_logged">
<li><a href="{getUrl('act', 'dispMemberLoginForm')}">{$lang->cmd_login}...</a></li> <li><a href="{getUrl('act', 'dispMemberLoginForm')}">{lang('member.cmd_login')}...</a></li>
<li><a href="{getUrl('act', 'dispMemberSignUpForm')}" cond="$member_config->enable_join === 'Y'">{$lang->cmd_signup}...</a></li> <li><a href="{getUrl('act', 'dispMemberSignUpForm')}" cond="$member_config->enable_join === 'Y'">{lang('member.cmd_signup')}...</a></li>
</ul> </ul>
<a href="{getUrl('act', 'dispMemberInfo')}" cond="$is_logged">{sprintf($lang->simple_hello, $logged_info->nick_name)}</a> <a href="{getUrl('act', 'dispMemberInfo')}" cond="$is_logged">{sprintf(lang('simple_hello'), $logged_info->nick_name)}</a>
<ul class="layout_dropdown-content" cond="$is_logged"> <ul class="layout_dropdown-content" cond="$is_logged">
<li><a href="{getUrl('act', 'dispMemberInfo')}">{$lang->cmd_view_member_info}</a></li> <li><a href="{getUrl('act', 'dispMemberInfo')}">{lang('member.cmd_view_member_info')}</a></li>
<li cond="$logged_info->is_admin == 'Y'"> <li cond="$logged_info->is_admin == 'Y'">
<a href="{getUrl('', 'module','admin')}">{$lang->cmd_management}</a> <a href="{getUrl('', 'module','admin')}">{lang('common.cmd_management')}</a>
</li> </li>
<li><a href="{getUrl('act', 'dispMemberLogout')}">{$lang->cmd_logout}</a></li> <li><a href="{getUrl('act', 'dispMemberLogout')}">{lang('member.cmd_logout')}</a></li>
</ul> </ul>
</li> </li>
<li loop="$GNB->list=>$key1,$val1" class="<!--@if($val1['selected'])-->active <!--@endif--><!--@if($val1['list'])-->layout_dropdown<!--@endif-->"> <li loop="$GNB->list=>$key1,$val1" class="<!--@if($val1['selected'])-->active <!--@endif--><!--@if($val1['list'])-->layout_dropdown<!--@endif-->">
@ -107,7 +107,7 @@
</ul> </ul>
</li> </li>
<li id="layout_search_link"> <li id="layout_search_link">
<a href="{getUrl('vid', $vid, 'mid', $mid, 'act', 'IS')}"><span>{$lang->cmd_search}</span></a> <a href="{getUrl('vid', $vid, 'mid', $mid, 'act', 'IS')}"><span>{lang('common.cmd_search')}</span></a>
</li> </li>
</ul> </ul>
</nav> </nav>

View file

@ -1,15 +1,20 @@
$(function() { $(function()
{
"use strict"; "use strict";
/* adjust the width of the right member menu */ /* adjust the width of the right member menu */
var menu_width = function() { var menu_width = function()
if($('#layout_gnb>ul>li:first-child').width() > 50) { {
if($('#layout_gnb>ul>li:first-child').width() > 50)
{
$('#layout_gnb>ul>li:first-child .layout_dropdown-content, #layout_gnb>ul>li:first-child .layout_dropdown-content a').css('width', $('#layout_gnb>ul>li:first-child').width()).css('min-width', $('#layout_gnb>ul>li:first-child').width()); $('#layout_gnb>ul>li:first-child .layout_dropdown-content, #layout_gnb>ul>li:first-child .layout_dropdown-content a').css('width', $('#layout_gnb>ul>li:first-child').width()).css('min-width', $('#layout_gnb>ul>li:first-child').width());
} }
} }
$( window ).resize(function() { $( window ).resize(function()
if($('#layout_gnb>ul>li:first-child').width() > 50) { {
if($('#layout_gnb>ul>li:first-child').width() > 50)
{
menu_width(); menu_width();
} }
}); });
@ -17,32 +22,65 @@ $(function() {
menu_width(); menu_width();
/* mobile hamburger menu toggle */ /* mobile hamburger menu toggle */
$(".layout_mobile_menu").each(function() { $(".layout_mobile_menu").each(function()
$( this ).click(function( event ) { {
$( this ).click(function( event )
{
event.preventDefault(); event.preventDefault();
layout_toggleMenuOpener( $( this ).get(0) ); layout_toggleMenuOpener( $( this ).get(0) );
}); });
}); });
/* detect scrolling up or down to hide or show hamburger menu */
var previousScroll = 0;
$( window ).scroll(function()
{
var currentScroll = $(this).scrollTop();
if (currentScroll > previousScroll)
{
if($("#layout_menu_toggle").css( 'position' ) === 'fixed' && $("#layout_menu_toggle").attr('data-scroll-down') !== 'true')
{
$("#layout_menu_toggle").attr('data-scroll-down', 'true');
$("#layout_menu_toggle").fadeOut();
}
}
else
{
if($("#layout_menu_toggle").css( 'position' ) === 'fixed' && $("#layout_menu_toggle").attr('data-scroll-down') === 'true')
{
$("#layout_menu_toggle").attr('data-scroll-down', '');
$("#layout_menu_toggle").fadeIn();
}
}
previousScroll = currentScroll;
});
/* keyboard accessibility for dropdown menu */ /* keyboard accessibility for dropdown menu */
$(".layout_dropdown").each(function() { $(".layout_dropdown").each(function()
$( this ).focusin( function( event ) { {
$( this ).focusin( function( event )
{
$( this ).addClass('layout_focus'); $( this ).addClass('layout_focus');
$( this ).find("ul.layout_dropdown-content").css('display', 'block').attr('data-dropdown', 'active'); $( this ).find("ul.layout_dropdown-content").css('display', 'block').attr('data-dropdown', 'active');
}); });
}); });
$('body').focusin(function( event ) { $('body').focusin(function( event )
if (!$(event.target).parents('.layout_dropdown').is('.layout_dropdown')) { {
if (!$(event.target).parents('.layout_dropdown').is('.layout_dropdown'))
{
$('*[data-dropdown="active"]').css('display', '').attr('data-dropdown', '').parents('li.layout_dropdown').removeClass('layout_focus'); $('*[data-dropdown="active"]').css('display', '').attr('data-dropdown', '').parents('li.layout_dropdown').removeClass('layout_focus');
} }
}); });
/* keyboard accessibility for dropdown menu END */ /* keyboard accessibility for dropdown menu END */
function layout_toggleMenuOpener(obj) { function layout_toggleMenuOpener(obj)
if(obj.classList.contains("is-active") === true){ {
if(obj.classList.contains("is-active") === true)
{
var targetMenu = $(obj).attr('data-target'); var targetMenu = $(obj).attr('data-target');
$('#' + targetMenu).slideUp('300', function() { $('#' + targetMenu).slideUp('300', function()
{
$(this).css('display', ''); $(this).css('display', '');
}); });
@ -62,7 +100,8 @@ $(function() {
} }
// Language Select // Language Select
$('.layout_language>.toggle').click(function(){ $('.layout_language>.toggle').click(function()
{
$('.selectLang').toggle(); $('.selectLang').toggle();
}); });
}); });

View file

@ -47,7 +47,7 @@
<block cond="count($addon_info->extra_vars)"> <block cond="count($addon_info->extra_vars)">
<block loop="$addon_info->extra_vars => $id, $var"> <block loop="$addon_info->extra_vars => $id, $var">
<block cond="$group != $var->group"> <block cond="$group != $var->group">
<h2>{$var->group}</h2> <h2 style="margin-top:50px;">{$var->group}</h2>
{@$group = $var->group} {@$group = $var->group}
</block> </block>
{@$not_first = true} {@$not_first = true}

View file

@ -549,12 +549,124 @@ class adminAdminController extends admin
$this->_saveDefaultImage($vars->is_delete_default_image); $this->_saveDefaultImage($vars->is_delete_default_image);
// Save // Save
Rhymix\Framework\Config::save(); if (!Rhymix\Framework\Config::save())
{
return new Object(-1, 'msg_failed_to_save_config');
}
$this->setMessage('success_updated'); $this->setMessage('success_updated');
$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigGeneral')); $this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigGeneral'));
} }
/**
* Update notification configuration.
*/
function procAdminUpdateNotification()
{
$vars = Context::getRequestVars();
// Load advanced mailer module (for lang).
$oAdvancedMailerAdminView = getAdminView('advanced_mailer');
// Validate the mail sender's information.
if (!$vars->mail_default_name)
{
return new Object(-1, 'msg_advanced_mailer_sender_name_is_empty');
}
if (!$vars->mail_default_from)
{
return new Object(-1, 'msg_advanced_mailer_sender_email_is_empty');
}
if (!Mail::isVaildMailAddress($vars->mail_default_from))
{
return new Object(-1, 'msg_advanced_mailer_sender_email_is_invalid');
}
if ($vars->mail_default_reply_to && !Mail::isVaildMailAddress($vars->mail_default_reply_to))
{
return new Object(-1, 'msg_advanced_mailer_reply_to_is_invalid');
}
// Validate the mail driver.
$mail_drivers = Rhymix\Framework\Mail::getSupportedDrivers();
$mail_driver = $vars->mail_driver;
if (!array_key_exists($mail_driver, $mail_drivers))
{
return new Object(-1, 'msg_advanced_mailer_sending_method_is_invalid');
}
// Validate the mail driver settings.
$mail_driver_config = array();
foreach ($mail_drivers[$mail_driver]['required'] as $conf_name)
{
$conf_value = $vars->{'mail_' . $mail_driver . '_' . $conf_name} ?: null;
if (!$conf_value)
{
return new Object(-1, 'msg_advanced_mailer_smtp_host_is_invalid');
}
$mail_driver_config[$conf_name] = $conf_value;
}
// Validate the SMS driver.
$sms_drivers = Rhymix\Framework\SMS::getSupportedDrivers();
$sms_driver = $vars->sms_driver;
if (!array_key_exists($sms_driver, $sms_drivers))
{
return new Object(-1, 'msg_advanced_mailer_sending_method_is_invalid');
}
// Validate the SMS driver settings.
$sms_driver_config = array();
foreach ($sms_drivers[$sms_driver]['required'] as $conf_name)
{
$conf_value = $vars->{'sms_' . $sms_driver . '_' . $conf_name} ?: null;
if (!$conf_value)
{
return new Object(-1, 'msg_advanced_mailer_smtp_host_is_invalid');
}
$sms_driver_config[$conf_name] = $conf_value;
}
foreach ($sms_drivers[$sms_driver]['optional'] as $conf_name)
{
$conf_value = $vars->{'sms_' . $sms_driver . '_' . $conf_name} ?: null;
$sms_driver_config[$conf_name] = $conf_value;
}
// Save advanced mailer config.
getController('module')->updateModuleConfig('advanced_mailer', (object)array(
'sender_name' => trim($vars->mail_default_name),
'sender_email' => trim($vars->mail_default_from),
'force_sender' => toBool($vars->mail_force_default_sender),
'reply_to' => trim($vars->mail_default_reply_to),
));
// Save member config.
getController('module')->updateModuleConfig('member', (object)array(
'webmaster_name' => trim($vars->mail_default_name),
'webmaster_email' => trim($vars->mail_default_from),
));
// Save system config.
Rhymix\Framework\Config::set("mail.default_name", trim($vars->mail_default_name));
Rhymix\Framework\Config::set("mail.default_from", trim($vars->mail_default_from));
Rhymix\Framework\Config::set("mail.default_force", toBool($vars->mail_force_default_sender));
Rhymix\Framework\Config::set("mail.default_reply_to", trim($vars->mail_default_reply_to));
Rhymix\Framework\Config::set("mail.type", $mail_driver);
Rhymix\Framework\Config::set("mail.$mail_driver", $mail_driver_config);
Rhymix\Framework\Config::set("sms.default_from", trim($vars->sms_default_from));
Rhymix\Framework\Config::set("sms.default_force", toBool($vars->sms_force_default_sender));
Rhymix\Framework\Config::set("sms.type", $sms_driver);
Rhymix\Framework\Config::set("sms.$sms_driver", $sms_driver_config);
Rhymix\Framework\Config::set("sms.allow_split.sms", toBool($vars->allow_split_sms));
Rhymix\Framework\Config::set("sms.allow_split.lms", toBool($vars->allow_split_lms));
if (!Rhymix\Framework\Config::save())
{
return new Object(-1, 'msg_failed_to_save_config');
}
$this->setMessage('success_updated');
$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigNotification'));
}
/** /**
* Update security configuration. * Update security configuration.
*/ */
@ -584,6 +696,14 @@ class adminAdminController extends admin
natcasesort($object_whitelist); natcasesort($object_whitelist);
Rhymix\Framework\Config::set('mediafilter.object', array_values($object_whitelist)); Rhymix\Framework\Config::set('mediafilter.object', array_values($object_whitelist));
// HTML classes
$classes = $vars->mediafilter_classes;
$classes = array_filter(array_map('trim', preg_split('/[\r\n]/', $classes)), function($item) {
return preg_match('/^[a-zA-Z0-9_-]+$/u', $item);
});
natcasesort($classes);
Rhymix\Framework\Config::set('mediafilter.classes', array_values($classes));
// Remove old embed filter // Remove old embed filter
$config = Rhymix\Framework\Config::getAll(); $config = Rhymix\Framework\Config::getAll();
unset($config['embedfilter']); unset($config['embedfilter']);
@ -616,7 +736,10 @@ class adminAdminController extends admin
Rhymix\Framework\Config::set('admin.deny', array_values($denied_ip)); Rhymix\Framework\Config::set('admin.deny', array_values($denied_ip));
// Save // Save
Rhymix\Framework\Config::save(); if (!Rhymix\Framework\Config::save())
{
return new Object(-1, 'msg_failed_to_save_config');
}
$this->setMessage('success_updated'); $this->setMessage('success_updated');
$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigSecurity')); $this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigSecurity'));
@ -631,7 +754,7 @@ class adminAdminController extends admin
// Default URL // Default URL
$default_url = rtrim(trim($vars->default_url), '/\\') . '/'; $default_url = rtrim(trim($vars->default_url), '/\\') . '/';
if (!filter_var($default_url, FILTER_VALIDATE_URL) || !preg_match('@^https?://@', $default_url)) if (!filter_var(Rhymix\Framework\URL::encodeIdna($default_url), FILTER_VALIDATE_URL) || !preg_match('@^https?://@', $default_url))
{ {
return new Object(-1, 'msg_invalid_default_url'); return new Object(-1, 'msg_invalid_default_url');
} }
@ -700,10 +823,11 @@ class adminAdminController extends admin
} }
// Thumbnail settings // Thumbnail settings
$args = new stdClass; $oDocumentModel = getModel('document');
$args->thumbnail_type = $vars->thumbnail_type === 'ratio' ? 'ratio' : 'crop'; $document_config = $oDocumentModel->getDocumentConfig();
$document_config->thumbnail_type = $vars->thumbnail_type ?: 'crop';
$oModuleController = getController('module'); $oModuleController = getController('module');
$oModuleController->insertModuleConfig('document', $args); $oModuleController->insertModuleConfig('document', $document_config);
// Other settings // Other settings
Rhymix\Framework\Config::set('use_rewrite', $vars->use_rewrite === 'Y'); Rhymix\Framework\Config::set('use_rewrite', $vars->use_rewrite === 'Y');
@ -716,7 +840,10 @@ class adminAdminController extends admin
Rhymix\Framework\Config::set('view.use_gzip', $vars->use_gzip === 'Y'); Rhymix\Framework\Config::set('view.use_gzip', $vars->use_gzip === 'Y');
// Save // Save
Rhymix\Framework\Config::save(); if (!Rhymix\Framework\Config::save())
{
return new Object(-1, 'msg_failed_to_save_config');
}
$this->setMessage('success_updated'); $this->setMessage('success_updated');
$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigAdvanced')); $this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigAdvanced'));
@ -776,7 +903,10 @@ class adminAdminController extends admin
Rhymix\Framework\Config::set('debug.allow', array_values($allowed_ip)); Rhymix\Framework\Config::set('debug.allow', array_values($allowed_ip));
// Save // Save
Rhymix\Framework\Config::save(); if (!Rhymix\Framework\Config::save())
{
return new Object(-1, 'msg_failed_to_save_config');
}
$this->setMessage('success_updated'); $this->setMessage('success_updated');
$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigDebug')); $this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigDebug'));
@ -805,7 +935,10 @@ class adminAdminController extends admin
Rhymix\Framework\Config::set('seo.og_use_timestamps', $vars->og_use_timestamps === 'Y'); Rhymix\Framework\Config::set('seo.og_use_timestamps', $vars->og_use_timestamps === 'Y');
// Save // Save
Rhymix\Framework\Config::save(); if (!Rhymix\Framework\Config::save())
{
return new Object(-1, 'msg_failed_to_save_config');
}
$this->setMessage('success_updated'); $this->setMessage('success_updated');
$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigSEO')); $this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigSEO'));
@ -844,7 +977,10 @@ class adminAdminController extends admin
Rhymix\Framework\Config::set('lock.title', trim($vars->sitelock_title)); Rhymix\Framework\Config::set('lock.title', trim($vars->sitelock_title));
Rhymix\Framework\Config::set('lock.message', trim($vars->sitelock_message)); Rhymix\Framework\Config::set('lock.message', trim($vars->sitelock_message));
Rhymix\Framework\Config::set('lock.allow', array_values($allowed_ip)); Rhymix\Framework\Config::set('lock.allow', array_values($allowed_ip));
Rhymix\Framework\Config::save(); if (!Rhymix\Framework\Config::save())
{
return new Object(-1, 'msg_failed_to_save_config');
}
$this->setMessage('success_updated'); $this->setMessage('success_updated');
$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigSitelock')); $this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigSitelock'));
@ -916,7 +1052,10 @@ class adminAdminController extends admin
Rhymix\Framework\Config::set('ftp.path', $vars->ftp_path); Rhymix\Framework\Config::set('ftp.path', $vars->ftp_path);
Rhymix\Framework\Config::set('ftp.pasv', $vars->ftp_pasv === 'Y'); Rhymix\Framework\Config::set('ftp.pasv', $vars->ftp_pasv === 'Y');
Rhymix\Framework\Config::set('ftp.sftp', $vars->ftp_sftp === 'Y'); Rhymix\Framework\Config::set('ftp.sftp', $vars->ftp_sftp === 'Y');
Rhymix\Framework\Config::save(); if (!Rhymix\Framework\Config::save())
{
return new Object(-1, 'msg_failed_to_save_config');
}
$this->setMessage('success_updated'); $this->setMessage('success_updated');
$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigFtp')); $this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdminConfigFtp'));
@ -934,7 +1073,11 @@ class adminAdminController extends admin
Rhymix\Framework\Config::set('ftp.path', null); Rhymix\Framework\Config::set('ftp.path', null);
Rhymix\Framework\Config::set('ftp.pasv', true); Rhymix\Framework\Config::set('ftp.pasv', true);
Rhymix\Framework\Config::set('ftp.sftp', false); Rhymix\Framework\Config::set('ftp.sftp', false);
Rhymix\Framework\Config::save(); if (!Rhymix\Framework\Config::save())
{
return new Object(-1, 'msg_failed_to_save_config');
}
$this->setMessage('success_deleted'); $this->setMessage('success_deleted');
} }

View file

@ -445,6 +445,42 @@ class adminAdminView extends admin
$this->setTemplateFile('config_general'); $this->setTemplateFile('config_general');
} }
/**
* Display Notification Settings page
* @return void
*/
function dispAdminConfigNotification()
{
// Load advanced mailer module (for lang).
$oAdvancedMailerAdminView = getAdminView('advanced_mailer');
// Load advanced mailer config.
$advanced_mailer_config = $oAdvancedMailerAdminView->getConfig();
Context::set('advanced_mailer_config', $advanced_mailer_config);
// Load member config.
$member_config = getModel('module')->getModuleConfig('member');
Context::set('member_config', $member_config);
Context::set('webmaster_name', $member_config->webmaster_name ? $member_config->webmaster_name : 'webmaster');
Context::set('webmaster_email', $member_config->webmaster_email);
// Load module config.
$module_config = getModel('module')->getModuleConfig('module');
Context::set('module_config', $module_config);
// Load mail drivers.
$mail_drivers = Rhymix\Framework\Mail::getSupportedDrivers();
Context::set('mail_drivers', $mail_drivers);
Context::set('mail_driver', config('mail.type') ?: 'mailfunction');
// Load SMS drivers.
$sms_drivers = Rhymix\Framework\SMS::getSupportedDrivers();
Context::set('sms_drivers', $sms_drivers);
Context::set('sms_driver', config('sms.type') ?: 'dummy');
$this->setTemplateFile('config_notification');
}
/** /**
* Display Security Settings page * Display Security Settings page
* @return void * @return void
@ -454,6 +490,7 @@ class adminAdminView extends admin
// Load embed filter. // Load embed filter.
context::set('mediafilter_iframe', implode(PHP_EOL, Rhymix\Framework\Filters\MediaFilter::getIframeWhitelist())); context::set('mediafilter_iframe', implode(PHP_EOL, Rhymix\Framework\Filters\MediaFilter::getIframeWhitelist()));
context::set('mediafilter_object', implode(PHP_EOL, Rhymix\Framework\Filters\MediaFilter::getObjectWhitelist())); context::set('mediafilter_object', implode(PHP_EOL, Rhymix\Framework\Filters\MediaFilter::getObjectWhitelist()));
context::set('mediafilter_classes', implode(PHP_EOL, Rhymix\Framework\Config::get('mediafilter.classes') ?: array()));
// Admin IP access control // Admin IP access control
$allowed_ip = Rhymix\Framework\Config::get('admin.allow'); $allowed_ip = Rhymix\Framework\Config::get('admin.allow');

View file

@ -5,6 +5,7 @@
<actions> <actions>
<action name="dispAdminIndex" type="view" index="true" /> <action name="dispAdminIndex" type="view" index="true" />
<action name="dispAdminConfigGeneral" type="view" menu_name="adminConfigurationGeneral" menu_index="true" /> <action name="dispAdminConfigGeneral" type="view" menu_name="adminConfigurationGeneral" menu_index="true" />
<action name="dispAdminConfigNotification" type="view" menu_name="adminConfigurationGeneral" />
<action name="dispAdminConfigSecurity" type="view" menu_name="adminConfigurationGeneral" /> <action name="dispAdminConfigSecurity" type="view" menu_name="adminConfigurationGeneral" />
<action name="dispAdminConfigAdvanced" type="view" menu_name="adminConfigurationGeneral" /> <action name="dispAdminConfigAdvanced" type="view" menu_name="adminConfigurationGeneral" />
<action name="dispAdminConfigDebug" type="view" menu_name="adminConfigurationGeneral" /> <action name="dispAdminConfigDebug" type="view" menu_name="adminConfigurationGeneral" />
@ -24,6 +25,7 @@
<action name="procAdminDeleteLogo" type="controller" /> <action name="procAdminDeleteLogo" type="controller" />
<action name="procAdminMenuReset" type="controller" /> <action name="procAdminMenuReset" type="controller" />
<action name="procAdminUpdateConfigGeneral" type="controller" /> <action name="procAdminUpdateConfigGeneral" type="controller" />
<action name="procAdminUpdateNotification" type="controller" />
<action name="procAdminUpdateSecurity" type="controller" /> <action name="procAdminUpdateSecurity" type="controller" />
<action name="procAdminUpdateAdvanced" type="controller" /> <action name="procAdminUpdateAdvanced" type="controller" />
<action name="procAdminUpdateDebug" type="controller" /> <action name="procAdminUpdateDebug" type="controller" />

View file

@ -2,6 +2,7 @@
$lang->admin = 'Admin'; $lang->admin = 'Admin';
$lang->cmd_configure = 'Configure'; $lang->cmd_configure = 'Configure';
$lang->subtitle_primary = 'General Settings'; $lang->subtitle_primary = 'General Settings';
$lang->subtitle_notification = 'Notification Settings';
$lang->subtitle_security = 'Security Settings'; $lang->subtitle_security = 'Security Settings';
$lang->subtitle_advanced = 'Advanced Settings'; $lang->subtitle_advanced = 'Advanced Settings';
$lang->subtitle_debug = 'Debug Settings'; $lang->subtitle_debug = 'Debug Settings';
@ -74,11 +75,28 @@ $lang->msg_blacklisted_reason['session_shield'] = 'The functionality that this a
$lang->msg_blacklisted_reason['smartphone'] = 'This module was disabled in XE long before Rhymix even existed.'; $lang->msg_blacklisted_reason['smartphone'] = 'This module was disabled in XE long before Rhymix even existed.';
$lang->msg_blacklisted_reason['zipperupper'] = 'Similar functionality can be configured in the <a href="./index.php?module=admin&act=dispAdminConfigAdvanced">Advanced Settings</a> page.'; $lang->msg_blacklisted_reason['zipperupper'] = 'Similar functionality can be configured in the <a href="./index.php?module=admin&act=dispAdminConfigAdvanced">Advanced Settings</a> page.';
$lang->msg_warning = 'Warning'; $lang->msg_warning = 'Warning';
$lang->msg_failed_to_save_config = 'Failed to save configuration. Please check permissions on files/config.';
$lang->welcome_to_xe = 'Welcome to the Rhymix admin page.'; $lang->welcome_to_xe = 'Welcome to the Rhymix admin page.';
$lang->about_lang_env = 'If you want to make the language setting same for first-time visitors, change the language setting to what you want and click [Save] button below.'; $lang->about_lang_env = 'If you want to make the language setting same for first-time visitors, change the language setting to what you want and click [Save] button below.';
$lang->xe_license = 'Rhymix complies with the GPL.'; $lang->xe_license = 'Rhymix complies with the GPL.';
$lang->yesterday = 'Yesterday'; $lang->yesterday = 'Yesterday';
$lang->today = 'Today'; $lang->today = 'Today';
$lang->cmd_admin_default_from_name = 'Default Sender Name';
$lang->cmd_admin_default_from_email = 'Default Sender E-mail';
$lang->cmd_admin_default_from_phone = 'Default Sender Number';
$lang->cmd_admin_default_reply_to = 'Reply-To';
$lang->cmd_admin_force_default_sender = 'Apply to All';
$lang->cmd_admin_sending_method = 'Sending Method';
$lang->cmd_admin_allow_split_sms = 'Split long SMS';
$lang->cmd_admin_allow_split_lms = 'Split long LMS/MMS';
$lang->cmd_admin_default_from_name_help = 'This name will be used in all outgoing emails, such as welcome e-mails, authentication e-mails, and other e-mail notifications.';
$lang->cmd_admin_default_from_email_help = 'This e-mail address will be used in all outgoing e-mails.<br>If you are using SMTP or an external API, please check &quot;Apply to All&quot; to avoid having your e-mails treated as spam.';
$lang->cmd_admin_default_reply_to_help = 'This e-mail will receive replies. Please leave it empty if it is the same as the address above.<br>Some sending methods may not support a separate Reply-To address.';
$lang->cmd_admin_default_from_phone_help = 'This number will be used in all outgoing SMS notifications.<br>Some SMS gateways may require that you pre-register your phone number. In that case, you should also check &quot;Apply to All&quot;.';
$lang->cmd_admin_sms_dummy_driver_help = 'Select &quot;Dummy&quot; if you are not going to send any SMS.';
$lang->cmd_admin_sms_sender_key_help = 'The sender key is used for Alimtalk. Please leave it empty if you do not use Alimtalk.';
$lang->cmd_admin_allow_split_sms_help = 'Split long texts into multiple SMS in order to prevent clipping.<br>This only applies to gateways that cannot send LMS/MMS. Otherwise, long texts will be automatically converted to LMS/MMS.';
$lang->cmd_admin_allow_split_lms_help = 'Split very long texts into multiple LMS/MMS in order to prevent clipping.<br>This may increase your LMS/MMS cost significantly.';
$lang->default_lang = 'Default Language'; $lang->default_lang = 'Default Language';
$lang->lang_select = 'Supported Languages'; $lang->lang_select = 'Supported Languages';
$lang->auto_select_lang = 'Auto-select Language'; $lang->auto_select_lang = 'Auto-select Language';
@ -198,8 +216,9 @@ $lang->tablets_as_mobile = 'Treat Tablets as Mobile';
$lang->thumbnail_type = 'Select thumbnail type.'; $lang->thumbnail_type = 'Select thumbnail type.';
$lang->input_footer_script = 'Footer script'; $lang->input_footer_script = 'Footer script';
$lang->detail_input_footer_script = 'The script is inserted into the bottom of body. It does not work at admin page.'; $lang->detail_input_footer_script = 'The script is inserted into the bottom of body. It does not work at admin page.';
$lang->corp = 'Crop (Cut)'; $lang->thumbnail_crop = 'Crop';
$lang->ratio = 'Ratio (Keep Aspect Ratio)'; $lang->thumbnail_ratio = 'Keep aspect ratio (may result in spaces)';
$lang->thumbnail_none = 'Do not create thumbnails';
$lang->admin_ip_allow = 'IP addresses allowed to log in as administrator'; $lang->admin_ip_allow = 'IP addresses allowed to log in as administrator';
$lang->admin_ip_deny = 'IP addresses forbidden to log in as administrator'; $lang->admin_ip_deny = 'IP addresses forbidden to log in as administrator';
$lang->local_ip_address = 'Local IP address'; $lang->local_ip_address = 'Local IP address';

View file

@ -125,8 +125,9 @@ $lang->about_use_mobile_view = 'モバイル機器で接続した際にモバイ
$lang->thumbnail_type = 'サムネイルの作成方式を選択してください。'; $lang->thumbnail_type = 'サムネイルの作成方式を選択してください。';
$lang->input_footer_script = '下段footerスクリプト'; $lang->input_footer_script = '下段footerスクリプト';
$lang->detail_input_footer_script = '最下段にコードを追加します。管理者ページでは遂行できません。'; $lang->detail_input_footer_script = '最下段にコードを追加します。管理者ページでは遂行できません。';
$lang->corp = '切り取り'; $lang->thumbnail_crop = 'トリミングする';
$lang->ratio = 'Ratio(縦横の比率をキープ)'; $lang->thumbnail_ratio = '比率に合わせる';
$lang->thumbnail_none = 'サムネイルを作成しない';
$lang->admin_ip_allow = '管理者ログイン許容IP'; $lang->admin_ip_allow = '管理者ログイン許容IP';
$lang->admin_ip_deny = '管理者ログイン禁止IP'; $lang->admin_ip_deny = '管理者ログイン禁止IP';
$lang->local_ip_address = 'ローカルIPアドレス'; $lang->local_ip_address = 'ローカルIPアドレス';

View file

@ -2,6 +2,7 @@
$lang->admin = '관리자'; $lang->admin = '관리자';
$lang->cmd_configure = '설정하기'; $lang->cmd_configure = '설정하기';
$lang->subtitle_primary = '기본 설정'; $lang->subtitle_primary = '기본 설정';
$lang->subtitle_notification = '알림 설정';
$lang->subtitle_security = '보안 설정'; $lang->subtitle_security = '보안 설정';
$lang->subtitle_advanced = '고급 설정'; $lang->subtitle_advanced = '고급 설정';
$lang->subtitle_debug = '디버그 설정'; $lang->subtitle_debug = '디버그 설정';
@ -74,11 +75,28 @@ $lang->msg_blacklisted_reason['session_shield'] = '이 애드온에서 제공하
$lang->msg_blacklisted_reason['smartphone'] = '이 모듈은 XE에서도 사용되지 않고 있었습니다.'; $lang->msg_blacklisted_reason['smartphone'] = '이 모듈은 XE에서도 사용되지 않고 있었습니다.';
$lang->msg_blacklisted_reason['zipperupper'] = '이 애드온에서 제공하던 기능은 <a href="./index.php?module=admin&act=dispAdminConfigAdvanced">고급 설정</a> 페이지에서 관리할 수 있습니다.'; $lang->msg_blacklisted_reason['zipperupper'] = '이 애드온에서 제공하던 기능은 <a href="./index.php?module=admin&act=dispAdminConfigAdvanced">고급 설정</a> 페이지에서 관리할 수 있습니다.';
$lang->msg_warning = '경고'; $lang->msg_warning = '경고';
$lang->msg_failed_to_save_config = '설정을 저장할 수 없습니다. files/config 폴더 및 설정파일들의 퍼미션을 확인해 주시기 바랍니다.';
$lang->welcome_to_xe = 'Rhymix 관리자'; $lang->welcome_to_xe = 'Rhymix 관리자';
$lang->about_lang_env = '처음 방문하는 사용자들의 언어 설정을 동일하게 하려면, 원하는 언어로 변경 후 아래 [저장] 버튼을 클릭하면 됩니다.'; $lang->about_lang_env = '처음 방문하는 사용자들의 언어 설정을 동일하게 하려면, 원하는 언어로 변경 후 아래 [저장] 버튼을 클릭하면 됩니다.';
$lang->xe_license = 'Rhymix는 GPL을 따릅니다.'; $lang->xe_license = 'Rhymix는 GPL을 따릅니다.';
$lang->yesterday = '어제'; $lang->yesterday = '어제';
$lang->today = '오늘'; $lang->today = '오늘';
$lang->cmd_admin_default_from_name = '기본 발신자 이름';
$lang->cmd_admin_default_from_email = '기본 발신자 주소';
$lang->cmd_admin_default_from_phone = '기본 발신자 번호';
$lang->cmd_admin_default_reply_to = 'Reply-To 주소';
$lang->cmd_admin_force_default_sender = '일괄 적용';
$lang->cmd_admin_sending_method = '발송 방법';
$lang->cmd_admin_allow_split_sms = 'SMS 분할 발송';
$lang->cmd_admin_allow_split_lms = 'LMS/MMS 분할 발송';
$lang->cmd_admin_default_from_name_help = '가입환영 메일, 인증메일, 알림 등을 발송할 때 사용할 이름입니다. 사이트 이름 사용을 권장합니다.';
$lang->cmd_admin_default_from_email_help = '가입환영 메일, 인증메일, 알림 등을 발송할 때 사용할 메일 주소입니다.<br>SMTP 또는 외부 API 사용시 일괄 적용 옵션을 선택하지 않으면 스팸으로 취급될 가능성이 높아지니 주의하십시오.';
$lang->cmd_admin_default_reply_to_help = '받는이가 &quot;답장&quot;을 클릭했을 때 발신자 주소가 아닌 다른 주소로 답장을 받을 수 있습니다.<br>발송 방법에 따라 이 기능을 지원하지 않을 수도 있습니다.';
$lang->cmd_admin_default_from_phone_help = 'SMS 알림을 발송할 때 사용할 번호입니다.<br>국내 서비스 사용시 API 제공 업체에 발신자 번호를 미리 등록하고, 일괄 적용 옵션을 사용하시기 바랍니다.';
$lang->cmd_admin_sms_dummy_driver_help = 'SMS를 사용하지 않는 경우 Dummy를 선택하십시오.';
$lang->cmd_admin_sms_sender_key_help = '알림톡 발송에 필요한 Sender Key입니다. 알림톡을 사용하지 않는 경우 비워 두시기 바랍니다.';
$lang->cmd_admin_allow_split_sms_help = '긴 내용을 SMS로 발송할 때 잘리지 않도록 2개 이상의 SMS로 분할 발송합니다.<br>LMS/MMS를 사용할 수 있는 경우 자동 변경되므로, LMS/MMS 사용이 불가능한 서비스에만 해당됩니다.';
$lang->cmd_admin_allow_split_lms_help = '매우 긴 내용을 LMS 또는 MMS로 발송할 때 잘리지 않도록 2개 이상의 LMS 또는 MMS로 분할 발송합니다.<br>내용이 지나치게 긴 경우 비용이 많이 발생할 수 있으니 주의하십시오.';
$lang->default_lang = '기본 언어 선택'; $lang->default_lang = '기본 언어 선택';
$lang->lang_select = '지원 언어 선택'; $lang->lang_select = '지원 언어 선택';
$lang->auto_select_lang = '언어 자동 선택'; $lang->auto_select_lang = '언어 자동 선택';
@ -193,8 +211,9 @@ $lang->tablets_as_mobile = '태블릿도 모바일 취급';
$lang->thumbnail_type = '썸네일 생성 방식'; $lang->thumbnail_type = '썸네일 생성 방식';
$lang->input_footer_script = '하단(footer) 스크립트'; $lang->input_footer_script = '하단(footer) 스크립트';
$lang->detail_input_footer_script = '최하단에 코드를 삽입합니다. 관리자 페이지에서는 수행되지 않습니다.'; $lang->detail_input_footer_script = '최하단에 코드를 삽입합니다. 관리자 페이지에서는 수행되지 않습니다.';
$lang->corp = 'Crop (잘라내기)'; $lang->thumbnail_crop = '크기에 맞추어 잘라내기';
$lang->ratio = 'Ratio (비율 맞추기)'; $lang->thumbnail_ratio = '비율 유지 (여백이 생길 수 있음)';
$lang->thumbnail_none = '썸네일 생성하지 않음';
$lang->admin_ip_allow = '관리자 로그인 허용 IP'; $lang->admin_ip_allow = '관리자 로그인 허용 IP';
$lang->admin_ip_deny = '관리자 로그인 금지 IP'; $lang->admin_ip_deny = '관리자 로그인 금지 IP';
$lang->local_ip_address = '로컬 IP 주소'; $lang->local_ip_address = '로컬 IP 주소';

View file

@ -11,7 +11,7 @@
<button type="button" class="x_btn-link" onclick="doResetAdminMenu();">{$lang->cmd_admin_menu_reset}</button> <span class="vr">|</span> <button type="button" class="x_btn-link" onclick="doResetAdminMenu();">{$lang->cmd_admin_menu_reset}</button> <span class="vr">|</span>
<button type="button" class="x_btn-link" onclick="doRecompileCacheFile();">{$lang->cmd_remake_cache}</button> <span class="vr">|</span> <button type="button" class="x_btn-link" onclick="doRecompileCacheFile();">{$lang->cmd_remake_cache}</button> <span class="vr">|</span>
<button type="button" class="x_btn-link" onclick="doClearSession();">{$lang->cmd_clear_session}</button> <span class="vr">|</span> <button type="button" class="x_btn-link" onclick="doClearSession();">{$lang->cmd_clear_session}</button> <span class="vr">|</span>
<a href="./index.php?module=admin&act=dispAdminViewServerEnv">{$lang->cmd_view_server_env}</a> <span class="vr">|</span> <a href="./index.php?module=admin&act=dispAdminViewServerEnv" style="vertical-align:middle">{$lang->cmd_view_server_env}</a> <span class="vr">|</span>
<a href="https://github.com/rhymix/rhymix/issues" target="_blank" style="vertical-align:middle">{$lang->bug_report}</a> <a href="https://github.com/rhymix/rhymix/issues" target="_blank" style="vertical-align:middle">{$lang->bug_report}</a>
</p> </p>
</footer> </footer>

View file

@ -62,12 +62,16 @@
<label class="x_control-label">{$lang->thumbnail_type}</label> <label class="x_control-label">{$lang->thumbnail_type}</label>
<div class="x_controls"> <div class="x_controls">
<label for="thumbnail_type_crop" class="x_inline"> <label for="thumbnail_type_crop" class="x_inline">
<input type="radio" name="thumbnail_type" id="thumbnail_type_crop" value="crop" checked="checked"|cond="$thumbnail_type != 'ratio'" /> <input type="radio" name="thumbnail_type" id="thumbnail_type_crop" value="crop" checked="checked"|cond="$thumbnail_type == 'crop' || !$thumbnail_type" />
{$lang->corp} {$lang->thumbnail_crop}
</label> </label>
<label for="thumbnail_type_ratio" class="x_inline"> <label for="thumbnail_type_ratio" class="x_inline">
<input type="radio" name="thumbnail_type" id="thumbnail_type_ratio" value="ratio" checked="checked"|cond="$thumbnail_type == 'ratio'" /> <input type="radio" name="thumbnail_type" id="thumbnail_type_ratio" value="ratio" checked="checked"|cond="$thumbnail_type == 'ratio'" />
{$lang->ratio} {$lang->thumbnail_ratio}
</label>
<label for="thumbnail_type_none" class="x_inline">
<input type="radio" name="thumbnail_type" id="thumbnail_type_none" value="none" checked="checked"|cond="$thumbnail_type == 'none'" />
{$lang->thumbnail_none}
</label> </label>
</div> </div>
</div> </div>

View file

@ -5,6 +5,7 @@
</div> </div>
<ul class="x_nav x_nav-tabs"> <ul class="x_nav x_nav-tabs">
<li class="x_active"|cond="$act == 'dispAdminConfigGeneral'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigGeneral')}">{$lang->subtitle_primary}</a></li> <li class="x_active"|cond="$act == 'dispAdminConfigGeneral'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigGeneral')}">{$lang->subtitle_primary}</a></li>
<li class="x_active"|cond="$act == 'dispAdminConfigNotification'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigNotification')}">{$lang->subtitle_notification}</a></li>
<li class="x_active"|cond="$act == 'dispAdminConfigSecurity'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigSecurity')}">{$lang->subtitle_security}</a></li> <li class="x_active"|cond="$act == 'dispAdminConfigSecurity'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigSecurity')}">{$lang->subtitle_security}</a></li>
<li class="x_active"|cond="$act == 'dispAdminConfigAdvanced'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigAdvanced')}">{$lang->subtitle_advanced}</a></li> <li class="x_active"|cond="$act == 'dispAdminConfigAdvanced'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigAdvanced')}">{$lang->subtitle_advanced}</a></li>
<li class="x_active"|cond="$act == 'dispAdminConfigDebug'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigDebug')}">{$lang->subtitle_debug}</a></li> <li class="x_active"|cond="$act == 'dispAdminConfigDebug'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdminConfigDebug')}">{$lang->subtitle_debug}</a></li>

View file

@ -0,0 +1,296 @@
<include target="config_header.html" />
<load target="js/notification_config.js" />
<div cond="$XE_VALIDATOR_MESSAGE && $XE_VALIDATOR_ID == 'modules/admin/tpl/config_notification/1'" class="message {$XE_VALIDATOR_MESSAGE_TYPE}">
<p>{$XE_VALIDATOR_MESSAGE}</p>
</div>
<script type="text/javascript">
var mail_drivers = {json_encode($mail_drivers)};
var sms_drivers = {json_encode($sms_drivers)};
</script>
<form action="./" method="post" class="x_form-horizontal">
<input type="hidden" name="module" value="admin" />
<input type="hidden" name="act" value="procAdminUpdateNotification" />
<input type="hidden" name="xe_validator_id" value="modules/admin/tpl/config_notification/1" />
<section class="section">
<h2>{$lang->email}</h2>
<div class="x_control-group">
<label class="x_control-label" for="mail_default_name">{$lang->cmd_admin_default_from_name}</label>
<div class="x_controls">
<input type="text" name="mail_default_name" id="mail_default_name" value="{escape($member_config->webmaster_name) ?: $module_config->siteTitle}" />
<br />
<p class="x_help-block">{$lang->cmd_admin_default_from_name_help}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="mail_default_from">{$lang->cmd_admin_default_from_email}</label>
<div class="x_controls">
<input type="text" name="mail_default_from" id="mail_default_from" value="{escape($member_config->webmaster_email)}" />
&nbsp;
<label for="mail_force_default_sender" class="x_inline">
<input type="checkbox" name="mail_force_default_sender" id="mail_force_default_sender" value="Y" checked="checked"|cond="toBool($advanced_mailer_config->force_sender)" />
{$lang->cmd_admin_force_default_sender}
</label>
<br />
<p class="x_help-block">{$lang->cmd_admin_default_from_email_help}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="mail_default_reply_to">{$lang->cmd_admin_default_reply_to}</label>
<div class="x_controls">
<input type="text" name="mail_default_reply_to" id="mail_default_reply_to" value="{escape($advanced_mailer_config->reply_to ?: config('mail.default_reply_to'))}" />
<br />
<p class="x_help-block">{$lang->cmd_admin_default_reply_to_help}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="mail_driver">{$lang->cmd_admin_sending_method}</label>
<div class="x_controls">
<select name="mail_driver" id="mail_driver">
<!--@foreach($mail_drivers as $driver_name => $driver_definition)-->
<option value="{$driver_name}" selected="selected"|cond="$mail_driver === $driver_name">{$driver_definition['name']}</option>
<!--@end-->
</select>
<p class="x_help-block hidden-by-default show-for-dummy">
{$lang->msg_advanced_mailer_about_dummy}<br />{$lang->msg_advanced_mailer_about_dummy_exceptions}
</p>
</div>
</div>
<!--@foreach($mail_drivers as $driver_name => $driver_definition)-->
<!--@foreach($driver_definition['required'] as $conf_name)-->
{@ $conf_value = escape(config("mail.$driver_name.$conf_name"))}
<!--@if($conf_name === 'smtp_host')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_{$driver_name}_smtp_host">{$lang->cmd_advanced_mailer_smtp_host}</label>
<div class="x_controls">
<input type="text" name="mail_{$driver_name}_smtp_host" id="mail_{$driver_name}_smtp_host" value="{$conf_value}" />
<select id="mail_{$driver_name}_manual_entry">
<option value="">{$lang->cmd_advanced_mailer_smtp_manual_entry}</option>
<option value="gmail">Gmail</option>
<option value="hanmail">Hanmail</option>
<option value="naver">Naver</option>
<option value="worksmobile">Works Mobile</option>
<option value="outlook">Outlook.com</option>
<option value="yahoo">Yahoo</option>
</select>
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'smtp_port')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_{$driver_name}_smtp_port">{$lang->cmd_advanced_mailer_smtp_port}</label>
<div class="x_controls">
<input type="text" name="mail_{$driver_name}_smtp_port" id="mail_{$driver_name}_smtp_port" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'smtp_security')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label">{$lang->cmd_advanced_mailer_smtp_security}</label>
<div class="x_controls">
<label class="x_inline" for="mail_{$driver_name}_security_none"><input type="radio" name="mail_{$driver_name}_smtp_security" id="mail_{$driver_name}_security_none" value="none" checked="checked"|cond="!in_array($conf_value, array('ssl', 'tls'))" /> {$lang->cmd_advanced_mailer_smtp_security_none}</label>
<label class="x_inline" for="mail_{$driver_name}_security_ssl"><input type="radio" name="mail_{$driver_name}_smtp_security" id="mail_{$driver_name}_security_ssl" value="ssl" checked="checked"|cond="$conf_value === 'ssl'" /> {$lang->cmd_advanced_mailer_smtp_security_ssl}</label>
<label class="x_inline" for="mail_{$driver_name}_security_tls"><input type="radio" name="mail_{$driver_name}_smtp_security" id="mail_{$driver_name}_security_tls" value="tls" checked="checked"|cond="$conf_value === 'tls'" /> {$lang->cmd_advanced_mailer_smtp_security_tls}</label>
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'smtp_user')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_{$driver_name}_smtp_user">{$lang->cmd_advanced_mailer_smtp_user}</label>
<div class="x_controls">
<input type="text" name="mail_{$driver_name}_smtp_user" id="mail_{$driver_name}_smtp_user" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'smtp_pass')-->
<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}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_type')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_{$driver_name}_api_type">{$lang->cmd_advanced_mailer_api_type}</label>
<div class="x_controls">
<select id="mail_{$driver_name}_api_type" name="mail_{$driver_name}_api_type">
<!--@foreach($driver_definition['api_types'] as $api_type)-->
<option value="{$api_type}" selected="selected"|cond="$api_type === $conf_value">{$api_type}</option>
<!--@end-->
</select>
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_domain')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_{$driver_name}_api_domain">{$lang->cmd_advanced_mailer_api_domain}</label>
<div class="x_controls">
<input type="text" name="mail_{$driver_name}_api_domain" id="mail_{$driver_name}_api_domain" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_token')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_{$driver_name}_api_token">{$lang->cmd_advanced_mailer_api_token}</label>
<div class="x_controls full-width">
<input type="text" name="mail_{$driver_name}_api_token" id="mail_{$driver_name}_api_token" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_user')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="mail_{$driver_name}_api_user">{$lang->cmd_advanced_mailer_api_user}</label>
<div class="x_controls">
<input type="text" name="mail_{$driver_name}_api_user" id="mail_{$driver_name}_api_user" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_pass')-->
<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}" />
</div>
</div>
<!--@end-->
<!--@end-->
<!--@end-->
</section>
<section class="section">
<h2>{$lang->sms}</h2>
<div class="x_control-group">
<label class="x_control-label" for="sms_default_from">{$lang->cmd_admin_default_from_phone}</label>
<div class="x_controls">
<input type="text" name="sms_default_from" id="sms_default_from" value="{escape(config('sms.default_from'))}" />
&nbsp;
<label for="sms_force_default_sender" class="x_inline">
<input type="checkbox" name="sms_force_default_sender" id="sms_force_default_sender" value="Y" checked="checked"|cond="config('sms.default_force') !== false" />
{$lang->cmd_admin_force_default_sender}
</label>
<br />
<p class="x_help-block">{$lang->cmd_admin_default_from_phone_help}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="sms_driver">{$lang->cmd_admin_sending_method}</label>
<div class="x_controls">
<select name="sms_driver" id="sms_driver">
<!--@foreach($sms_drivers as $driver_name => $driver_definition)-->
<option value="{$driver_name}" selected="selected"|cond="$sms_driver === $driver_name">{$driver_definition['name']}</option>
<!--@end-->
</select>
<p class="x_help-block hidden-by-default show-for-dummy">
{$lang->cmd_admin_sms_dummy_driver_help}
</p>
</div>
</div>
<!--@foreach($sms_drivers as $driver_name => $driver_definition)-->
{@ $conf_names = array_merge($driver_definition['required'], $driver_definition['optional'])}
<!--@foreach($conf_names as $conf_name)-->
{@ $conf_value = escape(config("sms.$driver_name.$conf_name"))}
<!--@if($conf_name === 'api_key')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="sms_{$driver_name}_api_key">{$lang->cmd_advanced_mailer_api_key}</label>
<div class="x_controls">
<input type="text" name="sms_{$driver_name}_api_key" id="sms_{$driver_name}_api_key" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_secret')-->
<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}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'sender_key')-->
<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="text" name="sms_{$driver_name}_sender_key" id="sms_{$driver_name}_sender_key" value="{$conf_value}" />
<br />
<p class="x_help-block">{$lang->cmd_admin_sms_sender_key_help}</p>
</div>
</div>
<!--@end-->
<!--@end-->
<!--@end-->
<div class="x_control-group">
<label class="x_control-label">{$lang->cmd_admin_allow_split_sms}</label>
<div class="x_controls">
<label for="allow_split_sms_y" class="x_inline">
<input type="radio" name="allow_split_sms" id="allow_split_sms_y" value="Y" checked="checked"|cond="config('sms.allow_split.sms') !== false" />
{$lang->cmd_yes}
</label>
<label for="allow_split_sms_n" class="x_inline">
<input type="radio" name="allow_split_sms" id="allow_split_sms_n" value="N" checked="checked"|cond="config('sms.allow_split.sms') === false" />
{$lang->cmd_no}
</label>
<br />
<p class="x_help-block">{$lang->cmd_admin_allow_split_sms_help}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label">{$lang->cmd_admin_allow_split_lms}</label>
<div class="x_controls">
<label for="allow_split_lms_y" class="x_inline">
<input type="radio" name="allow_split_lms" id="allow_split_lms_y" value="Y" checked="checked"|cond="config('sms.allow_split.lms') !== false" />
{$lang->cmd_yes}
</label>
<label for="allow_split_lms_n" class="x_inline">
<input type="radio" name="allow_split_lms" id="allow_split_lms_n" value="N" checked="checked"|cond="config('sms.allow_split.lms') === false" />
{$lang->cmd_no}
</label>
<br />
<p class="x_help-block">{$lang->cmd_admin_allow_split_lms_help}</p>
</div>
</div>
</section>
<div class="x_clearfix btnArea">
<div class="x_pull-right">
<button type="submit" class="x_btn x_btn-primary">{$lang->cmd_save}</button>
</div>
</div>
</form>

View file

@ -19,6 +19,12 @@
<textarea name="mediafilter_object" id="mediafilter_object" rows="8" style="width:100%;">{$mediafilter_object}</textarea> <textarea name="mediafilter_object" id="mediafilter_object" rows="8" style="width:100%;">{$mediafilter_object}</textarea>
</div> </div>
</div> </div>
<div class="x_control-group">
<label class="x_control-label" for="mediafilter_classes">HTML class</label>
<div class="x_controls" style="margin-right:14px">
<textarea name="mediafilter_classes" id="mediafilter_classes" rows="4" style="width:100%;">{$mediafilter_classes}</textarea>
</div>
</div>
<div class="x_control-group"> <div class="x_control-group">
<label class="x_control-label" for="admin_allowed_ip">{$lang->admin_ip_allow}</label> <label class="x_control-label" for="admin_allowed_ip">{$lang->admin_ip_allow}</label>
<div class="x_controls"> <div class="x_controls">

View file

@ -0,0 +1,82 @@
(function($) {
$(function() {
$("#mail_driver").on("change", function() {
var selected_driver = $(this).val();
$(this).parents("section").find("div.x_control-group.hidden-by-default, p.x_help-block.hidden-by-default").each(function() {
if ($(this).hasClass("show-for-" + selected_driver)) {
$(this).show();
} else {
$(this).hide();
}
});
}).triggerHandler("change");
$("#sms_driver").on("change", function() {
var selected_driver = $(this).val();
$(this).parents("section").find("div.x_control-group.hidden-by-default, p.x_help-block.hidden-by-default").each(function() {
if ($(this).hasClass("show-for-" + selected_driver)) {
$(this).show();
} else {
$(this).hide();
}
});
}).triggerHandler("change");
$("#mail_smtp_manual_entry").on("change", function() {
var auto_fill = $(this).val();
if (auto_fill === 'gmail') {
$("#mail_smtp_smtp_host").val('smtp.gmail.com');
$("#mail_smtp_smtp_port").val('465');
$("#mail_smtp_security_ssl").prop("checked", true).parent().addClass("checked");
$("#mail_smtp_security_tls").parent().removeClass("checked");
$("#mail_smtp_security_none").parent().removeClass("checked");
$("#mail_force_default_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'hanmail') {
$("#mail_smtp_smtp_host").val('smtp.daum.net');
$("#mail_smtp_smtp_port").val('465');
$("#mail_smtp_security_ssl").prop("checked", true).parent().addClass("checked");
$("#mail_smtp_security_tls").parent().removeClass("checked");
$("#mail_smtp_security_none").parent().removeClass("checked");
$("#mail_force_default_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'naver') {
$("#mail_smtp_smtp_host").val('smtp.naver.com');
$("#mail_smtp_smtp_port").val('587');
$("#mail_smtp_security_tls").prop("checked", true).parent().addClass("checked");
$("#mail_smtp_security_ssl").parent().removeClass("checked");
$("#mail_smtp_security_none").parent().removeClass("checked");
$("#mail_force_default_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'worksmobile') {
$("#mail_smtp_smtp_host").val('smtp.worksmobile.com');
$("#mail_smtp_smtp_port").val('587');
$("#mail_smtp_security_tls").prop("checked", true).parent().addClass("checked");
$("#mail_smtp_security_ssl").parent().removeClass("checked");
$("#mail_smtp_security_none").parent().removeClass("checked");
$("#mail_force_default_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'outlook') {
$("#mail_smtp_smtp_host").val('smtp-mail.outlook.com');
$("#mail_smtp_smtp_port").val('587');
$("#mail_smtp_security_tls").prop("checked", true).parent().addClass("checked");
$("#mail_smtp_security_ssl").parent().removeClass("checked");
$("#mail_smtp_security_none").parent().removeClass("checked");
$("#mail_force_default_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'yahoo') {
$("#mail_smtp_smtp_host").val('smtp.mail.yahoo.com');
$("#mail_smtp_smtp_port").val('465');
$("#mail_smtp_security_ssl").prop("checked", true).parent().addClass("checked");
$("#mail_smtp_security_tls").parent().removeClass("checked");
$("#mail_smtp_security_none").parent().removeClass("checked");
$("#mail_force_default_sender").prop("checked", true).parent().addClass("checked");
}
});
});
} (jQuery));

View file

@ -15,51 +15,13 @@ class Advanced_MailerAdminController extends Advanced_Mailer
{ {
// Get and validate the new configuration. // Get and validate the new configuration.
$vars = Context::getRequestVars(); $vars = Context::getRequestVars();
if (!$vars->sender_name)
{
return new Object(-1, 'msg_advanced_mailer_sender_name_is_empty');
}
if (!$vars->sender_email)
{
return new Object(-1, 'msg_advanced_mailer_sender_email_is_empty');
}
if (!Mail::isVaildMailAddress($vars->sender_email))
{
return new Object(-1, 'msg_advanced_mailer_sender_email_is_invalid');
}
if ($vars->reply_to && !Mail::isVaildMailAddress($vars->reply_to))
{
return new Object(-1, 'msg_advanced_mailer_reply_to_is_invalid');
}
// Validate the sending method.
$sending_methods = Rhymix\Framework\Mail::getSupportedDrivers();
$sending_method = $vars->sending_method;
if (!array_key_exists($sending_method, $sending_methods))
{
return new Object(-1, 'msg_advanced_mailer_sending_method_is_invalid');
}
// Validate the configuration for the selected sending method.
$sending_method_config = array();
foreach ($sending_methods[$sending_method]['required'] as $conf_name)
{
$conf_value = $vars->{$sending_method . '_' . $conf_name} ?: null;
if (!$conf_value)
{
return new Object(-1, 'msg_advanced_mailer_smtp_host_is_invalid');
}
$sending_method_config[$conf_name] = $conf_value;
}
// Update the current module's configuration. // Update the current module's configuration.
$config = $this->getConfig(); $config = $this->getConfig();
$config->sender_name = $vars->sender_name;
$config->sender_email = $vars->sender_email;
$config->reply_to = $vars->reply_to;
$config->force_sender = toBool($vars->force_sender);
$config->log_sent_mail = toBool($vars->log_sent_mail); $config->log_sent_mail = toBool($vars->log_sent_mail);
$config->log_errors = toBool($vars->log_errors); $config->log_errors = toBool($vars->log_errors);
$config->log_sent_sms = toBool($vars->log_sent_sms);
$config->log_sms_errors = toBool($vars->log_sms_errors);
$output = getController('module')->insertModuleConfig('advanced_mailer', $config); $output = getController('module')->insertModuleConfig('advanced_mailer', $config);
if ($output->toBool()) if ($output->toBool())
{ {
@ -70,17 +32,6 @@ class Advanced_MailerAdminController extends Advanced_Mailer
return $output; return $output;
} }
// Update the webmaster's name and email in the member module.
getController('module')->updateModuleConfig('member', (object)array(
'webmaster_name' => $config->sender_name,
'webmaster_email' => $config->sender_email,
));
// Update system configuration.
Rhymix\Framework\Config::set("mail.type", $sending_method);
Rhymix\Framework\Config::set("mail.$sending_method", $sending_method_config);
Rhymix\Framework\Config::save();
if (Context::get('success_return_url')) if (Context::get('success_return_url'))
{ {
$this->setRedirectUrl(Context::get('success_return_url')); $this->setRedirectUrl(Context::get('success_return_url'));
@ -198,7 +149,7 @@ class Advanced_MailerAdminController extends Advanced_Mailer
} }
/** /**
* Clear old sending log. * Clear old mail sending log.
*/ */
public function procAdvanced_mailerAdminClearSentMail() public function procAdvanced_mailerAdminClearSentMail()
{ {
@ -216,22 +167,53 @@ class Advanced_MailerAdminController extends Advanced_Mailer
$obj = new stdClass(); $obj = new stdClass();
$obj->status = $status; $obj->status = $status;
$obj->regdate = date('YmdHis', time() - ($clear_before_days * 86400) + zgap()); $obj->regdate = date('YmdHis', time() - ($clear_before_days * 86400) + zgap());
$output = executeQuery('advanced_mailer.deleteLogs', $obj); $output = executeQuery('advanced_mailer.deleteMailLogs', $obj);
if ($status === 'success') if ($status === 'success')
{ {
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSentMail')); $this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminMailLog'));
} }
else else
{ {
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminErrors')); $this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminMailErrors'));
} }
} }
/** /**
* Send a test email using a temporary configuration. * Clear old SMS sending log.
*/ */
public function procAdvanced_MailerAdminTestSend() public function procAdvanced_mailerAdminClearSentSMS()
{
$status = Context::get('status');
$clear_before_days = intval(Context::get('clear_before_days'));
if (!in_array($status, array('success', 'error')))
{
return new Object(-1, 'msg_invalid_request');
}
if ($clear_before_days < 0)
{
return new Object(-1, 'msg_invalid_request');
}
$obj = new stdClass();
$obj->status = $status;
$obj->regdate = date('YmdHis', time() - ($clear_before_days * 86400) + zgap());
$output = executeQuery('advanced_mailer.deleteSMSLogs', $obj);
if ($status === 'success')
{
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSMSLog'));
}
else
{
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSMSErrors'));
}
}
/**
* Send a test mail.
*/
public function procAdvanced_MailerAdminTestSendMail()
{ {
$advanced_mailer_config = $this->getConfig(); $advanced_mailer_config = $this->getConfig();
$recipient_config = Context::gets('recipient_name', 'recipient_email'); $recipient_config = Context::gets('recipient_name', 'recipient_email');
@ -268,23 +250,23 @@ class Advanced_MailerAdminController extends Advanced_Mailer
if (!$result) if (!$result)
{ {
if (count($oMail->errors)) if (count($oMail->getErrors()))
{ {
if (config('mail.type') === 'smtp') if (config('mail.type') === 'smtp')
{ {
if (strpos(config('mail.smtp.smtp_host'), 'gmail.com') !== false && strpos(implode("\n", $oMail->errors), 'code "535"') !== false) if (strpos(config('mail.smtp.smtp_host'), 'gmail.com') !== false && strpos(implode("\n", $oMail->getErrors()), 'code "535"') !== false)
{ {
$this->add('test_result', Context::getLang('msg_advanced_mailer_google_account_security')); $this->add('test_result', Context::getLang('msg_advanced_mailer_google_account_security'));
return; return;
} }
if (strpos(config('mail.smtp.smtp_host'), 'naver.com') !== false && strpos(implode("\n", $oMail->errors), 'Failed to authenticate') !== false) if (strpos(config('mail.smtp.smtp_host'), 'naver.com') !== false && strpos(implode("\n", $oMail->getErrors()), 'Failed to authenticate') !== false)
{ {
$this->add('test_result', Context::getLang('msg_advanced_mailer_naver_smtp_disabled')); $this->add('test_result', Context::getLang('msg_advanced_mailer_naver_smtp_disabled'));
return; return;
} }
} }
$this->add('test_result', nl2br(htmlspecialchars(implode("\n", $oMail->errors)))); $this->add('test_result', nl2br(htmlspecialchars(implode("\n", $oMail->getErrors()))));
return; return;
} }
else else
@ -303,4 +285,56 @@ class Advanced_MailerAdminController extends Advanced_Mailer
$this->add('test_result', Context::getLang('msg_advanced_mailer_test_success')); $this->add('test_result', Context::getLang('msg_advanced_mailer_test_success'));
return; return;
} }
/**
* Send a test SMS.
*/
public function procAdvanced_MailerAdminTestSendSMS()
{
$advanced_mailer_config = $this->getConfig();
$recipient_number = Context::get('recipient_number');
$country_code = intval(Context::get('country_code'));
$content = trim(Context::get('content'));
if (!$recipient_number)
{
$this->add('test_result', 'Error: ' . Context::getLang('msg_advanced_mailer_recipient_number_is_empty'));
return;
}
if (!$content)
{
$this->add('test_result', 'Error: ' . Context::getLang('msg_advanced_mailer_content_is_empty'));
return;
}
try
{
$oSMS = new Rhymix\Framework\SMS();
$oSMS->addTo($recipient_number, $country_code);
$oSMS->setBody($content);
$result = $oSMS->send();
if (!$result)
{
if (count($oSMS->getErrors()))
{
$this->add('test_result', nl2br(htmlspecialchars(implode("\n", $oSMS->getErrors()))));
return;
}
else
{
$this->add('test_result', Context::getLang('msg_advanced_mailer_unknown_error'));
return;
}
}
}
catch (Exception $e)
{
$this->add('test_result', nl2br(htmlspecialchars($e->getMessage())));
return;
}
$this->add('test_result', Context::getLang('msg_advanced_mailer_test_success_sms'));
return;
}
} }

View file

@ -14,14 +14,7 @@ class Advanced_MailerAdminView extends Advanced_Mailer
public function dispAdvanced_MailerAdminConfig() public function dispAdvanced_MailerAdminConfig()
{ {
$advanced_mailer_config = $this->getConfig(); $advanced_mailer_config = $this->getConfig();
$member_config = getModel('module')->getModuleConfig('member');
$sending_methods = Rhymix\Framework\Mail::getSupportedDrivers();
Context::set('advanced_mailer_config', $advanced_mailer_config); Context::set('advanced_mailer_config', $advanced_mailer_config);
Context::set('sending_methods', $sending_methods);
Context::set('sending_method', config('mail.type'));
Context::set('webmaster_name', $member_config->webmaster_name ? $member_config->webmaster_name : 'webmaster');
Context::set('webmaster_email', $member_config->webmaster_email);
$this->setTemplatePath($this->module_path.'tpl'); $this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('config'); $this->setTemplateFile('config');
@ -117,9 +110,9 @@ class Advanced_MailerAdminView extends Advanced_Mailer
} }
/** /**
* Display the test send form. * Display the mail test form.
*/ */
public function dispAdvanced_MailerAdminTestConfig() public function dispAdvanced_MailerAdminMailTest()
{ {
$advanced_mailer_config = $this->getConfig(); $advanced_mailer_config = $this->getConfig();
$sending_methods = Rhymix\Framework\Mail::getSupportedDrivers(); $sending_methods = Rhymix\Framework\Mail::getSupportedDrivers();
@ -129,23 +122,23 @@ class Advanced_MailerAdminView extends Advanced_Mailer
Context::set('sending_method', config('mail.type')); Context::set('sending_method', config('mail.type'));
$this->setTemplatePath($this->module_path.'tpl'); $this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('test'); $this->setTemplateFile('mail_test');
} }
/** /**
* Display the sent mail log. * Display the mail log.
*/ */
public function dispAdvanced_MailerAdminSentMail() public function dispAdvanced_MailerAdminMailLog()
{ {
$obj = new stdClass(); $obj = new stdClass();
$obj->status = 'success'; $obj->status = 'success';
$obj->page = $page = Context::get('page') ?: 1; $obj->page = $page = Context::get('page') ?: 1;
$maillog = executeQuery('advanced_mailer.getLogByType', $obj); $maillog = executeQueryArray('advanced_mailer.getMailLogByType', $obj);
$maillog = $maillog->toBool() ? $this->procMailLog($maillog->data) : array(); $maillog = $maillog->toBool() ? $this->procMailLog($maillog->data) : array();
Context::set('advanced_mailer_log', $maillog); Context::set('advanced_mailer_log', $maillog);
Context::set('advanced_mailer_status', 'success'); Context::set('advanced_mailer_status', 'success');
$paging = $this->procPaging('success', $page); $paging = $this->procPaging('success', 'mail', $page);
Context::set('total_count', $paging->total_count); Context::set('total_count', $paging->total_count);
Context::set('total_page', $paging->total_page); Context::set('total_page', $paging->total_page);
Context::set('page', $paging->page); Context::set('page', $paging->page);
@ -155,23 +148,23 @@ class Advanced_MailerAdminView extends Advanced_Mailer
Context::set('sending_methods', $sending_methods); Context::set('sending_methods', $sending_methods);
$this->setTemplatePath($this->module_path.'tpl'); $this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('view_log'); $this->setTemplateFile('mail_log');
} }
/** /**
* Display the error log. * Display the mail error log.
*/ */
public function dispAdvanced_MailerAdminErrors() public function dispAdvanced_MailerAdminMailErrors()
{ {
$obj = new stdClass(); $obj = new stdClass();
$obj->status = 'error'; $obj->status = 'error';
$obj->page = $page = Context::get('page') ?: 1; $obj->page = $page = Context::get('page') ?: 1;
$maillog = executeQuery('advanced_mailer.getLogByType', $obj); $maillog = executeQueryArray('advanced_mailer.getMailLogByType', $obj);
$maillog = $maillog->toBool() ? $this->procMailLog($maillog->data) : array(); $maillog = $maillog->toBool() ? $this->procMailLog($maillog->data) : array();
Context::set('advanced_mailer_log', $maillog); Context::set('advanced_mailer_log', $maillog);
Context::set('advanced_mailer_status', 'error'); Context::set('advanced_mailer_status', 'error');
$paging = $this->procPaging('error', $page); $paging = $this->procPaging('error', 'mail', $page);
Context::set('total_count', $paging->total_count); Context::set('total_count', $paging->total_count);
Context::set('total_page', $paging->total_page); Context::set('total_page', $paging->total_page);
Context::set('page', $paging->page); Context::set('page', $paging->page);
@ -181,7 +174,75 @@ class Advanced_MailerAdminView extends Advanced_Mailer
Context::set('sending_methods', $sending_methods); Context::set('sending_methods', $sending_methods);
$this->setTemplatePath($this->module_path.'tpl'); $this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('view_log'); $this->setTemplateFile('mail_log');
}
/**
* Display the SMS test form.
*/
public function dispAdvanced_MailerAdminSMSTest()
{
$advanced_mailer_config = $this->getConfig();
$sending_methods = Rhymix\Framework\Mail::getSupportedDrivers();
Context::set('advanced_mailer_config', $advanced_mailer_config);
Context::set('sending_methods', $sending_methods);
Context::set('sending_method', config('mail.type'));
$this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('sms_test');
}
/**
* Display the SMS log.
*/
public function dispAdvanced_MailerAdminSMSLog()
{
$obj = new stdClass();
$obj->status = 'success';
$obj->page = $page = Context::get('page') ?: 1;
$smslog = executeQueryArray('advanced_mailer.getSMSLogByType', $obj);
$smslog = $smslog->toBool() ? $smslog->data : array();
Context::set('advanced_mailer_log', $smslog);
Context::set('advanced_mailer_status', 'success');
$paging = $this->procPaging('success', 'sms', $page);
Context::set('total_count', $paging->total_count);
Context::set('total_page', $paging->total_page);
Context::set('page', $paging->page);
Context::set('page_navigation', $paging->page_navigation);
$sending_methods = Rhymix\Framework\SMS::getSupportedDrivers();
Context::set('sending_methods', $sending_methods);
$this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('sms_log');
}
/**
* Display the SMS error log.
*/
public function dispAdvanced_MailerAdminSMSErrors()
{
$obj = new stdClass();
$obj->status = 'error';
$obj->page = $page = Context::get('page') ?: 1;
$smslog = executeQueryArray('advanced_mailer.getSMSLogByType', $obj);
$smslog = $smslog->toBool() ? $smslog->data : array();
Context::set('advanced_mailer_log', $smslog);
Context::set('advanced_mailer_status', 'error');
$paging = $this->procPaging('error', 'sms', $page);
Context::set('total_count', $paging->total_count);
Context::set('total_page', $paging->total_page);
Context::set('page', $paging->page);
Context::set('page_navigation', $paging->page_navigation);
$sending_methods = Rhymix\Framework\SMS::getSupportedDrivers();
Context::set('sending_methods', $sending_methods);
$this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('sms_log');
} }
/** /**
@ -226,11 +287,18 @@ class Advanced_MailerAdminView extends Advanced_Mailer
/** /**
* Process paging. * Process paging.
*/ */
public function procPaging($status, $page = 1) public function procPaging($status, $type, $page = 1)
{ {
$args = new stdClass; $args = new stdClass;
$args->status = $status; $args->status = $status;
$count = executeQuery('advanced_mailer.countLogByType', $args); if ($type === 'mail')
{
$count = executeQuery('advanced_mailer.countMailLogByType', $args);
}
else
{
$count = executeQuery('advanced_mailer.countSMSLogByType', $args);
}
$total_count = $count->data->count; $total_count = $count->data->count;
$total_page = max(1, ceil($total_count / 20)); $total_page = max(1, ceil($total_count / 20));

View file

@ -196,6 +196,10 @@ class Advanced_Mailer extends ModuleObject
{ {
$oModuleController->insertTrigger('mail.send', 'advanced_mailer', 'controller', 'triggerAfterMailSend', 'after'); $oModuleController->insertTrigger('mail.send', 'advanced_mailer', 'controller', 'triggerAfterMailSend', 'after');
} }
if (!$oModuleModel->getTrigger('sms.send', 'advanced_mailer', 'controller', 'triggerAfterSMSSend', 'after'))
{
$oModuleController->insertTrigger('sms.send', 'advanced_mailer', 'controller', 'triggerAfterSMSSend', 'after');
}
} }
/** /**
@ -225,6 +229,10 @@ class Advanced_Mailer extends ModuleObject
{ {
return true; return true;
} }
if (!$oModuleModel->getTrigger('sms.send', 'advanced_mailer', 'controller', 'triggerAfterSMSSend', 'after'))
{
return true;
}
return false; return false;
} }

View file

@ -58,7 +58,6 @@ class Advanced_MailerController extends Advanced_Mailer
if (toBool($config->log_sent_mail) || (toBool($config->log_errors) && count($mail->errors))) if (toBool($config->log_sent_mail) || (toBool($config->log_errors) && count($mail->errors)))
{ {
$obj = new \stdClass(); $obj = new \stdClass();
$obj->mail_srl = getNextSequence();
$obj->mail_from = ''; $obj->mail_from = '';
$obj->mail_to = ''; $obj->mail_to = '';
@ -99,9 +98,9 @@ class Advanced_MailerController extends Advanced_Mailer
$obj->subject = $mail->message->getSubject(); $obj->subject = $mail->message->getSubject();
$obj->calling_script = $mail->getCaller(); $obj->calling_script = $mail->getCaller();
$obj->sending_method = strtolower(class_basename($mail->driver)); $obj->sending_method = strtolower(class_basename($mail->driver));
$obj->status = !count($mail->errors) ? 'success' : 'error'; $obj->status = !count($mail->getErrors()) ? 'success' : 'error';
$obj->errors = count($mail->errors) ? implode("\n", $mail->errors) : null; $obj->errors = count($mail->getErrors()) ? implode("\n", $mail->getErrors()) : null;
$output = executeQuery('advanced_mailer.insertLog', $obj); $output = executeQuery('advanced_mailer.insertMailLog', $obj);
if (!$output->toBool()) if (!$output->toBool())
{ {
return $output; return $output;
@ -145,4 +144,41 @@ class Advanced_MailerController extends Advanced_Mailer
return null; return null;
} }
/**
* After SMS send trigger.
*/
public function triggerAfterSMSSend($sms)
{
$config = $this->getConfig();
if (toBool($config->log_sent_sms) || (toBool($config->log_sms_errors) && count($sms->errors)))
{
$obj = new \stdClass();
$obj->sms_from = $sms->getFrom();
$obj->sms_to = array();
foreach ($sms->getRecipientsWithCountry() as $to)
{
if ($to->country)
{
$obj->sms_to[] = '+' . $to->country . '.' . $to->number;
}
else
{
$obj->sms_to[] = $to->number;
}
}
$obj->sms_to = implode(', ', $obj->sms_to);
$obj->content = trim($sms->getSubject() . "\n" . $sms->getBody());
$obj->calling_script = $sms->getCaller();
$obj->sending_method = strtolower(class_basename($sms->driver));
$obj->status = !count($sms->getErrors()) ? 'success' : 'error';
$obj->errors = count($sms->getErrors()) ? implode("\n", $sms->getErrors()) : null;
$output = executeQuery('advanced_mailer.insertSMSLog', $obj);
if (!$output->toBool())
{
return $output;
}
}
}
} }

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module version="0.2"> <module version="0.2">
<title xml:lang="ko">고급 메일 발송 모듈</title> <title xml:lang="ko">고급 메일 발송 모듈 (메일 및 SMS 관리)</title>
<title xml:lang="en">Advanced Mailer</title> <title xml:lang="en">Advanced Mailer (with SMS)</title>
<description xml:lang="ko"> <description xml:lang="ko">
외부 SMTP 서버 또는 API를 사용하여 메일을 발송합니다. 라이믹스에서 발송하는 메일과 SMS를 기록하고 테스트하는 기능을 제공합니다.
</description> </description>
<description xml:lang="en"> <description xml:lang="en">
Send mail using an external SMTP server or API service. Log and test e-mails and SMS sent from Rhymix.
</description> </description>
<version>2.0.0</version> <version>2.1.0</version>
<date>2016-05-22</date> <date>2016-12-14</date>
<author email_address="kijin@kijinsung.com" link="https://github.com/kijin"> <author link="https://www.poesis.org">
<name xml:lang="ko">Kijin Sung</name> <name xml:lang="ko">포에시스</name>
<name xml:lang="en">Kijin Sung</name> <name xml:lang="en">POESIS</name>
</author> </author>
</module> </module>

View file

@ -6,14 +6,19 @@
<action name="dispAdvanced_mailerAdminConfig" type="view" admin_index="true" menu_name="advanced_mailer" /> <action name="dispAdvanced_mailerAdminConfig" type="view" admin_index="true" menu_name="advanced_mailer" />
<action name="dispAdvanced_mailerAdminExceptions" type="view" /> <action name="dispAdvanced_mailerAdminExceptions" type="view" />
<action name="dispAdvanced_mailerAdminSpfDkim" type="view" /> <action name="dispAdvanced_mailerAdminSpfDkim" type="view" />
<action name="dispAdvanced_mailerAdminTestConfig" type="view" /> <action name="dispAdvanced_mailerAdminMailTest" type="view" />
<action name="dispAdvanced_mailerAdminSentMail" type="view" /> <action name="dispAdvanced_mailerAdminMailLog" type="view" />
<action name="dispAdvanced_mailerAdminErrors" type="view" /> <action name="dispAdvanced_mailerAdminMailErrors" type="view" />
<action name="dispAdvanced_mailerAdminSMSTest" type="view" />
<action name="dispAdvanced_mailerAdminSMSLog" type="view" />
<action name="dispAdvanced_mailerAdminSMSErrors" type="view" />
<action name="procAdvanced_mailerAdminInsertConfig" type="controller" /> <action name="procAdvanced_mailerAdminInsertConfig" type="controller" />
<action name="procAdvanced_mailerAdminInsertExceptions" type="controller" /> <action name="procAdvanced_mailerAdminInsertExceptions" type="controller" />
<action name="procAdvanced_mailerAdminCheckDNSRecord" type="controller" /> <action name="procAdvanced_mailerAdminCheckDNSRecord" type="controller" />
<action name="procAdvanced_mailerAdminClearSentMail" type="controller" /> <action name="procAdvanced_mailerAdminClearSentMail" type="controller" />
<action name="procAdvanced_mailerAdminTestSend" type="controller" /> <action name="procAdvanced_mailerAdminClearSentSMS" type="controller" />
<action name="procAdvanced_mailerAdminTestSendMail" type="controller" />
<action name="procAdvanced_mailerAdminTestSendSMS" type="controller" />
</actions> </actions>
<menus> <menus>
<menu name="advanced_mailer" type="all"> <menu name="advanced_mailer" type="all">

View file

@ -1,18 +1,20 @@
<?php <?php
$lang->cmd_advanced_mailer = 'Advanced Mailer'; $lang->cmd_advanced_mailer = 'Advanced Mailer (with SMS)';
$lang->cmd_advanced_mailer_general_config = 'General settings'; $lang->cmd_advanced_mailer_general_config = 'General settings';
$lang->cmd_advanced_mailer_is_enabled = 'Enable module'; $lang->cmd_advanced_mailer_is_enabled = 'Enable module';
$lang->cmd_advanced_mailer_is_enabled_yes = 'Enabled'; $lang->cmd_advanced_mailer_is_enabled_yes = 'Enabled';
$lang->cmd_advanced_mailer_is_enabled_no = 'Disabled'; $lang->cmd_advanced_mailer_is_enabled_no = 'Disabled';
$lang->cmd_advanced_mailer_logging = 'Logging'; $lang->cmd_advanced_mailer_logging = 'Logging';
$lang->cmd_advanced_mailer_log_sent_mail = 'Log Sent Mail'; $lang->cmd_advanced_mailer_log_mail = 'Log Mail';
$lang->cmd_advanced_mailer_log_errors = 'Log Errors'; $lang->cmd_advanced_mailer_log_mail_errors = 'Log Mail Errors';
$lang->cmd_advanced_mailer_log_sms = 'Log SMS';
$lang->cmd_advanced_mailer_log_sms_errors = 'Log SMS Errors';
$lang->cmd_advanced_mailer_log_yes = 'Yes'; $lang->cmd_advanced_mailer_log_yes = 'Yes';
$lang->cmd_advanced_mailer_log_no = 'No'; $lang->cmd_advanced_mailer_log_no = 'No';
$lang->cmd_advanced_mailer_sending_method_config = 'Default Sending Method'; $lang->cmd_advanced_mailer_sending_method_config = 'Default Sending Method';
$lang->cmd_advanced_mailer_about_sending_method_config = 'Please fill all of the boxes.'; $lang->cmd_advanced_mailer_about_sending_method_config = 'Please fill all of the boxes.';
$lang->cmd_advanced_mailer_sending_method = 'Sending method'; $lang->cmd_advanced_mailer_sending_method = 'Sending method';
$lang->cmd_advanced_mailer_about_sending_method = 'This method will be used for all emails where the recipient\'s email address does not belong to an <a href="./index.php?module=admin&amp;act=dispAdvanced_mailerAdminExceptions" target="_blank">exception domain</a>.'; $lang->cmd_advanced_mailer_about_sending_method = 'You can change the default sending method in the <a href="index.php?module=admin&act=dispAdminConfigNotification" target="_blank">Notification Settings</a> screen.';
$lang->cmd_advanced_mailer_sending_method_default = 'Default sending method'; $lang->cmd_advanced_mailer_sending_method_default = 'Default sending method';
$lang->cmd_advanced_mailer_sending_method_exceptions = 'Exceptions'; $lang->cmd_advanced_mailer_sending_method_exceptions = 'Exceptions';
$lang->cmd_advanced_mailer_smtp_manual_entry = 'Manual entry'; $lang->cmd_advanced_mailer_smtp_manual_entry = 'Manual entry';
@ -24,6 +26,8 @@ $lang->cmd_advanced_mailer_smtp_security_tls = 'TLS (STARTTLS)';
$lang->cmd_advanced_mailer_smtp_security_none = 'None'; $lang->cmd_advanced_mailer_smtp_security_none = 'None';
$lang->cmd_advanced_mailer_smtp_user = 'Username'; $lang->cmd_advanced_mailer_smtp_user = 'Username';
$lang->cmd_advanced_mailer_smtp_pass = 'Password'; $lang->cmd_advanced_mailer_smtp_pass = 'Password';
$lang->cmd_advanced_mailer_api_key = 'API key';
$lang->cmd_advanced_mailer_api_secret = 'API secret';
$lang->cmd_advanced_mailer_api_domain = 'Domain'; $lang->cmd_advanced_mailer_api_domain = 'Domain';
$lang->cmd_advanced_mailer_api_token = 'API token'; $lang->cmd_advanced_mailer_api_token = 'API token';
$lang->cmd_advanced_mailer_api_type = 'API type'; $lang->cmd_advanced_mailer_api_type = 'API type';
@ -31,8 +35,9 @@ $lang->cmd_advanced_mailer_api_type_free = 'Free account';
$lang->cmd_advanced_mailer_api_type_paid = 'Paid account'; $lang->cmd_advanced_mailer_api_type_paid = 'Paid account';
$lang->cmd_advanced_mailer_api_user = 'Username'; $lang->cmd_advanced_mailer_api_user = 'Username';
$lang->cmd_advanced_mailer_api_pass = 'Password'; $lang->cmd_advanced_mailer_api_pass = 'Password';
$lang->cmd_advanced_mailer_sender_key = 'Sender key';
$lang->cmd_advanced_mailer_sender_identity = 'Sender Identity'; $lang->cmd_advanced_mailer_sender_identity = 'Sender Identity';
$lang->cmd_advanced_mailer_about_sender_identity = 'Sender identity will be applied to the webmaster\'s name and email address in the <a href="./index.php?module=admin&amp;act=dispMemberAdminConfig" target="_blank">member module</a> as well.'; $lang->cmd_advanced_mailer_about_sender_identity = 'You can change the sender\'s name and e-mail address in the <a href="index.php?module=admin&act=dispAdminConfigNotification" target="_blank">Notification Settings</a> screen.';
$lang->cmd_advanced_mailer_sender_name = 'Sender\'s name'; $lang->cmd_advanced_mailer_sender_name = 'Sender\'s name';
$lang->cmd_advanced_mailer_sender_email = 'Sender\'s email'; $lang->cmd_advanced_mailer_sender_email = 'Sender\'s email';
$lang->cmd_advanced_mailer_reply_to = 'Reply-To email'; $lang->cmd_advanced_mailer_reply_to = 'Reply-To email';
@ -59,7 +64,7 @@ $lang->cmd_advanced_mailer_other_info_mailgun_dkim = 'The DKIM hostname may be d
$lang->cmd_advanced_mailer_other_info_postmark_dkim = 'Please see the Sender Signatures page of your Postmark account for the exact DKIm hostname to use.'; $lang->cmd_advanced_mailer_other_info_postmark_dkim = 'Please see the Sender Signatures page of your Postmark account for the exact DKIm hostname to use.';
$lang->cmd_advanced_mailer_other_info_woorimail_dkim = 'Please log into Woorimail to see your DKIM settings.'; $lang->cmd_advanced_mailer_other_info_woorimail_dkim = 'Please log into Woorimail to see your DKIM settings.';
$lang->cmd_advanced_mailer_ellipsis = '(see API for full value)'; $lang->cmd_advanced_mailer_ellipsis = '(see API for full value)';
$lang->cmd_advanced_mailer_test = 'Mail Test'; $lang->cmd_advanced_mailer_mail_test = 'Mail Test';
$lang->cmd_advanced_mailer_recipient_name = 'Recipient\'s name'; $lang->cmd_advanced_mailer_recipient_name = 'Recipient\'s name';
$lang->cmd_advanced_mailer_recipient_email = 'Recipient\'s email'; $lang->cmd_advanced_mailer_recipient_email = 'Recipient\'s email';
$lang->cmd_advanced_mailer_send = 'Send'; $lang->cmd_advanced_mailer_send = 'Send';
@ -102,6 +107,7 @@ $lang->msg_advanced_mailer_log_is_empty = 'There are no entries to display.';
$lang->cmd_advanced_mailer_status_sender = 'Sender'; $lang->cmd_advanced_mailer_status_sender = 'Sender';
$lang->cmd_advanced_mailer_status_recipient = 'Recipient'; $lang->cmd_advanced_mailer_status_recipient = 'Recipient';
$lang->cmd_advanced_mailer_status_subject = 'Subject'; $lang->cmd_advanced_mailer_status_subject = 'Subject';
$lang->cmd_advanced_mailer_status_content = 'Content';
$lang->cmd_advanced_mailer_status_sending_method = 'Method'; $lang->cmd_advanced_mailer_status_sending_method = 'Method';
$lang->cmd_advanced_mailer_status_time = 'Time'; $lang->cmd_advanced_mailer_status_time = 'Time';
$lang->cmd_advanced_mailer_status = 'Status'; $lang->cmd_advanced_mailer_status = 'Status';
@ -112,3 +118,11 @@ $lang->cmd_advanced_mailer_status_calling_script = 'Called from';
$lang->cmd_advanced_mailer_clear_log_condition_all = 'Everything'; $lang->cmd_advanced_mailer_clear_log_condition_all = 'Everything';
$lang->cmd_advanced_mailer_clear_log_condition = 'Over %d days'; $lang->cmd_advanced_mailer_clear_log_condition = 'Over %d days';
$lang->cmd_advanced_mailer_clear_log_button = 'Clear old logs'; $lang->cmd_advanced_mailer_clear_log_button = 'Clear old logs';
$lang->cmd_advanced_mailer_sms_test = 'SMS Test';
$lang->cmd_advanced_mailer_recipient_number = 'Recipient\'s number';
$lang->cmd_advanced_mailer_country_code = 'Country code';
$lang->cmd_advanced_mailer_country_code_help = 'Please leave the country code empty if you are sending to a domestic number.';
$lang->cmd_advanced_mailer_test_content = 'This is an SMS test from Rhymix.';
$lang->msg_advanced_mailer_recipient_number_is_empty = 'Please enter a phone number for the recipient.';
$lang->msg_advanced_mailer_content_is_empty = 'Please enter the content for your test SMS.';
$lang->msg_advanced_mailer_test_success_sms = 'The test was successful. Please check your SMS.';

View file

@ -1,18 +1,20 @@
<?php <?php
$lang->cmd_advanced_mailer = '고급 메일 발송 모듈'; $lang->cmd_advanced_mailer = '고급 메일 발송 모듈 (메일 및 SMS 관리)';
$lang->cmd_advanced_mailer_general_config = '기본 설정'; $lang->cmd_advanced_mailer_general_config = '기본 설정';
$lang->cmd_advanced_mailer_is_enabled = '모듈 사용'; $lang->cmd_advanced_mailer_is_enabled = '모듈 사용';
$lang->cmd_advanced_mailer_is_enabled_yes = '사용'; $lang->cmd_advanced_mailer_is_enabled_yes = '사용';
$lang->cmd_advanced_mailer_is_enabled_no = '사용하지 않음'; $lang->cmd_advanced_mailer_is_enabled_no = '사용하지 않음';
$lang->cmd_advanced_mailer_logging = '발송 내역 기록'; $lang->cmd_advanced_mailer_logging = '발송 내역 기록';
$lang->cmd_advanced_mailer_log_sent_mail = '발송 내역'; $lang->cmd_advanced_mailer_log_mail = '메일 발송 내역';
$lang->cmd_advanced_mailer_log_errors = '에러 내역'; $lang->cmd_advanced_mailer_log_mail_errors = '메일 에러 내역';
$lang->cmd_advanced_mailer_log_sms = 'SMS 발송 내역';
$lang->cmd_advanced_mailer_log_sms_errors = 'SMS 에러 내역';
$lang->cmd_advanced_mailer_log_yes = '기록'; $lang->cmd_advanced_mailer_log_yes = '기록';
$lang->cmd_advanced_mailer_log_no = '기록하지 않음'; $lang->cmd_advanced_mailer_log_no = '기록하지 않음';
$lang->cmd_advanced_mailer_sending_method_config = '기본 발송 방법 설정'; $lang->cmd_advanced_mailer_sending_method_config = '기본 발송 방법 설정';
$lang->cmd_advanced_mailer_about_sending_method_config = '반드시 모든 항목을 입력하시기 바랍니다.'; $lang->cmd_advanced_mailer_about_sending_method_config = '반드시 모든 항목을 입력하시기 바랍니다.';
$lang->cmd_advanced_mailer_sending_method = '발송 방법'; $lang->cmd_advanced_mailer_sending_method = '발송 방법';
$lang->cmd_advanced_mailer_about_sending_method = '받는이의 주소가 <a href="./index.php?module=admin&amp;act=dispAdvanced_mailerAdminExceptions" target="_blank">예외 도메인</a>에 해당하지 않을 경우 모두 이 방법으로 발송됩니다.'; $lang->cmd_advanced_mailer_about_sending_method = '기본 발송 방법은 <a href="index.php?module=admin&act=dispAdminConfigNotification" target="_blank">알림 설정</a> 화면에서 변경할 수 있습니다.';
$lang->cmd_advanced_mailer_sending_method_default = '기본 발송 방법'; $lang->cmd_advanced_mailer_sending_method_default = '기본 발송 방법';
$lang->cmd_advanced_mailer_sending_method_exceptions = '예외 발송 방법'; $lang->cmd_advanced_mailer_sending_method_exceptions = '예외 발송 방법';
$lang->cmd_advanced_mailer_smtp_manual_entry = '직접 입력'; $lang->cmd_advanced_mailer_smtp_manual_entry = '직접 입력';
@ -24,6 +26,8 @@ $lang->cmd_advanced_mailer_smtp_security_tls = 'TLS (STARTTLS)';
$lang->cmd_advanced_mailer_smtp_security_none = '사용하지 않음'; $lang->cmd_advanced_mailer_smtp_security_none = '사용하지 않음';
$lang->cmd_advanced_mailer_smtp_user = '아이디'; $lang->cmd_advanced_mailer_smtp_user = '아이디';
$lang->cmd_advanced_mailer_smtp_pass = '비밀번호'; $lang->cmd_advanced_mailer_smtp_pass = '비밀번호';
$lang->cmd_advanced_mailer_api_key = 'API 키';
$lang->cmd_advanced_mailer_api_secret = 'API 비밀';
$lang->cmd_advanced_mailer_api_domain = '도메인'; $lang->cmd_advanced_mailer_api_domain = '도메인';
$lang->cmd_advanced_mailer_api_token = 'API 토큰'; $lang->cmd_advanced_mailer_api_token = 'API 토큰';
$lang->cmd_advanced_mailer_api_type = 'API 구분'; $lang->cmd_advanced_mailer_api_type = 'API 구분';
@ -31,8 +35,9 @@ $lang->cmd_advanced_mailer_api_type_free = '무료';
$lang->cmd_advanced_mailer_api_type_paid = '유료'; $lang->cmd_advanced_mailer_api_type_paid = '유료';
$lang->cmd_advanced_mailer_api_user = '아이디'; $lang->cmd_advanced_mailer_api_user = '아이디';
$lang->cmd_advanced_mailer_api_pass = '비밀번호'; $lang->cmd_advanced_mailer_api_pass = '비밀번호';
$lang->cmd_advanced_mailer_sender_key = '센더 키';
$lang->cmd_advanced_mailer_sender_identity = '보낸이 설정'; $lang->cmd_advanced_mailer_sender_identity = '보낸이 설정';
$lang->cmd_advanced_mailer_about_sender_identity = '보낸이 설정은 <a href="./index.php?module=admin&amp;act=dispMemberAdminConfig" target="_blank">회원 모듈</a>의 웹마스터 이름 및 메일 주소에도 동일하게 적용됩니다.'; $lang->cmd_advanced_mailer_about_sender_identity = '보낸이의 이름과 메일 주소는 <a href="index.php?module=admin&act=dispAdminConfigNotification" target="_blank">알림 설정</a> 화면에서 변경할 수 있습니다.';
$lang->cmd_advanced_mailer_sender_name = '보낸이 이름'; $lang->cmd_advanced_mailer_sender_name = '보낸이 이름';
$lang->cmd_advanced_mailer_sender_email = '보낸이 메일 주소'; $lang->cmd_advanced_mailer_sender_email = '보낸이 메일 주소';
$lang->cmd_advanced_mailer_reply_to = 'Reply-To 주소'; $lang->cmd_advanced_mailer_reply_to = 'Reply-To 주소';
@ -59,7 +64,7 @@ $lang->cmd_advanced_mailer_other_info_mailgun_dkim = 'DKIM 호스트명은 달
$lang->cmd_advanced_mailer_other_info_postmark_dkim = '정확한 DKIM 호스트명은 Postmark 계정의 Sender Signatures 페이지를 참고하시기 바랍니다.'; $lang->cmd_advanced_mailer_other_info_postmark_dkim = '정확한 DKIM 호스트명은 Postmark 계정의 Sender Signatures 페이지를 참고하시기 바랍니다.';
$lang->cmd_advanced_mailer_other_info_woorimail_dkim = 'DKIM 설정은 우리메일에 로그인하여 확인하십시오.'; $lang->cmd_advanced_mailer_other_info_woorimail_dkim = 'DKIM 설정은 우리메일에 로그인하여 확인하십시오.';
$lang->cmd_advanced_mailer_ellipsis = '(중략)'; $lang->cmd_advanced_mailer_ellipsis = '(중략)';
$lang->cmd_advanced_mailer_test = '발송 테스트'; $lang->cmd_advanced_mailer_mail_test = '메일 테스트';
$lang->cmd_advanced_mailer_recipient_name = '받는이 이름'; $lang->cmd_advanced_mailer_recipient_name = '받는이 이름';
$lang->cmd_advanced_mailer_recipient_email = '받는이 메일 주소'; $lang->cmd_advanced_mailer_recipient_email = '받는이 메일 주소';
$lang->cmd_advanced_mailer_send = '발송'; $lang->cmd_advanced_mailer_send = '발송';
@ -102,6 +107,7 @@ $lang->msg_advanced_mailer_log_is_empty = '표시할 항목이 없습니다.';
$lang->cmd_advanced_mailer_status_sender = '보낸이'; $lang->cmd_advanced_mailer_status_sender = '보낸이';
$lang->cmd_advanced_mailer_status_recipient = '받는이'; $lang->cmd_advanced_mailer_status_recipient = '받는이';
$lang->cmd_advanced_mailer_status_subject = '제목'; $lang->cmd_advanced_mailer_status_subject = '제목';
$lang->cmd_advanced_mailer_status_content = '내용';
$lang->cmd_advanced_mailer_status_sending_method = '발송 방법'; $lang->cmd_advanced_mailer_status_sending_method = '발송 방법';
$lang->cmd_advanced_mailer_status_time = '발송 시간'; $lang->cmd_advanced_mailer_status_time = '발송 시간';
$lang->cmd_advanced_mailer_status = '상태'; $lang->cmd_advanced_mailer_status = '상태';
@ -112,3 +118,11 @@ $lang->cmd_advanced_mailer_status_calling_script = '호출 위치';
$lang->cmd_advanced_mailer_clear_log_condition_all = '모두'; $lang->cmd_advanced_mailer_clear_log_condition_all = '모두';
$lang->cmd_advanced_mailer_clear_log_condition = '%d일 이상'; $lang->cmd_advanced_mailer_clear_log_condition = '%d일 이상';
$lang->cmd_advanced_mailer_clear_log_button = '오래된 기록 삭제'; $lang->cmd_advanced_mailer_clear_log_button = '오래된 기록 삭제';
$lang->cmd_advanced_mailer_sms_test = 'SMS 테스트';
$lang->cmd_advanced_mailer_recipient_number = '받는이 전화번호';
$lang->cmd_advanced_mailer_country_code = '국가코드';
$lang->cmd_advanced_mailer_country_code_help = '국내 번호로 발송하실 경우 국가코드는 비워 두시기 바랍니다.';
$lang->cmd_advanced_mailer_test_content = '라이믹스 SMS 발송 테스트입니다.';
$lang->msg_advanced_mailer_recipient_number_is_empty = '받는이 전화번호를 입력해 주십시오.';
$lang->msg_advanced_mailer_content_is_empty = 'SMS 내용을 입력해 주십시오.';
$lang->msg_advanced_mailer_test_success_sms = '테스트에 성공하였습니다. SMS를 확인해 보시기 바랍니다.';

View file

@ -1,4 +1,4 @@
<query id="countLogByType" action="select"> <query id="countMailLogByType" action="select">
<tables> <tables>
<table name="advanced_mailer_log" /> <table name="advanced_mailer_log" />
</tables> </tables>

View file

@ -0,0 +1,11 @@
<query id="countSMSLogByType" action="select">
<tables>
<table name="advanced_mailer_sms_log" />
</tables>
<columns>
<column name="count(*)" alias="count" />
</columns>
<conditions>
<condition operation="equal" column="status" var="status" />
</conditions>
</query>

View file

@ -1,4 +1,4 @@
<query id="deleteLogs" action="delete"> <query id="deleteMailLogs" action="delete">
<tables> <tables>
<table name="advanced_mailer_log" /> <table name="advanced_mailer_log" />
</tables> </tables>

View file

@ -0,0 +1,9 @@
<query id="deleteSMSLogs" action="delete">
<tables>
<table name="advanced_mailer_sms_log" />
</tables>
<conditions>
<condition operation="equal" column="status" var="status" />
<condition operation="less" column="regdate" var="regdate" pipe="and" />
</conditions>
</query>

View file

@ -1,4 +1,4 @@
<query id="getLogByType" action="select"> <query id="getMailLogByType" action="select">
<tables> <tables>
<table name="advanced_mailer_log" /> <table name="advanced_mailer_log" />
</tables> </tables>

View file

@ -0,0 +1,17 @@
<query id="getSMSLogByType" action="select">
<tables>
<table name="advanced_mailer_sms_log" />
</tables>
<columns>
<column name="*" />
</columns>
<conditions>
<condition operation="equal" column="status" var="status" />
</conditions>
<navigation>
<index var="sort_index" default="sms_id" order="desc" />
<list_count var="list_count" default="20" />
<page_count var="page_count" default="10" />
<page var="page" default="1" />
</navigation>
</query>

View file

@ -1,4 +1,4 @@
<query id="insertLog" action="insert"> <query id="insertMailLog" action="insert">
<tables> <tables>
<table name="advanced_mailer_log" /> <table name="advanced_mailer_log" />
</tables> </tables>

View file

@ -0,0 +1,15 @@
<query id="insertSMSLog" action="insert">
<tables>
<table name="advanced_mailer_sms_log" />
</tables>
<columns>
<column name="sms_from" var="sms_from" notnull="notnull" />
<column name="sms_to" var="sms_to" notnull="notnull" />
<column name="content" var="content" notnull="notnull" />
<column name="calling_script" var="calling_script" notnull="notnull" />
<column name="sending_method" var="sending_method" notnull="notnull" />
<column name="regdate" var="regdate" notnull="notnull" default="curdate()" />
<column name="status" var="status" notnull="notnull" default="success" />
<column name="errors" var="errors" />
</columns>
</query>

View file

@ -0,0 +1,11 @@
<table name="advanced_mailer_sms_log">
<column name="sms_id" type="number" size="11" notnull="notnull" primary_key="primary_key" auto_increment="auto_increment" />
<column name="sms_from" type="varchar" size="250" notnull="notnull" />
<column name="sms_to" type="text" notnull="notnull" />
<column name="content" type="text" notnull="notnull" />
<column name="calling_script" type="varchar" size="250" notnull="notnull" />
<column name="sending_method" type="varchar" size="40" notnull="notnull" />
<column name="regdate" type="date" notnull="notnull" index="idx_regdate" />
<column name="status" type="varchar" size="40" notnull="notnull" index="idx_status" />
<column name="errors" type="bigtext" />
</table>

View file

@ -7,7 +7,10 @@
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminConfig'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminConfig')}">{$lang->cmd_advanced_mailer_general_config}</a></li> <li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminConfig'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminConfig')}">{$lang->cmd_advanced_mailer_general_config}</a></li>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminExceptions'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminExceptions')}">{$lang->cmd_advanced_mailer_exception_domains}</a></li> <li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminExceptions'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminExceptions')}">{$lang->cmd_advanced_mailer_exception_domains}</a></li>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminSpfDkim'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSpfDkim')}">{$lang->cmd_advanced_mailer_spf_dkim_setting}</a></li> <li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminSpfDkim'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSpfDkim')}">{$lang->cmd_advanced_mailer_spf_dkim_setting}</a></li>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminTestConfig'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminTestConfig')}">{$lang->cmd_advanced_mailer_test}</a></li> <li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminMailTest'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminMailTest')}">{$lang->cmd_advanced_mailer_mail_test}</a></li>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminSentMail'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSentMail')}">{$lang->cmd_advanced_mailer_log_sent_mail}</a></li> <li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminMailLog'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminMailLog')}">{$lang->cmd_advanced_mailer_log_mail}</a></li>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminErrors'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminErrors')}">{$lang->cmd_advanced_mailer_log_errors}</a></li> <li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminMailErrors'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminMailErrors')}">{$lang->cmd_advanced_mailer_log_mail_errors}</a></li>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminSMSTest'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSMSTest')}">{$lang->cmd_advanced_mailer_sms_test}</a></li>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminSMSLog'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSMSLog')}">{$lang->cmd_advanced_mailer_log_sms}</a></li>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminSMSErrors'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSMSErrors')}">{$lang->cmd_advanced_mailer_log_sms_errors}</a></li>
</ul> </ul>

View file

@ -19,140 +19,6 @@
※ {$lang->cmd_advanced_mailer_about_sending_method} ※ {$lang->cmd_advanced_mailer_about_sending_method}
</div> </div>
<div class="x_control-group show-always">
<label class="x_control-label" for="advanced_mailer_sending_method">{$lang->cmd_advanced_mailer_sending_method_default}</label>
<div class="x_controls">
<select name="sending_method" id="advanced_mailer_sending_method">
<!--@foreach($sending_methods as $driver_name => $driver_definition)-->
<option value="{$driver_name}" selected="selected"|cond="$sending_method === $driver_name">{$driver_definition['name']}</option>
<!--@end-->
</select>
</div>
</div>
<script type="text/javascript">
var advanced_mailer_sending_methods = {json_encode($sending_methods)};
</script>
<div class="x_control-group hidden-by-default show-for-dummy">
<label class="x_control-label"></label>
<div class="x_controls">
<p class="x_help-block">{$lang->msg_advanced_mailer_about_dummy}<br />{$lang->msg_advanced_mailer_about_dummy_exceptions}</p>
</div>
</div>
<!--@foreach($sending_methods as $driver_name => $driver_definition)-->
<!--@foreach($driver_definition['required'] as $conf_name)-->
{@ $conf_value = escape(config("mail.$driver_name.$conf_name"))}
<!--@if($conf_name === 'smtp_host')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="advanced_mailer_{$driver_name}_smtp_host">{$lang->cmd_advanced_mailer_smtp_host}</label>
<div class="x_controls">
<input type="text" name="{$driver_name}_smtp_host" id="advanced_mailer_{$driver_name}_smtp_host" value="{$conf_value}" />
<select id="advanced_mailer_{$driver_name}_manual_entry">
<option value="">{$lang->cmd_advanced_mailer_smtp_manual_entry}</option>
<option value="gmail">Gmail</option>
<option value="hanmail">Hanmail</option>
<option value="naver">Naver</option>
<option value="worksmobile">Works Mobile</option>
<option value="outlook">Outlook.com</option>
<option value="yahoo">Yahoo</option>
</select>
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'smtp_port')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="advanced_mailer_{$driver_name}_smtp_port">{$lang->cmd_advanced_mailer_smtp_port}</label>
<div class="x_controls">
<input type="text" name="{$driver_name}_smtp_port" id="advanced_mailer_{$driver_name}_smtp_port" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'smtp_security')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label">{$lang->cmd_advanced_mailer_smtp_security}</label>
<div class="x_controls">
<label class="x_inline" for="advanced_mailer_{$driver_name}_security_none"><input type="radio" name="{$driver_name}_smtp_security" id="advanced_mailer_{$driver_name}_security_none" value="none" checked="checked"|cond="!in_array($conf_value, array('ssl', 'tls'))" /> {$lang->cmd_advanced_mailer_smtp_security_none}</label>
<label class="x_inline" for="advanced_mailer_{$driver_name}_security_ssl"><input type="radio" name="{$driver_name}_smtp_security" id="advanced_mailer_{$driver_name}_security_ssl" value="ssl" checked="checked"|cond="$conf_value === 'ssl'" /> {$lang->cmd_advanced_mailer_smtp_security_ssl}</label>
<label class="x_inline" for="advanced_mailer_{$driver_name}_security_tls"><input type="radio" name="{$driver_name}_smtp_security" id="advanced_mailer_{$driver_name}_security_tls" value="tls" checked="checked"|cond="$conf_value === 'tls'" /> {$lang->cmd_advanced_mailer_smtp_security_tls}</label>
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'smtp_user')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="advanced_mailer_{$driver_name}_smtp_user">{$lang->cmd_advanced_mailer_smtp_user}</label>
<div class="x_controls">
<input type="text" name="{$driver_name}_smtp_user" id="advanced_mailer_{$driver_name}_smtp_user" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'smtp_pass')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="advanced_mailer_smtp_pass">{$lang->cmd_advanced_mailer_smtp_pass}</label>
<div class="x_controls">
<input type="smtp_pass" name="{$driver_name}_smtp_pass" id="advanced_mailer_{$driver_name}_smtp_pass" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_type')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="advanced_mailer_{$driver_name}_api_type">{$lang->cmd_advanced_mailer_api_type}</label>
<div class="x_controls">
<select id="advanced_mailer_{$driver_name}_api_type" name="{$driver_name}_api_type">
<!--@foreach($driver_definition['api_types'] as $api_type)-->
<option value="{$api_type}" selected="selected"|cond="$api_type === $conf_value">{$api_type}</option>
<!--@end-->
</select>
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_domain')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="advanced_mailer_{$driver_name}_api_domain">{$lang->cmd_advanced_mailer_api_domain}</label>
<div class="x_controls">
<input type="text" name="{$driver_name}_api_domain" id="advanced_mailer_{$driver_name}_api_domain" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_token')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="advanced_mailer_{$driver_name}_api_token">{$lang->cmd_advanced_mailer_api_token}</label>
<div class="x_controls full-width">
<input type="text" name="{$driver_name}_api_token" id="advanced_mailer_{$driver_name}_api_token" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_user')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="advanced_mailer_{$driver_name}_api_user">{$lang->cmd_advanced_mailer_api_user}</label>
<div class="x_controls">
<input type="text" name="{$driver_name}_api_user" id="advanced_mailer_{$driver_name}_api_user" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'api_pass')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="advanced_mailer_{$driver_name}_api_pass">{$lang->cmd_advanced_mailer_api_pass}</label>
<div class="x_controls full-width">
<input type="password" name="{$driver_name}_api_pass" id="advanced_mailer_{$driver_name}_api_pass" value="{$conf_value}" />
</div>
</div>
<!--@end-->
<!--@end-->
<!--@end-->
</section> </section>
<section class="section"> <section class="section">
@ -162,38 +28,6 @@
<div class="advanced_mailer_description"> <div class="advanced_mailer_description">
※ {$lang->cmd_advanced_mailer_about_sender_identity} ※ {$lang->cmd_advanced_mailer_about_sender_identity}
</div> </div>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_sender_name">{$lang->cmd_advanced_mailer_sender_name}</label>
<div class="x_controls">
<input type="text" name="sender_name" id="advanced_mailer_sender_name" value="{$webmaster_name}" />
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_sender_email">{$lang->cmd_advanced_mailer_sender_email}</label>
<div class="x_controls">
<input type="text" name="sender_email" id="advanced_mailer_sender_email" value="{$webmaster_email}" />
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_reply_to">{$lang->cmd_advanced_mailer_reply_to}</label>
<div class="x_controls">
<input type="text" name="reply_to" id="advanced_mailer_reply_to" value="{$advanced_mailer_config->reply_to}" />
</div>
</div>
<div class="x_control-group">
<label class="x_control-label">{$lang->cmd_advanced_mailer_force_sender}</label>
<div class="x_controls">
<label for="advanced_mailer_force_sender">
<input type="checkbox" name="force_sender" id="advanced_mailer_force_sender" value="Y" checked="checked"|cond="toBool($advanced_mailer_config->force_sender)" />
{$lang->cmd_advanced_mailer_about_force_sender}
</label>
<p>※ {$lang->cmd_advanced_mailer_about_force_sender_caution_line_1}<br />※ {$lang->cmd_advanced_mailer_about_force_sender_caution_line_2}</p>
</div>
</div>
</section> </section>
@ -202,7 +36,7 @@
<h2 style="padding-top:12px">{$lang->cmd_advanced_mailer_logging}</h2> <h2 style="padding-top:12px">{$lang->cmd_advanced_mailer_logging}</h2>
<div class="x_control-group"> <div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_log_sent_mail">{$lang->cmd_advanced_mailer_log_sent_mail}</label> <label class="x_control-label" for="advanced_mailer_log_sent_mail">{$lang->cmd_advanced_mailer_log_mail}</label>
<div class="x_controls"> <div class="x_controls">
<select name="log_sent_mail" id="advanced_mailer_log_sent_mail"> <select name="log_sent_mail" id="advanced_mailer_log_sent_mail">
<option value="Y" selected="selected"|cond="toBool($advanced_mailer_config->log_sent_mail)" />{$lang->cmd_advanced_mailer_log_yes}</option> <option value="Y" selected="selected"|cond="toBool($advanced_mailer_config->log_sent_mail)" />{$lang->cmd_advanced_mailer_log_yes}</option>
@ -212,7 +46,7 @@
</div> </div>
<div class="x_control-group"> <div class="x_control-group">
<label class="x_control-label">{$lang->cmd_advanced_mailer_log_errors}</label> <label class="x_control-label">{$lang->cmd_advanced_mailer_log_mail_errors}</label>
<div class="x_controls"> <div class="x_controls">
<select name="log_errors" id="advanced_mailer_log_errors"> <select name="log_errors" id="advanced_mailer_log_errors">
<option value="Y" selected="selected"|cond="toBool($advanced_mailer_config->log_errors)" />{$lang->cmd_advanced_mailer_log_yes}</option> <option value="Y" selected="selected"|cond="toBool($advanced_mailer_config->log_errors)" />{$lang->cmd_advanced_mailer_log_yes}</option>
@ -221,6 +55,26 @@
</div> </div>
</div> </div>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_log_sent_sms">{$lang->cmd_advanced_mailer_log_sms}</label>
<div class="x_controls">
<select name="log_sent_sms" id="advanced_mailer_log_sent_sms">
<option value="Y" selected="selected"|cond="toBool($advanced_mailer_config->log_sent_sms)" />{$lang->cmd_advanced_mailer_log_yes}</option>
<option value="N" selected="selected"|cond="!toBool($advanced_mailer_config->log_sent_sms)" />{$lang->cmd_advanced_mailer_log_no}</option>
</select>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label">{$lang->cmd_advanced_mailer_log_sms_errors}</label>
<div class="x_controls">
<select name="log_sms_errors" id="advanced_mailer_log_sms_errors">
<option value="Y" selected="selected"|cond="toBool($advanced_mailer_config->log_sms_errors)" />{$lang->cmd_advanced_mailer_log_yes}</option>
<option value="N" selected="selected"|cond="!toBool($advanced_mailer_config->log_sms_errors)" />{$lang->cmd_advanced_mailer_log_no}</option>
</select>
</div>
</div>
</section> </section>
<div class="btnArea x_clearfix"> <div class="btnArea x_clearfix">

View file

@ -3,93 +3,38 @@
$(function() { $(function() {
$("#advanced_mailer_sending_method").on("change", function() { $("#advanced_mailer_test_send_mail").click(function(event) {
var sending_method = $(this).val();
$("div.x_control-group.hidden-by-default").not(".show-always").each(function() {
if ($(this).hasClass("show-for-" + sending_method)) {
$(this).show();
} else {
$(this).hide();
}
});
var reply_to = $("#advanced_mailer_reply_to").parents("div.x_control-group");
if (sending_method === "woorimail") {
reply_to.hide();
} else {
reply_to.show();
}
}).triggerHandler("change");
$("#advanced_mailer_smtp_manual_entry").on("change", function() {
var auto_fill = $(this).val();
if (auto_fill === 'gmail') {
$("#advanced_mailer_smtp_host").val('smtp.gmail.com');
$("#advanced_mailer_smtp_port").val('465');
$("#advanced_mailer_smtp_security_ssl").prop("checked", true).parent().addClass("checked");
$("#advanced_mailer_smtp_security_tls").parent().removeClass("checked");
$("#advanced_mailer_smtp_security_none").parent().removeClass("checked");
$("#advanced_mailer_force_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'hanmail') {
$("#advanced_mailer_smtp_host").val('smtp.daum.net');
$("#advanced_mailer_smtp_port").val('465');
$("#advanced_mailer_smtp_security_ssl").prop("checked", true).parent().addClass("checked");
$("#advanced_mailer_smtp_security_tls").parent().removeClass("checked");
$("#advanced_mailer_smtp_security_none").parent().removeClass("checked");
$("#advanced_mailer_force_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'naver') {
$("#advanced_mailer_smtp_host").val('smtp.naver.com');
$("#advanced_mailer_smtp_port").val('587');
$("#advanced_mailer_smtp_security_tls").prop("checked", true).parent().addClass("checked");
$("#advanced_mailer_smtp_security_ssl").parent().removeClass("checked");
$("#advanced_mailer_smtp_security_none").parent().removeClass("checked");
$("#advanced_mailer_force_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'worksmobile') {
$("#advanced_mailer_smtp_host").val('smtp.worksmobile.com');
$("#advanced_mailer_smtp_port").val('587');
$("#advanced_mailer_smtp_security_tls").prop("checked", true).parent().addClass("checked");
$("#advanced_mailer_smtp_security_ssl").parent().removeClass("checked");
$("#advanced_mailer_smtp_security_none").parent().removeClass("checked");
$("#advanced_mailer_force_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'outlook') {
$("#advanced_mailer_smtp_host").val('smtp-mail.outlook.com');
$("#advanced_mailer_smtp_port").val('587');
$("#advanced_mailer_smtp_security_tls").prop("checked", true).parent().addClass("checked");
$("#advanced_mailer_smtp_security_ssl").parent().removeClass("checked");
$("#advanced_mailer_smtp_security_none").parent().removeClass("checked");
$("#advanced_mailer_force_sender").prop("checked", true).parent().addClass("checked");
}
if (auto_fill === 'yahoo') {
$("#advanced_mailer_smtp_host").val('smtp.mail.yahoo.com');
$("#advanced_mailer_smtp_port").val('465');
$("#advanced_mailer_smtp_security_ssl").prop("checked", true).parent().addClass("checked");
$("#advanced_mailer_smtp_security_tls").parent().removeClass("checked");
$("#advanced_mailer_smtp_security_none").parent().removeClass("checked");
$("#advanced_mailer_force_sender").prop("checked", true).parent().addClass("checked");
}
});
$("#advanced_mailer_woorimail_account_type_free,#advanced_mailer_woorimail_account_type_paid").on("change", function() {
if ($("#advanced_mailer_woorimail_account_type_paid").is(":checked")) {
$("#advanced_mailer_reply_to").attr("disabled", "disabled");
} else {
$("#advanced_mailer_reply_to").removeAttr("disabled");
}
}).triggerHandler("change");
$("#advanced_mailer_test_send").click(function(event) {
event.preventDefault(); event.preventDefault();
$("#advanced_mailer_test_result").text(""); $("#advanced_mailer_test_result").text("");
$(this).attr("disabled", "disabled"); $(this).attr("disabled", "disabled");
var ajax_data = { var ajax_data = {
recipient_name: $("#advanced_mailer_recipient_name").val(), recipient_name: $("#advanced_mailer_recipient_name").val(),
recipient_email: $("#advanced_mailer_recipient_email").val(), recipient_email: $("#advanced_mailer_recipient_email").val()
}; };
$.exec_json( $.exec_json(
"advanced_mailer.procAdvanced_mailerAdminTestSend", ajax_data, "advanced_mailer.procAdvanced_mailerAdminTestSendMail", ajax_data,
function(response) {
$("#advanced_mailer_test_result").html(response.test_result);
$("#advanced_mailer_test_send").removeAttr("disabled");
},
function(response) {
$("#advanced_mailer_test_result").text("AJAX Error");
$("#advanced_mailer_test_send").removeAttr("disabled");
}
);
});
$("#advanced_mailer_test_send_sms").click(function(event) {
event.preventDefault();
$("#advanced_mailer_test_result").text("");
$(this).attr("disabled", "disabled");
var ajax_data = {
recipient_number: $("#advanced_mailer_recipient_number").val(),
country_code: $("#advanced_mailer_country_code").val(),
content: $("#advanced_mailer_content").val()
};
$.exec_json(
"advanced_mailer.procAdvanced_mailerAdminTestSendSMS", ajax_data,
function(response) { function(response) {
$("#advanced_mailer_test_result").html(response.test_result); $("#advanced_mailer_test_result").html(response.test_result);
$("#advanced_mailer_test_send").removeAttr("disabled"); $("#advanced_mailer_test_send").removeAttr("disabled");

View file

@ -4,7 +4,7 @@
<form class="x_form-horizontal" action="./" method="post" id="advanced_mailer"> <form class="x_form-horizontal" action="./" method="post" id="advanced_mailer">
<input type="hidden" name="module" value="advanced_mailer" /> <input type="hidden" name="module" value="advanced_mailer" />
<input type="hidden" name="act" value="procAdvanced_mailerAdminTestConfig" /> <input type="hidden" name="act" value="procAdvanced_mailerAdminTestSendMail" />
<input type="hidden" name="success_return_url" value="{getRequestUriByServerEnviroment()}" /> <input type="hidden" name="success_return_url" value="{getRequestUriByServerEnviroment()}" />
<div cond="$XE_VALIDATOR_MESSAGE" class="message {$XE_VALIDATOR_MESSAGE_TYPE}"> <div cond="$XE_VALIDATOR_MESSAGE" class="message {$XE_VALIDATOR_MESSAGE_TYPE}">
@ -13,7 +13,7 @@
<section class="section"> <section class="section">
<h2>{$lang->cmd_advanced_mailer_test}</h2> <h2>{$lang->cmd_advanced_mailer_mail_test}</h2>
<div class="x_control-group"> <div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_recipient_name">{$lang->cmd_advanced_mailer_recipient_name}</label> <label class="x_control-label" for="advanced_mailer_recipient_name">{$lang->cmd_advanced_mailer_recipient_name}</label>
@ -39,7 +39,7 @@
</section> </section>
<div class="btnArea x_clearfix"> <div class="btnArea x_clearfix">
<button id="advanced_mailer_test_send" type="submit" class="x_btn x_btn-primary x_pull-right">{$lang->cmd_advanced_mailer_send}</button> <button id="advanced_mailer_test_send_mail" type="submit" class="x_btn x_btn-primary x_pull-right">{$lang->cmd_advanced_mailer_send}</button>
</div> </div>
</form> </form>

View file

@ -0,0 +1,79 @@
<include target="./common.html" />
<load target="css/view_log.css" />
<load target="js/view_log.js" />
<table id="advanced_mailer_log" class="x_table x_table-striped x_table-hover">
<caption>
<strong>Total: {number_format($total_count)}, Page: {number_format($page)}/{number_format($total_page)}</strong>
</caption>
<thead>
<tr>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_status_sender}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_status_recipient}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_status_content}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_status_sending_method}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_status_time}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_status}</th>
</tr>
</thead>
<tbody>
<tr loop="$advanced_mailer_log => $sms_id, $val">
<td class="nowr">
{htmlspecialchars($val->sms_from)}
</td>
<td class="nowr">
{htmlspecialchars($val->sms_to)}
</td>
<td class="nowr">{nl2br(htmlspecialchars($val->content))}</td>
<td class="nowr">
{@ if($val->sending_method === 'mail') $val->sending_method = 'mailfunction'}
{strval(isset($sending_methods[$val->sending_method]['name']) ? $sending_methods[$val->sending_method]['name'] : $val->sending_method)}
</td>
<td class="nowr">{(zdate($val->regdate, "Y-m-d\nH:i:s"))}</td>
<td class="nowr">
<!--@if($val->status === 'success')-->
{$lang->cmd_advanced_mailer_status_success}
<!--@else-->
<a href="javascript:void(0)" class="show-errors">{$lang->cmd_advanced_mailer_status_error}</a>
<div class="mail-log-errors">
<strong>{$lang->cmd_advanced_mailer_status_error_msg}:</strong><br />
{nl2br(htmlspecialchars(trim($val->errors)))}<br /><br />
<strong>{$lang->cmd_advanced_mailer_status_calling_script}:</strong><br />
{htmlspecialchars($val->calling_script)}
</div>
<!--@end-->
</td>
</tr>
<tr cond="!$advanced_mailer_log">
<td>{$lang->msg_advanced_mailer_log_is_empty}</td>
</tr>
</tbody>
</table>
<div class="x_clearfix">
<form class="x_pagination x_pull-left" style="margin-top:8px" action="{Context::getUrl('')}" method="post" no-error-return-url="true">
<input loop="$param => $key, $val" cond="!in_array($key, array('mid', 'vid', 'act'))" type="hidden" name="{$key}" value="{$val}" />
<ul>
<li class="x_disabled"|cond="$page == 1"><a href="{getUrl('page', '')}">&laquo; {$lang->first_page}</a></li>
<!--@while($page_no = $page_navigation->getNextPage())-->
<li class="x_active"|cond="$page_no == $page"><a href="{getUrl('page', $page_no)}">{$page_no}</a></li>
<!--@end-->
<li class="x_disabled"|cond="$page == $page_navigation->last_page"><a href="{getUrl('page', $page_navigation->last_page)}">{$lang->last_page} &raquo;</a></li>
</ul>
</form>
<form class="x_pull-right x_input-append" style="margin-top:8px" action="{Context::getUrl('')}" method="post">
<input type="hidden" name="module" value="advanced_mailer" />
<input type="hidden" name="act" value="procAdvanced_mailerAdminClearSentSMS" />
<input type="hidden" name="status" value="{$advanced_mailer_status}" />
<select name="clear_before_days" style="width:120px">
<option value="0">{$lang->cmd_advanced_mailer_clear_log_condition_all}</option>
<option value="1">{sprintf($lang->cmd_advanced_mailer_clear_log_condition, 1)}</option>
<option value="3">{sprintf($lang->cmd_advanced_mailer_clear_log_condition, 3)}</option>
<option value="7" selected="selected">{sprintf($lang->cmd_advanced_mailer_clear_log_condition, 7)}</option>
<option value="14">{sprintf($lang->cmd_advanced_mailer_clear_log_condition, 14)}</option>
<option value="30">{sprintf($lang->cmd_advanced_mailer_clear_log_condition, 30)}</option>
<option value="60">{sprintf($lang->cmd_advanced_mailer_clear_log_condition, 60)}</option>
</select>
<button class="x_btn" type="submit" disabled="disabled"|cond="!count($advanced_mailer_log)">{$lang->cmd_advanced_mailer_clear_log_button}</button>
</form>
</div>

View file

@ -0,0 +1,48 @@
<include target="./common.html" />
<load target="css/config.css" />
<load target="js/config.js" />
<form class="x_form-horizontal" action="./" method="post" id="advanced_mailer">
<input type="hidden" name="module" value="advanced_mailer" />
<input type="hidden" name="act" value="procAdvanced_mailerAdminTestSendSMS" />
<input type="hidden" name="success_return_url" value="{getRequestUriByServerEnviroment()}" />
<div cond="$XE_VALIDATOR_MESSAGE" class="message {$XE_VALIDATOR_MESSAGE_TYPE}">
<p>{$XE_VALIDATOR_MESSAGE}</p>
</div>
<section class="section">
<h2>{$lang->cmd_advanced_mailer_sms_test}</h2>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_recipient_number">{$lang->cmd_advanced_mailer_recipient_number}</label>
<div class="x_controls">
<input type="text" id="advanced_mailer_recipient_number" value="{config('sms.default_from')}" />
&nbsp;{$lang->cmd_advanced_mailer_country_code}&nbsp;
<input type="number" id="advanced_mailer_country_code" value="" />
<p class="x_help-block">{$lang->cmd_advanced_mailer_country_code_help}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_content">{$lang->cmd_advanced_mailer_status_content}</label>
<div class="x_controls">
<textarea id="advanced_mailer_content">{$lang->cmd_advanced_mailer_test_content}</textarea>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label">{$lang->cmd_advanced_mailer_test_result}</label>
<div class="x_controls">
<div id="advanced_mailer_test_result"></div>
</div>
</div>
</section>
<div class="btnArea x_clearfix">
<button id="advanced_mailer_test_send_sms" type="submit" class="x_btn x_btn-primary x_pull-right">{$lang->cmd_advanced_mailer_send}</button>
</div>
</form>

View file

@ -488,6 +488,7 @@ class autoinstallAdminView extends autoinstall
$security = new Security(); $security = new Security();
$security->encodeHTML('package.', 'package.depends..', 'item_list..'); $security->encodeHTML('package.', 'package.depends..', 'item_list..');
$security->encodeHTML('search_target', 'search_keyword');
} }
/** /**

View file

@ -161,7 +161,7 @@ class boardController extends board
$obj->update_order = $obj->list_order = (getNextSequence() * -1); $obj->update_order = $obj->list_order = (getNextSequence() * -1);
} }
$obj->reason_update = escape($obj->reason_update); $obj->reason_update = escape($obj->reason_update);
$output = $oDocumentController->updateDocument($oDocument, $obj, true); $output = $oDocumentController->updateDocument($oDocument, $obj, $bAnonymous);
$msg_code = 'success_updated'; $msg_code = 'success_updated';
// insert a new document otherwise // insert a new document otherwise
@ -196,8 +196,6 @@ class boardController extends board
$oMail->send(); $oMail->send();
} }
} }
} }
} }
@ -523,17 +521,13 @@ class boardController extends board
if($this->module_info->comment_delete_message === 'yes' && $instant_delete != 'Y') if($this->module_info->comment_delete_message === 'yes' && $instant_delete != 'Y')
{ {
$comment->content = '';
$comment->status = 7;
$output = $oCommentController->updateCommentByDelete($comment, $this->grant->manager); $output = $oCommentController->updateCommentByDelete($comment, $this->grant->manager);
} }
elseif($this->module_info->comment_delete_message === 'only_commnet' && $instant_delete != 'Y') elseif(starts_with('only_comm', $this->module_info->comment_delete_message) && $instant_delete != 'Y')
{ {
$childs = $oCommentModel->getChildComments($comment_srl); $childs = $oCommentModel->getChildComments($comment_srl);
if(count($childs) > 0) if(count($childs) > 0)
{ {
$comment->content = '';
$comment->status = 7;
$output = $oCommentController->updateCommentByDelete($comment, $this->grant->manager); $output = $oCommentController->updateCommentByDelete($comment, $this->grant->manager);
} }
else else

View file

@ -673,6 +673,7 @@ class boardView extends board
$group_srls_count = count($group_srls); $group_srls_count = count($group_srls);
// check the grant after obtained the category list // check the grant after obtained the category list
$category_list = array();
$normal_category_list = $oDocumentModel->getCategoryList($this->module_srl); $normal_category_list = $oDocumentModel->getCategoryList($this->module_srl);
if(count($normal_category_list)) if(count($normal_category_list))
{ {
@ -689,7 +690,25 @@ class boardView extends board
if($is_granted) $category_list[$category_srl] = $category; if($is_granted) $category_list[$category_srl] = $category;
} }
} }
Context::set('category_list', $category_list);
// check if at least one category is granted
$grant_exists = false;
foreach ($category_list as $category)
{
if ($category->grant)
{
$grant_exists = true;
}
}
if ($grant_exists)
{
Context::set('category_list', $category_list);
}
else
{
$this->module_info->use_category = 'N';
Context::set('category_list', array());
}
} }
// GET parameter document_srl from request // GET parameter document_srl from request
@ -1191,6 +1210,69 @@ class boardView extends board
$this->setTemplateFile('update_view'); $this->setTemplateFile('update_view');
} }
function dispBoardVoteLog()
{
iF($this->grant->vote_log_view !== true)
{
return new Object(-1, 'msg_not_permitted');
}
$oMemberModel = getModel('member');
$target = Context::get('target');
$target_srl = Context::get('target_srl');
$args = new stdClass();
if($target === 'document')
{
$queryId = 'document.getDocumentVotedLog';
$args->document_srl = $target_srl;
}
elseif($target === 'comment')
{
$queryId = 'comment.getCommentVotedLog';
$args->comment_srl = $target_srl;
}
else
{
return new Object(-1, 'msg_not_target');
}
$output = executeQueryArray($queryId, $args);
if(!$output->toBool())
{
return $output;
}
$vote_member_infos = array();
$blame_member_infos = array();
if(count($output->data) > 0)
{
foreach($output->data as $key => $log)
{
if($log->point > 0)
{
if($log->member_srl == $vote_member_infos[$log->member_srl]->member_srl)
{
continue;
}
$vote_member_infos[$log->member_srl] = $oMemberModel->getMemberInfoByMemberSrl($log->member_srl);
}
else
{
if($log->member_srl == $blame_member_infos[$log->member_srl]->member_srl)
{
continue;
}
$blame_member_infos[$log->member_srl] = $oMemberModel->getMemberInfoByMemberSrl($log->member_srl);
}
}
}
Context::set('vote_member_info', $vote_member_infos);
Context::set('blame_member_info', $blame_member_infos);
$this->setTemplateFile('vote_log');
}
/** /**
* @brief the method for displaying the warning messages * @brief the method for displaying the warning messages
* display an error message if it has not a special design * display an error message if it has not a special design

View file

@ -24,6 +24,9 @@
<grant name="update_view" default="guest"> <grant name="update_view" default="guest">
<title xml:lang="ko">수정내역 조회</title> <title xml:lang="ko">수정내역 조회</title>
</grant> </grant>
<grant name="vote_log_view" default="guest">
<title xml:lang="ko">추천인 조회</title>
</grant>
<grant name="write_document" default="guest"> <grant name="write_document" default="guest">
<title xml:lang="ko">글 작성</title> <title xml:lang="ko">글 작성</title>
<title xml:lang="zh-CN">发表新主题</title> <title xml:lang="zh-CN">发表新主题</title>
@ -72,6 +75,7 @@
<action name="dispBoardContentCommentList" type="view" /> <action name="dispBoardContentCommentList" type="view" />
<action name="dispBoardContentFileList" type="view" /> <action name="dispBoardContentFileList" type="view" />
<action name="dispBoardUpdateLog" type="view" /> <action name="dispBoardUpdateLog" type="view" />
<action name="dispBoardVoteLog" type="view" />
<action name="dispBoardTagList" type="view" /> <action name="dispBoardTagList" type="view" />
<action name="dispBoardWrite" type="view" standalone="false" /> <action name="dispBoardWrite" type="view" standalone="false" />

View file

@ -39,9 +39,21 @@ $lang->comment_status = 'Comments';
$lang->category_settings = 'Category settings'; $lang->category_settings = 'Category settings';
$lang->hide_category = 'Hide categories'; $lang->hide_category = 'Hide categories';
$lang->about_hide_category = 'You can disable a category feature.'; $lang->about_hide_category = 'You can disable a category feature.';
$lang->protect_content = 'Protect contents'; $lang->protect_content = 'Protect Content';
$lang->about_protect_content = 'If there is any comment on document, document\'s owner cannot modify or delete that.'; $lang->protect_comment = 'Protect Comment';
$lang->msg_protect_content = 'You cannot modify or delete document which has any comment on it.'; $lang->protect_regdate = 'Update/Delete Time Limit';
$lang->about_protect_regdate = 'Prevent updating or deleting a document or comment after a certain amount of time has passed. (Unit: day)';
$lang->about_protect_content = 'Prevent updating a document if there are comments on it.';
$lang->msg_protect_delete_content = 'You cannot delete a document with comments on it.';
$lang->msg_protect_update_content = 'You cannot update a document with comments on it.';
$lang->msg_admin_document_no_modify = 'You cannot edit the administrator\'s document.';
$lang->msg_admin_comment_no_modify = 'You cannot edit the administrator\'s comment.';
$lang->msg_board_delete_protect_comment = 'You cannot delete a comment when there are replies.';
$lang->msg_board_update_protect_comment = 'You cannot update a comment when there are replies.';
$lang->msg_protect_regdate_document = 'You cannot update or delete a document after %d days.';
$lang->msg_protect_regdate_comment = 'You cannot update or delete a comment after %d days.';
$lang->msg_dont_have_update_log = 'This document has no update log.';
$lang->original_letter = 'Original';
$lang->comment_delete_message = 'Leave Placeholder for Deleted Comment'; $lang->comment_delete_message = 'Leave Placeholder for Deleted Comment';
$lang->about_comment_delete_message = 'When a comment is deleted, leave a placeholder saying that it has been deleted.'; $lang->about_comment_delete_message = 'When a comment is deleted, leave a placeholder saying that it has been deleted.';
$lang->cmd_only_p_comment = 'Only if there are replies'; $lang->cmd_only_p_comment = 'Only if there are replies';

View file

@ -48,16 +48,17 @@ $lang->about_hide_category = '임시로 분류를 사용하지 않으려면 체
$lang->protect_content = '글 보호 기능'; $lang->protect_content = '글 보호 기능';
$lang->protect_comment = '댓글 보호 기능'; $lang->protect_comment = '댓글 보호 기능';
$lang->protect_regdate = '기간 제한 기능'; $lang->protect_regdate = '기간 제한 기능';
$lang->about_protect_regdate = '작성된 글이나 댓글의 작성기간이 설정한 기간보다 이전일일 경우 글을 수정 또는 삭제할 수 없도록 합니다. (단위 : day)'; $lang->non_login_vote = '비 로그인 추천기능';
$lang->about_protect_content = '작성된 글에 댓글이 작성된 경우 글 작성자는 해당 글을 수정 또는 삭제를 할 수 없습니다.'; $lang->about_protect_regdate = '글이나 댓글을 작성한 후 일정 기간이 지나면 수정 또는 삭제할 수 없도록 합니다. (단위 : day)';
$lang->msg_protect_delete_content = '작성된 글에 댓글이 작성된 경우 글 작성자는 해당 글을 삭제할 수 없습니다. '; $lang->about_protect_content = '댓글이 달린 글은 수정 또는 삭제할 수 없도록 합니다.';
$lang->msg_protect_update_content = '작성된 글에 댓글이 작성된 경우 글 작성자는 해당 글을 수정할 수 없습니다. '; $lang->msg_protect_delete_content = '댓글이 달린 글은 삭제할 수 없습니다.';
$lang->msg_protect_update_content = '댓글이 달린 글은 수정할 수 없습니다.';
$lang->msg_admin_document_no_modify = '최고관리자의 게시물을 수정할 권한이 없습니다.'; $lang->msg_admin_document_no_modify = '최고관리자의 게시물을 수정할 권한이 없습니다.';
$lang->msg_admin_comment_no_modify = '최고관리자의 댓글을 수정할 권한이 없습니다.'; $lang->msg_admin_comment_no_modify = '최고관리자의 댓글을 수정할 권한이 없습니다.';
$lang->msg_board_delete_protect_comment = '댓글이 작성된 댓글의 글을 삭제할 수 없습니다.'; $lang->msg_board_delete_protect_comment = '대댓글이 달린 댓글은 삭제할 수 없습니다.';
$lang->msg_board_update_protect_comment = '댓글이 작성된 댓글의 글을 수정할 수 없습니다.'; $lang->msg_board_update_protect_comment = '대댓글이 달린 댓글은 수정할 수 없습니다.';
$lang->msg_protect_regdate_document = '%s일 이전의 게시글은 수정 또는 삭제 할 수 없습니다.'; $lang->msg_protect_regdate_document = '%d일 이상 지난 글은 수정 또는 삭제할 수 없습니다.';
$lang->msg_protect_regdate_comment = '%s일 이전의 댓글은 수정 또는 삭제 할 수 없습니다.'; $lang->msg_protect_regdate_comment = '%d일 이상 지난 댓글은 수정 또는 삭제할 수 없습니다.';
$lang->msg_dont_have_update_log = '업데이트 로그가 기록되어 있지 않은 게시글입니다.'; $lang->msg_dont_have_update_log = '업데이트 로그가 기록되어 있지 않은 게시글입니다.';
$lang->original_letter = '원본글'; $lang->original_letter = '원본글';
$lang->msg_warning_update_log = '<span class="x_label x_label-important">주의!</span> 사용시 디비가 많이 늘어날 수 있습니다.'; $lang->msg_warning_update_log = '<span class="x_label x_label-important">주의!</span> 사용시 디비가 많이 늘어날 수 있습니다.';
@ -78,3 +79,6 @@ $lang->cmd_all_comment_message = '모든 댓글에 남김';
$lang->cmd_do_not_message = '남기지 않음'; $lang->cmd_do_not_message = '남기지 않음';
$lang->delete_placeholder = '완전 삭제'; $lang->delete_placeholder = '완전 삭제';
$lang->msg_document_notify_mail = '[%s] 새로운 게시글이 등록되었습니다 : %s'; $lang->msg_document_notify_mail = '[%s] 새로운 게시글이 등록되었습니다 : %s';
$lang->cmd_document_vote_user = '이 글의 추천인 목록';
$lang->cmd_comment_vote_user = '이 댓글의 추천인 목록';
$lang->msg_not_target = '문서 또는 댓글의 추천인목록만 조회가능합니다.';

View file

@ -25,16 +25,7 @@
<input type="hidden" name="comment_srl" value="{$comment->get('comment_srl')}" /> <input type="hidden" name="comment_srl" value="{$comment->get('comment_srl')}" />
</form> </form>
<!--@else--> <!--@else-->
<div class="xe_content"> {$comment->getContent(false)}
<!--@if($comment->status == 7)-->
{$lang->msg_deleted_comment}
<!--@elseif($comment->status == 8)-->
{$lang->msg_admin_deleted_comment}
<!--@end-->
</div>
<block cond="$comment->status < 7">
{$comment->getContent(false)}
</block>
<!--@end--> <!--@end-->
<div cond="$comment->hasUploadedFiles()" class="fileList"> <div cond="$comment->hasUploadedFiles()" class="fileList">
<button type="button" class="toggleFile" onclick="jQuery(this).next('ul.files').toggle();"><i class="xi-diskette"></i> {$lang->uploaded_file} [<strong>{$comment->get('uploaded_count')}</strong>]</button> <button type="button" class="toggleFile" onclick="jQuery(this).next('ul.files').toggle();"><i class="xi-diskette"></i> {$lang->uploaded_file} [<strong>{$comment->get('uploaded_count')}</strong>]</button>
@ -42,7 +33,8 @@
<li loop="$comment->getUploadedFiles()=>$key,$file"><a href="{getUrl('')}{$file->download_url}">{$file->source_filename} <span class="fileSize">[File Size:{FileHandler::filesize($file->file_size)}/Download:{number_format($file->download_count)}]</span></a></li> <li loop="$comment->getUploadedFiles()=>$key,$file"><a href="{getUrl('')}{$file->download_url}">{$file->source_filename} <span class="fileSize">[File Size:{FileHandler::filesize($file->file_size)}/Download:{number_format($file->download_count)}]</span></a></li>
</ul> </ul>
</div> </div>
<p class="action" cond="$comment->status < 7"> <p class="action" cond="!$comment->isDeleted()">
<a href="{getUrl('act','dispBoardVoteLog','target_srl',$comment->comment_srl,'target','comment')}">{$lang->cmd_comment_vote_user}</a>
<a cond="$comment->getVote() === false || $comment->getVote() < 0" href="#" onclick="doCallModuleAction('comment','procCommentVoteUp','{$comment->comment_srl}');return false;"|cond="$is_logged" class="voted"><i class="xi-thumbs-up"></i>{$lang->cmd_vote}{$comment->get('voted_count')}</a> <a cond="$comment->getVote() === false || $comment->getVote() < 0" href="#" onclick="doCallModuleAction('comment','procCommentVoteUp','{$comment->comment_srl}');return false;"|cond="$is_logged" class="voted"><i class="xi-thumbs-up"></i>{$lang->cmd_vote}{$comment->get('voted_count')}</a>
<a cond="$comment->getVote() > 0" href="#" onclick="doCallModuleAction('comment','procCommentVoteUpCancel','{$comment->comment_srl}');return false;"|cond="$is_logged" class="voted"><i class="xi-thumbs-up"></i>{$lang->cmd_vote}{$comment->get('voted_count')}</a> <a cond="$comment->getVote() > 0" href="#" onclick="doCallModuleAction('comment','procCommentVoteUpCancel','{$comment->comment_srl}');return false;"|cond="$is_logged" class="voted"><i class="xi-thumbs-up"></i>{$lang->cmd_vote}{$comment->get('voted_count')}</a>
<a cond="$comment->getVote() === false || $comment->getVote() > 0" href="#" onclick="doCallModuleAction('comment','procCommentVoteDown','{$comment->comment_srl}');return false;"|cond="$is_logged" class="voted"><i class="xi-thumbs-up"></i>{$lang->cmd_vote_down}{$comment->get('blamed_count')}</a> <a cond="$comment->getVote() === false || $comment->getVote() > 0" href="#" onclick="doCallModuleAction('comment','procCommentVoteDown','{$comment->comment_srl}');return false;"|cond="$is_logged" class="voted"><i class="xi-thumbs-up"></i>{$lang->cmd_vote_down}{$comment->get('blamed_count')}</a>
@ -52,7 +44,7 @@
<a cond="$comment->isGranted()||!$comment->get('member_srl')" href="{getUrl('act','dispBoardDeleteComment','comment_srl',$comment->comment_srl)}" class="delete"><i class="xi-trash"></i> {$lang->cmd_delete}</a> <a cond="$comment->isGranted()||!$comment->get('member_srl')" href="{getUrl('act','dispBoardDeleteComment','comment_srl',$comment->comment_srl)}" class="delete"><i class="xi-trash"></i> {$lang->cmd_delete}</a>
<a cond="$is_logged" class="comment_{$comment->comment_srl} this" href="#popup_menu_area" onclick="return false">{$lang->cmd_comment_do}</a> <a cond="$is_logged" class="comment_{$comment->comment_srl} this" href="#popup_menu_area" onclick="return false">{$lang->cmd_comment_do}</a>
</p> </p>
<p class="action" cond="$comment->status >= 7"> <p class="action" cond="$comment->isDeleted()">
<a cond="$grant->manager" href="{getUrl('act','dispBoardDeleteComment','comment_srl',$comment->comment_srl)}" class="delete"><i class="xi-trash"></i> {$lang->delete_placeholder}</a> <a cond="$grant->manager" href="{getUrl('act','dispBoardDeleteComment','comment_srl',$comment->comment_srl)}" class="delete"><i class="xi-trash"></i> {$lang->delete_placeholder}</a>
</p> </p>
</li> </li>

View file

@ -84,12 +84,12 @@
<div class="vote"> <div class="vote">
<ul> <ul>
<li> <li>
<a cond="$oDocument->getVoted() === false || $oDocument->getVoted() < 0" href="#" onclick="doCallModuleAction('document','procDocumentVoteUp','{$oDocument->document_srl}');return false;"|cond="$is_logged" class="voted"> <i class="xi-thumbs-up"></i><br>{$lang->cmd_vote} {$oDocument->get('voted_count')}</a> <a cond="$oDocument->getVoted() === false || $oDocument->getVoted() < 0" href="#" onclick="doCallModuleAction('document','procDocumentVoteUp','{$oDocument->document_srl}');return false;" class="voted"> <i class="xi-thumbs-up"></i><br>{$lang->cmd_vote} {$oDocument->get('voted_count')}</a>
<a cond="$oDocument->getVoted() > 0" href="#" onclick="doCallModuleAction('document','procDocumentVoteUpCancel','{$oDocument->document_srl}');return false;"|cond="$is_logged" class="voted"> <i class="xi-thumbs-up"></i><br>{$lang->cmd_vote} {$oDocument->get('voted_count')}</a> <a cond="$oDocument->getVoted() > 0" href="#" onclick="doCallModuleAction('document','procDocumentVoteUpCancel','{$oDocument->document_srl}');return false;" class="voted"> <i class="xi-thumbs-up"></i><br>{$lang->cmd_vote} {$oDocument->get('voted_count')}</a>
</li> </li>
<li> <li>
<a cond="$oDocument->getVoted() === false || $oDocument->getVoted() > 0" herf="#" onclick="doCallModuleAction('document','procDocumentVoteDown','{$oDocument->document_srl}');return false;"|cond="$is_logged" class="voted"> <i class="xi-thumbs-down"></i><br>{$lang->cmd_vote_down} {$oDocument->get('blamed_count')}</a> <a cond="$oDocument->getVoted() === false || $oDocument->getVoted() > 0" herf="#" onclick="doCallModuleAction('document','procDocumentVoteDown','{$oDocument->document_srl}');return false;" class="voted"> <i class="xi-thumbs-down"></i><br>{$lang->cmd_vote_down} {$oDocument->get('blamed_count')}</a>
<a cond="$oDocument->getVoted() < 0" herf="#" onclick="doCallModuleAction('document','procDocumentVoteDownCancel','{$oDocument->document_srl}');return false;"|cond="$is_logged" class="voted"> <i class="xi-thumbs-down"></i><br>{$lang->cmd_vote_down} {$oDocument->get('blamed_count')}</a> <a cond="$oDocument->getVoted() < 0" herf="#" onclick="doCallModuleAction('document','procDocumentVoteDownCancel','{$oDocument->document_srl}');return false;" class="voted"> <i class="xi-thumbs-down"></i><br>{$lang->cmd_vote_down} {$oDocument->get('blamed_count')}</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -115,6 +115,7 @@
<div cond="$oDocument->getSignature()" class="tx">{$oDocument->getSignature()}</div> <div cond="$oDocument->getSignature()" class="tx">{$oDocument->getSignature()}</div>
</div> </div>
<div class="btnArea"> <div class="btnArea">
<a class="btn" href="{getUrl('act','dispBoardVoteLog','target_srl',$oDocument->document_srl,'target','document')}"><i class="xi-list-ul"></i>{$lang->cmd_document_vote_user}</a>
<a cond="$update_view" class="btn" href="{getUrl('act','dispBoardUpdateLog','document_srl',$oDocument->document_srl,'comment_srl','')}"><i class="xi-list-ul"></i>{$lang->update_log}</a> <a cond="$update_view" class="btn" href="{getUrl('act','dispBoardUpdateLog','document_srl',$oDocument->document_srl,'comment_srl','')}"><i class="xi-list-ul"></i>{$lang->update_log}</a>
<a cond="$oDocument->isEditable()" class="btn" href="{getUrl('act','dispBoardWrite','document_srl',$oDocument->document_srl,'comment_srl','')}"><i class="xi-eraser"></i>{$lang->cmd_modify}</a> <a cond="$oDocument->isEditable()" class="btn" href="{getUrl('act','dispBoardWrite','document_srl',$oDocument->document_srl,'comment_srl','')}"><i class="xi-eraser"></i>{$lang->cmd_modify}</a>
<a cond="$oDocument->isEditable()" class="btn" href="{getUrl('act','dispBoardDelete','document_srl',$oDocument->document_srl,'comment_srl','')}"><i class="xi-trash"></i>{$lang->cmd_delete}</a> <a cond="$oDocument->isEditable()" class="btn" href="{getUrl('act','dispBoardDelete','document_srl',$oDocument->document_srl,'comment_srl','')}"><i class="xi-trash"></i>{$lang->cmd_delete}</a>

View file

@ -737,6 +737,10 @@
.feedback .xe_content *:first-child { .feedback .xe_content *:first-child {
margin-top: 0; margin-top: 0;
} }
.feedback .xe_content.is_deleted,
.feedback .xe_content.is_secret {
color: #999;
}
#trackback .xe_content { #trackback .xe_content {
color: #888; color: #888;
} }
@ -1127,4 +1131,25 @@
.board .secretForm .btn {border-radius: 0 2px 2px 0; } .board .secretForm .btn {border-radius: 0 2px 2px 0; }
.board .secretForm p {margin-bottom:5px;} .board .secretForm p {margin-bottom:5px;}
.vote-list {
display:block;
overflow-x: hidden;
overflow-y: auto;
margin: 0px !important;
padding: 20px;
color: rgb(34, 34, 34);
font-size: 15px;
font-family: "Open Sans","나눔바른고딕",NanumBarunGothic,"맑은 고딕","Malgun Gothic","돋움",Dotum,"애플 SD 산돌고딕 Neo","Apple SD Gothic Neo",AppleGothic,Helvetica,sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
background: rgb(255, 255, 255);
}
.votelog {
display:inline-block;
padding:2px 15px;
background:#00b0a2;
border-radius: 23px;
color:#fff;
text-decoration: inherit;
}

View file

@ -0,0 +1,14 @@
<include target="_header.html" />
<div class="vote-list">
<h2>추천인</h2>
<block loop="$vote_member_info => $val">
<a href="#popup_menu_area" class="votelog member_{$val->member_srl}" onclick="return false">{$val->nick_name}</a>
</block>
<h2>비추천인</h2>
<block loop="$blame_member_info => $val">
<a href="#popup_menu_area" class="votelog member_{$val->member_srl}" onclick="return false">{$val->nick_name}</a>
</block>
</div>
<include target="_footer.html" />

View file

@ -280,12 +280,11 @@
<select name="comment_delete_message" id="comment_delete_message"> <select name="comment_delete_message" id="comment_delete_message">
<option value="no" selected="selected"|cond="$module_info->comment_delete_message == 'no'">{$lang->cmd_do_not_message}</option> <option value="no" selected="selected"|cond="$module_info->comment_delete_message == 'no'">{$lang->cmd_do_not_message}</option>
<option value="yes" selected="selected"|cond="$module_info->comment_delete_message == 'yes'">{$lang->cmd_all_comment_message}</option> <option value="yes" selected="selected"|cond="$module_info->comment_delete_message == 'yes'">{$lang->cmd_all_comment_message}</option>
<option value="only_commnet" selected="selected"|cond="$module_info->comment_delete_message == 'only_commnet'">{$lang->cmd_only_p_comment}</option> <option value="only_comment" selected="selected"|cond="starts_with('only_comm', $module_info->comment_delete_message)">{$lang->cmd_only_p_comment}</option>
</select> </select>
<p class="x_help-block">{$lang->about_comment_delete_message}</p> <p class="x_help-block">{$lang->about_comment_delete_message}</p>
</div> </div>
</div> </div>
<div class="x_control-group"> <div class="x_control-group">
<label class="x_control-label">{$lang->status}</label> <label class="x_control-label">{$lang->status}</label>
<div class="x_controls"> <div class="x_controls">
@ -299,6 +298,18 @@
<p class="x_help-block">{$lang->about_use_status}</p> <p class="x_help-block">{$lang->about_use_status}</p>
</div> </div>
</div> </div>
<div class="x_control-group">
<label class="x_control-label">{$lang->non_login_vote}</label>
<div class="x_controls">
<label class="x_inline">
<input type="radio" id="non_login_vote_y" name="non_login_vote" value="Y" checked="checked"|cond="$module_info->non_login_vote == 'Y'" /> {$lang->use}
</label>
<label class="x_inline">
<input type="radio" id="non_login_vote_n" name="non_login_vote" value="N" checked="checked"|cond="$module_info->non_login_vote != 'Y'" /> {$lang->notuse}
</label>
<p class="x_help-block">{$lang->about_document_force_to_move}</p>
</div>
</div>
<div class="x_control-group"> <div class="x_control-group">
<label class="x_control-label" for="admin_mail">{$lang->admin_mail}</label> <label class="x_control-label" for="admin_mail">{$lang->admin_mail}</label>
<div class="x_controls"> <div class="x_controls">

View file

@ -97,7 +97,7 @@
<option value="mid" selected="selected"|cond="$search_target=='mid'">{$lang->mid}</option> <option value="mid" selected="selected"|cond="$search_target=='mid'">{$lang->mid}</option>
<option value="browser_title" selected="selected"|cond="$search_target=='browser_title'">{$lang->browser_title}</option> <option value="browser_title" selected="selected"|cond="$search_target=='browser_title'">{$lang->browser_title}</option>
</select> </select>
<input type="search" required name="search_keyword" value="{htmlspecialchars($search_keyword)}" /> <input type="search" name="search_keyword" value="{htmlspecialchars($search_keyword)}" />
<button class="x_btn x_btn-inverse" type="submit">{$lang->cmd_search}</button> <button class="x_btn x_btn-inverse" type="submit">{$lang->cmd_search}</button>
<a class="x_btn" href="{getUrl('', 'module', $module, 'act', $act)}">{$lang->cmd_cancel}</a> <a class="x_btn" href="{getUrl('', 'module', $module, 'act', $act)}">{$lang->cmd_cancel}</a>
</form> </form>

View file

@ -116,6 +116,9 @@ class commentAdminView extends comment
} }
Context::set('member_nick_name', $member_nick_neme); Context::set('member_nick_name', $member_nick_neme);
$security = new Security();
$security->encodeHTML('search_target', 'search_keyword');
// set the template // set the template
$this->setTemplatePath($this->module_path . 'tpl'); $this->setTemplatePath($this->module_path . 'tpl');
$this->setTemplateFile('comment_list'); $this->setTemplateFile('comment_list');

View file

@ -912,23 +912,36 @@ class commentController extends comment
*/ */
function updateCommentByDelete($obj, $is_admin = FALSE) function updateCommentByDelete($obj, $is_admin = FALSE)
{ {
$logged_info = Context::get('logged_info'); if (!$obj->comment_srl)
{
return new Object(-1, 'msg_invalid_request');
}
// call a trigger (before)
$output = ModuleHandler::triggerCall('comment.deleteComment', 'before', $comment);
if(!$output->toBool())
{
return $output;
}
// begin transaction // begin transaction
$oDB = DB::getInstance(); $oDB = DB::getInstance();
$oDB->begin(); $oDB->begin();
// If the case manager to delete comments, it indicated that the administrator deleted. // If the case manager to delete comments, it indicated that the administrator deleted.
$logged_info = Context::get('logged_info');
if($is_admin === true && $obj->member_srl !== $logged_info->member_srl) if($is_admin === true && $obj->member_srl !== $logged_info->member_srl)
{ {
$obj->content = lang('msg_admin_deleted_comment'); $obj->content = lang('msg_admin_deleted_comment');
$obj->status = 8; $obj->status = RX_STATUS_DELETED_BY_ADMIN;
} }
else else
{ {
$obj->content = lang('msg_deleted_comment'); $obj->content = lang('msg_deleted_comment');
$obj->status = RX_STATUS_DELETED;
} }
$obj->member_srl = 0; $obj->member_srl = 0;
unset($obj->last_update);
$output = executeQuery('comment.updateCommentByDelete', $obj); $output = executeQuery('comment.updateCommentByDelete', $obj);
if(!$output->toBool()) if(!$output->toBool())
{ {
@ -936,8 +949,8 @@ class commentController extends comment
return $output; return $output;
} }
// call a trigger by delete (after) // call a trigger (after)
ModuleHandler::triggerCall('comment.updateCommentByDelete', 'after', $obj); ModuleHandler::triggerCall('comment.deleteComment', 'after', $obj);
// update the number of comments // update the number of comments
$oCommentModel = getModel('comment'); $oCommentModel = getModel('comment');
@ -1116,6 +1129,9 @@ class commentController extends comment
$output = executeQuery('file.updateFileValid', $args); $output = executeQuery('file.updateFileValid', $args);
} }
// Remove the thumbnail file
Rhymix\Framework\Storage::deleteEmptyDirectory(RX_BASEDIR . sprintf('files/thumbnails/%s', getNumberingPath($comment_srl, 3)), true);
// commit // commit
$oDB->commit(); $oDB->commit();
@ -1372,13 +1388,15 @@ class commentController extends comment
{ {
return $output; return $output;
} }
$declared_count = ($output->data->declared_count) ? $output->data->declared_count : 0; $declared_count = ($output->data->declared_count) ? $output->data->declared_count : 0;
$declare_message = trim(htmlspecialchars($declare_message));
$trigger_obj = new stdClass(); $trigger_obj = new stdClass();
$trigger_obj->comment_srl = $comment_srl; $trigger_obj->comment_srl = $comment_srl;
$trigger_obj->declared_count = $declared_count; $trigger_obj->declared_count = $declared_count;
$trigger_obj->declare_message = $declare_message;
// Call a trigger (before) // Call a trigger (before)
$trigger_output = ModuleHandler::triggerCall('comment.declaredComment', 'before', $trigger_obj); $trigger_output = ModuleHandler::triggerCall('comment.declaredComment', 'before', $trigger_obj);
if(!$trigger_output->toBool()) if(!$trigger_output->toBool())
@ -1423,7 +1441,7 @@ class commentController extends comment
} }
$args->comment_srl = $comment_srl; $args->comment_srl = $comment_srl;
$args->declare_message = trim(htmlspecialchars($declare_message)); $args->declare_message = $declare_message;
$log_output = executeQuery('comment.getCommentDeclaredLogInfo', $args); $log_output = executeQuery('comment.getCommentDeclaredLogInfo', $args);
// session registered if log info contains report log. // session registered if log info contains report log.

View file

@ -151,6 +151,16 @@ class commentItem extends Object
return $this->get('is_secret') == 'Y' ? TRUE : FALSE; return $this->get('is_secret') == 'Y' ? TRUE : FALSE;
} }
function isDeleted()
{
return $this->get('status') == RX_STATUS_DELETED || $this->get('status') == RX_STATUS_DELETED_BY_ADMIN;
}
function isDeletedByAdmin()
{
return $this->get('status') == RX_STATUS_DELETED_BY_ADMIN;
}
function isAccessible() function isAccessible()
{ {
if($_SESSION['accessibled_comment'][$this->comment_srl]) if($_SESSION['accessibled_comment'][$this->comment_srl])
@ -311,12 +321,22 @@ class commentItem extends Object
*/ */
function getContentText($strlen = 0) function getContentText($strlen = 0)
{ {
if($this->isSecret() && !$this->isAccessible()) if($this->isDeletedByAdmin())
{ {
return lang('msg_is_secret'); $content = lang('msg_admin_deleted_comment');
}
elseif($this->isDeleted())
{
$content = lang('msg_deleted_comment');
}
elseif($this->isSecret() && !$this->isAccessible())
{
$content = lang('msg_is_secret');
}
else
{
$content = $this->get('content');
} }
$content = $this->get('content');
if($strlen) if($strlen)
{ {
@ -332,13 +352,27 @@ class commentItem extends Object
*/ */
function getContent($add_popup_menu = TRUE, $add_content_info = TRUE, $add_xe_content_class = TRUE) function getContent($add_popup_menu = TRUE, $add_content_info = TRUE, $add_xe_content_class = TRUE)
{ {
if($this->isSecret() && !$this->isAccessible()) if($this->isDeletedByAdmin())
{ {
return lang('msg_is_secret'); $content = lang('msg_admin_deleted_comment');
$additional_class = ' is_deleted is_deleted_by_admin';
}
elseif($this->isDeleted())
{
$content = lang('msg_deleted_comment');
$additional_class = ' is_deleted';
}
elseif($this->isSecret() && !$this->isAccessible())
{
$content = lang('msg_is_secret');
$additional_class = ' is_secret';
}
else
{
$content = $this->get('content');
$additional_class = '';
stripEmbedTagForAdmin($content, $this->get('member_srl'));
} }
$content = $this->get('content');
stripEmbedTagForAdmin($content, $this->get('member_srl'));
// when displaying the comment on the pop-up menu // when displaying the comment on the pop-up menu
if($add_popup_menu && Context::get('is_logged')) if($add_popup_menu && Context::get('is_logged'))
@ -351,21 +385,20 @@ class commentItem extends Object
// if additional information which can access contents is set // if additional information which can access contents is set
if($add_content_info) if($add_content_info)
{ {
$memberSrl = $this->get('member_srl'); $member_srl = $this->get('member_srl');
if($memberSrl < 0) if($member_srl < 0)
{ {
$memberSrl = 0; $member_srl = 0;
} }
$content = sprintf( $content = vsprintf('<!--BeforeComment(%d,%d)--><div class="comment_%d_%d xe_content%s">%s</div><!--AfterComment(%d,%d)-->', array(
'<!--BeforeComment(%d,%d)--><div class="comment_%d_%d xe_content">%s</div><!--AfterComment(%d,%d)-->', $this->comment_srl, $memberSrl, $this->comment_srl, $memberSrl, $content, $this->comment_srl, $memberSrl $this->comment_srl, $member_srl, $this->comment_srl, $member_srl, $additional_class, $content, $this->comment_srl, $member_srl
); ));
// xe_content class name should be specified although content access is not necessary.
} }
else else
{ {
if($add_xe_content_class) if($add_xe_content_class)
{ {
$content = sprintf('<div class="xe_content">%s</div>', $content); $content = sprintf('<div class="xe_content%s">%s</div>', $additional_class, $content);
} }
} }
@ -567,6 +600,24 @@ class commentItem extends Object
return; return;
} }
// Get thumbnai_type information from document module's configuration
if(!in_array($thumbnail_type, array('crop', 'ratio', 'none')))
{
$config = $GLOBALS['__document_config__'];
if(!$config)
{
$oDocumentModel = getModel('document');
$config = $oDocumentModel->getDocumentConfig();
$GLOBALS['__document_config__'] = $config;
}
$thumbnail_type = $config->thumbnail_type ?: 'crop';
}
if ($thumbnail_type === 'none')
{
return;
}
if($this->isSecret() && !$this->isGranted()) if($this->isSecret() && !$this->isGranted())
{ {
return; return;
@ -584,12 +635,6 @@ class commentItem extends Object
return; return;
} }
// get thumbail generation info on the doc module configuration.
if(!in_array($thumbnail_type, array('crop', 'ratio')))
{
$thumbnail_type = 'crop';
}
// Define thumbnail information // Define thumbnail information
$thumbnail_path = sprintf('files/thumbnails/%s', getNumberingPath($this->comment_srl, 3)); $thumbnail_path = sprintf('files/thumbnails/%s', getNumberingPath($this->comment_srl, 3));
$thumbnail_file = sprintf('%s%dx%d.%s.jpg', $thumbnail_path, $width, $height, $thumbnail_type); $thumbnail_file = sprintf('%s%dx%d.%s.jpg', $thumbnail_path, $width, $height, $thumbnail_type);

View file

@ -3,19 +3,8 @@
<table name="comments" /> <table name="comments" />
</tables> </tables>
<columns> <columns>
<column name="module_srl" var="module_srl" filter="number" default="0" />
<column name="member_srl" var="member_srl" /> <column name="member_srl" var="member_srl" />
<column name="parent_srl" var="parent_srl" filter="number" />
<column name="is_secret" var="is_secret" default="N" />
<column name="notify_message" var="notify_message" default="N" />
<column name="content" var="content" notnull="notnull" /> <column name="content" var="content" notnull="notnull" />
<column name="password" var="password" minlength="2" maxlength="60" />
<column name="user_id" var="user_id" />
<column name="user_name" var="user_name" default="" />
<column name="nick_name" var="nick_name" notnull="notnull" minlength="1" maxlength="40" />
<column name="email_address" var="email_address" filter="email" maxlength="250" />
<column name="homepage" var="homepage" filter="homepage" maxlength="250" />
<column name="uploaded_count" var="uploaded_count" default="0" />
<column name="last_update" var="last_update" default="curdate()" /> <column name="last_update" var="last_update" default="curdate()" />
<column name="status" var="status" default="1" /> <column name="status" var="status" default="1" />
</columns> </columns>

View file

@ -835,6 +835,10 @@ class communicationController extends communication
{ {
return new Object(); return new Object();
} }
if(!$oCommunicationModel->checkGrant($config->grant_send))
{
return new Object();
}
$mid = Context::get('cur_mid'); $mid = Context::get('cur_mid');
$member_srl = Context::get('target_srl'); $member_srl = Context::get('target_srl');

View file

@ -178,6 +178,10 @@ class communicationView extends communication
{ {
return $this->stop('msg_invalid_request'); return $this->stop('msg_invalid_request');
} }
if(!getModel('communication')->checkGrant($this->config->grant_send))
{
return $this->stop('msg_not_permitted');
}
// Error appears if not logged-in // Error appears if not logged-in
if(!Context::get('is_logged')) if(!Context::get('is_logged'))
@ -230,7 +234,7 @@ class communicationView extends communication
// set a signiture by calling getEditor of the editor module // set a signiture by calling getEditor of the editor module
$oEditorModel = getModel('editor'); $oEditorModel = getModel('editor');
$option = new stdClass(); $option = $oEditorModel->getEditorConfig();
$option->primary_key_name = 'receiver_srl'; $option->primary_key_name = 'receiver_srl';
$option->content_key_name = 'content'; $option->content_key_name = 'content';
$option->allow_fileupload = FALSE; $option->allow_fileupload = FALSE;

View file

@ -3,7 +3,7 @@ $lang->communication = '커뮤니케이션';
$lang->about_communication = '회원 간의 쪽지나 친구 관리 등 커뮤니케이션 기능을 수행합니다. 이 기능을 사용하려면 [설치된 애드온] &gt; [커뮤니케이션] 애드온을 활성화 하세요.'; $lang->about_communication = '회원 간의 쪽지나 친구 관리 등 커뮤니케이션 기능을 수행합니다. 이 기능을 사용하려면 [설치된 애드온] &gt; [커뮤니케이션] 애드온을 활성화 하세요.';
$lang->allow_message = '쪽지 수신 허용'; $lang->allow_message = '쪽지 수신 허용';
$lang->allow_message_type['Y'] = '전체 수신'; $lang->allow_message_type['Y'] = '전체 수신';
$lang->allow_message_type['N'] = '거부'; $lang->allow_message_type['N'] = '수신 거부';
$lang->allow_message_type['F'] = '친구만 허용'; $lang->allow_message_type['F'] = '친구만 허용';
$lang->message_box['R'] = '받은 쪽지함'; $lang->message_box['R'] = '받은 쪽지함';
$lang->message_box['S'] = '보낸 쪽지함'; $lang->message_box['S'] = '보낸 쪽지함';
@ -14,12 +14,12 @@ $lang->receiver = '받는이';
$lang->friend_group = '친구 그룹'; $lang->friend_group = '친구 그룹';
$lang->default_friend_group = '그룹 미지정'; $lang->default_friend_group = '그룹 미지정';
$lang->cmd_send_message = '쪽지 보내기'; $lang->cmd_send_message = '쪽지 보내기';
$lang->cmd_reply_message = '쪽지 답장'; $lang->cmd_reply_message = '답장 보내기';
$lang->cmd_view_friend = '친구 보기'; $lang->cmd_view_friend = '친구 보기';
$lang->cmd_add_friend = '친구 등록'; $lang->cmd_add_friend = '친구 등록';
$lang->cmd_message_box = '쪽지함'; $lang->cmd_message_box = '쪽지함';
$lang->cmd_view_message_box = '쪽지함 보기'; $lang->cmd_view_message_box = '쪽지함 보기';
$lang->cmd_store = '보관'; $lang->cmd_store = '보관함 이동';
$lang->cmd_add_friend_group = '친구 그룹 추가'; $lang->cmd_add_friend_group = '친구 그룹 추가';
$lang->cmd_rename_friend_group = '친구 그룹 이름 변경'; $lang->cmd_rename_friend_group = '친구 그룹 이름 변경';
$lang->cmd_delete_friend_group = '친구 그룹 삭제'; $lang->cmd_delete_friend_group = '친구 그룹 삭제';

Some files were not shown because too many files have changed in this diff Show more