Merge branch 'next' into next-db

This commit is contained in:
Kijin Sung 2020-06-26 10:26:29 +09:00
commit 99e74a0e20
49 changed files with 2269 additions and 240 deletions

View file

@ -0,0 +1,87 @@
<?php
namespace Rhymix\Framework\Drivers\Push;
/**
* The APNs (Apple) Push driver.
*/
class APNs extends Base implements \Rhymix\Framework\Drivers\PushInterface
{
/**
* Config keys used by this driver are stored here.
*/
protected static $_required_config = array('certificate', 'passphrase');
protected static $_optional_config = array();
/**
* Get the human-readable name of this Push driver.
*
* @return string
*/
public static function getName(): string
{
return 'iOS (APNs)';
}
/**
* Check if the current Push driver is supported on this server.
*
* This method returns true on success and false on failure.
*
* @return bool
*/
public static function isSupported(): bool
{
return true;
}
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param object $message
* @param array $tokens
* @return object
*/
public function send(\Rhymix\Framework\Push $message, array $tokens)
{
$output = new \stdClass;
$output->success = [];
$output->invalid = [];
$output->needUpdate = [];
// Set parameters
$local_cert = $this->_config['certificate'];
$passphrase = $this->_config['passphrase'];
$alert = [];
$alert['title'] = $message->getSubject();
$alert['body'] = $message->getContent();
$body['aps'] = array('alert' => $alert);
$payload = json_encode($body);
foreach($tokens as $token)
{
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $local_cert);
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 5, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if(!$fp)
{
$message->addError('Failed to connect socket - error code: '. $err .' - '. $errstr);
}
$msg = chr(0) . pack('n', 32) . pack('H*', $token) . pack('n', strlen($payload)) . $payload;
$result = fwrite($fp, $msg, strlen($msg));
if(!$result)
{
$message->addError('APNs return empty response.');
}
$output->success[] = $token;
fclose($fp);
}
return $output;
}
}

View file

@ -0,0 +1,97 @@
<?php
namespace Rhymix\Framework\Drivers\Push;
use stdClass;
/**
* The base class for other Push drivers.
*/
abstract class Base implements \Rhymix\Framework\Drivers\PushInterface
{
/**
* The configuration is stored here.
*/
protected $_config = null;
/**
* 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 Push driver, using the given settings.
*
* @param array $config
* @return object
*/
public static function getInstance(array $config): object
{
return new static($config);
}
/**
* Get the human-readable name of this Push driver.
*
* @return string
*/
public static function getName(): string
{
return class_basename(get_called_class());
}
/**
* Get the list of configuration fields required by this Push driver.
*
* @return array
*/
public static function getRequiredConfig(): array
{
return static::$_required_config;
}
/**
* Get the list of configuration fields optionally used by this Push driver.
*
* @return array
*/
public static function getOptionalConfig(): array
{
return static::$_optional_config;
}
/**
* Check if the current Push driver is supported on this server.
*
* This method returns true on success and false on failure.
*
* @return bool
*/
public static function isSupported(): bool
{
return false;
}
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param object $message
* @param array $tokens
* @return object
*/
public function send(\Rhymix\Framework\Push $message, array $tokens)
{
return new \stdClass;
}
}

View file

@ -0,0 +1,114 @@
<?php
namespace Rhymix\Framework\Drivers\Push;
/**
* The FCM (Google) Push driver.
*/
class FCM extends Base implements \Rhymix\Framework\Drivers\PushInterface
{
/**
* Config keys used by this driver are stored here.
*/
protected static $_required_config = array('api_key');
protected static $_optional_config = array();
/**
* Get the human-readable name of this Push driver.
*
* @return string
*/
public static function getName(): string
{
return 'Android (FCM)';
}
/**
* Check if the current Push driver is supported on this server.
*
* This method returns true on success and false on failure.
*
* @return bool
*/
public static function isSupported(): bool
{
return true;
}
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param object $message
* @param array $tokens
* @return object
*/
public function send(\Rhymix\Framework\Push $message, array $tokens)
{
$output = new \stdClass;
$output->success = [];
$output->invalid = [];
$output->needUpdate = [];
$url = 'https://fcm.googleapis.com/fcm/send';
$api_key = $this->_config['api_key'];
$headers = array(
'Authorization' => 'key=' . $api_key,
'Content-Type' => 'application/json',
);
// Set notification
$notification = [];
$notification['title'] = $message->getSubject();
$notification['body'] = $message->getContent();
if($message->getClickAction())
{
$notification['click_action'] = $message->getClickAction();
}
$chunked_token = array_chunk($tokens, 1000);
foreach($chunked_token as $token_unit)
{
$data = json_encode(array(
'registration_ids' => $token_unit,
'notification' => $notification,
'priority' => 'normal',
'data' => $message->getData() ?: new \stdClass,
));
$response = \FileHandler::getRemoteResource($url, $data, 5, 'POST', 'application/json', $headers);
if($response)
{
$decoded_response = json_decode($response);
if(!$decoded_response)
{
$message->addError('FCM return invalid json : '. $response);
return $output;
}
$results = $decoded_response->results ?: [];
foreach($results as $i => $result)
{
if($result->error)
{
$message->addError('FCM error code: '. $result->error);
$output->invalid[$token_unit[$i]] = $token_unit[$i];
}
else if($result->message_id && $result->registration_id)
{
$output->needUpdate[$token_unit[$i]] = $result->registration_id;
}
else
{
$output->success[$token_unit[$i]] = $result->message_id;
}
}
}
else
{
$message->addError('FCM return empty response.');
}
}
return $output;
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace Rhymix\Framework\Drivers;
/**
* The Push driver interface.
*/
interface PushInterface
{
/**
* Create a new instance of the current Push driver, using the given settings.
*
* @param array $config
* @return void
*/
public static function getInstance(array $config): object;
/**
* Get the human-readable name of this Push driver.
*
* @return string
*/
public static function getName(): string;
/**
* Get the list of configuration fields required by this Push driver.
*
* @return array
*/
public static function getRequiredConfig(): array;
/**
* Get the list of configuration fields optionally used by this Push driver.
*
* @return array
*/
public static function getOptionalConfig(): 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(): bool;
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param object $message
* @param array $tokens
* @return object
*/
public function send(\Rhymix\Framework\Push $message, array $tokens);
}

View file

@ -0,0 +1,141 @@
<?php
namespace Rhymix\Framework\Drivers\SMS;
/**
* The ApiStore SMS driver.
*/
class ApiStore extends Base implements \Rhymix\Framework\Drivers\SMSInterface
{
/**
* API specifications.
*/
protected static $_spec = array(
'max_recipients' => 500,
'sms_max_length' => 90,
'sms_max_length_in_charset' => 'CP949',
'lms_supported' => true,
'lms_supported_country_codes' => array(82),
'lms_max_length' => 2000,
'lms_max_length_in_charset' => 'CP949',
'lms_subject_supported' => true,
'lms_subject_max_length' => 60,
'mms_supported' => false,
'delay_supported' => true,
);
/**
* Config keys used by this driver are stored here.
*/
protected static $_required_config = array('api_user', 'api_key');
/**
* Check if the current SMS driver is supported on this server.
*
* This method returns true on success and false on failure.
*
* @return bool
*/
public static function isSupported()
{
return true;
}
/**
* Store the last response.
*/
protected $_last_response = '';
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param array $messages
* @param object $original
* @return bool
*/
public function send(array $messages, \Rhymix\Framework\SMS $original)
{
$status = true;
foreach ($messages as $i => $message)
{
$data = array();
$data['send_phone'] = $message->from;
$data['dest_phone'] = implode(',', $message->to);
$data['msg_body'] = strval($message->content);
if ($message->type !== 'SMS' && $message->subject)
{
$data['subject'] = $message->subject;
}
$result = $this->_apiCall(sprintf('message/%s', strtolower($message->type)), $data);
if (!$result)
{
$message->errors[] = 'ApiStore API returned invalid response: ' . $this->_getLastResponse();
$status = false;
}
if ($result->result_message !== 'OK')
{
$message->errors[] = 'ApiStore API error: ' . $result->result_code . ' ' . $result->result_message;
}
}
return $status;
}
/**
* API call.
*
* @param string $url
* @param array $data
* @param string $method (optional)
* @return object|false
*/
protected function _apiCall(string $url, array $data, string $method = 'POST')
{
// Build the request URL.
if ($data['version'])
{
$version = $data['version'];
unset($data['version']);
}
else
{
$version = 1;
}
$url = sprintf('http://api.apistore.co.kr/ppurio/%d/%s/%s', $version, trim($url, '/'), $this->_config['api_user']);
// Set the API key in the header.
$headers = array(
'x-waple-authorization' => $this->_config['api_key'],
);
// Send the API reqeust.
if ($method === 'GET')
{
if ($data)
{
$url .= '?' . http_build_query($data);
}
$this->_last_response = \FileHandler::getRemoteResource($url, null, 5, $method, null, $headers) ?: '';
}
else
{
$this->_last_response = \FileHandler::getRemoteResource($url, $data, 5, $method, null, $headers) ?: '';
}
$result = @json_decode($this->_last_response);
return $result ?: false;
}
/**
* Fetch the last API response.
*
* @return string
*/
protected function _getLastResponse()
{
return $this->_last_response;
}
}

View file

@ -0,0 +1,116 @@
<?php
namespace Rhymix\Framework\Drivers\SMS;
/**
* The Cafe24 SMS driver.
*/
class Cafe24 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' => 50,
'mms_supported' => false,
'delay_supported' => true,
);
/**
* Config keys used by this driver are stored here.
*/
protected static $_required_config = array('api_user', 'api_key');
/**
* Check if the current SMS driver is supported on this server.
*
* This method returns true on success and false on failure.
*
* @return bool
*/
public static function isSupported()
{
return true;
}
/**
* Send a message.
*
* This method returns true on success and false on failure.
*
* @param array $messages
* @param object $original
* @return bool
*/
public function send(array $messages, \Rhymix\Framework\SMS $original)
{
$status = true;
foreach ($messages as $i => $message)
{
// Authentication and basic information
$data = array();
$data['user_id'] = $this->_config['api_user'];
$data['secure'] = $this->_config['api_key'];
$data['nointeractive'] = 1;
if ($message->type === 'LMS')
{
$data['smsType'] = 'L';
}
// Sender and recipient
$from = explode('-', \Rhymix\Framework\Korea::formatPhoneNumber($message->from));
$data['sphone1'] = $from[0];
$data['sphone2'] = $from[1];
if (isset($from[2]))
{
$data['sphone3'] = $from[2];
}
$data['rphone'] = implode(',', array_map(function($num) {
return \Rhymix\Framework\Korea::formatPhoneNumber($num);
}, $message->to));
// Subject and content
if ($message->type === 'LMS' && $message->subject)
{
$data['subject'] = $message->subject;
}
$data['msg'] = $message->content;
// Set delay
if ($message->delay && $message->delay > time() + 600)
{
$data['rdate'] = gmdate('Ymd', $message->delay + (3600 * 9));
$data['rtime'] = gmdate('His', $message->delay + (3600 * 9));
}
// Send!
$url = 'https://sslsms.cafe24.com/sms_sender.php';
$result = \FileHandler::getRemoteResource($url, $data, 5, 'POST');
if(strval($result) === '')
{
$original->addError('Unknown API error while sending message ' . ($i + 1) . ' of ' . count($messages));
$status = false;
}
else
{
$result = explode(',', $result);
if ($result[0] !== 'success' && $result[0] !== 'reserved')
{
$original->addError('API error ' . $result[0] . ' while sending message ' . ($i + 1) . ' of ' . count($messages));
$status = false;
}
}
}
return $status;
}
}

View file

@ -0,0 +1,103 @@
<?php
namespace Rhymix\Framework\Drivers\SMS;
/**
* The Ppurio SMS driver.
*/
class Ppurio 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' => 30,
'mms_supported' => false,
'delay_supported' => true,
);
/**
* Config keys used by this driver are stored here.
*/
protected static $_required_config = array('api_user');
/**
* 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)
{
$status = true;
foreach ($messages as $i => $message)
{
// Authentication and basic information
$data = array();
$data['userid'] = $this->_config['api_user'];
// Sender and recipient
$data['callback'] = preg_replace('/[^0-9]/', '', $message->from);
$data['phone'] = implode('|', array_map(function($num) {
return preg_replace('/[^0-9]/', '', $num);
}, $message->to));
// Subject and content
if ($message->type === 'LMS' && $message->subject)
{
$data['subject'] = $message->subject;
}
$data['msg'] = $message->content;
// Set delay
if ($message->delay && $message->delay > time() + 600)
{
$data['appdate'] = gmdate('YmdHis', $message->delay + (3600 * 9));
}
// Send!
$url = 'https://www.ppurio.com/api/send_utf8_json.php';
$result = \FileHandler::getRemoteResource($url, $data, 5, 'POST');
if(strval($result) === '')
{
$original->addError('Unknown API error while sending message ' . ($i + 1) . ' of ' . count($messages));
$status = false;
}
else
{
$result = @json_decode($result);
if ($result->result !== 'ok')
{
$original->addError('API error (' . $result->result . ') while sending message ' . ($i + 1) . ' of ' . count($messages));
$status = false;
}
}
}
return $status;
}
}

483
common/framework/push.php Normal file
View file

@ -0,0 +1,483 @@
<?php
namespace Rhymix\Framework;
/**
* The Push class.
*/
class Push
{
/**
* Instance properties.
*/
protected $from = 0;
protected $to = array();
protected $subject = '';
protected $content = '';
protected $click_action = '';
protected $data = [];
protected $errors = array();
protected $success_tokens = array();
protected $deleted_tokens = array();
protected $updated_tokens = array();
protected $sent = false;
/**
* Static properties.
*/
protected static $_drivers = array();
/**
* Add a custom Push driver.
*
* @param string $name
* @param object $driver
* @return void
*/
public static function addDriver(string $name, Drivers\PushInterface $driver)
{
self::$_drivers[$name] = $driver;
}
/**
* Get the default driver.
*
* @param string $name
* @return object|null
*/
public static function getDriver(string $name)
{
if (isset(self::$_drivers[$name]))
{
return self::$_drivers[$name];
}
$driver_class = '\Rhymix\Framework\Drivers\Push\\' . $name;
if (class_exists($driver_class))
{
$driver_config = config('push.' . $name) ?: array();
return self::$_drivers[$name] = $driver_class::getInstance($driver_config);
}
else
{
return null;
}
}
/**
* Get the list of supported Push drivers.
*
* @return array
*/
public static function getSupportedDrivers()
{
$result = array();
foreach (Storage::readDirectory(__DIR__ . '/drivers/push', false) as $filename)
{
$driver_name = substr($filename, 0, -4);
$class_name = '\Rhymix\Framework\Drivers\Push\\' . $driver_name;
if ($class_name::isSupported())
{
$result[$driver_name] = array(
'name' => $class_name::getName(),
'required' => $class_name::getRequiredConfig(),
'optional' => $class_name::getOptionalConfig(),
);
}
}
foreach (self::$_drivers as $driver_name => $driver)
{
if ($driver->isSupported())
{
$result[$driver_name] = array(
'name' => $driver->getName(),
'required' => $driver->getRequiredConfig(),
'optional' => $driver->getOptionalConfig(),
);
}
}
ksort($result);
return $result;
}
/**
* The constructor.
*/
public function __construct()
{
}
/**
* Set the sender's member_srl.
*
* @param int $member_srl
* @return bool
*/
public function setFrom(int $member_srl): bool
{
$this->from = $member_srl;
return true;
}
/**
* Get the sender's phone number.
*
* @return int|null
*/
public function getFrom(): int
{
return intval($this->from);
}
/**
* Add a recipient.
*
* @param int $member_srl
* @return bool
*/
public function addTo(int $member_srl): bool
{
$this->to[] = $member_srl;
return true;
}
/**
* Get the list of recipients without country codes.
*
* @return array
*/
public function getRecipients(): array
{
return $this->to;
}
/**
* Set the subject.
*
* @param string $subject
* @return bool
*/
public function setSubject(string $subject): bool
{
$this->subject = utf8_trim(utf8_clean($subject));
return true;
}
/**
* Get the subject.
*
* @return string
*/
public function getSubject(): string
{
return $this->subject;
}
/**
* Set the content.
*
* @param string $content
* @return bool
*/
public function setContent(string $content): bool
{
$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 getContent(): string
{
return $this->content;
}
/**
* Set an click-action to associate with this push notification.
*
* @param string $click_action
* @return bool
*/
public function setClickAction(string $click_action): bool
{
$this->click_action = utf8_trim(utf8_clean($click_action));
return true;
}
/**
* Get the click-action associated with this push notification.
*
* @return string
*/
public function getClickAction(): string
{
return $this->click_action;
}
/**
* Set a data to associate with this push notification.
*
* @param array $data
* @return bool
*/
public function setData(array $data): bool
{
$this->data = $data;
return true;
}
/**
* Get the data associated with this push notification.
*
* @return array
*/
public function getData(): array
{
return $this->data;
}
/**
* Set a URL to associate with this push notification.
*
* @param string $url
* @return bool
*/
public function setURL(string $url): bool
{
$this->data['url'] = $url;
return true;
}
/**
* Get the URL associated with this push notification.
*
* @return string
*/
public function getURL(): string
{
return $this->data['url'];
}
/**
* Send the message.
*
* @return bool
*/
public function send(): bool
{
// 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('push.send', 'before', $this);
if(!$output->toBool())
{
$this->errors[] = $output->getMessage();
return false;
}
try
{
$tokens = $this->_getDeviceTokens();
$output = null;
// Android FCM
if(count($tokens->android))
{
$fcm_driver = $this->getDriver('fcm');
$output = $fcm_driver->send($this, $tokens->android);
$this->sent = count($output->success) ? true : false;
$this->success_tokens = $output ? $output->success : [];
$this->deleted_tokens = $output ? $output->invalid : [];
$this->updated_tokens = $output ? $output->needUpdate : [];
$this->_deleteInvalidTokens($output->invalid);
$this->_updateDeviceTokens($output->needUpdate);
}
// iOS APNs
if(count($tokens->ios))
{
$apns_driver =$this->getDriver('apns');
$output = $apns_driver->send($this, $tokens->ios);
$this->sent = count($output->success) ? true : false;
$this->success_tokens += $output ? $output->success : [];
$this->deleted_tokens += $output ? $output->invalid : [];
$this->updated_tokens += $output ? $output->needUpdate : [];
$this->_deleteInvalidTokens($output->invalid);
$this->_updateDeviceTokens($output->needUpdate);
}
}
catch(\Exception $e)
{
$this->errors[] = class_basename($e) . ': ' . $e->getMessage();
$this->sent = false;
}
$output = \ModuleHandler::triggerCall('push.send', 'after', $this);
if(!$output->toBool())
{
$this->errors[] = $output->getMessage();
}
return $this->sent;
}
/**
* Get the device token
*
* @return object
*
*/
protected function _getDeviceTokens()
{
$member_srl_list = $this->getRecipients();
$result = new \stdClass;
$result->android = [];
$result->ios = [];
$args = new \stdClass;
$args->member_srl = $member_srl_list;
$args->device_type = [];
$driver_types = config('push.types') ?: array();
if(!count($driver_types))
{
return $result;
}
if(isset($driver_types['fcm']))
{
$args->device_type[] = 'android';
}
if(isset($driver_types['apns']))
{
$args->device_type[] = 'ios';
}
$output = executeQueryArray('member.getMemberDeviceTokensByMemberSrl', $args);
if(!$output->toBool() || !$output->data)
{
return $result;
}
foreach($output->data as $row)
{
$result->{$row->device_type}[] = $row->device_token;
}
return $result;
}
/**
* Delete the device toekn
*
* @param array
* @return void
*/
protected function _deleteInvalidTokens(array $invalid_tokens)
{
if(!count($invalid_tokens))
{
return;
}
$args = new \stdClass;
$args->device_token = $invalid_tokens;
executeQueryArray('member.deleteMemberDevice', $args);
}
/**
* Update the device toekn
*
* @param array
* @return void
*/
protected function _updateDeviceTokens(array $update_tokens)
{
$args = new \stdClass;
foreach($update_tokens as $key => $value)
{
$args->old_token = $key;
$args->new_token = $value;
executeQueryArray('member.updateMemberDevice', $args);
}
}
/**
* Check if the message was sent.
*
* @return bool
*/
public function isSent(): bool
{
return $this->sent;
}
/**
* Get caller information.
*
* @return string
*/
public function getCaller(): string
{
return $this->caller;
}
/**
* Get errors.
*
* @return array
*/
public function getErrors(): array
{
return $this->errors;
}
/**
* Get success tokens.
*
* @return array
*/
public function getSuccessTokens(): array
{
return $this->success_tokens;
}
/**
* Get deleted tokens.
*
* @return array
*/
public function getDeletedTokens(): array
{
return $this->deleted_tokens;
}
/**
* Get updated tokens.
*
* @return array
*/
public function getUpdatedTokens(): array
{
return $this->updated_tokens;
}
/**
* Add an error message.
*
* @param string $message
* @return void
*/
public function addError(string $message)
{
$this->errors[] = $message;
}
}

View file

@ -14,6 +14,18 @@ class UA
protected static $_tablet_cache = array();
protected static $_robot_cache = array();
/**
* Windows version lookup table.
*/
protected static $_windows_versions = array(
'5.1' => 'XP',
'5.2' => 'XP',
'6.0' => 'Vista',
'6.1' => '7',
'6.2' => '8',
'6.3' => '8.1',
);
/**
* Check whether the current visitor is using a mobile device.
*
@ -181,6 +193,8 @@ class UA
'browser' => null,
'version' => null,
'os' => null,
'os_version' => null,
'device' => null,
'is_mobile' => null,
'is_tablet' => null,
'is_webview' => null,
@ -197,18 +211,36 @@ class UA
if ($matches[1] === 'Linux' && strpos($ua, 'Android') !== false)
{
$result->os = 'Android';
if (preg_match('#Android ([0-9\.]+);(?: ([^;]+) Build/)?#', $ua, $matches))
{
$result->os_version = $matches[1];
$result->device = $matches[2] ?: null;
}
}
elseif ($matches[1] === 'iPhone' || $matches[1] === 'iPad' || $matches[1] === 'iPod')
{
$result->os = 'iOS';
if (preg_match('#(i(?:Phone|P[ao]d)); (?:.+?) OS ([0-9_\.]+)#s', $ua, $matches))
{
$result->os_version = str_replace('_', '.', $matches[2]);
$result->device = $matches[1];
}
}
elseif ($matches[1] === 'Macintosh' || $matches[1] === 'OS X')
{
$result->os = 'macOS';
if (preg_match('#OS X ([0-9_\.]+)#', $ua, $matches))
{
$result->os_version = str_replace('_', '.', $matches[1]);
}
}
else
{
$result->os = $matches[1];
if (preg_match('#Windows NT ([0-9\.]+)#', $ua, $matches))
{
$result->os_version = isset(self::$_windows_versions[$matches[1]]) ? self::$_windows_versions[$matches[1]] : $matches[1];
}
}
}

View file

@ -115,6 +115,7 @@ $lang->tag = 'Tag';
$lang->mail = 'Mail';
$lang->email = 'E-mail';
$lang->sms = 'SMS';
$lang->push_notification = 'Push Notification';
$lang->allow_comment = 'Allow Comments';
$lang->lock_comment = 'Block Comments';
$lang->allow_trackback = 'Allow Trackbacks';

View file

@ -117,6 +117,7 @@ $lang->tag = '태그';
$lang->mail = '메일';
$lang->email = '이메일';
$lang->sms = 'SMS';
$lang->push_notification = '푸시 알림';
$lang->allow_comment = '댓글 허용';
$lang->lock_comment = '댓글 잠금';
$lang->allow_trackback = '엮인글 허용';

View file

@ -569,7 +569,7 @@ class adminAdminController extends admin
$conf_value = $vars->{'sms_' . $sms_driver . '_' . $conf_name} ?: null;
if (!$conf_value)
{
throw new Rhymix\Framework\Exception('msg_advanced_mailer_smtp_host_is_invalid');
throw new Rhymix\Framework\Exception('msg_advanced_mailer_sms_config_invalid');
}
$sms_driver_config[$conf_name] = $conf_value;
}
@ -579,6 +579,61 @@ class adminAdminController extends admin
$sms_driver_config[$conf_name] = $conf_value;
}
// Validate the selected Push drivers.
$push_config = array('types' => array());
$push_drivers = Rhymix\Framework\Push::getSupportedDrivers();
$push_driver_list = $vars->push_driver ?: [];
foreach ($push_driver_list as $driver_name)
{
if (array_key_exists($driver_name, $push_drivers))
{
$push_config['types'][$driver_name] = true;
}
else
{
throw new Rhymix\Framework\Exception('msg_advanced_mailer_sending_method_is_invalid');
}
}
// Validate the Push driver settings.
foreach ($push_drivers as $driver_name => $driver_definition)
{
foreach ($push_drivers[$driver_name]['required'] as $conf_name)
{
$conf_value = utf8_trim($vars->{'push_' . $driver_name . '_' . $conf_name}) ?: null;
if (!$conf_value && in_array($driver_name, $push_driver_list))
{
throw new Rhymix\Framework\Exception('msg_advanced_mailer_push_config_invalid');
}
$push_config[$driver_name][$conf_name] = $conf_value;
// Save certificates in a separate file and only store the filename in config.php.
if ($conf_name === 'certificate')
{
$filename = Rhymix\Framework\Config::get('push.' . $driver_name . '.certificate');
if (!$filename)
{
$filename = './files/config/' . $driver_name . '/cert-' . Rhymix\Framework\Security::getRandom(32) . '.pem';
}
if ($conf_value !== null)
{
Rhymix\Framework\Storage::write($filename, $conf_value);
$push_config[$driver_name][$conf_name] = $filename;
}
elseif (Rhymix\Framework\Storage::exists($filename))
{
Rhymix\Framework\Storage::delete($filename);
}
}
}
foreach ($push_drivers[$driver_name]['optional'] as $conf_name)
{
$conf_value = utf8_trim($vars->{'push_' . $driver_name . '_' . $conf_name}) ?: null;
$push_config[$driver_name][$conf_name] = $conf_value;
}
}
// Save advanced mailer config.
getController('module')->updateModuleConfig('advanced_mailer', (object)array(
'sender_name' => trim($vars->mail_default_name),
@ -606,6 +661,7 @@ class adminAdminController extends admin
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));
Rhymix\Framework\Config::set("push", $push_config);
if (!Rhymix\Framework\Config::save())
{
throw new Rhymix\Framework\Exception('msg_failed_to_save_config');

View file

@ -443,6 +443,18 @@ class adminAdminView extends admin
Context::set('sms_drivers', $sms_drivers);
Context::set('sms_driver', config('sms.type') ?: 'dummy');
// Load Push drivers.
$push_drivers = Rhymix\Framework\Push::getSupportedDrivers();
uasort($push_drivers, function($a, $b) { return strcmp($a['name'], $b['name']); });
Context::set('push_drivers', $push_drivers);
Context::set('push_config', config('push') ?: []);
$apns_certificate = false;
if ($apns_certificate_filename = config('push.apns.certificate'))
{
$apns_certificate = Rhymix\Framework\Storage::read($apns_certificate_filename);
}
Context::set('apns_certificate', $apns_certificate);
// Workaround for compatibility with older version of Amazon SES driver.
config('mail.ses.api_key', config('mail.ses.api_user'));
config('mail.ses.api_secret', config('mail.ses.api_pass'));

View file

@ -337,6 +337,60 @@
</div>
</section>
<section class="section">
<h2>{$lang->push_notification}</h2>
<div class="x_control-group">
<label class="x_control-label">{$lang->cmd_admin_sending_method}</label>
<div class="x_controls">
<!--@foreach($push_drivers as $driver_name => $driver_definition)-->
<label for="push_driver_{$driver_name}" class="x_inline"><input type="checkbox" name="push_driver[]" id="push_driver_{$driver_name}" value="{$driver_name}" checked="checked"|cond="isset($push_config['types'][$driver_name])" /> {$driver_definition['name']}</label>
<!--@end-->
</div>
</div>
<!--@foreach($push_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("push.$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="push_{$driver_name}_api_key">{$lang->cmd_advanced_mailer_fcm_api_key}</label>
<div class="x_controls">
<input type="password" name="push_{$driver_name}_api_key" id="push_{$driver_name}_api_key" value="{$conf_value|escape}" />
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'certificate')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="push_{$driver_name}_certificate">{$lang->cmd_advanced_mailer_apns_certificate}</label>
<div class="x_controls full-width">
<textarea name="push_{$driver_name}_certificate" id="push_{$driver_name}_certificate">{$apns_certificate|escape}</textarea>
</div>
</div>
<!--@end-->
<!--@if($conf_name === 'passphrase')-->
<div class="x_control-group hidden-by-default show-for-{$driver_name}">
<label class="x_control-label" for="push_{$driver_name}_passphrase">{$lang->cmd_advanced_mailer_apns_passphrase}</label>
<div class="x_controls">
<input type="password" name="push_{$driver_name}_passphrase" id="push_{$driver_name}_passphrase" value="{$conf_value|escape}" />
</div>
</div>
<!--@end-->
<!--@end-->
<!--@end-->
</section>
<div class="x_clearfix btnArea">
<div class="x_pull-right">

View file

@ -22,6 +22,8 @@ class Advanced_MailerAdminController extends Advanced_Mailer
$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);
$config->log_sent_push = toBool($vars->log_sent_push);
$config->log_push_errors = toBool($vars->log_push_errors);
$output = getController('module')->insertModuleConfig('advanced_mailer', $config);
if ($output->toBool())
{
@ -155,7 +157,7 @@ class Advanced_MailerAdminController extends Advanced_Mailer
$clear_before_days = intval(Context::get('clear_before_days'));
if (!in_array($status, array('success', 'error')))
{
throw new Rhymix\Framework\Exceptions\InvalidRequest;
$status = null;
}
if ($clear_before_days < 0)
{
@ -167,14 +169,7 @@ class Advanced_MailerAdminController extends Advanced_Mailer
$obj->regdate = date('YmdHis', time() - ($clear_before_days * 86400) + zgap());
$output = executeQuery('advanced_mailer.deleteMailLogs', $obj);
if ($status === 'success')
{
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminMailLog'));
}
else
{
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminMailErrors'));
}
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminMailLog', 'status', $status));
}
/**
@ -186,7 +181,7 @@ class Advanced_MailerAdminController extends Advanced_Mailer
$clear_before_days = intval(Context::get('clear_before_days'));
if (!in_array($status, array('success', 'error')))
{
throw new Rhymix\Framework\Exceptions\InvalidRequest;
$status = null;
}
if ($clear_before_days < 0)
{
@ -198,14 +193,31 @@ class Advanced_MailerAdminController extends Advanced_Mailer
$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', 'status', $status));
}
/**
* Clear old Push sending log.
*/
public function procAdvanced_mailerAdminClearSentPush()
{
$status = Context::get('status');
$clear_before_days = intval(Context::get('clear_before_days'));
if (!in_array($status, array('success', 'error')))
{
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSMSLog'));
$status = null;
}
else
if ($clear_before_days < 0)
{
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminSMSErrors'));
throw new Rhymix\Framework\Exceptions\InvalidRequest;
}
$obj = new stdClass();
$obj->status = $status;
$obj->regdate = date('YmdHis', time() - ($clear_before_days * 86400) + zgap());
$output = executeQuery('advanced_mailer.deletePushLogs', $obj);
$this->setRedirectUrl(getNotEncodedUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminPushLog', 'status', $status));
}
/**
@ -213,7 +225,6 @@ class Advanced_MailerAdminController extends Advanced_Mailer
*/
public function procAdvanced_MailerAdminTestSendMail()
{
$advanced_mailer_config = $this->getConfig();
$recipient_config = Context::gets('recipient_name', 'recipient_email');
$recipient_name = $recipient_config->recipient_name;
$recipient_email = $recipient_config->recipient_email;
@ -289,7 +300,6 @@ class Advanced_MailerAdminController extends Advanced_Mailer
*/
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'));
@ -335,4 +345,79 @@ class Advanced_MailerAdminController extends Advanced_Mailer
$this->add('test_result', Context::getLang('msg_advanced_mailer_test_success_sms'));
return;
}
/**
* Send a test Push Notification.
*/
public function procAdvanced_MailerAdminTestSendPush()
{
$recipient_user_id = Context::get('recipient_user_id');
$subject = trim(Context::get('subject'));
$content = trim(Context::get('content'));
$url = trim(Context::get('url'));
$member_info = MemberModel::getMemberInfoByUserID($recipient_user_id);
if (!$member_info || !$member_info->member_srl)
{
$this->add('test_result', 'Error: ' . Context::getLang('msg_advanced_mailer_recipient_user_id_not_found'));
return;
}
$args = new stdClass;
$args->member_srl = $member_info->member_srl;
$output = executeQueryArray('member.getMemberDeviceTokensByMemberSrl', $args);
if (!$output->toBool() || !count($output->data))
{
$this->add('test_result', 'Error: ' . Context::getLang('msg_advanced_mailer_recipient_has_no_devices'));
return;
}
if (!$subject)
{
$this->add('test_result', 'Error: ' . Context::getLang('msg_advanced_mailer_subject_is_empty'));
return;
}
if (!$content)
{
$this->add('test_result', 'Error: ' . Context::getLang('msg_advanced_mailer_content_is_empty'));
return;
}
if (!$url || !Rhymix\Framework\URL::isInternalURL($url))
{
$this->add('test_result', 'Error: ' . Context::getLang('msg_advanced_mailer_url_is_invalid'));
return;
}
try
{
$oPush = new Rhymix\Framework\Push;
$oPush->addTo($member_info->member_srl);
$oPush->setSubject($subject);
$oPush->setContent($content);
$oPush->setURL($url);
$result = $oPush->send();
if (!$result)
{
if (count($oPush->getErrors()))
{
$this->add('test_result', nl2br(htmlspecialchars(implode("\n", $oPush->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

@ -131,40 +131,14 @@ class Advanced_MailerAdminView extends Advanced_Mailer
public function dispAdvanced_MailerAdminMailLog()
{
$obj = new stdClass();
$obj->status = 'success';
$obj->status = preg_replace('/[^a-z]/', '', Context::get('status')) ?: null;
$obj->page = $page = Context::get('page') ?: 1;
$maillog = executeQueryArray('advanced_mailer.getMailLogByType', $obj);
$maillog = $maillog->toBool() ? $this->procMailLog($maillog->data) : array();
Context::set('advanced_mailer_log', $maillog);
Context::set('advanced_mailer_status', 'success');
Context::set('advanced_mailer_status', $obj->status);
$paging = $this->procPaging('success', 'mail', $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\Mail::getSupportedDrivers();
Context::set('sending_methods', $sending_methods);
$this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('mail_log');
}
/**
* Display the mail error log.
*/
public function dispAdvanced_MailerAdminMailErrors()
{
$obj = new stdClass();
$obj->status = 'error';
$obj->page = $page = Context::get('page') ?: 1;
$maillog = executeQueryArray('advanced_mailer.getMailLogByType', $obj);
$maillog = $maillog->toBool() ? $this->procMailLog($maillog->data) : array();
Context::set('advanced_mailer_log', $maillog);
Context::set('advanced_mailer_status', 'error');
$paging = $this->procPaging('error', 'mail', $page);
$paging = $this->procPaging($obj->status, 'mail', $page);
Context::set('total_count', $paging->total_count);
Context::set('total_page', $paging->total_page);
Context::set('page', $paging->page);
@ -199,14 +173,14 @@ class Advanced_MailerAdminView extends Advanced_Mailer
public function dispAdvanced_MailerAdminSMSLog()
{
$obj = new stdClass();
$obj->status = 'success';
$obj->status = preg_replace('/[^a-z]/', '', Context::get('status')) ?: null;
$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');
Context::set('advanced_mailer_status', $obj->status);
$paging = $this->procPaging('success', 'sms', $page);
$paging = $this->procPaging($obj->status, 'sms', $page);
Context::set('total_count', $paging->total_count);
Context::set('total_page', $paging->total_page);
Context::set('page', $paging->page);
@ -220,29 +194,38 @@ class Advanced_MailerAdminView extends Advanced_Mailer
}
/**
* Display the SMS error log.
* Display the Push test form.
*/
public function dispAdvanced_MailerAdminSMSErrors()
public function dispAdvanced_MailerAdminPushTest()
{
$advanced_mailer_config = $this->getConfig();
Context::set('advanced_mailer_config', $advanced_mailer_config);
$this->setTemplatePath($this->module_path.'tpl');
$this->setTemplateFile('push_test');
}
/**
* Display the Push log.
*/
public function dispAdvanced_MailerAdminPushLog()
{
$obj = new stdClass();
$obj->status = 'error';
$obj->status = preg_replace('/[^a-z]/', '', Context::get('status')) ?: null;
$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');
$pushlog = executeQueryArray('advanced_mailer.getPushLogByType', $obj);
$pushlog = $pushlog->toBool() ? $pushlog->data : array();
Context::set('advanced_mailer_log', $pushlog);
Context::set('advanced_mailer_status', $obj->status);
$paging = $this->procPaging('error', 'sms', $page);
$paging = $this->procPaging($obj->status, 'push', $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');
$this->setTemplateFile('push_log');
}
/**
@ -295,10 +278,14 @@ class Advanced_MailerAdminView extends Advanced_Mailer
{
$count = executeQuery('advanced_mailer.countMailLogByType', $args);
}
else
elseif ($type === 'sms')
{
$count = executeQuery('advanced_mailer.countSMSLogByType', $args);
}
else
{
$count = executeQuery('advanced_mailer.countPushLogByType', $args);
}
$total_count = $count->data->count;
$total_page = max(1, ceil($total_count / 20));

View file

@ -200,6 +200,10 @@ class Advanced_Mailer extends ModuleObject
{
$oModuleController->insertTrigger('sms.send', 'advanced_mailer', 'controller', 'triggerAfterSMSSend', 'after');
}
if (!$oModuleModel->getTrigger('push.send', 'advanced_mailer', 'controller', 'triggerAfterPushSend', 'after'))
{
$oModuleController->insertTrigger('push.send', 'advanced_mailer', 'controller', 'triggerAfterPushSend', 'after');
}
}
/**
@ -232,6 +236,10 @@ class Advanced_Mailer extends ModuleObject
{
return true;
}
if (!$oModuleModel->getTrigger('push.send', 'advanced_mailer', 'controller', 'triggerAfterPushSend', 'after'))
{
return true;
}
return false;
}

View file

@ -190,4 +190,52 @@ class Advanced_MailerController extends Advanced_Mailer
}
}
}
/**
* After Push send trigger.
*/
public function triggerAfterPushSend($push)
{
$config = $this->getConfig();
if (toBool($config->log_sent_push) || (toBool($config->log_push_errors) && count($push->getErrors())))
{
$obj = new \stdClass();
$obj->push_from = $push->getFrom();
$token_count = count($push->getSuccessTokens()) + count($push->getDeletedTokens()) + count($push->getUpdatedTokens());
$obj->push_to = sprintf('%d members, %d devices', count($push->getRecipients()), $token_count);
$obj->push_to .= "\n\n" . 'members: ' . implode(', ', $push->getRecipients());
if (count($push->getSuccessTokens()))
{
$obj->push_to .= "\n\n" . 'success: ' . "\n";
$obj->push_to .= implode("\n", array_keys($push->getSuccessTokens()));
}
if (count($push->getDeletedTokens()))
{
$obj->push_to .= "\n\n" . 'deleted: ' . "\n";
$obj->push_to .= implode("\n", array_keys($push->getDeletedTokens()));
}
if (count($push->getUpdatedTokens()))
{
$obj->push_to .= "\n\n" . 'updated: ' . "\n";
foreach ($push->getUpdatedTokens() as $from => $to)
{
$obj->push_to .= $from . ' => ' . $to . "\n";
}
}
$obj->subject = trim($push->getSubject());
$obj->content = trim($push->getContent());
$obj->calling_script = $push->getCaller();
$obj->success_count = count($push->getSuccessTokens());
$obj->deleted_count = count($push->getDeletedTokens());
$obj->updated_count = count($push->getUpdatedTokens());
$obj->status = $push->isSent() ? 'success' : 'error';
$obj->errors = count($push->getErrors()) ? implode("\n", $push->getErrors()) : null;
$output = executeQuery('advanced_mailer.insertPushLog', $obj);
if (!$output->toBool())
{
return $output;
}
}
}
}

View file

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="0.2">
<title xml:lang="ko">고급 메일 발송 모듈 (메일 및 SMS 관리)</title>
<title xml:lang="en">Advanced Mailer (with SMS)</title>
<title xml:lang="ko">메일, SMS 및 푸시 알림 관리 모듈</title>
<title xml:lang="en">Mail, SMS and Push Notification Manager</title>
<description xml:lang="ko">
라이믹스에서 발송하는 메일과 SMS를 기록하고 테스트하는 기능을 제공합니다.
라이믹스에서 발송하는 메일, SMS, 푸시 알림 등을 기록하고 테스트하는 기능을 제공합니다.
</description>
<description xml:lang="en">
Log and test e-mails and SMS sent from Rhymix.
Log and test e-mails, SMS, and push notifications sent from Rhymix.
</description>
<version>2.1.0</version>
<date>2016-12-14</date>
<version>2.2.0</version>
<date>2020-06-25</date>
<author link="https://www.poesis.org">
<name xml:lang="ko">포에시스</name>
<name xml:lang="en">POESIS</name>

View file

@ -7,18 +7,20 @@
<action name="dispAdvanced_mailerAdminSpfDkim" type="view" />
<action name="dispAdvanced_mailerAdminMailTest" type="view" />
<action name="dispAdvanced_mailerAdminMailLog" 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="dispAdvanced_mailerAdminPushTest" type="view" />
<action name="dispAdvanced_mailerAdminPushLog" type="view" />
<action name="procAdvanced_mailerAdminInsertConfig" type="controller" />
<action name="procAdvanced_mailerAdminInsertExceptions" type="controller" />
<action name="procAdvanced_mailerAdminCheckDNSRecord" type="controller" />
<action name="procAdvanced_mailerAdminClearSentMail" type="controller" />
<action name="procAdvanced_mailerAdminClearSentSMS" type="controller" />
<action name="procAdvanced_mailerAdminClearSentPush" type="controller" />
<action name="procAdvanced_mailerAdminTestSendMail" type="controller" />
<action name="procAdvanced_mailerAdminTestSendSMS" type="controller" />
<action name="procAdvanced_mailerAdminTestSendPush" type="controller" />
</actions>
<menus>
<menu name="advanced_mailer" type="all">

View file

@ -1,5 +1,5 @@
<?php
$lang->cmd_advanced_mailer = 'Advanced Mailer (with SMS)';
$lang->cmd_advanced_mailer = 'Mail, SMS and Push Notification Manager';
$lang->cmd_advanced_mailer_general_config = 'General settings';
$lang->cmd_advanced_mailer_is_enabled = 'Enable module';
$lang->cmd_advanced_mailer_is_enabled_yes = 'Enabled';
@ -9,6 +9,8 @@ $lang->cmd_advanced_mailer_log_mail = 'Log Mail';
$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_push = 'Log Push';
$lang->cmd_advanced_mailer_log_push_errors = 'Log Push Errors';
$lang->cmd_advanced_mailer_log_yes = 'Yes';
$lang->cmd_advanced_mailer_log_no = 'No';
$lang->cmd_advanced_mailer_sending_method_config = 'Default Sending Method';
@ -35,6 +37,9 @@ $lang->cmd_advanced_mailer_api_type_free = 'Free account';
$lang->cmd_advanced_mailer_api_type_paid = 'Paid account';
$lang->cmd_advanced_mailer_api_user = 'Username';
$lang->cmd_advanced_mailer_api_pass = 'Password';
$lang->cmd_advanced_mailer_fcm_api_key = 'FCM API key';
$lang->cmd_advanced_mailer_apns_certificate = 'APNs certificate file';
$lang->cmd_advanced_mailer_apns_passphrase = 'APNs certificate passphrase';
$lang->cmd_advanced_mailer_sender_key = 'Sender key';
$lang->cmd_advanced_mailer_sender_identity = 'Sender Identity';
$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.';
@ -67,6 +72,7 @@ $lang->cmd_advanced_mailer_ellipsis = '(see API for full value)';
$lang->cmd_advanced_mailer_mail_test = 'Mail Test';
$lang->cmd_advanced_mailer_recipient_name = 'Recipient\'s name';
$lang->cmd_advanced_mailer_recipient_email = 'Recipient\'s email';
$lang->cmd_advanced_mailer_recipient_user_id = 'Recipient\'s user ID';
$lang->cmd_advanced_mailer_send = 'Send';
$lang->cmd_advanced_mailer_test_result = 'Test result';
$lang->cmd_advanced_mailer_exception_domains = 'Exception domains';
@ -101,14 +107,19 @@ $lang->msg_advanced_mailer_recipient_name_is_empty = 'Please enter the recipient
$lang->msg_advanced_mailer_recipient_email_is_empty = 'Please enter the recipient\'s email address.';
$lang->msg_advanced_mailer_recipient_email_is_invalid = 'The recipient\'s email address is invalid.';
$lang->msg_advanced_mailer_test_success = 'The test was successful. Please check your email.';
$lang->cmd_advanced_mailer_test_push_subject = 'Test Notification';
$lang->cmd_advanced_mailer_test_push_content = 'This is a test notification.';
$lang->msg_advanced_mailer_google_account_security = 'Either your login credentials are incorrect, or the SMTP connection was blocked by Google account security settings.<br />Please <a href="https://support.google.com/mail/answer/14257" target="_blank">see here</a> for more information.';
$lang->msg_advanced_mailer_naver_smtp_disabled = 'Either your login credentials are incorrect, or POP3/SMTP is not enabled on your Naver account.';
$lang->msg_advanced_mailer_sms_config_invalid = 'There are errors or omissions in the SMS API configuration.';
$lang->msg_advanced_mailer_push_config_invalid = 'There are errors or omissions in the push notification configuration.';
$lang->msg_advanced_mailer_unknown_error = 'An unknown error occurred.';
$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_recipient = 'Recipient';
$lang->cmd_advanced_mailer_status_subject = 'Subject';
$lang->cmd_advanced_mailer_status_content = 'Content';
$lang->cmd_advanced_mailer_status_url = 'URL';
$lang->cmd_advanced_mailer_status_sending_method = 'Method';
$lang->cmd_advanced_mailer_status_time = 'Time';
$lang->cmd_advanced_mailer_status = 'Status';
@ -125,6 +136,14 @@ $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_recipient_user_id_not_found = 'No member matches the user ID you entered.';
$lang->msg_advanced_mailer_recipient_has_no_devices = 'The user ID you entered has not registered any mobile devices.';
$lang->msg_advanced_mailer_subject_is_empty = 'Please enter the subject for your test message.';
$lang->msg_advanced_mailer_content_is_empty = 'Please enter the content for your test message.';
$lang->msg_advanced_mailer_url_is_invalid = 'Please enter a valid URL that belongs to this site.';
$lang->msg_advanced_mailer_test_success_sms = 'The test was successful. Please check your SMS.';
$lang->cmd_advanced_mailer_push_test = 'Push Test';
$lang->cmd_advanced_mailer_success_count = 'Success';
$lang->cmd_advanced_mailer_deleted_count = 'Deleted';
$lang->cmd_advanced_mailer_updated_count = 'Updated';
$lang->cmd_advanced_mailer_not_rhymix = 'This module is for XE. It is incompatible with Rhymix. Please use the version included with Rhymix.';

View file

@ -1,5 +1,5 @@
<?php
$lang->cmd_advanced_mailer = '고급 메일 발송 모듈 (메일 및 SMS 관리)';
$lang->cmd_advanced_mailer = '메일, SMS 및 푸시 알림 관리 모듈';
$lang->cmd_advanced_mailer_general_config = '기본 설정';
$lang->cmd_advanced_mailer_is_enabled = '모듈 사용';
$lang->cmd_advanced_mailer_is_enabled_yes = '사용';
@ -9,6 +9,8 @@ $lang->cmd_advanced_mailer_log_mail = '메일 발송 내역';
$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_push = '푸시 알림 발송 내역';
$lang->cmd_advanced_mailer_log_push_errors = '푸시 알림 에러 내역';
$lang->cmd_advanced_mailer_log_yes = '기록';
$lang->cmd_advanced_mailer_log_no = '기록하지 않음';
$lang->cmd_advanced_mailer_sending_method_config = '기본 발송 방법 설정';
@ -35,6 +37,9 @@ $lang->cmd_advanced_mailer_api_type_free = '무료';
$lang->cmd_advanced_mailer_api_type_paid = '유료';
$lang->cmd_advanced_mailer_api_user = '아이디';
$lang->cmd_advanced_mailer_api_pass = '비밀번호';
$lang->cmd_advanced_mailer_fcm_api_key = 'FCM API 키';
$lang->cmd_advanced_mailer_apns_certificate = 'APNs 인증서 파일';
$lang->cmd_advanced_mailer_apns_passphrase = 'APNs 인증서 암호';
$lang->cmd_advanced_mailer_sender_key = '센더 키';
$lang->cmd_advanced_mailer_sender_identity = '보낸이 설정';
$lang->cmd_advanced_mailer_about_sender_identity = '보낸이의 이름과 메일 주소는 <a href="index.php?module=admin&act=dispAdminConfigNotification" target="_blank">알림 설정</a> 화면에서 변경할 수 있습니다.';
@ -67,6 +72,7 @@ $lang->cmd_advanced_mailer_ellipsis = '(중략)';
$lang->cmd_advanced_mailer_mail_test = '메일 테스트';
$lang->cmd_advanced_mailer_recipient_name = '받는이 이름';
$lang->cmd_advanced_mailer_recipient_email = '받는이 메일 주소';
$lang->cmd_advanced_mailer_recipient_user_id = '받는이 아이디';
$lang->cmd_advanced_mailer_send = '발송';
$lang->cmd_advanced_mailer_test_result = '테스트 결과';
$lang->cmd_advanced_mailer_exception_domains = '예외 도메인';
@ -101,14 +107,19 @@ $lang->msg_advanced_mailer_recipient_name_is_empty = '받는이 이름을 입력
$lang->msg_advanced_mailer_recipient_email_is_empty = '받는이 메일 주소를 입력해 주십시오.';
$lang->msg_advanced_mailer_recipient_email_is_invalid = '받는이 메일 주소가 올바른 메일 주소가 아닙니다.';
$lang->msg_advanced_mailer_test_success = '테스트에 성공하였습니다. 메일을 확인해 보시기 바랍니다.';
$lang->cmd_advanced_mailer_test_push_subject = '테스트 알림';
$lang->cmd_advanced_mailer_test_push_content = '테스트 알림 내용입니다.';
$lang->msg_advanced_mailer_google_account_security = '아이디 또는 비밀번호가 틀렸거나, 구글 보안 설정 때문에 SMTP 접속이 차단되었습니다.<br />자세한 정보는 <a href="https://support.google.com/mail/answer/14257?hl=ko" target="_blank">여기</a>를 참고하시기 바랍니다.';
$lang->msg_advanced_mailer_naver_smtp_disabled = '아이디 또는 비밀번호가 틀렸거나, 네이버 계정 환경설정에서 POP3/SMTP를 사용하지 않도록 설정되어 있습니다.';
$lang->msg_advanced_mailer_sms_config_invalid = 'SMS API 설정에 잘못되었거나 누락된 부분이 있습니다. 확인해 주십시오.';
$lang->msg_advanced_mailer_push_config_invalid = '푸시 알림 설정에 잘못되었거나 누락된 부분이 있습니다. 확인해 주십시오.';
$lang->msg_advanced_mailer_unknown_error = '알 수 없는 오류가 발생하였습니다.';
$lang->msg_advanced_mailer_log_is_empty = '표시할 항목이 없습니다.';
$lang->cmd_advanced_mailer_status_sender = '보낸이';
$lang->cmd_advanced_mailer_status_recipient = '받는이';
$lang->cmd_advanced_mailer_status_subject = '제목';
$lang->cmd_advanced_mailer_status_content = '내용';
$lang->cmd_advanced_mailer_status_url = 'URL';
$lang->cmd_advanced_mailer_status_sending_method = '발송 방법';
$lang->cmd_advanced_mailer_status_time = '발송 시간';
$lang->cmd_advanced_mailer_status = '상태';
@ -125,6 +136,14 @@ $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_recipient_user_id_not_found = '입력하신 아이디와 일치하는 회원을 찾을 수 없습니다.';
$lang->msg_advanced_mailer_recipient_has_no_devices = '입력하신 아이디로 등록된 모바일 기기가 없습니다.';
$lang->msg_advanced_mailer_subject_is_empty = '제목을 입력해 주십시오.';
$lang->msg_advanced_mailer_content_is_empty = '내용을 입력해 주십시오.';
$lang->msg_advanced_mailer_url_is_invalid = '사이트 내부의 URL을 입력해 주십시오.';
$lang->msg_advanced_mailer_test_success_sms = '테스트에 성공하였습니다. SMS를 확인해 보시기 바랍니다.';
$lang->cmd_advanced_mailer_push_test = '푸시 알림 테스트';
$lang->cmd_advanced_mailer_success_count = '성공';
$lang->cmd_advanced_mailer_deleted_count = '삭제';
$lang->cmd_advanced_mailer_updated_count = '변경';
$lang->cmd_advanced_mailer_not_rhymix = '이 모듈은 XE용으로, 라이믹스와는 호환되지 않습니다. 라이믹스에 기본 포함된 버전을 사용하시기 바랍니다.';

View file

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

View file

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

View file

@ -0,0 +1,17 @@
<query id="getPushLogByType" action="select">
<tables>
<table name="advanced_mailer_push_log" />
</tables>
<columns>
<column name="*" />
</columns>
<conditions>
<condition operation="equal" column="status" var="status" />
</conditions>
<navigation>
<index var="sort_index" default="push_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

@ -0,0 +1,18 @@
<query id="insertPushLog" action="insert">
<tables>
<table name="advanced_mailer_push_log" />
</tables>
<columns>
<column name="push_from" var="push_from" notnull="notnull" />
<column name="push_to" var="push_to" notnull="notnull" />
<column name="subject" var="subject" notnull="notnull" />
<column name="content" var="content" notnull="notnull" />
<column name="calling_script" var="calling_script" notnull="notnull" />
<column name="success_count" var="success_count" notnull="notnull" />
<column name="deleted_count" var="deleted_count" notnull="notnull" />
<column name="updated_count" var="updated_count" 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,14 @@
<table name="advanced_mailer_push_log">
<column name="push_id" type="number" size="11" notnull="notnull" primary_key="primary_key" auto_increment="auto_increment" />
<column name="push_from" type="varchar" size="250" notnull="notnull" />
<column name="push_to" type="bigtext" notnull="notnull" />
<column name="subject" type="varchar" size="250" notnull="notnull" />
<column name="content" type="text" notnull="notnull" />
<column name="calling_script" type="varchar" size="250" notnull="notnull" />
<column name="success_count" type="int" notnull="notnull" />
<column name="deleted_count" type="int" notnull="notnull" />
<column name="updated_count" type="int" 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

@ -9,8 +9,8 @@
<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_mailerAdminMailTest'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminMailTest')}">{$lang->cmd_advanced_mailer_mail_test}</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_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>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminPushTest'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminPushTest')}">{$lang->cmd_advanced_mailer_push_test}</a></li>
<li class="x_active"|cond="$act == 'dispAdvanced_mailerAdminPushLog'"><a href="{getUrl('', 'module', 'admin', 'act', 'dispAdvanced_mailerAdminPushLog')}">{$lang->cmd_advanced_mailer_log_push}</a></li>
</ul>

View file

@ -75,6 +75,26 @@
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_log_sent_push">{$lang->cmd_advanced_mailer_log_push}</label>
<div class="x_controls">
<select name="log_sent_push" id="advanced_mailer_log_sent_push">
<option value="Y" selected="selected"|cond="toBool($advanced_mailer_config->log_sent_push)" />{$lang->cmd_advanced_mailer_log_yes}</option>
<option value="N" selected="selected"|cond="!toBool($advanced_mailer_config->log_sent_push)" />{$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_push_errors}</label>
<div class="x_controls">
<select name="log_push_errors" id="advanced_mailer_log_push_errors">
<option value="Y" selected="selected"|cond="toBool($advanced_mailer_config->log_push_errors)" />{$lang->cmd_advanced_mailer_log_yes}</option>
<option value="N" selected="selected"|cond="!toBool($advanced_mailer_config->log_push_errors)" />{$lang->cmd_advanced_mailer_log_no}</option>
</select>
</div>
</div>
</section>
<div class="btnArea x_clearfix">

View file

@ -46,6 +46,29 @@
);
});
$("#advanced_mailer_test_send_push").click(function(event) {
event.preventDefault();
$("#advanced_mailer_test_result").text("");
$(this).attr("disabled", "disabled");
var ajax_data = {
recipient_user_id: $("#advanced_mailer_recipient_user_id").val(),
subject: $("#advanced_mailer_subject").val(),
content: $("#advanced_mailer_content").val(),
url: $("#advanced_mailer_url").val()
};
$.exec_json(
"advanced_mailer.procAdvanced_mailerAdminTestSendPush", 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");
}
);
});
});
})(jQuery);

View file

@ -4,7 +4,15 @@
<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>
<div style="float: left">
<a href="{getUrl('status', null)}" class="active"|cond="!$status">{$lang->all}</a> <i>|</i>
<a href="{getUrl('status', 'success')}" class="active"|cond="$status == 'success'">{$lang->cmd_advanced_mailer_status_success}</a> <i>|</i>
<a href="{getUrl('status', 'error')}" class="active"|cond="$status == 'error'">{$lang->cmd_advanced_mailer_status_error}</a>
</div>
<div style="float: right">
<strong>Total: {number_format($total_count)}, Page: {number_format($page)}/{number_format($total_page)}</strong>
</div>
<div class="clear: both"></div>
</caption>
<thead>
<tr>

View file

@ -0,0 +1,86 @@
<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>
<div style="float: left">
<a href="{getUrl('status', null)}" class="active"|cond="!$status">{$lang->all}</a> <i>|</i>
<a href="{getUrl('status', 'success')}" class="active"|cond="$status == 'success'">{$lang->cmd_advanced_mailer_status_success}</a> <i>|</i>
<a href="{getUrl('status', 'error')}" class="active"|cond="$status == 'error'">{$lang->cmd_advanced_mailer_status_error}</a>
</div>
<div style="float: right">
<strong>Total: {number_format($total_count)}, Page: {number_format($page)}/{number_format($total_page)}</strong>
</div>
<div class="clear: both"></div>
</caption>
<thead>
<tr>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_status_recipient}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_status_subject}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_status_content}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_success_count}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_deleted_count}</th>
<th scope="col" class="nowr">{$lang->cmd_advanced_mailer_updated_count}</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 => $push_id, $val">
<td class="nowr">
{substr($val->push_to, 0, strpos($val->push_to, "\n"))}
</td>
<td class="nowr" style="white-space:normal">{nl2br(htmlspecialchars(cut_str($val->subject, 40)))}</td>
<td class="nowr" style="white-space:normal">{nl2br(htmlspecialchars(cut_str($val->content, 40)))}</td>
<td class="nowr">{number_format($val->success_count)}</td>
<td class="nowr">{number_format($val->deleted_count)}</td>
<td class="nowr">{number_format($val->updated_count)}</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_mailerAdminClearSentPush" />
<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,59 @@
<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_mailerAdminTestSendPush" />
<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_push_test}</h2>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_recipient_user_id">{$lang->cmd_advanced_mailer_recipient_user_id}</label>
<div class="x_controls">
<input type="text" id="advanced_mailer_recipient_user_id" value="{$this->user->user_id}" />
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_subject">{$lang->cmd_advanced_mailer_status_subject}</label>
<div class="x_controls">
<input type="text" id="advanced_mailer_subject" value="{$lang->cmd_advanced_mailer_test_push_subject}" />
</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_push_content}</textarea>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label" for="advanced_mailer_url">{$lang->cmd_advanced_mailer_status_url}</label>
<div class="x_controls">
<input type="text" id="advanced_mailer_url" value="{getFullUrl('')}" />
</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_push" type="submit" class="x_btn x_btn-primary x_pull-right">{$lang->cmd_advanced_mailer_send}</button>
</div>
</form>

View file

@ -4,7 +4,15 @@
<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>
<div style="float: left">
<a href="{getUrl('status', null)}" class="active"|cond="!$status">{$lang->all}</a> <i>|</i>
<a href="{getUrl('status', 'success')}" class="active"|cond="$status == 'success'">{$lang->cmd_advanced_mailer_status_success}</a> <i>|</i>
<a href="{getUrl('status', 'error')}" class="active"|cond="$status == 'error'">{$lang->cmd_advanced_mailer_status_error}</a>
</div>
<div style="float: right">
<strong>Total: {number_format($total_count)}, Page: {number_format($page)}/{number_format($total_page)}</strong>
</div>
<div class="clear: both"></div>
</caption>
<thead>
<tr>

View file

@ -1,89 +1,25 @@
<?php
/* Copyright (C) NAVER <http://www.navercorp.com> */
class boardMobile extends boardView
{
function init()
{
$oSecurity = new Security();
$oSecurity->encodeHTML('document_srl', 'comment_srl', 'vid', 'mid', 'page', 'category', 'search_target', 'search_keyword', 'sort_index', 'order_type', 'trackback_srl');
if($this->module_info->list_count) $this->list_count = $this->module_info->list_count;
if($this->module_info->mobile_list_count) $this->list_count = $this->module_info->mobile_list_count;
if($this->module_info->search_list_count) $this->search_list_count = $this->module_info->search_list_count;
if($this->module_info->mobile_search_list_count) $this->search_list_count = $this->module_info->mobile_search_list_count;
if($this->module_info->page_count) $this->page_count = $this->module_info->page_count;
if($this->module_info->mobile_page_count) $this->page_count = $this->module_info->mobile_page_count;
$this->except_notice = $this->module_info->except_notice == 'N' ? false : true;
// $this->_getStatusNameListecret option backward compatibility
$oDocumentModel = getModel('document');
$statusList = $this->_getStatusNameList($oDocumentModel);
if(isset($statusList['SECRET']))
{
$this->module_info->secret = 'Y';
}
// use_category <=1.5.x, hide_category >=1.7.x
$count_category = count($oDocumentModel->getCategoryList($this->module_info->module_srl));
if($count_category)
{
if($this->module_info->hide_category)
{
$this->module_info->use_category = ($this->module_info->hide_category == 'Y') ? 'N' : 'Y';
}
else if($this->module_info->use_category)
{
$this->module_info->hide_category = ($this->module_info->use_category == 'Y') ? 'N' : 'Y';
}
else
{
$this->module_info->hide_category = 'N';
$this->module_info->use_category = 'Y';
}
}
else
{
$this->module_info->hide_category = 'Y';
$this->module_info->use_category = 'N';
}
/**
* check the consultation function, if the user is admin then swich off consultation function
* if the user is not logged, then disppear write document/write comment./ view document
**/
if($this->module_info->consultation == 'Y' && !$this->grant->manager && !$this->grant->consultation_read)
{
$this->consultation = true;
if(!Context::get('is_logged')) $this->grant->list = $this->grant->write_document = $this->grant->write_comment = $this->grant->view = false;
} else {
$this->consultation = false;
}
$extra_keys = $oDocumentModel->getExtraKeys($this->module_info->module_srl);
Context::set('extra_keys', $extra_keys);
<?php
/* Copyright (C) NAVER <http://www.navercorp.com> */
Context::addJsFilter($this->module_path.'tpl/filter', 'input_password.xml');
}
function getBoardCommentPage()
class boardMobile extends boardView
{
function getBoardCommentPage()
{
$this->dispBoardCommentPage();
$this->dispBoardCommentPage();
$oTemplate = TemplateHandler::getInstance();
$html = $oTemplate->compile($this->getTemplatePath(), 'comment.html');
$this->add('html', $html);
}
function dispBoardMessage($msg_code)
{
$msg = lang($msg_code);
$oMessageObject = &ModuleHandler::getModuleInstance('message','mobile');
$oMessageObject->setError(-1);
$oMessageObject->setMessage($msg);
$oMessageObject->dispMessage();
$this->setTemplatePath($oMessageObject->getTemplatePath());
$this->setTemplateFile($oMessageObject->getTemplateFile());
}
}
}
function dispBoardMessage($msg_code)
{
$msg = lang($msg_code);
$oMessageObject = &ModuleHandler::getModuleInstance('message','mobile');
$oMessageObject->setError(-1);
$oMessageObject->setMessage($msg);
$oMessageObject->dispMessage();
$this->setTemplatePath($oMessageObject->getTemplatePath());
$this->setTemplateFile($oMessageObject->getTemplateFile());
}
}

