Commit 83b770af by Prasong Putichanchai

update user

parent 3e1fe137
...@@ -444,6 +444,46 @@ return [ ...@@ -444,6 +444,46 @@ return [
*/ */
'Session' => [ 'Session' => [
'defaults' => 'php', 'defaults' => 'php',
'ini' => [
'session.cookie_path' => '/',
'session.cookie_domain' => '.pakgon.com'
]
],
'OAuth2Client' => [
'routes' => [
'base_uri' => 'http://oauth-uat.connect.pakgon.com',
'access_token_path' => '/oauth',
'refresh_token_path' => '/oauth/access_token',
],
'keys' => [
'client_id' => 'NWFmYTdjYzA3OTI4M2Jj',
'client_secret' => '2cf5e8f82bfaa9971e8af9ed06c23d94ef1326ba'
]
],
'Muffin/OAuth2', [
'providers' => [
'generic' => [
'className' => 'League\OAuth2\Client\Provider\GenericProvider',
// all options defined here are passed to the provider's constructor
'options' => [
'clientId' => 'NWFmYTdjYzA3OTI4M2Jj',
'clientSecret' => '2cf5e8f82bfaa9971e8af9ed06c23d94ef1326ba',
],
'mapFields' => [
'username' => 'login', // maps the app's username to github's login
],
// ... add here the usual AuthComponent configuration if needed like fields, etc.
],
],
],
'OAUTH2_PROVIDER' => [
'URL' => 'http://oauth-uat.connect.pakgon.com',
'CLIENT_ID' => 'NWFmYTdjYzA3OTI4M2Jj',
'CLIENT_SECRET' => '2cf5e8f82bfaa9971e8af9ed06c23d94ef1326ba',
'REDIRECT_URI' => 'http://commu-uat.connect.pakgon.com/home/callback',
'ACCESS_TOKEN_URL' => 'http://oauth-uat.connect.pakgon.com/oauth/access_token.json',
'REFRESH_TOKEN_URL' => 'xxx'
], ],
'Config' => [ 'Config' => [
......
...@@ -217,3 +217,4 @@ if (Configure::read('debug')) { ...@@ -217,3 +217,4 @@ if (Configure::read('debug')) {
//Plugin::load('AdminLTE', ['bootstrap' => true, 'routes' => true]); //Plugin::load('AdminLTE', ['bootstrap' => true, 'routes' => true]);
//Plugin::load('Porto',['bootstrap' => true,'routes' => true ]); //Plugin::load('Porto',['bootstrap' => true,'routes' => true ]);
} }
Plugin::load('Muffin/OAuth2');
\ No newline at end of file
...@@ -49,12 +49,16 @@ Router::scope('/', function (RouteBuilder $routes) { ...@@ -49,12 +49,16 @@ Router::scope('/', function (RouteBuilder $routes) {
* its action called 'display', and we pass a param to select the view file * its action called 'display', and we pass a param to select the view file
* to use (in this case, src/Template/Pages/home.ctp)... * to use (in this case, src/Template/Pages/home.ctp)...
*/ */
$routes->connect('/', ['controller' => 'Users', 'action' => 'signin']); // $routes->connect('/', ['controller' => 'Users', 'action' => 'signin']);
$routes->connect('/', ['controller' => 'homes', 'action' => 'index']);
/** /**
* ...and connect the rest of 'Pages' controller's URLs. * ...and connect the rest of 'Pages' controller's URLs.
*/ */
////$routes->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']); $routes->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']);
//Adding by sarawutt.b for oauth2 authenticatio
//$routes->connect('/oauth/:provider', ['controller' => 'users', 'action' => 'login'], ['provider' => 'generic']);
$routes->setExtensions(['json', 'xml', 'html']);
/** /**
* Connect catchall routes for all controllers. * Connect catchall routes for all controllers.
......
<?php <?php
/** /**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org) * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
...@@ -12,10 +13,13 @@ ...@@ -12,10 +13,13 @@
* @since 0.2.9 * @since 0.2.9
* @license https://opensource.org/licenses/mit-license.php MIT License * @license https://opensource.org/licenses/mit-license.php MIT License
*/ */
namespace App\Controller; namespace App\Controller;
use Cake\Controller\Controller; use Cake\Controller\Controller;
use Cake\Event\Event; use Cake\Event\Event;
use Cake\I18n\I18n;
use Cake\Core\Configure;
/** /**
* Application Controller * Application Controller
...@@ -25,9 +29,16 @@ use Cake\Event\Event; ...@@ -25,9 +29,16 @@ use Cake\Event\Event;
* *
* @link https://book.cakephp.org/3.0/en/controllers.html#the-app-controller * @link https://book.cakephp.org/3.0/en/controllers.html#the-app-controller
*/ */
class AppController extends Controller class AppController extends Controller {
{
protected $selectEmptyMsg = '---- please select ----';
/**
*
* @var redirect url when authorize to success
*/
protected $_redirectApplicationURL = 'http://commu-uat.connect.pakgon.com';
/** /**
* Initialization hook method. * Initialization hook method.
* *
...@@ -37,8 +48,7 @@ class AppController extends Controller ...@@ -37,8 +48,7 @@ class AppController extends Controller
* *
* @return void * @return void
*/ */
public function initialize() public function initialize() {
{
parent::initialize(); parent::initialize();
//$this->viewBuilder()->theme('Porto'); //$this->viewBuilder()->theme('Porto');
$this->loadComponent('RequestHandler'); $this->loadComponent('RequestHandler');
...@@ -48,42 +58,193 @@ class AppController extends Controller ...@@ -48,42 +58,193 @@ class AppController extends Controller
* Enable the following components for recommended CakePHP security settings. * Enable the following components for recommended CakePHP security settings.
* see https://book.cakephp.org/3.0/en/controllers/components/security.html * see https://book.cakephp.org/3.0/en/controllers/components/security.html
*/ */
$this->loadComponent('Auth', [
'loginRedirect' => ['controller' => 'Homes', 'action' => 'index'],
'logoutRedirect' => ['controller' => 'Users', 'action' => 'signin'],
'authenticate' => [
'Form' => [
'fields' => ['username' => 'username', 'password' => 'password'],
'userModel' => 'Users'
]
],
'loginAction' => ['controller' => 'Users', 'action' => 'signin'],
'authorize' => ['Controller'],
'unauthorizedRedirect' => $this->referer()// If unauthorized, return them to page they were just on
]);
//$this->loadComponent('Security'); //$this->loadComponent('Security');
//$this->loadComponent('Csrf'); //$this->loadComponent('Csrf');
} }
/** /**
*
* Function trigger before filter process
* @author sarawutt.b
* @param Event $event
*/
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
/**
*
* Set appication language this can be thai|english
* @author Sarawutt.b
* @since 2018-02-28
* @return void
*/
if ($this->request->session()->check('SessionLanguage') == false) {
$this->request->session()->write('SessionLanguage', 'tha');
}
$this->Auth->allow(['signin', 'signout', 'signup', 'verify']);
}
/**
* Before render callback. * Before render callback.
* *
* @param \Cake\Event\Event $event The beforeRender event. * @param \Cake\Event\Event $event The beforeRender event.
* @return \Cake\Http\Response|null|void * @return \Cake\Http\Response|null|void
*/ */
public function beforeRender(Event $event) public function beforeRender(Event $event) {
{
//$this->viewBuilder()->theme('Gentelella'); //$this->viewBuilder()->theme('Gentelella');
//$this->viewBuilder()->theme('AdminLTE'); //$this->viewBuilder()->theme('AdminLTE');
//$this->viewBuilder()->theme('Porto'); //$this->viewBuilder()->theme('Porto');
//$this->viewBuilder()->className('AdminLTE.AdminLTE'); //$this->viewBuilder()->className('AdminLTE.AdminLTE');
//$this->viewBuilder()->theme('Porto'); //$this->viewBuilder()->theme('Porto');
//$this->viewBuilder()->className('Porto.Porto'); //$this->viewBuilder()->className('Porto.Porto');
// Note: These defaults are just to get started quickly with development // Note: These defaults are just to get started quickly with development
// and should not be used in production. You should instead set "_serialize" // and should not be used in production. You should instead set "_serialize"
// in each action as required. // in each action as required.
if (!array_key_exists('_serialize', $this->viewVars) && if (!array_key_exists('_serialize', $this->viewVars) &&
in_array($this->response->type(), ['application/json', 'application/xml']) in_array($this->response->type(), ['application/json', 'application/xml'])
) { ) {
$this->set('_serialize', true); $this->set('_serialize', true);
//$this->set('theme', Configure::read('Theme')); //$this->set('theme', Configure::read('Theme'));
} }
//$this->set('theme', Configure::read('Theme')); //$this->set('theme', Configure::read('Theme'));
} }
function checkToken(){
if(empty($this->request->getHeaderLine('Authorization'))){ /**
return $this->redirect(['controller' => 'Users', 'action' => 'signin']); *
} * Function check authorize
* @author sarawutt.b
* @param type $user
* @return boolean
*/
public function isAuthorized($user) {
return true;
} }
/**
*
* Function check fore token
* @return type
*/
function checkToken() {
if (empty($this->request->getHeaderLine('Authorization'))) {
return $this->redirect(['controller' => 'Users', 'action' => 'signin']);
}
}
/**
* Set language used this in mutiple language application concept
* @author Sarawutt.b
* @since 2016/03/21 10:23:33
* @return void
*/
public function _setLanguage() {
$this->L10n = new L10n();
$language = $this->request->session()->read('SessionLanguage');
Configure::write('Config.language', $language);
$this->L10n->get($language);
}
/**
*
* Function get for current session user language
* @author sarawutt.b
* @return string
*/
public function getCurrentLanguage() {
return $this->request->session()->read('SessionLanguage');
}
/**
*
* Function used fro generate _VERSION_
* @author sarawutt.b
* @return biginteger of the version number
*/
public function VERSION() {
$parts = explode(' ', microtime());
$micro = $parts[0] * 1000000;
return(substr(date('YmdHis'), 2) . sprintf("%06d", $micro));
}
/**
*
* Function used for generate UUID key patern
* @author sarawutt.b
* @return string uuid in version
*/
public function UUID() {
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
}
/**
*
* Function get for current session user authentication full name
* @author sarawutt.b
* @since 2018/02/06
* @return string of authentication user full name
*/
protected function getAuthFullname() {
return $this->readAuth('Auth.User.first_name') . ' ' . $this->readAuth('Auth.User.last_name');
}
/**
*
* Function get for current session user authentication user id
* @author sarawutt.b
* @since 2018/02/06
* @return string of authentication user id
*/
protected function getAuthUserId() {
return $this->readAuth('Auth.User.id');
}
/**
*
* Function get for current session user authentication role id
* @author sarawutt.b
* @since 2018/02/06
* @return string of authentication user id
*/
protected function getAuthUserRoleId() {
return $this->readAuth('Auth.User.role_id');
}
/**
*
* Function get for current session with user authentication
* @author sarawutt.b
* @since 2018/02/06
* @return string of authentication session info
*/
protected function readAuth($name = null) {
return $this->request->session()->read($name);
}
/**
* Function get for empty option in DDL
* @author sarawutt.b
* @return array() of empty select DDL
*/
public function getEmptySelect() {
return ['' => __($this->selectEmptyMsg)];
}
} }
...@@ -589,4 +589,5 @@ if (!empty($SubjectEnrolls)) { ...@@ -589,4 +589,5 @@ if (!empty($SubjectEnrolls)) {
$this->viewBuilder()->layout('blank'); $this->viewBuilder()->layout('blank');
} }
} }
...@@ -10,80 +10,87 @@ use Cake\Mailer\Email; ...@@ -10,80 +10,87 @@ use Cake\Mailer\Email;
use Cake\Utility\Security; use Cake\Utility\Security;
use Cake\Auth\AbstractPasswordHasher; use Cake\Auth\AbstractPasswordHasher;
class UsersController extends AppController class UsersController extends AppController {
{
/**
*
* Function initialize make for automatically trigger when contructure
*/
public function initialize() {
parent::initialize();
$this->Auth->allow(['signin', 'signout', 'signup', 'verify','forgotPassword']);
}
public function index() public function index() {
{ return $this->redirect(['controller' => 'Users', 'action' => 'signin']);
return $this->redirect(['controller' => 'Users', 'action' => 'signin']);
} }
public function signin() public function signin() {
{
$this->viewBuilder()->layout('blank'); $this->viewBuilder()->layout('blank');
} }
#Signin #Signin
public function verify()
{
$this->viewBuilder()->layout('blank');
$data = $this->request->data(); public function verify() {
#$data['data']['ip'] = $this->request->clientIp(); $this->viewBuilder()->layout('blank');
if ($this->request->is('post')) {
if(!empty($data)){ $data = $this->request->getData();
if(!empty($data['data']['username']) && !empty($data['data']['password'])){ if (!empty($data)) {
$data['data']['ip'] = $this->request->clientIp();#prr($data); $data['data']['ip'] = $this->request->clientIp();
$api_core_signin = Configure::read('Config.apiCore.signin');
$api_core_signin = Configure::read('Config.apiCore.signin'); $http = new Client();
$http = new Client(); $response = $http->post($api_core_signin, $data['data'])->body();
$response = $http->post($api_core_signin,$data['data'])->body(); $response = json_decode($response, '_full');
$response = json_decode($response,'_full');#prd($response); if (!empty($response)) {
if (strtolower(trim($response['status'])) == 'success') {
if(!empty($response)){ $this->Auth->setUser($response['result']);
if(trim($response['status']) == 'Success'){ //$url = Router::url(['controller' => 'Homes', 'action' => 'index'], true);
$url = Router::url(['controller' => 'Homes', 'action' => 'index'], true);
// Before chan logic to OAUTH
echo "<script>setTimeout(function(){Login.onLogin('".$response['result']['token']."','".$url."','".$response['result']['topic']."')},1000);</script>"; // echo "<script>setTimeout(function(){Login.onLogin('" . $response['result']['token'] . "','" . $url . "','" . $response['result']['topic'] . "')},1000);</script>";
#echo "<script>setTimeout(function(){alert('".$response['result']['token']."')},1000);</script>";
#echo "<script>Login.onLogin('".$response['result']['token']."','".$url."','".$response['result']['topic']."');</script>"; $statusCode = '200';
#echo "<button onclick=Login.onLogin('".$response['result']['token']."','".$url."','".$response['result']['topic']."')>CLICK</button>"; echo "<script type='text/javascript'>setTimeout(function(){Login.onLogin('" . $statusCode . "','" . $this->_redirectApplicationURL . "','" . $response['result']['topic'] . "')},1000);</script>";
#------------------------------------------------------------------
$param = []; #echo "<script>setTimeout(function(){alert('".$response['result']['token']."')},1000);</script>";
$param['topic'] = '/topics/'.$response['result']['topic']; #echo "<script>Login.onLogin('".$response['result']['token']."','".$url."','".$response['result']['topic']."');</script>";
$param['title'] = ''; #echo "<button onclick=Login.onLogin('".$response['result']['token']."','".$url."','".$response['result']['topic']."')>CLICK</button>";
$param['message'] = ''; #------------------------------------------------------------------
$param['badge'] = 1; $param = [];
$param['topic'] = '/topics/' . $response['result']['topic'];
#$api_notification = 'http://connect05.pakgon.com/api/Notifications/push'; $param['title'] = '';
$api_notification = Configure::read('Config.apiCommunication.getNotification'); $param['message'] = '';
$param['badge'] = 1;
$http = new Client();
$options = [ #$api_notification = 'http://connect05.pakgon.com/api/Notifications/push';
'headers' => [ $api_notification = Configure::read('Config.apiCommunication.getNotification');
'Content-Type' => 'application/x-www-form-urlencoded',
] $http = new Client();
]; $options = [
$response = $http->post($api_notification, $param, $options)->body(); 'headers' => [
#prr($response); 'Content-Type' => 'application/x-www-form-urlencoded',
#------------------------------------------------------------------ ]
];
die; $response = $http->post($api_notification, $param, $options)->body();
#prr($response);
}else{ #------------------------------------------------------------------
$this->Flash->error(__('Verify Fail'));
return $this->redirect(['controller' => 'Users', 'action' => 'signin']); //$this->redirect($this->_redirectApplicationURL);
} header('Location: ' . $this->_redirectApplicationURL);
}else{ die;
$this->Flash->error(__('Verify Fail')); } else {
return $this->redirect(['controller' => 'Users', 'action' => 'signin']); $this->Flash->error(__('Verify Fail'));
} return $this->redirect(['controller' => 'Users', 'action' => 'signin']);
}
}else{ } else {
$this->Flash->error(__('Data Empty')); $this->Flash->error(__('Verify Fail'));
return $this->redirect(['controller' => 'Users', 'action' => 'signin']); return $this->redirect(['controller' => 'Users', 'action' => 'signin']);
} }
} else {
$this->Flash->error(__('Data Empty'));
return $this->redirect(['controller' => 'Users', 'action' => 'signin']);
}
} }
} }
...@@ -113,7 +120,7 @@ class UsersController extends AppController ...@@ -113,7 +120,7 @@ class UsersController extends AppController
$this->set(compact('provinces','countries')); $this->set(compact('provinces','countries'));
$this->viewBuilder()->layout('blank'); $this->viewBuilder()->layout('blank');
} }
#--------------------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------------------
...@@ -182,10 +189,10 @@ class UsersController extends AppController ...@@ -182,10 +189,10 @@ class UsersController extends AppController
// pr($users);die; // pr($users);die;
//------------------------------ ส่งอีเมล์ ----------------------------------------------------- //------------------------------ ส่งอีเมล์ -----------------------------------------------------
// $data_notification = []; $data_notification = [];
// $data_notification['email'] = $this->request->data['email']; $data_notification['email'] = $this->request->data['email'];
// $data_notification['pin_code'] = $users['pin_code']; $data_notification['pin_code'] = $users['pin_code'];
// $this->notification($data_notification); $this->notification($data_notification);
//---------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------
$this->Users->save($users); $this->Users->save($users);
$user_personals['master_country_id'] = $master_country_id; $user_personals['master_country_id'] = $master_country_id;
...@@ -245,19 +252,17 @@ class UsersController extends AppController ...@@ -245,19 +252,17 @@ class UsersController extends AppController
} }
#--------------------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------------------
function validateAccount($data = null) function validateAccount($data = null) {
{
$error = [];
$error = [];
if (!empty($error)) {
if(!empty($error)){ $this->set('error', $error);
$this->set('error',$error); return false;
return false; } else {
}else{ return true;
return true; }
}
} }
// public function notification() // public function notification()
...@@ -318,22 +323,23 @@ class UsersController extends AppController ...@@ -318,22 +323,23 @@ class UsersController extends AppController
// $pass[] = $alphabet[$n]; // $pass[] = $alphabet[$n];
// } // }
// $password = implode($pass); // $password = implode($pass);
//--------------------ตัวส่ง Email --------------------------------------
//pr($user_personals);die;
// $data_notification = [];
// $data_notification['email'] = $this->request->data['email'];
// $data_notification['password'] = $password;
// $this->notification($data_notification);
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
// $users['password'] = $hasher->hash($password);
$user_personals = $this->UserPersonals->find('all',[ $user_personals = $this->UserPersonals->find('all',[
'conditions' => [ 'conditions' => [
'UserPersonals.email' => $email 'UserPersonals.email' => $email
] ]
])->first(); ])->first();
if(!empty($user_personals)){ if(!empty($user_personals)){
//--------------------ตัวส่ง Email ---------------------------------------------------------
// pr($user_personals);die;
// $data_notification = [];
// $data_notification['email'] = $this->request->data['email'];
// $data_notification['pin_code'] = $users['pin_pass'];
// $this->notification($data_notification);
//----------------------------------------------------------------------------------------
$users = $this->Users->find('all',[ $users = $this->Users->find('all',[
'conditions' => [ 'conditions' => [
'Users.id' => $user_personals['user_id'] 'Users.id' => $user_personals['user_id']
...@@ -342,10 +348,15 @@ class UsersController extends AppController ...@@ -342,10 +348,15 @@ class UsersController extends AppController
$digits = 4; $digits = 4;
$users['pin_pass'] = str_pad(rand(0, pow(10, $digits)-1), $digits, '0', STR_PAD_LEFT); $users['pin_pass'] = str_pad(rand(0, pow(10, $digits)-1), $digits, '0', STR_PAD_LEFT);
$token = $users['token']; $token = $users['token'];
// pr($users);die;
// $users = $this->Users->patchEntity($users, $this->request->getData()); //--------------------ตัวส่ง Email ---------------------------------------------------------
//pr($user_personals);die;
$data_notification = [];
$data_notification['email'] = $this->request->data['email'];
$data_notification['pin_code'] = $users['pin_pass'];
$this->notification($data_notification);
//----------------------------------------------------------------------------------------
$this->Users->save($users); $this->Users->save($users);
// pr($users);die;
$this->Flash->success(__('send password to email success')); $this->Flash->success(__('send password to email success'));
return $this->redirect(['action' => 'pinCodepassword/'.$token]); return $this->redirect(['action' => 'pinCodepassword/'.$token]);
...@@ -415,28 +426,7 @@ class UsersController extends AppController ...@@ -415,28 +426,7 @@ class UsersController extends AppController
} }
public function getProvince() {
$this->autoRender = false;
$this->loadModel('MasterProvinces');
$states = array();
$id = $this->request->data('id');
$states = $this->MasterProvinces->find('list', array(
'keyField' => 'id',
'valueField' => 'province_name_th',
'conditions' => array(
'master_country_id' =>$id
)
))->toArray();
$arr = array();
if(!empty($states)){
echo '<option value=""> ---Select--- </option>';
foreach($states as $key => $val){
echo '<option value="' . $key . '">' . $val. '</option>';
}
}else{
return false;
}
}
} }
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
<?php echo $this->Html->image('../img/core/img/logo-connect-login@3x.png', ['alt' => 'Logo']); ?> <?php echo $this->Html->image('../img/core/img/logo-connect-login@3x.png', ['alt' => 'Logo']); ?>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<?php echo $this->Form->create(null, ['url' => ['controller' => 'Users', 'action' => 'verify'], <?php
echo $this->Form->create(null, ['url' => ['controller' => 'Users', 'action' => 'verify'],
'id' => '', 'name' => 'Users', 'role' => 'form', 'onsubmit' => 'return validateForm()']); 'id' => '', 'name' => 'Users', 'role' => 'form', 'onsubmit' => 'return validateForm()']);
?> ?>
<?php echo $this->Flash->render() ?> <?php echo $this->Flash->render() ?>
...@@ -53,12 +54,11 @@ ...@@ -53,12 +54,11 @@
</label> </label>
</div> </div>
</div> </div>
<?php echo $this->Form->end(); ?> <?php echo $this->Form->end(); ?>
</div> </div>
</div> </div>
<!----------------------------------------------------------------------> <script type="text/javascript">
<script>
function validateForm() { function validateForm() {
var x = document.forms["Users"]["data[username]"].value; var x = document.forms["Users"]["data[username]"].value;
if (x == null || x == "") { if (x == null || x == "") {
...@@ -72,7 +72,10 @@ ...@@ -72,7 +72,10 @@
} }
} }
</script> </script>
<style>
<style type="text/css">
#alertBox { #alertBox {
position:relative; position:relative;
width:300px; width:300px;
...@@ -179,7 +182,7 @@ ...@@ -179,7 +182,7 @@
} }
</style> </style>
<script> <script type="text/javascript">
var ALERT_TITLE = ""; var ALERT_TITLE = "";
var ALERT_BUTTON_TEXT = "Ok"; var ALERT_BUTTON_TEXT = "Ok";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment