[+] BO : now you can sync imap mailbox with the Customer Service (SAV)
This commit is contained in:
@@ -722,3 +722,75 @@ if (Tools::isSubmit('updateElementEmployee') && Tools::getValue('updateElementEm
|
|||||||
$notification = new Notification;
|
$notification = new Notification;
|
||||||
die($notification->updateEmployeeLastElement(Tools::getValue('updateElementEmployeeType')));
|
die($notification->updateEmployeeLastElement(Tools::getValue('updateElementEmployeeType')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Tools::isSubmit('syncImapMail'))
|
||||||
|
{
|
||||||
|
if (!$url = Configuration::get('PS_SAV_IMAP_URL')
|
||||||
|
OR !$port = Configuration::get('PS_SAV_IMAP_PORT')
|
||||||
|
OR !$user = Configuration::get('PS_SAV_IMAP_USER')
|
||||||
|
OR !$password = Configuration::get('PS_SAV_IMAP_PWD'))
|
||||||
|
die('{"hasError" : true, "errors" : "Configuration is not correct"}');
|
||||||
|
|
||||||
|
$mbox = @imap_open('{'.$url.':'.$port.'}', $user, $password);
|
||||||
|
|
||||||
|
$errors = imap_errors();
|
||||||
|
if (sizeof($errors))
|
||||||
|
{
|
||||||
|
$str_errors = '["';
|
||||||
|
foreach($errors as $error)
|
||||||
|
$str_errors .= $error.',';
|
||||||
|
$str_errors = rtrim($str_errors, ',').'"]';
|
||||||
|
}
|
||||||
|
if (!$mbox)
|
||||||
|
die('{"hasError" : true, "errors" : ["Can not connect to the mailbox"]}');
|
||||||
|
|
||||||
|
$check = imap_check($mbox);
|
||||||
|
|
||||||
|
if ($check->Nmsgs == 0)
|
||||||
|
die('{"hasError" : true, "errors" : ["NO message to sync"]}');
|
||||||
|
|
||||||
|
$result = imap_fetch_overview($mbox,"1:{$check->Nmsgs}",0);
|
||||||
|
foreach ($result as $overview)
|
||||||
|
{
|
||||||
|
//check if message exist in database
|
||||||
|
if (isset($overview->subject))
|
||||||
|
$subject = $overview->subject;
|
||||||
|
else
|
||||||
|
$subject = '';
|
||||||
|
|
||||||
|
$md5 = md5($overview->date.$overview->from.$subject);
|
||||||
|
$exist = Db::getInstance()->getValue(
|
||||||
|
'SELECT md5_header
|
||||||
|
FROM `'._DB_PREFIX_.'customer_message_sync_imap`
|
||||||
|
WHERE md5_header = \''.pSQL($md5).'\'');
|
||||||
|
if ($exist)
|
||||||
|
{
|
||||||
|
if (Configuration::get('PS_SAV_IMAP_DELETE_MSG'))
|
||||||
|
imap_delete($mbox, $overview->msgno);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//check if subject has id_order
|
||||||
|
preg_match('/\#ct([0-9]*)/', $subject, $matches1);
|
||||||
|
preg_match('/\#tc([0-9-a-z-A-Z]*)/', $subject, $matches2);
|
||||||
|
|
||||||
|
if (isset($matches1[1]) AND isset($matches2[1]))
|
||||||
|
{
|
||||||
|
//check if order exist in database
|
||||||
|
$ct = new CustomerThread((int)$matches1[1]);
|
||||||
|
|
||||||
|
if (Validate::isLoadedObject($ct) && $ct->token == $matches2[1])
|
||||||
|
{
|
||||||
|
$cm = new CustomerMessage();
|
||||||
|
$cm->id_customer_thread = $ct->id;
|
||||||
|
$cm->message = imap_fetchbody($mbox, $overview->msgno, 1);
|
||||||
|
$cm->add();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'customer_message_sync_imap` VALUES (\''.pSQL($md5).'\')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imap_expunge($mbox);
|
||||||
|
imap_close($mbox);
|
||||||
|
die('{"hasError" : false, "errors" : '.$str_errors.'}');
|
||||||
|
}
|
||||||
|
|||||||
@@ -86,6 +86,22 @@ class AdminCustomerThreads extends AdminTab
|
|||||||
);
|
);
|
||||||
$this->shopLinkType = 'shop';
|
$this->shopLinkType = 'shop';
|
||||||
|
|
||||||
|
$this->optionsList = array(
|
||||||
|
'general' => array(
|
||||||
|
'title' => $this->l('Customer service options'),
|
||||||
|
'fields' => array(
|
||||||
|
'PS_SAV_IMAP_URL' => array('title' => $this->l('Imap url'), 'desc' => $this->l('Url for imap server (mail.server.com)'), 'type' => 'text', 'size' => 40, 'visibility' => Shop::CONTEXT_ALL),
|
||||||
|
'PS_SAV_IMAP_PORT' => array('title' => $this->l('Imap port'), 'desc' => $this->l('Port to use to connect imap server'), 'type' => 'text', 'defaultValue' => 143, 'visibility' => Shop::CONTEXT_ALL),
|
||||||
|
'PS_SAV_IMAP_USER' => array('title' => $this->l('Imap user'), 'desc' => $this->l('User to use to connect imap server'), 'type' => 'text', 'size' => 40, 'visibility' => Shop::CONTEXT_ALL),
|
||||||
|
'PS_SAV_IMAP_PWD' => array('title' => $this->l('Imap password'), 'desc' => $this->l('Password to use to connect imap server'), 'type' => 'text', 'size' => 40, 'visibility' => Shop::CONTEXT_ALL),
|
||||||
|
'PS_SAV_IMAP_DELETE_MSG' => array('title' => $this->l('Delete messages'), 'desc' => $this->l('Deletes message after sync. If you do not active this option, the sync will be longer'), 'cast' => 'intval', 'type' => 'select', 'identifier' => 'value', 'list' => array(
|
||||||
|
'0' => array('value' => 0, 'name' => $this->l('No')),
|
||||||
|
'1' => array('value' => 1, 'name' => $this->l('Yes'))
|
||||||
|
), 'visibility' => Shop::CONTEXT_ALL)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +196,7 @@ class AdminCustomerThreads extends AdminTab
|
|||||||
'{reply}' => Tools::nl2br(Tools::getValue('reply_message')),
|
'{reply}' => Tools::nl2br(Tools::getValue('reply_message')),
|
||||||
'{link}' => Tools::url($this->context->link->getPageLink('contact', true), 'id_customer_thread='.(int)($ct->id).'&token='.$ct->token),
|
'{link}' => Tools::url($this->context->link->getPageLink('contact', true), 'id_customer_thread='.(int)($ct->id).'&token='.$ct->token),
|
||||||
);
|
);
|
||||||
if (Mail::Send($ct->id_lang, 'reply_msg', Mail::l('An answer to your message is available'),
|
if (Mail::Send($ct->id_lang, 'reply_msg', Mail::l('An answer to your message is available').' #ct'.$ct->id.'#tc'.$ct->token,
|
||||||
$params, Tools::getValue('msg_email'), NULL, NULL, NULL, $fileAttachment, NULL,
|
$params, Tools::getValue('msg_email'), NULL, NULL, NULL, $fileAttachment, NULL,
|
||||||
_PS_MAIL_DIR_, true))
|
_PS_MAIL_DIR_, true))
|
||||||
{
|
{
|
||||||
@@ -207,7 +223,10 @@ class AdminCustomerThreads extends AdminTab
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
$this->getList($this->context->language->id, !Tools::getValue($this->table.'Orderby') ? 'date_upd' : NULL, !Tools::getValue($this->table.'Orderway') ? 'DESC' : NULL);
|
$this->getList($this->context->language->id, !Tools::getValue($this->table.'Orderby') ? 'date_upd' : NULL, !Tools::getValue($this->table.'Orderway') ? 'DESC' : NULL);
|
||||||
|
|
||||||
$this->displayList();
|
$this->displayList();
|
||||||
|
$this->displayOptionsList();
|
||||||
|
$this->displaySyncOption();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,5 +624,67 @@ class AdminCustomerThreads extends AdminTab
|
|||||||
</p>
|
</p>
|
||||||
</div>';
|
</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function displaySyncOption()
|
||||||
|
{
|
||||||
|
if (Configuration::get('PS_SAV_IMAP_URL')
|
||||||
|
AND Configuration::get('PS_SAV_IMAP_PORT')
|
||||||
|
AND Configuration::get('PS_SAV_IMAP_USER')
|
||||||
|
AND Configuration::get('PS_SAV_IMAP_PWD'))
|
||||||
|
echo '
|
||||||
|
<fieldset><legend>'.$this->l('Sync').'</legend>
|
||||||
|
<label>'.$this->l('Run sync').' :</label>
|
||||||
|
<div class="margin-form">
|
||||||
|
<button class="button" id="run_sync" onclick="run_sync();">'.$this->l('Run sync').'</button>
|
||||||
|
<p>'.$this->l('Click to synchronize mail automatically').'</p>
|
||||||
|
<div id="ajax_loader"></div>
|
||||||
|
<div class="error" style="display:none" id="ajax_error"></div>
|
||||||
|
<div class="conf" style="display:none" id="ajax_conf"></div>
|
||||||
|
</div>
|
||||||
|
</fieldset><br/>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function run_sync () {
|
||||||
|
$(\'#ajax_error\').html(\'\');
|
||||||
|
$(\'#ajax_error\').hide();
|
||||||
|
$(\'#ajax_conf\').html(\'\');
|
||||||
|
$(\'#ajax_conf\').hide();
|
||||||
|
|
||||||
|
$(\'#ajax_loader\').html(\'<img src="'._PS_ADMIN_IMG_.'ajax-loader.gif">\');
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "ajax.php",
|
||||||
|
data: "syncImapMail=1",
|
||||||
|
dataType : "json",
|
||||||
|
success: function(jsonData) {
|
||||||
|
jsonError = \'\';
|
||||||
|
if (jsonData.hasError)
|
||||||
|
{
|
||||||
|
for (i=0;i<jsonData.errors.length;i++)
|
||||||
|
jsonError = jsonError+\'<li>\'+jsonData.errors[i]+\'</li>\';
|
||||||
|
$(\'#ajax_error\').html(\'<ul>\'+jsonError+\'</ul>\');
|
||||||
|
$(\'#ajax_error\').fadeIn();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jsonError = \'<li>'.$this->l('Sync success').'</li>\';
|
||||||
|
for (i=0;i<jsonData.errors.length;i++)
|
||||||
|
jsonError = jsonError+\'<li>\'+jsonData.errors[i]+\'</li>\';
|
||||||
|
$(\'#ajax_conf\').html(\'<ul>\'+jsonError+\'</ul>\');
|
||||||
|
$(\'#ajax_conf\').fadeIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
$(\'#ajax_loader\').html(\'\');
|
||||||
|
},
|
||||||
|
error: function(XMLHttpRequest, textStatus, errorThrown) {
|
||||||
|
alert("TECHNICAL ERROR: unable to refresh the cart.\n\nDetails:\nError thrown: " + XMLHttpRequest + "\n" + \'Text status: \' + textStatus);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -167,21 +167,44 @@ class AdminOrders extends AdminTab
|
|||||||
$this->_errors[] = Tools::displayError('field').' <b>'.$field.'</b> '.Tools::displayError('is invalid.');
|
$this->_errors[] = Tools::displayError('field').' <b>'.$field.'</b> '.Tools::displayError('is invalid.');
|
||||||
if (!sizeof($this->_errors))
|
if (!sizeof($this->_errors))
|
||||||
{
|
{
|
||||||
$message = new Message();
|
$order = new Order((int)(Tools::getValue('id_order')));
|
||||||
$message->id_employee = (int)$this->context->employee->id;
|
$customer = new Customer((int)$order->id_customer);
|
||||||
$message->message = htmlentities(Tools::getValue('message'), ENT_COMPAT, 'UTF-8');
|
//check if a thread already exist
|
||||||
$message->id_order = $id_order;
|
$id_customer_thread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder($customer->email, $order->id);
|
||||||
$message->private = Tools::getValue('visibility');
|
$cm = new CustomerMessage();
|
||||||
if (!$message->add())
|
if (!$id_customer_thread)
|
||||||
|
{
|
||||||
|
$ct = new CustomerThread();
|
||||||
|
$ct->id_contact = 0;
|
||||||
|
$ct->id_customer = (int)$order->id_customer;
|
||||||
|
$ct->id_shop = (int)$this->context->shop->getId(true);
|
||||||
|
$ct->id_order = (int)$order->id;
|
||||||
|
$ct->id_lang = (int)$this->context->language->id;
|
||||||
|
$ct->email = $customer->email;
|
||||||
|
$ct->status = 'open';
|
||||||
|
$ct->token = Tools::passwdGen(12);
|
||||||
|
$ct->add();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$ct = new CustomerThread((int)$id_customer_thread);
|
||||||
|
$cm->id_customer_thread = $ct->id;
|
||||||
|
$cm->id_employee = (int)$this->context->employee->id;
|
||||||
|
$cm->message = htmlentities(Tools::getValue('message'), ENT_COMPAT, 'UTF-8');
|
||||||
|
$cm->private = Tools::getValue('visibility');
|
||||||
|
if (!$cm->add())
|
||||||
$this->_errors[] = Tools::displayError('An error occurred while sending message.');
|
$this->_errors[] = Tools::displayError('An error occurred while sending message.');
|
||||||
elseif ($message->private)
|
elseif ($message->private)
|
||||||
Tools::redirectAdmin(self::$currentIndex.'&id_order='.$id_order.'&vieworder&conf=11'.'&token='.$this->token);
|
Tools::redirectAdmin($currentIndex.'&id_order='.$id_order.'&vieworder&conf=11'.'&token='.$this->token);
|
||||||
elseif (Validate::isLoadedObject($customer = new Customer($id_customer)))
|
elseif (Validate::isLoadedObject($customer = new Customer($id_customer)))
|
||||||
{
|
{
|
||||||
$order = new Order((int)($message->id_order));
|
|
||||||
if (Validate::isLoadedObject($order))
|
if (Validate::isLoadedObject($order))
|
||||||
{
|
{
|
||||||
$varsTpl = array('{lastname}' => $customer->lastname, '{firstname}' => $customer->firstname, '{id_order}' => $message->id_order, '{message}' => (Configuration::get('PS_MAIL_TYPE') == 2 ? $message->message : Tools::nl2br($message->message)));
|
$varsTpl = array(
|
||||||
|
'{lastname}' => $customer->lastname,
|
||||||
|
'{firstname}' => $customer->firstname,
|
||||||
|
'{id_order}' => $order->id,
|
||||||
|
'{message}' => (Configuration::get('PS_MAIL_TYPE') == 2 ? $cm->message : Tools::nl2br($cm->message))
|
||||||
|
);
|
||||||
if (@Mail::Send((int)($order->id_lang), 'order_merchant_comment',
|
if (@Mail::Send((int)($order->id_lang), 'order_merchant_comment',
|
||||||
Mail::l('New message regarding your order'), $varsTpl, $customer->email,
|
Mail::l('New message regarding your order'), $varsTpl, $customer->email,
|
||||||
$customer->firstname.' '.$customer->lastname, NULL, NULL, NULL, NULL, _PS_MAIL_DIR_, true))
|
$customer->firstname.' '.$customer->lastname, NULL, NULL, NULL, NULL, _PS_MAIL_DIR_, true))
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class CustomerMessageCore extends ObjectModel
|
|||||||
public $file_name;
|
public $file_name;
|
||||||
public $ip_address;
|
public $ip_address;
|
||||||
public $user_agent;
|
public $user_agent;
|
||||||
|
public $private;
|
||||||
public $date_add;
|
public $date_add;
|
||||||
|
|
||||||
protected $table = 'customer_message';
|
protected $table = 'customer_message';
|
||||||
@@ -52,11 +53,12 @@ class CustomerMessageCore extends ObjectModel
|
|||||||
$fields['file_name'] = pSQL($this->file_name);
|
$fields['file_name'] = pSQL($this->file_name);
|
||||||
$fields['ip_address'] = (int)($this->ip_address);
|
$fields['ip_address'] = (int)($this->ip_address);
|
||||||
$fields['user_agent'] = pSQL($this->user_agent);
|
$fields['user_agent'] = pSQL($this->user_agent);
|
||||||
|
$fields['private'] = pSQL($this->private);
|
||||||
$fields['date_add'] = pSQL($this->date_add);
|
$fields['date_add'] = pSQL($this->date_add);
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getMessagesByOrderId($id_order)
|
public static function getMessagesByOrderId($id_order, $private = true)
|
||||||
{
|
{
|
||||||
return Db::getInstance()->ExecuteS('
|
return Db::getInstance()->ExecuteS('
|
||||||
SELECT cm.*, c.`firstname` AS cfirstname, c.`lastname` AS clastname, e.`firstname` AS efirstname, e.`lastname` AS elastname, (COUNT(cm.id_customer_message) = 0 AND ct.id_customer != 0) AS is_new_for_me
|
SELECT cm.*, c.`firstname` AS cfirstname, c.`lastname` AS clastname, e.`firstname` AS efirstname, e.`lastname` AS elastname, (COUNT(cm.id_customer_message) = 0 AND ct.id_customer != 0) AS is_new_for_me
|
||||||
@@ -64,7 +66,7 @@ class CustomerMessageCore extends ObjectModel
|
|||||||
LEFT JOIN `'._DB_PREFIX_.'customer_thread` ct ON ct.`id_customer_thread` = cm.`id_customer_thread`
|
LEFT JOIN `'._DB_PREFIX_.'customer_thread` ct ON ct.`id_customer_thread` = cm.`id_customer_thread`
|
||||||
LEFT JOIN `'._DB_PREFIX_.'customer` c ON ct.`id_customer` = c.`id_customer`
|
LEFT JOIN `'._DB_PREFIX_.'customer` c ON ct.`id_customer` = c.`id_customer`
|
||||||
LEFT OUTER JOIN `'._DB_PREFIX_.'employee` e ON e.`id_employee` = cm.`id_employee`
|
LEFT OUTER JOIN `'._DB_PREFIX_.'employee` e ON e.`id_employee` = cm.`id_employee`
|
||||||
WHERE ct.id_order = '.(int)$id_order.'
|
WHERE ct.id_order = '.(int)$id_order.' AND '.(!$private ? 'cm.`private` = 0' : '').'
|
||||||
GROUP BY cm.id_customer_message
|
GROUP BY cm.id_customer_message
|
||||||
ORDER BY cm.date_add DESC');
|
ORDER BY cm.date_add DESC');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,8 +62,6 @@ class OrderDetailControllerCore extends FrontController
|
|||||||
//check if a thread already exist
|
//check if a thread already exist
|
||||||
$id_customer_thread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder($this->context->customer->email, $order->id);
|
$id_customer_thread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder($this->context->customer->email, $order->id);
|
||||||
|
|
||||||
p(var_dump($id_customer_thread));
|
|
||||||
|
|
||||||
$cm = new CustomerMessage();
|
$cm = new CustomerMessage();
|
||||||
if (!$id_customer_thread)
|
if (!$id_customer_thread)
|
||||||
{
|
{
|
||||||
@@ -164,7 +162,7 @@ class OrderDetailControllerCore extends FrontController
|
|||||||
'deliveryAddressFormatedValues' => $deliveryAddressFormatedValues,
|
'deliveryAddressFormatedValues' => $deliveryAddressFormatedValues,
|
||||||
'deliveryState' => (Validate::isLoadedObject($addressDelivery) AND $addressDelivery->id_state) ? new State($addressDelivery->id_state) : false,
|
'deliveryState' => (Validate::isLoadedObject($addressDelivery) AND $addressDelivery->id_state) ? new State($addressDelivery->id_state) : false,
|
||||||
'is_guest' => false,
|
'is_guest' => false,
|
||||||
'messages' => CustomerMessage::getMessagesByOrderId((int)($order->id)),
|
'messages' => CustomerMessage::getMessagesByOrderId((int)($order->id), false),
|
||||||
'CUSTOMIZE_FILE' => Product::CUSTOMIZE_FILE,
|
'CUSTOMIZE_FILE' => Product::CUSTOMIZE_FILE,
|
||||||
'CUSTOMIZE_TEXTFIELD' => _CUSTOMIZE_TEXTFIELD_,
|
'CUSTOMIZE_TEXTFIELD' => _CUSTOMIZE_TEXTFIELD_,
|
||||||
'isRecyclable' => Configuration::get('PS_RECYCLABLE_PACK'),
|
'isRecyclable' => Configuration::get('PS_RECYCLABLE_PACK'),
|
||||||
|
|||||||
@@ -468,6 +468,14 @@ CREATE TABLE `PREFIX_customer_message` (
|
|||||||
KEY `id_employee` (`id_employee`)
|
KEY `id_employee` (`id_employee`)
|
||||||
) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8;
|
) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE `PREFIX_customer_message_sync_imap` (
|
||||||
|
`md5_header` varbinary(32) NOT NULL,
|
||||||
|
KEY `md5_header_index` (`md5_header`(4))
|
||||||
|
) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
CREATE INDEX `md5_header_index` ON `PREFIX_customer_message_sync_imap` (md5_header(4));
|
||||||
|
|
||||||
CREATE TABLE `PREFIX_customer_thread` (
|
CREATE TABLE `PREFIX_customer_thread` (
|
||||||
`id_customer_thread` int(11) unsigned NOT NULL auto_increment,
|
`id_customer_thread` int(11) unsigned NOT NULL auto_increment,
|
||||||
`id_shop` INT(11) UNSIGNED NOT NULL DEFAULT '1',
|
`id_shop` INT(11) UNSIGNED NOT NULL DEFAULT '1',
|
||||||
|
|||||||
@@ -92,3 +92,11 @@ ALTER TABLE `PREFIX_order_detail`
|
|||||||
DROP `tax_name`,
|
DROP `tax_name`,
|
||||||
DROP `tax_rate`;
|
DROP `tax_rate`;
|
||||||
|
|
||||||
|
CREATE TABLE `PREFIX_customer_message_sync_imap` (
|
||||||
|
`md5_header` varbinary(32) NOT NULL,
|
||||||
|
KEY `md5_header_index` (`md5_header`(4))
|
||||||
|
) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
CREATE INDEX `md5_header_index` ON `PREFIX_customer_message_sync_imap` (md5_header(4));
|
||||||
|
|
||||||
|
ALTER TABLE `PREFIX_customer_message` ADD `private` TINYINT NOT NULL DEFAULT '0' AFTER `user_agent`;
|
||||||
|
|||||||
Reference in New Issue
Block a user