View file

@ -26,6 +26,8 @@
<action name="procMemberInsert" type="controller" ruleset="@insertMember" use-ssl="true" route="signup" />
<action name="procMemberCheckValue" type="controller" />
<action name="procMemberLogin" type="controller" ruleset="@login" use-ssl="true" route="login" />
<action name="procMemberRegisterDevice" type="controller" route="device/register" />
<action name="procMemberLoginWithDevice" type="controller" route="device/login" />
<action name="procMemberFindAccount" type="controller" method="GET|POST" ruleset="findAccount" use-ssl="true" />
<action name="procMemberFindAccountByQuestion" type="controller" method="GET|POST" use-ssl="true" />
<action name="procMemberAuthAccount" type="controller" method="GET|POST" use-ssl="true" />

View file

@ -80,6 +80,141 @@ class memberController extends member
return $this->setRedirectUrl($returnUrl, $output);
}
/**
* Register device
*/
function procMemberRegisterDevice()
{
Context::setResponseMethod('JSON');
// Check user_id, password, device_token
$user_id = Context::get('user_id');
$password = Context::get('password');
$device_token = Context::get('device_token');
$device_model = escape(Context::get('device_model'));
// Return an error when id and password doesn't exist
if(!$user_id) return new BaseObject(-1, 'NULL_USER_ID');
if(!$password) return new BaseObject(-1, 'NULL_PASSWORD');
if(!$device_token) return new BaseObject(-1, 'NULL_DEVICE_TOKEN');
$browserInfo = Rhymix\Framework\UA::getBrowserInfo();
$device_type = strtolower($browserInfo->os);
$device_version = $browserInfo->os_version;
if(!$device_model)
{
$device_model = escape($browserInfo->device);
}
if('ios' === $device_type)
{
if(!preg_match("/^[0-9a-z]{64}$/", $device_token))
{
return new BaseObject(-1, 'INVALID_DEVICE_TOKEN');
}
}
else if('android' === $device_type)
{
if(!preg_match("/^[0-9a-zA-Z:_-]+$/", $device_token))
{
return new BaseObject(-1, 'INVALID_DEVICE_TOKEN');
}
}
else
{
return new BaseObject(-1, 'NOT_SUPPORTED_OS');
}
$output = $this->procMemberLogin($user_id, $password);
if(!$output->toBool())
{
return new BaseObject(-1, 'LOGIN_FAILED');
}
$logged_info = Context::get('logged_info');
$random_key = Rhymix\Framework\Security::getRandom();
$device_key = hash_hmac('sha256', $random_key, $logged_info->member_srl . ':' . config('crypto.authentication_key'));
// Start transaction
$oDB = DB::getInstance();
$oDB->begin();
// Remove duplicated token key
$args = new stdClass;
$args->device_token = $device_token;
executeQuery('member.deleteMemberDevice', $args);
// Create member_device
$args = new stdClass;
$args->device_srl = getNextSequence();
$args->member_srl = $logged_info->member_srl;
$args->device_token = $device_token;
$args->device_key = $device_key;
$args->device_type = $device_type;
$args->device_version = $device_version;
$args->device_model = $device_model;
$output3 = executeQuery('member.insertMemberDevice', $args);
if(!$output3->toBool())
{
$oDB->rollback();
return $output3;
}
$oDB->commit();
// Set parameters
$this->add('member_srl', $logged_info->member_srl);
$this->add('user_id', $logged_info->user_id);
$this->add('user_name', $logged_info->user_name);
$this->add('nick_name', $logged_info->nick_name);
$this->add('device_key', $random_key);
}
/**
* Automatically log-in to registered device
*/
function procMemberLoginWithDevice()
{
Context::setResponseMethod('JSON');
// Check member_srl, device_token, device_key
$member_srl = Context::get('member_srl');
$device_token = Context::get('device_token');
$random_key = Context::get('device_key');
// Return an error when id, password and device_key doesn't exist
if(!$member_srl) return new BaseObject(-1, 'NULL_MEMBER_SRL');
if(!$device_token) return new BaseObject(-1, 'NULL_DEVICE_TOKEN');
if(!$random_key) return new BaseObject(-1, 'NULL_DEVICE_KEY');
$args = new stdClass;
$args->member_srl = $member_srl;
$args->device_token = $device_token;
$args->device_key = hash_hmac('sha256', $random_key, $member_srl . ':' . config('crypto.authentication_key'));
$output = executeQueryArray('member.getMemberDevice', $args);
if(!$output->toBool())
{
return new BaseObject(-1, 'DEVICE_RETRIEVE_FAILED');
}
if(!$output->data)
{
return new BaseObject(-1, 'UNREGISTERED_DEVICE');
}
// Log-in
$member_info = MemberModel::getMemberInfoByMemberSrl($member_srl);
$output = $this->doLogin($member_info->user_id);
if(!$output->toBool())
{
return new BaseObject(-1, 'LOGIN_FAILED');
}
$this->add('member_srl', $member_info->member_srl);
$this->add('user_id', $member_info->user_id);
$this->add('user_name', $member_info->user_name);
$this->add('nick_name', $member_info->nick_name);
}
/**
* Log-out
*
@ -612,40 +747,18 @@ class memberController extends member
}
$accept_agreement_rearranged[$i] = $accept_agreement[$i] === 'Y' ? 'Y' : 'N';
}
// Check phone number
if ($config->phone_number_verify_by_sms === 'Y')
{
if (!isset($_SESSION['verify_by_sms']) || !$_SESSION['verify_by_sms']['status'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
$phone_country = Context::get('phone_country');
if ($config->phone_number_default_country && (!$phone_country || $config->phone_number_hide_country === 'Y'))
{
$phone_country = $config->phone_number_default_country;
}
if ($phone_country && !preg_match('/^[A-Z]{3}$/', $phone_country))
{
$phone_country = Rhymix\Framework\i18n::getCountryCodeByCallingCode($phone_country);
}
if ($phone_country !== $_SESSION['verify_by_sms']['country'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
$phone_number = Context::get('phone_number');
if ($phone_number !== $_SESSION['verify_by_sms']['number'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
}
// Extract the necessary information in advance
$getVars = array();
$use_phone = false;
if($config->signupForm)
{
foreach($config->signupForm as $formInfo)
{
if($formInfo->name === 'phone_number' && $formInfo->isUse)
{
$use_phone = true;
}
if($formInfo->isDefaultForm && ($formInfo->isUse || $formInfo->required || $formInfo->mustRequired))
{
$getVars[] = $formInfo->name;
@ -689,6 +802,31 @@ class memberController extends member
$args->allow_message = Context::get('allow_message');
if($args->password1) $args->password = $args->password1;
// Check phone number
if ($config->phone_number_verify_by_sms === 'Y' && $use_phone)
{
if (!isset($_SESSION['verify_by_sms']) || !$_SESSION['verify_by_sms']['status'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
if ($config->phone_number_default_country && (!$args->phone_country || $config->phone_number_hide_country === 'Y'))
{
$args->phone_country = $config->phone_number_default_country;
}
if ($args->phone_country && !preg_match('/^[A-Z]{3}$/', $args->phone_country))
{
$args->phone_country = Rhymix\Framework\i18n::getCountryCodeByCallingCode($args->phone_country);
}
if ($args->phone_country !== $_SESSION['verify_by_sms']['country'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
if ($args->phone_number !== $_SESSION['verify_by_sms']['number'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
}
// check password strength
if(!MemberModel::checkPasswordStrength($args->password, $config->password_strength))
@ -906,51 +1044,17 @@ class memberController extends member
$config = MemberModel::getMemberConfig();
$logged_info = Context::get('logged_info');
// Check phone number
if ($config->phone_number_verify_by_sms === 'Y')
{
$phone_verify_needed = false;
$phone_country = Context::get('phone_country');
$phone_number = Context::get('phone_number');
if ($config->phone_number_default_country && (!$phone_country || $config->phone_number_hide_country === 'Y'))
{
$phone_country = $config->phone_number_default_country;
}
if ($phone_country && !preg_match('/^[A-Z]{3}$/', $phone_country))
{
$phone_country = Rhymix\Framework\i18n::getCountryCodeByCallingCode($phone_country);
}
if ($phone_country !== $logged_info->phone_country)
{
$phone_verify_needed = true;
}
if (preg_replace('/[^0-9]/', '', $phone_number) !== $logged_info->phone_number)
{
$phone_verify_needed = true;
}
if ($phone_verify_needed)
{
if (!isset($_SESSION['verify_by_sms']) || !$_SESSION['verify_by_sms']['status'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
if ($phone_country !== $_SESSION['verify_by_sms']['country'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
if ($phone_number !== $_SESSION['verify_by_sms']['number'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
}
}
// Extract the necessary information in advance
$getVars = array('allow_mailing','allow_message');
$use_phone = false;
if($config->signupForm)
{
foreach($config->signupForm as $formInfo)
{
if($formInfo->name === 'phone_number' && $formInfo->isUse)
{
$use_phone = true;
}
if($formInfo->isDefaultForm && ($formInfo->isUse || $formInfo->required || $formInfo->mustRequired))
{
$getVars[] = $formInfo->name;
@ -990,6 +1094,43 @@ class memberController extends member
$args->birthday = intval(strtr($args->birthday_ui, array('-'=>'', '/'=>'', '.'=>'', ' '=>'')));
}
// Check phone number
if ($config->phone_number_verify_by_sms === 'Y' && $use_phone)
{
$phone_verify_needed = false;
if ($config->phone_number_default_country && (!$args->phone_country || $config->phone_number_hide_country === 'Y'))
{
$args->phone_country = $config->phone_number_default_country;
}
if ($args->phone_country && !preg_match('/^[A-Z]{3}$/', $args->phone_country))
{
$args->phone_country = Rhymix\Framework\i18n::getCountryCodeByCallingCode($args->phone_country);
}
if ($args->phone_country !== $logged_info->phone_country)
{
$phone_verify_needed = true;
}
if (preg_replace('/[^0-9]/', '', $args->phone_number) !== $logged_info->phone_number)
{
$phone_verify_needed = true;
}
if ($phone_verify_needed)
{
if (!isset($_SESSION['verify_by_sms']) || !$_SESSION['verify_by_sms']['status'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
if ($args->phone_country !== $_SESSION['verify_by_sms']['country'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
if ($args->phone_number !== $_SESSION['verify_by_sms']['number'])
{
throw new Rhymix\Framework\Exception('verify_by_sms_incomplete');
}
}
}
$args->member_srl = $logged_info->member_srl;
// Remove some unnecessary variables from all the vars

View file

@ -0,0 +1,10 @@
<query id="deleteMemberDevice" action="delete">
<tables>
<table name="member_devices" />
</tables>
<conditions>
<condition operation="in" column="device_srl" var="device_srl" />
<condition operation="in" column="member_srl" var="member_srl" pipe="and" />
<condition operation="in" column="device_token" var="device_token" notnull="notnull" pipe="and" />
</conditions>
</query>

View file

@ -0,0 +1,13 @@
<query id="getMemberDevice" action="select">
<tables>
<table name="member_devices" />
</tables>
<columns>
<column name="*" />
</columns>
<conditions>
<condition operation="equal" column="member_srl" var="member_srl" notnull="notnull" />
<condition operation="equal" column="device_token" var="device_token" notnull="notnull" pipe="and" />
<condition operation="equal" column="device_key" var="device_key" notnull="notnull" pipe="and" />
</conditions>
</query>

View file

@ -0,0 +1,13 @@
<query id="getMemberDeviceTokensByMemberSrl" action="select">
<tables>
<table name="member_devices" />
</tables>
<columns>
<column name="device_token" />
<column name="device_type" />
</columns>
<conditions>
<condition operation="in" column="member_srl" var="member_srl" notnull="notnull" />
<condition operation="in" column="device_type" var="device_type" pipe="and" />
</conditions>
</query>

View file

@ -0,0 +1,17 @@
<query id="insertMemberDevice" action="insert">
<tables>
<table name="member_devices" />
</tables>
<columns>
<column name="device_srl" var="device_srl" notnull="notnull" />
<column name="member_srl" var="member_srl" notnull="notnull" />
<column name="device_token" var="device_token" notnull="notnull" />
<column name="device_key" var="device_key" notnull="notnull" />
<column name="device_type" var="device_type" notnull="notnull" />
<column name="device_version" var="device_version" notnull="notnull" />
<column name="device_model" var="device_model" notnull="notnull" />
<column name="device_description" var="device_description" />
<column name="ipaddress" var="ipaddress" default="ipaddress()" />
<column name="regdate" var="regdate" default="curdate()" />
</columns>
</query>

View file

@ -0,0 +1,11 @@
<query id="updateMemberDevice" action="update">
<tables>
<table name="member_devices" />
</tables>
<columns>
<column name="device_token" var="new_token" notnull="notnull" />
</columns>
<conditions>
<condition operation="equal" column="device_token" var="old_token" notnull="notnull" />
</conditions>
</query>

View file

@ -0,0 +1,12 @@
<table name="member_devices">
<column name="device_srl" type="number" notnull="notnull" primary_key="primary_key" />
<column name="member_srl" type="number" notnull="notnull" index="idx_member_srl" />
<column name="device_token" type="varchar" size="191" notnull="notnull" unique="unique_device_token" />
<column name="device_key" type="char" size="64" notnull="notnull" />
<column name="device_type" type="varchar" size="20" notnull="notnull" index="idx_device_type" />
<column name="device_version" type="varchar" size="20" notnull="notnull" />
<column name="device_model" type="varchar" size="40" notnull="notnull" />
<column name="device_description" type="varchar" size="200" />
<column name="regdate" type="date" notnull="notnull" index="idx_regdate" />
<column name="ipaddress" type="varchar" size="120" notnull="notnull" />
</table>

View file

@ -157,7 +157,6 @@ $lang->mention_limit = '멘션 갯수 제한';
$lang->about_mention_limit = '서버 과부하와 스팸을 방지하기 위해 한 글에서 지나치게 많은 회원들을 호출하지 못하도록 합니다.';
$lang->ncenterlite_msg_setting_error = '설정에 오류가 있습니다. 다시 설정해 주세요.';
$lang->ncenterlite_use_help = '회원들에게 전송할 알림을 선택할 수 있습니다. <br /> 모든 댓글 작성자에게 알림 기능은 게시글의 작성자가 댓글을 남길경우 게시글을 작성한 작성자를 제외하고 해당 글의 <strong>모든 댓글 작성자</strong>들에게 알림을 전송합니다.';
$lang->ncenterlite_dont_use_push = '푸시 알림은 현재 지원중이 아닙니다.';
$lang->member_phone_variable = '회원 전화번호 변수';
$lang->member_phone_variable_about = '문자 알림 사용시 회원의 전화번호를 어디에서 불러올지 선택합니다. 회원정보의 전화번호 또는 확장변수를 선택할 수 있습니다.<br />전화번호 형태의 확장변수가 1개뿐인 경우 설치시 자동으로 설정이 저장됩니다.';
$lang->member_phone_builtin_field = '회원정보의 전화정보';

View file

@ -19,9 +19,16 @@ class ncenterliteAdminView extends ncenterlite
{
$sms_available = true;
}
$push_avaliable = false;
if(count(Rhymix\Framework\Config::get('push.types')))
{
$push_avaliable = true;
}
else
{
$push_avaliable = false;
}
$config = $oNcenterliteModel->getConfig();
Context::set('config', $config);
Context::set('sms_available', $sms_available);

View file

@ -7,12 +7,12 @@ class ncenterliteController extends ncenterlite
*
* @param int $from_member_srl Sender
* @param int $to_member_srl Recipient
* @param string $message Message content
* @param string|object $message Message content
* @param string $url The URL to redirect to when the recipient clicks the notification
* @param int $target_srl The sequence number associated with this notification
* @return BaseObject
*/
public function sendNotification($from_member_srl, $to_member_srl, $message, $url, $target_srl = 0)
public function sendNotification($from_member_srl, $to_member_srl, $message, $url = '', $target_srl = 0)
{
$args = new stdClass();
$args->config_type = 'custom';
@ -27,7 +27,19 @@ class ncenterliteController extends ncenterlite
$args->target_url = $url;
$args->target_browser = '';
$args->target_summary = '';
$args->target_body = $message;
if (is_object($message))
{
$args->target_body = $message->subject;
$args->target_url = $message->url ?: $args->target_url;
$args->extra_content = $message->content;
$args->extra_data = $message->data ?: [];
}
else
{
$args->target_body = $message;
}
$output = $this->_insertNotify($args);
if(!$output->toBool())
{
@ -1337,6 +1349,7 @@ class ncenterliteController extends ncenterlite
if($output->toBool())
{
ModuleHandler::triggerCall('ncenterlite._insertNotify', 'after', $args);
$this->sendPushMessage($args);
$this->sendSmsMessage($args);
$this->sendMailMessage($args);
$this->removeFlagFile($args->member_srl);
@ -1473,6 +1486,39 @@ class ncenterliteController extends ncenterlite
return array_values($members);
}
function sendPushMessage($args)
{
$oNcenterliteModel = getModel('ncenterlite');
$config = $oNcenterliteModel->getConfig();
if(!isset($config->use[$args->config_type]['push']))
{
return false;
}
if($this->user->member_srl == $args->member_srl && $args->target_type != $this->_TYPE_CUSTOM)
{
return false;
}
$content = $oNcenterliteModel->getNotificationText($args);
$content = htmlspecialchars_decode(preg_replace('/<\/?(strong|)[^>]*>/', '', $content));
$target_url = $args->target_url;
if (!preg_match('!^https?://!', $target_url))
{
$target_url = Rhymix\Framework\URL::getCurrentDomainUrl($target_url);
}
$oPush = new \Rhymix\Framework\Push();
$oPush->setSubject($content);
$oPush->setContent(strval($args->extra_content));
$oPush->setData($args->extra_data ?: []);
$oPush->setURL(strval($target_url));
$oPush->addTo(intval($args->member_srl));
$oPush->send();
}
function sendSmsMessage($args)
{
$oNcenterliteModel = getModel('ncenterlite');
@ -1483,13 +1529,13 @@ class ncenterliteController extends ncenterlite
return false;
}
if($this->user->member_srl == $args->member_srl)
if($this->user->member_srl == $args->member_srl && $args->target_type != $this->_TYPE_CUSTOM)
{
return false;
}
$content = $oNcenterliteModel->getNotificationText($args);
$content = preg_replace('/<\/?(strong|)[^>]*>/', '', $content);
$content = htmlspecialchars_decode(preg_replace('/<\/?(strong|)[^>]*>/', '', $content));
$sms = $this->getSmsHandler();
if($sms === false)
@ -1544,11 +1590,11 @@ class ncenterliteController extends ncenterlite
return false;
}
$logged_info = Context::get('logged_info');
if($logged_info->member_srl == $args->member_srl)
if($this->user->member_srl == $args->member_srl && $args->target_type != $this->_TYPE_CUSTOM)
{
return false;
}
$content = $oNcenterliteModel->getNotificationText($args);
switch ($args->config_type)

View file

@ -66,7 +66,6 @@
<label for="custom_push" class="x_inline" disabled="disabled"|cond="!$push_available"><input type="checkbox" name="use[custom][push]" id="custom_push" value="1" disabled="disabled"|cond="!$push_available" checked="checked"|cond="isset($config->use['custom']['push'])" /> {$lang->cmd_push_notify}</label>
<p>
<div>{$lang->ncenterlite_use_help}</div>
<div><span class="x_label x_label-important">{$lang->ncenterlite_warning}</span> {$lang->ncenterlite_dont_use_push}</div>
</p>
</div>
</div>

View file

@ -16,15 +16,22 @@ class SMSTest extends \Codeception\TestCase\Test
{
$drivers = Rhymix\Framework\SMS::getSupportedDrivers();
$this->assertTrue(isset($drivers['dummy']));
$this->assertTrue(isset($drivers['apistore']));
$this->assertTrue(isset($drivers['cafe24']));
$this->assertTrue(isset($drivers['coolsms']));
$this->assertTrue(isset($drivers['solapi']));
$this->assertTrue(isset($drivers['ppurio']));
$this->assertEquals('Dummy', $drivers['dummy']['name']);
$this->assertTrue(in_array('api_user', $drivers['apistore']['required']));
$this->assertTrue(in_array('api_user', $drivers['cafe24']['required']));
$this->assertTrue(in_array('api_key', $drivers['coolsms']['required']));
$this->assertTrue(in_array('api_user', $drivers['ppurio']['required']));
$this->assertTrue(in_array('api_key', $drivers['solapi']['required']));
$this->assertTrue($drivers['coolsms']['api_spec']['mms_supported']);
$this->assertTrue($drivers['coolsms']['api_spec']['delay_supported']);
$this->assertTrue($drivers['solapi']['api_spec']['mms_supported']);
$this->assertTrue($drivers['solapi']['api_spec']['delay_supported']);
$this->assertFalse($drivers['cafe24']['api_spec']['mms_supported']);
}
public function testSenderAndRecipients()

View file

@ -54,6 +54,7 @@ class UATest extends \Codeception\TestCase\Test
$this->assertEquals('Android', $browser->browser);
$this->assertEquals('4.0', $browser->version);
$this->assertEquals('Android', $browser->os);
$this->assertEquals('4.0.3', $browser->os_version);
$this->assertTrue($browser->is_mobile);
$this->assertTrue($browser->is_tablet);
$this->assertFalse($browser->is_webview);
@ -63,6 +64,8 @@ class UATest extends \Codeception\TestCase\Test
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Linux; Android 4.4.4; One Build/KTU84L.H4) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Mobile Safari/537.36');
$this->assertEquals('Android', $browser->browser);
$this->assertEquals('4.4', $browser->version);
$this->assertEquals('4.4.4', $browser->os_version);
$this->assertEquals('One', $browser->device);
$this->assertTrue($browser->is_mobile);
$this->assertFalse($browser->is_tablet);
$this->assertFalse($browser->is_webview);
@ -72,6 +75,7 @@ class UATest extends \Codeception\TestCase\Test
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36');
$this->assertEquals('Android', $browser->browser);
$this->assertEquals('5.1', $browser->version);
$this->assertEquals('Nexus 5', $browser->device);
$this->assertTrue($browser->is_mobile);
$this->assertFalse($browser->is_tablet);
$this->assertTrue($browser->is_webview);
@ -91,6 +95,7 @@ class UATest extends \Codeception\TestCase\Test
$this->assertEquals('Chrome', $browser->browser);
$this->assertEquals('51.0', $browser->version);
$this->assertEquals('Windows', $browser->os);
$this->assertEquals('10.0', $browser->os_version);
$this->assertFalse($browser->is_mobile);
// Linux Chrome
@ -125,27 +130,36 @@ class UATest extends \Codeception\TestCase\Test
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('11.0', $browser->version);
$this->assertEquals('Windows', $browser->os);
$this->assertEquals('10.0', $browser->os_version);
// IE 10 in compatibility mode
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/4.0 (Compatible; MSIE 8.0; Windows NT 5.2; Trident/6.0)');
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('10.0', $browser->version);
$this->assertEquals('Windows', $browser->os);
$this->assertEquals('XP', $browser->os_version);
// IE 9
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)');
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('9.0', $browser->version);
$this->assertEquals('Windows', $browser->os);
$this->assertEquals('7', $browser->os_version);
// IE 8 in compatibility mode
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)');
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('8.0', $browser->version);
$this->assertEquals('Windows', $browser->os);
$this->assertEquals('Vista', $browser->os_version);
// iOS Safari
$browser = Rhymix\Framework\UA::getBrowserInfo('iPad: Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3');
$this->assertEquals('Safari', $browser->browser);
$this->assertEquals('5.1', $browser->version);
$this->assertEquals('iOS', $browser->os);
$this->assertEquals('5.1', $browser->os_version);
$this->assertEquals('iPad', $browser->device);
$this->assertTrue($browser->is_mobile);
$this->assertTrue($browser->is_tablet);
@ -154,6 +168,8 @@ class UATest extends \Codeception\TestCase\Test
$this->assertEquals('Chrome', $browser->browser);
$this->assertEquals('19.0', $browser->version);
$this->assertEquals('iOS', $browser->os);
$this->assertEquals('5.1.1', $browser->os_version);
$this->assertEquals('iPhone', $browser->device);
$this->assertTrue($browser->is_mobile);
$this->assertFalse($browser->is_tablet);
@ -162,6 +178,8 @@ class UATest extends \Codeception\TestCase\Test
$this->assertEquals('Firefox', $browser->browser);
$this->assertEquals('1.0', $browser->version);
$this->assertEquals('iOS', $browser->os);
$this->assertEquals('8.3', $browser->os_version);
$this->assertEquals('iPad', $browser->device);
$this->assertTrue($browser->is_mobile);
$this->assertTrue($browser->is_tablet);
@ -170,6 +188,7 @@ class UATest extends \Codeception\TestCase\Test
$this->assertEquals('Safari', $browser->browser);
$this->assertEquals('8.0', $browser->version);
$this->assertEquals('macOS', $browser->os);
$this->assertEquals('10.10.2', $browser->os_version);
$this->assertFalse($browser->is_mobile);
// macOS (OS X) Chrome
@ -177,6 +196,7 @@ class UATest extends \Codeception\TestCase\Test
$this->assertEquals('Chrome', $browser->browser);
$this->assertEquals('41.0', $browser->version);
$this->assertEquals('macOS', $browser->os);
$this->assertEquals('10.10.1', $browser->os_version);
$this->assertFalse($browser->is_mobile);
// macOS (OS X) Firefox