Merge branch 'master' into Development
This commit is contained in:
commit
86b18e27cd
@ -22,7 +22,7 @@ switch ($action) {
|
|||||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
$appUrl = APP_URL;
|
$appUrl = APP_URL;
|
||||||
|
|
||||||
$select2_customer = <<<EOT
|
$select2_customer = <<<EOT
|
||||||
<script>
|
<script>
|
||||||
@ -74,22 +74,22 @@ EOT;
|
|||||||
$message = str_replace('[[user_name]]', $c['username'], $message);
|
$message = str_replace('[[user_name]]', $c['username'], $message);
|
||||||
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
|
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
|
||||||
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
|
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
|
||||||
if (strpos($message, '[[payment_link]]') !== false) {
|
if (strpos($message, '[[payment_link]]') !== false) {
|
||||||
// token only valid for 1 day, for security reason
|
// token only valid for 1 day, for security reason
|
||||||
$token = User::generateToken($c['id'], 1);
|
$token = User::generateToken($c['id'], 1);
|
||||||
if (!empty($token['token'])) {
|
if (!empty($token['token'])) {
|
||||||
$tur = ORM::for_table('tbl_user_recharges')
|
$tur = ORM::for_table('tbl_user_recharges')
|
||||||
->where('customer_id', $c['id'])
|
->where('customer_id', $c['id'])
|
||||||
//->where('namebp', $package)
|
//->where('namebp', $package)
|
||||||
->find_one();
|
->find_one();
|
||||||
if ($tur) {
|
if ($tur) {
|
||||||
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
|
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
|
||||||
$message = str_replace('[[payment_link]]', $url, $message);
|
$message = str_replace('[[payment_link]]', $url, $message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$message = str_replace('[[payment_link]]', '', $message);
|
$message = str_replace('[[payment_link]]', '', $message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Send the message
|
//Send the message
|
||||||
@ -113,158 +113,274 @@ EOT;
|
|||||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ui->assign('routers', ORM::forTable('tbl_routers')->where('enabled', '1')->find_many());
|
||||||
|
$ui->display('admin/message/bulk.tpl');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'send_bulk_ajax':
|
||||||
|
// Check user permissions
|
||||||
|
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||||
|
die(json_encode(['status' => 'error', 'message' => 'Permission denied']));
|
||||||
|
}
|
||||||
|
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
// Initialize counters
|
|
||||||
|
// Get request parameters
|
||||||
|
$group = $_REQUEST['group'] ?? '';
|
||||||
|
$message = $_REQUEST['message'] ?? '';
|
||||||
|
$via = $_REQUEST['via'] ?? '';
|
||||||
|
$batch = $_REQUEST['batch'] ?? 100;
|
||||||
|
$page = $_REQUEST['page'] ?? 0;
|
||||||
|
$router = $_REQUEST['router'] ?? null;
|
||||||
|
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on' ? true : false;
|
||||||
|
|
||||||
|
if (empty($group) || empty($message) || empty($via)) {
|
||||||
|
die(json_encode(['status' => 'error', 'message' => 'All fields are required']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get batch of customers based on group
|
||||||
|
$startpoint = $page * $batch;
|
||||||
|
$customers = [];
|
||||||
|
|
||||||
|
if (isset($router) && !empty($router)) {
|
||||||
|
$router = ORM::for_table('tbl_routers')->find_one($router);
|
||||||
|
if (!$router) {
|
||||||
|
die(json_encode(['status' => 'error', 'message' => 'Invalid router']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = ORM::for_table('tbl_user_recharges')
|
||||||
|
->left_outer_join('tbl_customers', 'tbl_user_recharges.customer_id = tbl_customers.id')
|
||||||
|
->where('tbl_user_recharges.routers', $router->name)
|
||||||
|
->offset($startpoint)
|
||||||
|
->limit($batch);
|
||||||
|
|
||||||
|
switch ($group) {
|
||||||
|
case 'all':
|
||||||
|
// No additional conditions needed
|
||||||
|
break;
|
||||||
|
case 'new':
|
||||||
|
$query->where_raw("DATE(recharged_on) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)");
|
||||||
|
break;
|
||||||
|
case 'expired':
|
||||||
|
$query->where('tbl_user_recharges.status', 'off');
|
||||||
|
break;
|
||||||
|
case 'active':
|
||||||
|
$query->where('tbl_user_recharges.status', 'on');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->selects([
|
||||||
|
['tbl_customers.phonenumber', 'phonenumber'],
|
||||||
|
['tbl_user_recharges.customer_id', 'customer_id'],
|
||||||
|
['tbl_customers.fullname', 'fullname'],
|
||||||
|
]);
|
||||||
|
$customers = $query->find_array();
|
||||||
|
} else {
|
||||||
|
switch ($group) {
|
||||||
|
case 'all':
|
||||||
|
$customers = ORM::for_table('tbl_customers')->offset($startpoint)->limit($batch)->find_array();
|
||||||
|
break;
|
||||||
|
case 'new':
|
||||||
|
$customers = ORM::for_table('tbl_customers')
|
||||||
|
->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)")
|
||||||
|
->offset($startpoint)->limit($batch)->find_array();
|
||||||
|
break;
|
||||||
|
case 'expired':
|
||||||
|
$customers = ORM::for_table('tbl_user_recharges')->where('status', 'off')
|
||||||
|
->select('customer_id')->offset($startpoint)->limit($batch)->find_array();
|
||||||
|
break;
|
||||||
|
case 'active':
|
||||||
|
$customers = ORM::for_table('tbl_user_recharges')->where('status', 'on')
|
||||||
|
->select('customer_id')->offset($startpoint)->limit($batch)->find_array();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure $customers is always an array
|
||||||
|
if (!$customers) {
|
||||||
|
$customers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate total customers for the group
|
||||||
|
$totalCustomers = 0;
|
||||||
|
if ($router) {
|
||||||
|
switch ($group) {
|
||||||
|
case 'all':
|
||||||
|
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('routers', $router->routers)->count();
|
||||||
|
break;
|
||||||
|
case 'new':
|
||||||
|
$totalCustomers = ORM::for_table('tbl_user_recharges')
|
||||||
|
->where_raw("DATE(recharged_on) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)")
|
||||||
|
->where('routers', $router->routers)
|
||||||
|
->count();
|
||||||
|
break;
|
||||||
|
case 'expired':
|
||||||
|
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('status', 'off')->where('routers', $router->routers)->count();
|
||||||
|
break;
|
||||||
|
case 'active':
|
||||||
|
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('status', 'on')->where('routers', $router->routers)->count();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch ($group) {
|
||||||
|
case 'all':
|
||||||
|
$totalCustomers = ORM::for_table('tbl_customers')->count();
|
||||||
|
break;
|
||||||
|
case 'new':
|
||||||
|
$totalCustomers = ORM::for_table('tbl_customers')
|
||||||
|
->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)")
|
||||||
|
->count();
|
||||||
|
break;
|
||||||
|
case 'expired':
|
||||||
|
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('status', 'off')->count();
|
||||||
|
break;
|
||||||
|
case 'active':
|
||||||
|
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('status', 'on')->count();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send messages
|
||||||
$totalSMSSent = 0;
|
$totalSMSSent = 0;
|
||||||
$totalSMSFailed = 0;
|
$totalSMSFailed = 0;
|
||||||
$totalWhatsappSent = 0;
|
$totalWhatsappSent = 0;
|
||||||
$totalWhatsappFailed = 0;
|
$totalWhatsappFailed = 0;
|
||||||
$totalCustomers = 0;
|
$batchStatus = [];
|
||||||
$batchStatus = $_SESSION['batchStatus'];
|
|
||||||
$page = _req('page', -1);
|
|
||||||
|
|
||||||
if (_req('send') == 'now') {
|
foreach ($customers as $customer) {
|
||||||
// Get form data
|
$currentMessage = str_replace(
|
||||||
$group = $_REQUEST['group'];
|
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
|
||||||
$message = $_REQUEST['message'];
|
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
|
||||||
$via = $_REQUEST['via'];
|
$message
|
||||||
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on' ? 'yes' : 'no';
|
);
|
||||||
$batch = $_REQUEST['batch'];
|
|
||||||
$delay = $_REQUEST['delay'];
|
|
||||||
|
|
||||||
$ui->assign('group', $group);
|
$phoneNumber = preg_replace('/\D/', '', $customer['phonenumber']);
|
||||||
$ui->assign('message', $message);
|
|
||||||
$ui->assign('via', $via);
|
if (empty($phoneNumber)) {
|
||||||
$ui->assign('test', $test);
|
$batchStatus[] = [
|
||||||
$ui->assign('batch', $batch);
|
'name' => $customer['fullname'],
|
||||||
$ui->assign('delay', $delay);
|
'phone' => '',
|
||||||
if($page<0){
|
'status' => 'No Phone Number'
|
||||||
$batchStatus = [];
|
];
|
||||||
$page = 0;
|
continue;
|
||||||
}
|
}
|
||||||
$startpoint = $page * $batch;
|
|
||||||
$page++;
|
if ($test) {
|
||||||
// Check if fields are empty
|
$batchStatus[] = [
|
||||||
if ($group == '' || $message == '' || $via == '') {
|
'name' => $customer['fullname'],
|
||||||
r2(getUrl('message/send_bulk'), 'e', Lang::T('All fields are required'));
|
'phone' => $customer['phonenumber'],
|
||||||
|
'status' => 'Test Mode',
|
||||||
|
'message' => $currentMessage
|
||||||
|
];
|
||||||
} else {
|
} else {
|
||||||
// Get customer details from the database based on the selected group
|
if ($via == 'sms' || $via == 'both') {
|
||||||
if ($group == 'all') {
|
if (Message::sendSMS($customer['phonenumber'], $currentMessage)) {
|
||||||
$customers = ORM::for_table('tbl_customers')
|
$totalSMSSent++;
|
||||||
->offset($startpoint)
|
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Sent', 'message' => $currentMessage];
|
||||||
->limit($batch)->find_array();
|
} else {
|
||||||
} elseif ($group == 'new') {
|
$totalSMSFailed++;
|
||||||
// Get customers created just a month ago
|
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Failed', 'message' => $currentMessage];
|
||||||
$customers = ORM::for_table('tbl_customers')->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)")
|
}
|
||||||
->offset($startpoint)->limit($batch)
|
|
||||||
->find_array();
|
|
||||||
} elseif ($group == 'expired') {
|
|
||||||
// Get expired user recharges where status is 'off'
|
|
||||||
$expired = ORM::for_table('tbl_user_recharges')->select('customer_id')->where('status', 'off')
|
|
||||||
->offset($startpoint)->limit($batch)
|
|
||||||
->find_array();
|
|
||||||
$customer_ids = array_column($expired, 'customer_id');
|
|
||||||
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_array();
|
|
||||||
} elseif ($group == 'active') {
|
|
||||||
// Get active user recharges where status is 'on'
|
|
||||||
$active = ORM::for_table('tbl_user_recharges')->select('customer_id')->where('status', 'on')
|
|
||||||
->offset($startpoint)->limit($batch)
|
|
||||||
->find_array();
|
|
||||||
$customer_ids = array_column($active, 'customer_id');
|
|
||||||
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_array();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the batch size
|
if ($via == 'wa' || $via == 'both') {
|
||||||
$batchSize = $batch;
|
if (Message::sendWhatsapp($customer['phonenumber'], $currentMessage)) {
|
||||||
|
$totalWhatsappSent++;
|
||||||
// Calculate the number of batches
|
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
|
||||||
$totalCustomers = count($customers);
|
|
||||||
$totalBatches = ceil($totalCustomers / $batchSize);
|
|
||||||
|
|
||||||
// Loop through customers in the current batch and send messages
|
|
||||||
foreach ($customers as $customer) {
|
|
||||||
// Create a copy of the original message for each customer and save it as currentMessage
|
|
||||||
$currentMessage = $message;
|
|
||||||
$currentMessage = str_replace('[[name]]', $customer['fullname'], $currentMessage);
|
|
||||||
$currentMessage = str_replace('[[user_name]]', $customer['username'], $currentMessage);
|
|
||||||
$currentMessage = str_replace('[[phone]]', $customer['phonenumber'], $currentMessage);
|
|
||||||
$currentMessage = str_replace('[[company_name]]', $config['CompanyName'], $currentMessage);
|
|
||||||
|
|
||||||
if(empty($customer['phonenumber'])){
|
|
||||||
$batchStatus[] = [
|
|
||||||
'name' => $customer['fullname'],
|
|
||||||
'phone' => $customer['phonenumber'],
|
|
||||||
'message' => $currentMessage,
|
|
||||||
'status' => 'No Phone Number'
|
|
||||||
];
|
|
||||||
}else
|
|
||||||
// Send the message based on the selected method
|
|
||||||
if ($test === 'yes') {
|
|
||||||
// Only for testing, do not send messages to customers
|
|
||||||
$batchStatus[] = [
|
|
||||||
'name' => $customer['fullname'],
|
|
||||||
'phone' => $customer['phonenumber'],
|
|
||||||
'message' => $currentMessage,
|
|
||||||
'status' => 'Test Mode - Message not sent'
|
|
||||||
];
|
|
||||||
} else {
|
} else {
|
||||||
// Send the actual messages
|
$totalWhatsappFailed++;
|
||||||
if ($via == 'sms' || $via == 'both') {
|
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
|
||||||
$smsSent = Message::sendSMS($customer['phonenumber'], $currentMessage);
|
|
||||||
if ($smsSent) {
|
|
||||||
$totalSMSSent++;
|
|
||||||
$batchStatus[] = [
|
|
||||||
'name' => $customer['fullname'],
|
|
||||||
'phone' => $customer['phonenumber'],
|
|
||||||
'message' => $currentMessage,
|
|
||||||
'status' => 'SMS Message Sent'
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$totalSMSFailed++;
|
|
||||||
$batchStatus[] = [
|
|
||||||
'name' => $customer['fullname'],
|
|
||||||
'phone' => $customer['phonenumber'],
|
|
||||||
'message' => $currentMessage,
|
|
||||||
'status' => 'SMS Message Failed'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($via == 'wa' || $via == 'both') {
|
|
||||||
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentMessage);
|
|
||||||
if ($waSent) {
|
|
||||||
$totalWhatsappSent++;
|
|
||||||
$batchStatus[] = [
|
|
||||||
'name' => $customer['fullname'],
|
|
||||||
'phone' => $customer['phonenumber'],
|
|
||||||
'message' => $currentMessage,
|
|
||||||
'status' => 'WhatsApp Message Sent'
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$totalWhatsappFailed++;
|
|
||||||
$batchStatus[] = [
|
|
||||||
'name' => $customer['fullname'],
|
|
||||||
'phone' => $customer['phonenumber'],
|
|
||||||
'message' => $currentMessage,
|
|
||||||
'status' => 'WhatsApp Message Failed'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$ui->assign('page', $page);
|
|
||||||
$ui->assign('totalCustomers', $totalCustomers);
|
// Calculate if there are more customers to process
|
||||||
$_SESSION['batchStatus'] = $batchStatus;
|
$hasMore = ($startpoint + $batch) < $totalCustomers;
|
||||||
$ui->assign('batchStatus', $batchStatus);
|
|
||||||
$ui->assign('totalSMSSent', $totalSMSSent);
|
// Return JSON response
|
||||||
$ui->assign('totalSMSFailed', $totalSMSFailed);
|
echo json_encode([
|
||||||
$ui->assign('totalWhatsappSent', $totalWhatsappSent);
|
'status' => 'success',
|
||||||
$ui->assign('totalWhatsappFailed', $totalWhatsappFailed);
|
'page' => $page + 1,
|
||||||
$ui->display('admin/message/bulk.tpl');
|
'batchStatus' => $batchStatus,
|
||||||
|
'message' => $currentMessage,
|
||||||
|
'totalSent' => $totalSMSSent + $totalWhatsappSent,
|
||||||
|
'totalFailed' => $totalSMSFailed + $totalWhatsappFailed,
|
||||||
|
'hasMore' => $hasMore
|
||||||
|
]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'send_bulk_selected':
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// Set headers
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||||
|
|
||||||
|
// Get the posted data
|
||||||
|
$customerIds = $_POST['customer_ids'] ?? [];
|
||||||
|
$via = $_POST['message_type'] ?? '';
|
||||||
|
$message = isset($_POST['message']) ? trim($_POST['message']) : '';
|
||||||
|
if (empty($customerIds) || empty($message) || empty($via)) {
|
||||||
|
echo json_encode(['status' => 'error', 'message' => Lang::T('Invalid customer IDs, Message, or Message Type.')]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare to send messages
|
||||||
|
$sentCount = 0;
|
||||||
|
$failedCount = 0;
|
||||||
|
$subject = Lang::T('Notification Message');
|
||||||
|
$form = 'Admin';
|
||||||
|
|
||||||
|
foreach ($customerIds as $customerId) {
|
||||||
|
$customer = ORM::for_table('tbl_customers')->where('id', $customerId)->find_one();
|
||||||
|
if ($customer) {
|
||||||
|
$messageSent = false;
|
||||||
|
|
||||||
|
// Check the message type and send accordingly
|
||||||
|
try {
|
||||||
|
if ($via === 'sms' || $via === 'all') {
|
||||||
|
$messageSent = Message::sendSMS($customer['phonenumber'], $message);
|
||||||
|
}
|
||||||
|
if (!$messageSent && ($via === 'wa' || $via === 'all')) {
|
||||||
|
$messageSent = Message::sendWhatsapp($customer['phonenumber'], $message);
|
||||||
|
}
|
||||||
|
if (!$messageSent && ($via === 'inbox' || $via === 'all')) {
|
||||||
|
Message::addToInbox($customer['id'], $subject, $message, $form);
|
||||||
|
$messageSent = true;
|
||||||
|
}
|
||||||
|
if (!$messageSent && ($via === 'email' || $via === 'all')) {
|
||||||
|
$messageSent = Message::sendEmail($customer['email'], $subject, $message);
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$messageSent = false;
|
||||||
|
$failedCount++;
|
||||||
|
sendTelegram('Failed to send message to ' . $e->getMessage());
|
||||||
|
_log('Failed to send message to ' . $customer['fullname'] . ': ' . $e->getMessage());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($messageSent) {
|
||||||
|
$sentCount++;
|
||||||
|
} else {
|
||||||
|
$failedCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$failedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the response
|
||||||
|
echo json_encode([
|
||||||
|
'status' => 'success',
|
||||||
|
'totalSent' => $sentCount,
|
||||||
|
'totalFailed' => $failedCount
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['status' => 'error', 'message' => Lang::T('Invalid request method.')]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
r2(getUrl('message/send_sms'), 'e', 'action not defined');
|
r2(getUrl('message/send_sms'), 'e', 'action not defined');
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ switch ($action) {
|
|||||||
r2(getUrl('pluginmanager'), 's', 'Refresh success');
|
r2(getUrl('pluginmanager'), 's', 'Refresh success');
|
||||||
break;
|
break;
|
||||||
case 'dlinstall':
|
case 'dlinstall':
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
r2(getUrl('pluginmanager'), 'e', 'Demo Mode cannot install as it Security risk');
|
r2(getUrl('pluginmanager'), 'e', 'Demo Mode cannot install as it Security risk');
|
||||||
}
|
}
|
||||||
if (!is_writeable($CACHE_PATH)) {
|
if (!is_writeable($CACHE_PATH)) {
|
||||||
|
164
system/cron.php
164
system/cron.php
@ -30,7 +30,7 @@ if (php_sapi_name() !== 'cli') {
|
|||||||
echo "PHP Time\t" . date('Y-m-d H:i:s') . "\n";
|
echo "PHP Time\t" . date('Y-m-d H:i:s') . "\n";
|
||||||
$res = ORM::raw_execute('SELECT NOW() AS WAKTU;');
|
$res = ORM::raw_execute('SELECT NOW() AS WAKTU;');
|
||||||
$statement = ORM::get_last_statement();
|
$statement = ORM::get_last_statement();
|
||||||
$rows = array();
|
$rows = [];
|
||||||
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
|
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
|
||||||
echo "MYSQL Time\t" . $row['WAKTU'] . "\n";
|
echo "MYSQL Time\t" . $row['WAKTU'] . "\n";
|
||||||
}
|
}
|
||||||
@ -45,80 +45,111 @@ echo "Found " . count($d) . " user(s)\n";
|
|||||||
run_hook('cronjob'); #HOOK
|
run_hook('cronjob'); #HOOK
|
||||||
|
|
||||||
foreach ($d as $ds) {
|
foreach ($d as $ds) {
|
||||||
$date_now = strtotime(date("Y-m-d H:i:s"));
|
try {
|
||||||
$expiration = strtotime($ds['expiration'] . ' ' . $ds['time']);
|
$date_now = strtotime(date("Y-m-d H:i:s"));
|
||||||
echo $ds['expiration'] . " : " . (($isCli) ? $ds['username'] : Lang::maskText($ds['username']));
|
$expiration = strtotime($ds['expiration'] . ' ' . $ds['time']);
|
||||||
if ($date_now >= $expiration) {
|
echo $ds['expiration'] . " : " . ($isCli ? $ds['username'] : Lang::maskText($ds['username']));
|
||||||
echo " : EXPIRED \r\n";
|
|
||||||
$u = ORM::for_table('tbl_user_recharges')->where('id', $ds['id'])->find_one();
|
|
||||||
$c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
|
|
||||||
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
|
|
||||||
if (empty($c)) {
|
|
||||||
$c = $u;
|
|
||||||
}
|
|
||||||
$dvc = Package::getDevice($p);
|
|
||||||
if ($_app_stage != 'demo') {
|
|
||||||
if (file_exists($dvc)) {
|
|
||||||
require_once $dvc;
|
|
||||||
(new $p['device'])->remove_customer($c, $p);
|
|
||||||
} else {
|
|
||||||
echo "Cron error Devices $p[device] not found, cannot disconnect $c[username]";
|
|
||||||
Message::sendTelegram("Cron error Devices $p[device] not found, cannot disconnect $c[username]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo Message::sendPackageNotification($c, $u['namebp'], $p['price'], $textExpired, $config['user_notification_expired']) . "\n";
|
|
||||||
//update database user dengan status off
|
|
||||||
$u->status = 'off';
|
|
||||||
$u->save();
|
|
||||||
|
|
||||||
// autorenewal from deposit
|
if ($date_now >= $expiration) {
|
||||||
if ($config['enable_balance'] == 'yes' && $c['auto_renewal']) {
|
echo " : EXPIRED \r\n";
|
||||||
list($bills, $add_cost) = User::getBills($ds['customer_id']);
|
|
||||||
if ($add_cost != 0) {
|
// Fetch user recharge details
|
||||||
if (!empty($add_cost)) {
|
$u = ORM::for_table('tbl_user_recharges')->where('id', $ds['id'])->find_one();
|
||||||
|
if (!$u) {
|
||||||
|
throw new Exception("User recharge record not found for ID: " . $ds['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch customer details
|
||||||
|
$c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
|
||||||
|
if (!$c) {
|
||||||
|
$c = $u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch plan details
|
||||||
|
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
|
||||||
|
if (!$p) {
|
||||||
|
throw new Exception("Plan not found for ID: " . $u['plan_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dvc = Package::getDevice($p);
|
||||||
|
if ($_app_stage != 'demo') {
|
||||||
|
if (file_exists($dvc)) {
|
||||||
|
require_once $dvc;
|
||||||
|
try {
|
||||||
|
(new $p['device'])->remove_customer($c, $p);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
_log($e->getMessage());
|
||||||
|
sendTelegram($e->getMessage());
|
||||||
|
echo "Error: " . $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Exception("Cron error: Devices " . $p['device'] . "not found, cannot disconnect ".$c['username']."\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send notification and update user status
|
||||||
|
try {
|
||||||
|
echo Message::sendPackageNotification($c, $u['namebp'], $p['price'], $textExpired, $config['user_notification_expired']) . "\n";
|
||||||
|
$u->status = 'off';
|
||||||
|
$u->save();
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
_log($e->getMessage());
|
||||||
|
sendTelegram($e->getMessage());
|
||||||
|
echo "Error: " . $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-renewal from deposit
|
||||||
|
if ($config['enable_balance'] == 'yes' && $c['auto_renewal']) {
|
||||||
|
[$bills, $add_cost] = User::getBills($ds['customer_id']);
|
||||||
|
if ($add_cost != 0) {
|
||||||
$p['price'] += $add_cost;
|
$p['price'] += $add_cost;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if ($p && $c['balance'] >= $p['price']) {
|
if ($p && $c['balance'] >= $p['price']) {
|
||||||
if (Package::rechargeUser($ds['customer_id'], $ds['routers'], $p['id'], 'Customer', 'Balance')) {
|
if (Package::rechargeUser($ds['customer_id'], $ds['routers'], $p['id'], 'Customer', 'Balance')) {
|
||||||
// if success, then get the balance
|
Balance::min($ds['customer_id'], $p['price']);
|
||||||
Balance::min($ds['customer_id'], $p['price']);
|
echo "plan enabled: " . (string) $p['enabled'] . " | User balance: " . (string) $c['balance'] . " | price " . (string) $p['price'] . "\n";
|
||||||
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
echo "auto renewal Success\n";
|
||||||
echo "auto renewall Success\n";
|
} else {
|
||||||
|
echo "plan enabled: " . $p['enabled'] . " | User balance: " . $c['balance'] . " | price " . $p['price'] . "\n";
|
||||||
|
echo "auto renewal Failed\n";
|
||||||
|
Message::sendTelegram("FAILED RENEWAL #cron\n\n#u." . $c['username'] . " #buy #Hotspot \n" . $p['name_plan'] .
|
||||||
|
"\nRouter: " . $p['routers'] .
|
||||||
|
"\nPrice: " . $p['price']);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
echo "no renewal | plan enabled: " . (string) $p['enabled'] . " | User balance: " . (string) $c['balance'] . " | price " . (string) $p['price'] . "\n";
|
||||||
echo "auto renewall Failed\n";
|
|
||||||
Message::sendTelegram("FAILED RENEWAL #cron\n\n#u$c[username] #buy #Hotspot \n" . $p['name_plan'] .
|
|
||||||
"\nRouter: " . $p['routers'] .
|
|
||||||
"\nPrice: " . $p['price']);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
echo "no renewall | plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
echo "no renewal | balance" . $config['enable_balance'] . " auto_renewal " . $c['auto_renewal'] . "\n";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
echo "no renewall | balance $config[enable_balance] auto_renewal $c[auto_renewal]\n";
|
echo " : ACTIVE \r\n";
|
||||||
}
|
}
|
||||||
} else {
|
} catch (Throwable $e) {
|
||||||
echo " : ACTIVE \r\n";
|
// Catch any unexpected errors
|
||||||
|
_log($e->getMessage());
|
||||||
|
sendTelegram($e->getMessage());
|
||||||
|
echo "Unexpected Error: " . $e->getMessage() . "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Cek interim-update radiusrest
|
//Cek interim-update radiusrest
|
||||||
if ($config['frrest_interim_update'] != 0) {
|
if ($config['frrest_interim_update'] != 0) {
|
||||||
|
|
||||||
$r_a = ORM::for_table('rad_acct')
|
$r_a = ORM::for_table('rad_acct')
|
||||||
->whereRaw("BINARY acctstatustype = 'Start' OR acctstatustype = 'Interim-Update'")
|
->whereRaw("BINARY acctstatustype = 'Start' OR acctstatustype = 'Interim-Update'")
|
||||||
->where_lte('dateAdded', date("Y-m-d H:i:s"))->find_many();
|
->where_lte('dateAdded', date("Y-m-d H:i:s"))->find_many();
|
||||||
|
|
||||||
foreach ($r_a as $ra) {
|
foreach ($r_a as $ra) {
|
||||||
$interval = $_c['frrest_interim_update']*60;
|
$interval = $_c['frrest_interim_update'] * 60;
|
||||||
$timeUpdate = strtotime($ra['dateAdded'])+$interval;
|
$timeUpdate = strtotime($ra['dateAdded']) + $interval;
|
||||||
$timeNow = strtotime(date("Y-m-d H:i:s"));
|
$timeNow = strtotime(date("Y-m-d H:i:s"));
|
||||||
if ($timeNow >= $timeUpdate) {
|
if ($timeNow >= $timeUpdate) {
|
||||||
$ra->acctstatustype = 'Stop';
|
$ra->acctstatustype = 'Stop';
|
||||||
$ra->save();
|
$ra->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($config['router_check']) {
|
if ($config['router_check']) {
|
||||||
@ -137,7 +168,7 @@ if ($config['router_check']) {
|
|||||||
|
|
||||||
foreach ($routers as $router) {
|
foreach ($routers as $router) {
|
||||||
// check if custom port
|
// check if custom port
|
||||||
if (strpos($router->ip_address, ':') === false){
|
if (strpos($router->ip_address, ':') === false) {
|
||||||
$ip = $router->ip_address;
|
$ip = $router->ip_address;
|
||||||
$port = 8728;
|
$port = 8728;
|
||||||
} else {
|
} else {
|
||||||
@ -207,14 +238,7 @@ if ($config['router_check']) {
|
|||||||
Message::SendEmail($adminEmail, $subject, $message);
|
Message::SendEmail($adminEmail, $subject, $message);
|
||||||
sendTelegram($message);
|
sendTelegram($message);
|
||||||
}
|
}
|
||||||
echo "Router monitoring finished\n";
|
echo "Router monitoring finished checking.\n";
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (defined('PHP_SAPI') && PHP_SAPI === 'cli') {
|
|
||||||
echo "Cronjob finished\n";
|
|
||||||
} else {
|
|
||||||
echo "</pre>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flock($lock, LOCK_UN);
|
flock($lock, LOCK_UN);
|
||||||
@ -224,5 +248,5 @@ unlink($lockFile);
|
|||||||
$timestampFile = "$UPLOAD_PATH/cron_last_run.txt";
|
$timestampFile = "$UPLOAD_PATH/cron_last_run.txt";
|
||||||
file_put_contents($timestampFile, time());
|
file_put_contents($timestampFile, time());
|
||||||
|
|
||||||
|
|
||||||
run_hook('cronjob_end'); #HOOK
|
run_hook('cronjob_end'); #HOOK
|
||||||
|
echo "Cron job finished and completed successfully.\n";
|
@ -16,12 +16,12 @@
|
|||||||
<div class="panel panel-hovered mb20 panel-primary">
|
<div class="panel panel-hovered mb20 panel-primary">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
<a class="btn btn-primary btn-xs" title="save"
|
<a class="btn btn-primary btn-xs" title="save"
|
||||||
href="{Text::url('customers/csv&token=', $csrf_token)}"
|
href="{Text::url('customers/csv&token=', $csrf_token)}"
|
||||||
onclick="return ask(this, 'This will export to CSV?')"><span
|
onclick="return ask(this, 'This will export to CSV?')"><span
|
||||||
class="glyphicon glyphicon-download" aria-hidden="true"></span> CSV</a>
|
class="glyphicon glyphicon-download" aria-hidden="true"></span> CSV</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{Lang::T('Manage Contact')}
|
{Lang::T('Manage Contact')}
|
||||||
</div>
|
</div>
|
||||||
@ -65,8 +65,8 @@
|
|||||||
<span class="input-group-addon">Status</span>
|
<span class="input-group-addon">Status</span>
|
||||||
<select class="form-control" id="filter" name="filter">
|
<select class="form-control" id="filter" name="filter">
|
||||||
{foreach $statuses as $status}
|
{foreach $statuses as $status}
|
||||||
<option value="{$status}" {if $filter eq $status }selected{/if}>{Lang::T($status)}
|
<option value="{$status}" {if $filter eq $status }selected{/if}>{Lang::T($status)}
|
||||||
</option>
|
</option>
|
||||||
{/foreach}
|
{/foreach}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -97,6 +97,7 @@
|
|||||||
<table id="customerTable" class="table table-bordered table-striped table-condensed">
|
<table id="customerTable" class="table table-bordered table-striped table-condensed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th><input type="checkbox" id="select-all"></th>
|
||||||
<th>{Lang::T('Username')}</th>
|
<th>{Lang::T('Username')}</th>
|
||||||
<th>Photo</th>
|
<th>Photo</th>
|
||||||
<th>{Lang::T('Account Type')}</th>
|
<th>{Lang::T('Account Type')}</th>
|
||||||
@ -113,65 +114,222 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{foreach $d as $ds}
|
{foreach $d as $ds}
|
||||||
<tr {if $ds['status'] != 'Active'}class="danger" {/if}>
|
<tr {if $ds['status'] !='Active' }class="danger" {/if}>
|
||||||
<td onclick="window.location.href = '{Text::url('customers/view/', $ds['id'])}'"
|
<td><input type="checkbox" name="customer_ids[]" value="{$ds['id']}"></td>
|
||||||
style="cursor:pointer;">{$ds['username']}</td>
|
<td onclick="window.location.href = '{Text::url('customers/view/', $ds['id'])}'"
|
||||||
<td>
|
style="cursor:pointer;">{$ds['username']}</td>
|
||||||
<a href="{$app_url}/{$UPLOAD_PATH}{$ds['photo']}" target="photo">
|
<td>
|
||||||
<img src="{$app_url}/{$UPLOAD_PATH}{$ds['photo']}.thumb.jpg" width="32" alt="">
|
<a href="{$app_url}/{$UPLOAD_PATH}{$ds['photo']}" target="photo">
|
||||||
</a>
|
<img src="{$app_url}/{$UPLOAD_PATH}{$ds['photo']}.thumb.jpg" width="32" alt="">
|
||||||
</td>
|
</a>
|
||||||
<td>{$ds['account_type']}</td>
|
</td>
|
||||||
<td onclick="window.location.href = '{Text::url('customers/view/', $ds['id'])}'"
|
<td>{$ds['account_type']}</td>
|
||||||
style="cursor: pointer;">{$ds['fullname']}</td>
|
<td onclick="window.location.href = '{Text::url('customers/view/', $ds['id'])}'"
|
||||||
<td>{Lang::moneyFormat($ds['balance'])}</td>
|
style="cursor: pointer;">{$ds['fullname']}</td>
|
||||||
<td align="center">
|
<td>{Lang::moneyFormat($ds['balance'])}</td>
|
||||||
{if $ds['phonenumber']}
|
<td align="center">
|
||||||
<a href="tel:{$ds['phonenumber']}" class="btn btn-default btn-xs"
|
{if $ds['phonenumber']}
|
||||||
title="{$ds['phonenumber']}"><i class="glyphicon glyphicon-earphone"></i></a>
|
<a href="tel:{$ds['phonenumber']}" class="btn btn-default btn-xs"
|
||||||
{/if}
|
title="{$ds['phonenumber']}"><i class="glyphicon glyphicon-earphone"></i></a>
|
||||||
{if $ds['email']}
|
{/if}
|
||||||
<a href="mailto:{$ds['email']}" class="btn btn-default btn-xs"
|
{if $ds['email']}
|
||||||
title="{$ds['email']}"><i class="glyphicon glyphicon-envelope"></i></a>
|
<a href="mailto:{$ds['email']}" class="btn btn-default btn-xs"
|
||||||
{/if}
|
title="{$ds['email']}"><i class="glyphicon glyphicon-envelope"></i></a>
|
||||||
{if $ds['coordinates']}
|
{/if}
|
||||||
<a href="https://www.google.com/maps/dir//{$ds['coordinates']}/" target="_blank"
|
{if $ds['coordinates']}
|
||||||
class="btn btn-default btn-xs" title="{$ds['coordinates']}"><i
|
<a href="https://www.google.com/maps/dir//{$ds['coordinates']}/" target="_blank"
|
||||||
class="glyphicon glyphicon-map-marker"></i></a>
|
class="btn btn-default btn-xs" title="{$ds['coordinates']}"><i
|
||||||
{/if}
|
class="glyphicon glyphicon-map-marker"></i></a>
|
||||||
</td>
|
{/if}
|
||||||
<td align="center" api-get-text="{Text::url('autoload/plan_is_active/')}{$ds['id']}">
|
</td>
|
||||||
<span class="label label-default">•</span>
|
<td align="center" api-get-text="{Text::url('autoload/plan_is_active/')}{$ds['id']}">
|
||||||
</td>
|
<span class="label label-default">•</span>
|
||||||
<td>{$ds['service_type']}</td>
|
</td>
|
||||||
<td>
|
<td>{$ds['service_type']}</td>
|
||||||
{$ds['pppoe_username']}
|
<td>
|
||||||
{if !empty($ds['pppoe_username']) && !empty($ds['pppoe_ip'])}:{/if}
|
{$ds['pppoe_username']}
|
||||||
{$ds['pppoe_ip']}
|
{if !empty($ds['pppoe_username']) && !empty($ds['pppoe_ip'])}:{/if}
|
||||||
</td>
|
{$ds['pppoe_ip']}
|
||||||
<td>{Lang::T($ds['status'])}</td>
|
</td>
|
||||||
<td>{Lang::dateTimeFormat($ds['created_at'])}</td>
|
<td>{Lang::T($ds['status'])}</td>
|
||||||
<td align="center">
|
<td>{Lang::dateTimeFormat($ds['created_at'])}</td>
|
||||||
<a href="{Text::url('customers/view/')}{$ds['id']}" id="{$ds['id']}"
|
<td align="center">
|
||||||
style="margin: 0px; color:black"
|
<a href="{Text::url('customers/view/')}{$ds['id']}" id="{$ds['id']}"
|
||||||
class="btn btn-success btn-xs"> {Lang::T('View')} </a>
|
style="margin: 0px; color:black"
|
||||||
<a href="{Text::url('customers/edit/', $ds['id'], '&token=', $csrf_token)}"
|
class="btn btn-success btn-xs"> {Lang::T('View')} </a>
|
||||||
id="{$ds['id']}" style="margin: 0px; color:black"
|
<a href="{Text::url('customers/edit/', $ds['id'], '&token=', $csrf_token)}"
|
||||||
class="btn btn-info btn-xs"> {Lang::T('Edit')} </a>
|
id="{$ds['id']}" style="margin: 0px; color:black"
|
||||||
<a href="{Text::url('customers/sync/', $ds['id'], '&token=', $csrf_token)}"
|
class="btn btn-info btn-xs"> {Lang::T('Edit')} </a>
|
||||||
id="{$ds['id']}" style="margin: 5px; color:black"
|
<a href="{Text::url('customers/sync/', $ds['id'], '&token=', $csrf_token)}"
|
||||||
class="btn btn-success btn-xs"> {Lang::T('Sync')} </a>
|
id="{$ds['id']}" style="margin: 5px; color:black"
|
||||||
<a href="{Text::url('plan/recharge/', $ds['id'], '&token=', $csrf_token)}" id="{$ds['id']}"
|
class="btn btn-success btn-xs"> {Lang::T('Sync')} </a>
|
||||||
style="margin: 0px;" class="btn btn-primary btn-xs">{Lang::T('Recharge')}</a>
|
<a href="{Text::url('plan/recharge/', $ds['id'], '&token=', $csrf_token)}"
|
||||||
</td>
|
id="{$ds['id']}" style="margin: 0px;"
|
||||||
</tr>
|
class="btn btn-primary btn-xs">{Lang::T('Recharge')}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
{/foreach}
|
{/foreach}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div class="row" style="padding: 5px">
|
||||||
|
<div class="col-lg-3 col-lg-offset-9">
|
||||||
|
<div class="btn-group btn-group-justified" role="group">
|
||||||
|
<!-- <div class="btn-group" role="group">
|
||||||
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||||
|
<button id="deleteSelectedTokens" class="btn btn-danger">{Lang::T('Delete
|
||||||
|
Selected')}</button>
|
||||||
|
{/if}
|
||||||
|
</div> -->
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button id="sendMessageToSelected" class="btn btn-success">{Lang::T('Send
|
||||||
|
Message')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{include file="pagination.tpl"}
|
{include file="pagination.tpl"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{include file="sections/footer.tpl"}
|
<!-- Modal for Sending Messages -->
|
||||||
|
<div id="sendMessageModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="sendMessageModalLabel"
|
||||||
|
aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="sendMessageModalLabel">{Lang::T('Send Message')}</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<select id="messageType" class="form-control">
|
||||||
|
<option value="all">{Lang::T('All')}</option>
|
||||||
|
<option value="email">{Lang::T('Email')}</option>
|
||||||
|
<option value="inbox">{Lang::T('Inbox')}</option>
|
||||||
|
<option value="sms">{Lang::T('SMS')}</option>
|
||||||
|
<option value="wa">{Lang::T('WhatsApp')}</option>
|
||||||
|
</select>
|
||||||
|
<br>
|
||||||
|
<textarea id="messageContent" class="form-control" rows="4"
|
||||||
|
placeholder="{Lang::T('Enter your message here...')}"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">{Lang::T('Close')}</button>
|
||||||
|
<button type="button" id="sendMessageButton" class="btn btn-primary">{Lang::T('Send Message')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// Select or deselect all checkboxes
|
||||||
|
document.getElementById('select-all').addEventListener('change', function () {
|
||||||
|
var checkboxes = document.querySelectorAll('input[name="customer_ids[]"]');
|
||||||
|
for (var checkbox of checkboxes) {
|
||||||
|
checkbox.checked = this.checked;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
let selectedCustomerIds = [];
|
||||||
|
|
||||||
|
// Collect selected customer IDs when the button is clicked
|
||||||
|
$('#sendMessageToSelected').on('click', function () {
|
||||||
|
selectedCustomerIds = $('input[name="customer_ids[]"]:checked').map(function () {
|
||||||
|
return $(this).val();
|
||||||
|
}).get();
|
||||||
|
|
||||||
|
if (selectedCustomerIds.length === 0) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Error!',
|
||||||
|
text: "{Lang::T('Please select at least one customer to send a message.')}",
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the modal
|
||||||
|
$('#sendMessageModal').modal('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle sending the message
|
||||||
|
$('#sendMessageButton').on('click', function () {
|
||||||
|
const message = $('#messageContent').val().trim();
|
||||||
|
const messageType = $('#messageType').val();
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Error!',
|
||||||
|
text: "{Lang::T('Please enter a message to send.')}",
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable the button and show loading text
|
||||||
|
$(this).prop('disabled', true).text('{Lang::T('Sending...')}');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '?_route=message/send_bulk_selected',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
customer_ids: selectedCustomerIds,
|
||||||
|
message_type: messageType,
|
||||||
|
message: message
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (response) {
|
||||||
|
// Handle success response
|
||||||
|
if (response.status === 'success') {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Success!',
|
||||||
|
text: "{Lang::T('Message sent successfully.')}",
|
||||||
|
icon: 'success',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Error!',
|
||||||
|
text: "{Lang::T('Error sending message: ')}" + response.message,
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$('#sendMessageModal').modal('hide');
|
||||||
|
$('#messageContent').val(''); // Clear the message content
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Error!',
|
||||||
|
text: "{Lang::T('Failed to send the message. Please try again.')}",
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
// Re-enable the button and reset text
|
||||||
|
$('#sendMessageButton').prop('disabled', false).text('{Lang::T('Send Message')}');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('#sendMessageModal').on('show.bs.modal', function () {
|
||||||
|
$(this).attr('inert', 'true');
|
||||||
|
});
|
||||||
|
$('#sendMessageModal').on('shown.bs.modal', function () {
|
||||||
|
$('#messageContent').focus();
|
||||||
|
$(this).removeAttr('inert');
|
||||||
|
});
|
||||||
|
$('#sendMessageModal').on('hidden.bs.modal', function () {
|
||||||
|
// $('#button').focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{include file = "sections/footer.tpl" }
|
@ -1,180 +1,219 @@
|
|||||||
{include file="sections/header.tpl"}
|
{include file="sections/header.tpl"}
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">
|
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
{if $page>0 && $totalCustomers>0}
|
<div id="status" class="mb-3"></div>
|
||||||
<div class="alert alert-info" role="alert"><span class="loading"></span> {Lang::T("Sending message in progress. Don't close this page.")}</div>
|
<div class="panel panel-primary panel-hovered panel-stacked mb30 {if $page>0 && $totalCustomers >0}hidden{/if}">
|
||||||
{/if}
|
<div class="panel-heading">{Lang::T('Send Bulk Message')}</div>
|
||||||
<div class="panel panel-primary panel-hovered panel-stacked mb30 {if $page>0 && $totalCustomers >0}hidden{/if}">
|
<div class="panel-body">
|
||||||
<div class="panel-heading">{Lang::T('Send Bulk Message')}</div>
|
<form class="form-horizontal" method="get" role="form" id="bulkMessageForm" action="">
|
||||||
<div class="panel-body">
|
<input type="hidden" name="page" value="{if $page>0 && $totalCustomers==0}-1{else}{$page}{/if}">
|
||||||
<form class="form-horizontal" method="get" role="form" id="bulkMessageForm" action="">
|
<div class="form-group">
|
||||||
<input type="hidden" name="page" value="{if $page>0 && $totalCustomers==0}-1{else}{$page}{/if}">
|
<label class="col-md-2 control-label">{Lang::T('Router')}</label>
|
||||||
<div class="form-group">
|
<div class="col-md-6">
|
||||||
<label class="col-md-2 control-label">{Lang::T('Group')}</label>
|
<select class="form-control select2" name="router" id="router">
|
||||||
<div class="col-md-6">
|
<option value="">{Lang::T('All Routers')}</option>
|
||||||
<select class="form-control" name="group" id="group">
|
{foreach $routers as $router}
|
||||||
<option value="all" {if $group == 'all'}selected{/if}>{Lang::T('All Customers')}
|
<option value="{$router['id']}">{$router['name']}</option>
|
||||||
</option>
|
{/foreach}
|
||||||
<option value="new" {if $group == 'new'}selected{/if}>{Lang::T('New Customers')}
|
</select>
|
||||||
</option>
|
</div>
|
||||||
<option value="expired" {if $group == 'expired'}selected{/if}>
|
</div>
|
||||||
{Lang::T('Expired Customers')}</option>
|
<div class="form-group">
|
||||||
<option value="active" {if $group == 'active'}selected{/if}>
|
<label class="col-md-2 control-label">{Lang::T('Group')}</label>
|
||||||
{Lang::T('Active Customers')}</option>
|
<div class="col-md-6">
|
||||||
</select>
|
<select class="form-control" name="group" id="group">
|
||||||
</div>
|
<option value="all" {if $group=='all' }selected{/if}>{Lang::T('All Customers')}</option>
|
||||||
</div>
|
<option value="new" {if $group=='new' }selected{/if}>{Lang::T('New Customers')}</option>
|
||||||
<div class="form-group">
|
<option value="expired" {if $group=='expired' }selected{/if}>{Lang::T('Expired Customers')}</option>
|
||||||
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
<option value="active" {if $group=='active' }selected{/if}>{Lang::T('Active Customers')}</option>
|
||||||
<div class="col-md-6">
|
</select>
|
||||||
<select class="form-control" name="via" id="via">
|
</div>
|
||||||
<option value="sms" {if $via == 'sms'}selected{/if}>{Lang::T('SMS')}</option>
|
</div>
|
||||||
<option value="wa" {if $via == 'wa'}selected{/if}>{Lang::T('WhatsApp')}</option>
|
<div class="form-group">
|
||||||
<option value="both" {if $via == 'both'}selected{/if}>{Lang::T('SMS and WhatsApp')}
|
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
||||||
</option>
|
<div class="col-md-6">
|
||||||
</select>
|
<select class="form-control" name="via" id="via">
|
||||||
</div>
|
<option value="sms" {if $via=='sms' }selected{/if}>{Lang::T('SMS')}</option>
|
||||||
</div>
|
<option value="wa" {if $via=='wa' }selected{/if}>{Lang::T('WhatsApp')}</option>
|
||||||
<div class="form-group">
|
<option value="both" {if $via=='both' }selected{/if}>{Lang::T('SMS and WhatsApp')}</option>
|
||||||
<label class="col-md-2 control-label">{Lang::T('Message per time')}</label>
|
</select>
|
||||||
<div class="col-md-6">
|
</div>
|
||||||
<select class="form-control" name="batch" id="batch">
|
</div>
|
||||||
<option value="5" {if $batch == '5'}selected{/if}>{Lang::T('5 Messages')}</option>
|
<div class="form-group">
|
||||||
<option value="10" {if $batch == '10'}selected{/if}>{Lang::T('10 Messages')}</option>
|
<label class="col-md-2 control-label">{Lang::T('Message per time')}</label>
|
||||||
<option value="15" {if $batch == '15'}selected{/if}>{Lang::T('15 Messages')}</option>
|
<div class="col-md-6">
|
||||||
<option value="20" {if $batch == '20'}selected{/if}>{Lang::T('20 Messages')}</option>
|
<select class="form-control" name="batch" id="batch">
|
||||||
<option value="30" {if $batch == '30'}selected{/if}>{Lang::T('30 Messages')}</option>
|
<option value="5" {if $batch=='5' }selected{/if}>{Lang::T('5 Messages')}</option>
|
||||||
<option value="40" {if $batch == '40'}selected{/if}>{Lang::T('40 Messages')}</option>
|
<option value="10" {if $batch=='10' }selected{/if}>{Lang::T('10 Messages')}</option>
|
||||||
<option value="50" {if $batch == '50'}selected{/if}>{Lang::T('50 Messages')}</option>
|
<option value="15" {if $batch=='15' }selected{/if}>{Lang::T('15 Messages')}</option>
|
||||||
<option value="60" {if $batch == '60'}selected{/if}>{Lang::T('60 Messages')}</option>
|
<option value="20" {if $batch=='20' }selected{/if}>{Lang::T('20 Messages')}</option>
|
||||||
</select>{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
|
<option value="30" {if $batch=='30' }selected{/if}>{Lang::T('30 Messages')}</option>
|
||||||
</div>
|
<option value="40" {if $batch=='40' }selected{/if}>{Lang::T('40 Messages')}</option>
|
||||||
</div>
|
<option value="50" {if $batch=='50' }selected{/if}>{Lang::T('50 Messages')}</option>
|
||||||
<div class="form-group">
|
<option value="60" {if $batch=='60' }selected{/if}>{Lang::T('60 Messages')}</option>
|
||||||
<label class="col-md-2 control-label">{Lang::T('Delay')}</label>
|
</select>
|
||||||
<div class="col-md-6">
|
{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
|
||||||
<select class="form-control" name="delay" id="delay">
|
</div>
|
||||||
<option value="1" {if $delay == '1'}selected{/if}>{Lang::T('No Delay')}</option>
|
</div>
|
||||||
<option value="5" {if $delay == '5'}selected{/if}>{Lang::T('5 Seconds')}</option>
|
<div class="form-group">
|
||||||
<option value="10" {if $delay == '10'}selected{/if}>{Lang::T('10 Seconds')}</option>
|
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
||||||
<option value="15" {if $delay == '15'}selected{/if}>{Lang::T('15 Seconds')}</option>
|
<div class="col-md-6">
|
||||||
<option value="20" {if $delay == '20'}selected{/if}>{Lang::T('20 Seconds')}</option>
|
<textarea class="form-control" id="message" name="message" required placeholder="{Lang::T('Compose your message...')}" rows="5">{$message}</textarea>
|
||||||
</select>{Lang::T('Use at least 5 secs if you are sending to all customers to avoid being banned by your message provider')}
|
<input name="test" id="test" type="checkbox">
|
||||||
</div>
|
{Lang::T('Testing [if checked no real message is sent]')}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<p class="help-block col-md-4">
|
||||||
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
{Lang::T('Use placeholders:')}
|
||||||
<div class="col-md-6">
|
<br>
|
||||||
<textarea class="form-control" id="message" name="message" required
|
<b>[[name]]</b> - {Lang::T('Customer Name')}
|
||||||
placeholder="{Lang::T('Compose your message...')}" rows="5">{$message}</textarea>
|
<br>
|
||||||
<input name="test" type="checkbox">
|
<b>[[user_name]]</b> - {Lang::T('Customer Username')}
|
||||||
{Lang::T('Testing [if checked no real message is sent]')}
|
<br>
|
||||||
</div>
|
<b>[[phone]]</b> - {Lang::T('Customer Phone')}
|
||||||
<p class="help-block col-md-4">
|
<br>
|
||||||
{Lang::T('Use placeholders:')}
|
<b>[[company_name]]</b> - {Lang::T('Your Company Name')}
|
||||||
<br>
|
</p>
|
||||||
<b>[[name]]</b> - {Lang::T('Customer Name')}
|
</div>
|
||||||
<br>
|
<div class="form-group">
|
||||||
<b>[[user_name]]</b> - {Lang::T('Customer Username')}
|
<div class="col-lg-offset-2 col-lg-10">
|
||||||
<br>
|
<button type="button" id="startBulk" class="btn btn-primary">Start Bulk Messaging</button>
|
||||||
<b>[[phone]]</b> - {Lang::T('Customer Phone')}
|
<a href="{Text::url('dashboard')}" class="btn btn-default">{Lang::T('Cancel')}</a>
|
||||||
<br>
|
</div>
|
||||||
<b>[[company_name]]</b> - {Lang::T('Your Company Name')}
|
</div>
|
||||||
</p>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<div class="col-lg-offset-2 col-lg-10">
|
</div>
|
||||||
{if $page >= 0}
|
|
||||||
<button class="btn btn-success" id="submit" type="submit" name=send value=now>
|
|
||||||
{Lang::T('Send Message')}</button>
|
|
||||||
{else}
|
|
||||||
<button class="btn btn-success"
|
|
||||||
onclick="return ask(this, 'Continue the process of sending mass messages?')"
|
|
||||||
type="submit" name=send value=now>
|
|
||||||
{Lang::T('Send Message')}</button>
|
|
||||||
{/if}
|
|
||||||
<a href="{Text::url('dashboard')}" class="btn btn-default">{Lang::T('Cancel')}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{if $batchStatus}
|
<!-- Add a Table for Sent History -->
|
||||||
<p><span class="label label-success">{Lang::T('Total SMS Sent')}: {$totalSMSSent}</span> <span
|
<div class="panel panel-default">
|
||||||
class="label label-danger">{Lang::T('Total SMS
|
<div class="panel-heading">{Lang::T('Message Sending History')}</div>
|
||||||
Failed')}: {$totalSMSFailed}</span> <span class="label label-success">{Lang::T('Total WhatsApp Sent')}:
|
<div class="panel-body">
|
||||||
{$totalWhatsappSent}</span> <span class="label label-danger">{Lang::T('Total WhatsApp Failed')}:
|
<div id="status"></div>
|
||||||
{$totalWhatsappFailed}</span></p>
|
<table class="table table-bordered" id="historyTable">
|
||||||
{/if}
|
<thead>
|
||||||
<div class="box">
|
<tr>
|
||||||
<div class="box-header">
|
<th>{Lang::T('Customer')}</th>
|
||||||
<h3 class="box-title">{Lang::T('Message Results')}</h3>
|
<th>{Lang::T('Phone')}</th>
|
||||||
</div>
|
<th>{Lang::T('Status')}</th>
|
||||||
<!-- /.box-header -->
|
<th>{Lang::T('Message')}</th>
|
||||||
<div class="box-body">
|
</tr>
|
||||||
<table id="messageResultsTable" class="table table-bordered table-striped table-condensed">
|
</thead>
|
||||||
<thead>
|
<tbody></tbody>
|
||||||
<tr>
|
</table>
|
||||||
<th>{Lang::T('Name')}</th>
|
</div>
|
||||||
<th>{Lang::T('Phone')}</th>
|
|
||||||
<th>{Lang::T('Message')}</th>
|
|
||||||
<th>{Lang::T('Status')}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{foreach $batchStatus as $customer}
|
|
||||||
<tr>
|
|
||||||
<td>{$customer.name}</td>
|
|
||||||
<td>{$customer.phone}</td>
|
|
||||||
<td>{$customer.message}</td>
|
|
||||||
<td>{$customer.status}</td>
|
|
||||||
</tr>
|
|
||||||
{/foreach}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- /.box-body -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- /.box -->
|
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
||||||
|
{literal}
|
||||||
<script>
|
<script>
|
||||||
var $j = jQuery.noConflict();
|
let page = 0;
|
||||||
|
let totalSent = 0;
|
||||||
|
let totalFailed = 0;
|
||||||
|
let hasMore = true;
|
||||||
|
|
||||||
$j(document).ready(function() {
|
// Initialize DataTable
|
||||||
$j('#messageResultsTable').DataTable();
|
let historyTable = $('#historyTable').DataTable({
|
||||||
});
|
paging: true,
|
||||||
|
searching: true,
|
||||||
|
ordering: true,
|
||||||
|
info: true,
|
||||||
|
autoWidth: false,
|
||||||
|
responsive: true
|
||||||
|
});
|
||||||
|
|
||||||
{if $page>0 && $totalCustomers >0}
|
function sendBatch() {
|
||||||
setTimeout(() => {
|
if (!hasMore) return;
|
||||||
document.getElementById('submit').click();
|
|
||||||
}, {$delay}000);
|
$.ajax({
|
||||||
{/if}
|
url: '?_route=message/send_bulk_ajax',
|
||||||
{if $page>0 && $totalCustomers==0}
|
method: 'POST',
|
||||||
Swal.fire({
|
data: {
|
||||||
icon: 'success',
|
group: $('#group').val(),
|
||||||
title: 'Bulk Send Done',
|
message: $('#message').val(),
|
||||||
position: 'top-end',
|
via: $('#via').val(),
|
||||||
showConfirmButton: false,
|
batch: $('#batch').val(),
|
||||||
timer: 5000,
|
router: $('#router').val() || '',
|
||||||
timerProgressBar: true,
|
page: page,
|
||||||
didOpen: (toast) => {
|
test: $('#test').is(':checked') ? 'on' : 'off'
|
||||||
toast.addEventListener('mouseenter', Swal.stopTimer)
|
},
|
||||||
toast.addEventListener('mouseleave', Swal.resumeTimer)
|
dataType: 'json',
|
||||||
}
|
beforeSend: function () {
|
||||||
});
|
$('#status').html(`
|
||||||
{/if}
|
<div class="alert alert-info">
|
||||||
|
<i class="fas fa-spinner fa-spin"></i> Sending batch ${page + 1}...
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
},
|
||||||
|
success: function (response) {
|
||||||
|
console.log("Response received:", response);
|
||||||
|
|
||||||
|
if (response && response.status === 'success') {
|
||||||
|
totalSent += response.totalSent || 0;
|
||||||
|
totalFailed += response.totalFailed || 0;
|
||||||
|
page = response.page || 0;
|
||||||
|
hasMore = response.hasMore || false;
|
||||||
|
|
||||||
|
$('#status').html(`
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<i class="fas fa-check-circle"></i> Batch ${page} sent! (Total Sent: ${totalSent}, Failed: ${totalFailed})
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
(response.batchStatus || []).forEach(msg => {
|
||||||
|
let statusClass = msg.status.includes('Failed') ? 'danger' : 'success';
|
||||||
|
historyTable.row.add([
|
||||||
|
msg.name,
|
||||||
|
msg.phone,
|
||||||
|
`<span class="text-${statusClass}">${msg.status}</span>`,
|
||||||
|
msg.message || 'No message'
|
||||||
|
]).draw(false); // Add row without redrawing the table
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasMore) {
|
||||||
|
sendBatch();
|
||||||
|
} else {
|
||||||
|
$('#status').html(`
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<i class="fas fa-check-circle"></i> All batches sent! Total Sent: ${totalSent}, Failed: ${totalFailed}
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("Unexpected response format:", response);
|
||||||
|
$('#status').html(`
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<i class="fas fa-exclamation-circle"></i> Error: Unexpected response format.
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
$('#status').html(`
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<i class="fas fa-exclamation-circle"></i> Error: Failed to send batch ${page + 1}.
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start sending on button click
|
||||||
|
$('#startBulk').on('click', function () {
|
||||||
|
page = 0;
|
||||||
|
totalSent = 0;
|
||||||
|
totalFailed = 0;
|
||||||
|
hasMore = true;
|
||||||
|
$('#status').html('<div class="alert alert-info"><i class="fas fa-spinner fa-spin"></i> Starting bulk message sending...</div>');
|
||||||
|
historyTable.clear().draw(); // Clear history table before starting
|
||||||
|
sendBatch();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
{/literal}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{include file="sections/footer.tpl"}
|
{include file="sections/footer.tpl"}
|
Loading…
x
Reference in New Issue
Block a user