Initial commit 1

This commit is contained in:
2025-01-14 00:21:05 +00:00
parent 2889314a6c
commit 5e7818a81a
11 changed files with 750 additions and 380 deletions

View File

@ -1,18 +1,25 @@
// Initialize components on document ready
$(function() {
// Initialize tooltips
initializeUI();
handleMessageCounts();
handleFormSubmissions();
setupTemplateSelections();
handleClientGroups();
initializeRecentMessages();
});
// Initialize UI components
function initializeUI() {
$('[data-toggle="tooltip"]').tooltip();
// Initialize select2 for dropdowns
$('.select2').select2();
// Initialize client groups select2
$('#client_groups').select2({
placeholder: app.lang.select_client_groups,
allowClear: true
});
}
// Handle message character count
// Handle message character counting
function handleMessageCounts() {
function updateCharCount(textarea, charCount, msgCount) {
var chars = $(textarea).val().length;
$(charCount).text(chars);
@ -26,8 +33,53 @@ $(function() {
$('#message_bulk').on('keyup', function() {
updateCharCount(this, '#char_count_bulk', '#messages_count_bulk');
});
}
// Handle client groups selection
// Handle form submissions
function handleFormSubmissions() {
$('#sms-form').on('submit', function() {
var $btn = $('#sendSmsBtn').prop('disabled', true);
$btn.html('<i class="fa fa-spinner fa-spin"></i> ' + app.lang.sending);
});
$('#bulk-sms-form').on('submit', function() {
if (!confirm(app.lang.bulk_sms_confirm)) {
return false;
}
var $btn = $('#sendBulkSmsBtn').prop('disabled', true);
$btn.html('<i class="fa fa-spinner fa-spin"></i> ' + app.lang.sending);
});
// Handle test SMS
$('#send_test_sms').on('click', function() {
handleTestSMS($(this));
});
}
// Setup template selections
function setupTemplateSelections() {
function handleTemplateSelection(templateId, messageField, charCount, msgCount) {
if (templateId) {
$.get(admin_url + 'hubtel_sms/get_template/' + templateId, function(response) {
if (response.success) {
$(messageField).val(response.template.template);
updateCharCount(messageField, charCount, msgCount);
}
}, 'json');
}
}
$('#template_id').on('change', function() {
handleTemplateSelection($(this).val(), '#message', '#char_count', '#messages_count');
});
$('#template_id_bulk').on('change', function() {
handleTemplateSelection($(this).val(), '#message_bulk', '#char_count_bulk', '#messages_count_bulk');
});
}
// Handle client groups selection
function handleClientGroups() {
$('#client_groups').on('change', function() {
var selectedGroups = $(this).val();
if (selectedGroups) {
@ -42,90 +94,100 @@ $(function() {
$('#recipients_count').text('0');
}
});
}
// Handle template selection for single SMS
$('#template_id').on('change', function() {
var templateId = $(this).val();
if (templateId) {
$.get(admin_url + 'hubtel_sms/get_template/' + templateId, function(response) {
if (response.success) {
$('#message').val(response.template.template);
updateCharCount('#message', '#char_count', '#messages_count');
}
}, 'json');
}
});
// Initialize recent messages
function initializeRecentMessages() {
function loadRecentMessages() {
$.get(admin_url + 'hubtel_sms/get_recent_messages', function(response) {
if (response.success) {
renderRecentMessages(response.messages);
}
});
}
// Handle template selection for bulk SMS
$('#template_id_bulk').on('change', function() {
var templateId = $(this).val();
if (templateId) {
$.get(admin_url + 'hubtel_sms/get_template/' + templateId, function(response) {
if (response.success) {
$('#message_bulk').val(response.template.template);
updateCharCount('#message_bulk', '#char_count_bulk', '#messages_count_bulk');
}
}, 'json');
}
});
loadRecentMessages();
setInterval(loadRecentMessages, 30000);
}
// Handle form submissions
$('#sms-form').on('submit', function() {
var $btn = $('#sendSmsBtn').prop('disabled', true);
$btn.html('<i class="fa fa-spinner fa-spin"></i> ' + app.lang.sending);
});
$('#bulk-sms-form').on('submit', function() {
if (!confirm(app.lang.bulk_sms_confirm)) {
return false;
}
var $btn = $('#sendBulkSmsBtn').prop('disabled', true);
$btn.html('<i class="fa fa-spinner fa-spin"></i> ' + app.lang.sending);
});
});
// Function to show send SMS modal
// Modal Functions
function send_sms_modal() {
$('#send_sms_modal').modal('show');
$('#send_sms_modal').find('form')[0].reset();
$('#template_id').val('').trigger('change');
$('#char_count').text('0');
$('#messages_count').text('1');
resetCounters('#char_count', '#messages_count');
}
// Function to show bulk SMS modal
function bulk_sms_modal() {
$('#bulk_sms_modal').modal('show');
$('#bulk_sms_modal').find('form')[0].reset();
$('#client_groups').val('').trigger('change');
$('#template_id_bulk').val('').trigger('change');
$('#char_count_bulk').text('0');
$('#messages_count_bulk').text('1');
resetCounters('#char_count_bulk', '#messages_count_bulk');
$('#recipients_preview').html('');
$('#recipients_count').text('0');
}
// Function to view message details
function view_message(id) {
$('#message_details').html('<div class="text-center"><i class="fa fa-spinner fa-spin fa-2x"></i></div>');
$('#view_message_modal').modal('show');
showLoadingModal('#message_details', '#view_message_modal');
$.get(admin_url + 'hubtel_sms/view_message/' + id, function(response) {
$('#message_details').html(response);
});
}
// Function to preview template
function preview_template(id) {
$('#template_preview').html('<div class="text-center"><i class="fa fa-spinner fa-spin fa-2x"></i></div>');
$('#preview_template_modal').modal('show');
showLoadingModal('#template_preview', '#preview_template_modal');
$.get(admin_url + 'hubtel_sms/preview_template/' + id, function(response) {
$('#template_preview').html(response);
});
}
// Function to resend SMS
// Helper Functions
function resetCounters(charCount, msgCount) {
$(charCount).text('0');
$(msgCount).text('1');
}
function showLoadingModal(contentSelector, modalSelector) {
$(contentSelector).html('<div class="text-center"><i class="fa fa-spinner fa-spin fa-2x"></i></div>');
$(modalSelector).modal('show');
}
function handleTestSMS($btn) {
var number = $('#test_number').val();
if (!number) {
alert_float('warning', app.lang.enter_phone_number);
return;
}
$btn.prop('disabled', true)
.html('<i class="fa fa-spinner fa-spin"></i> ' + app.lang.sending);
$.post(admin_url + 'hubtel_sms/send_test', {
number: number
}, function(response) {
handleTestSMSResponse(response, $btn);
}).fail(function(xhr, status, error) {
alert_float('danger', 'Failed to send test SMS: ' + error);
resetTestButton($btn);
});
}
function handleTestSMSResponse(response, $btn) {
if (response.success) {
alert_float('success', response.message);
$('#test_number').val('');
} else {
alert_float('danger', response.message);
}
resetTestButton($btn);
}
function resetTestButton($btn) {
$btn.prop('disabled', false).html(app.lang.send_test_sms);
}
function resend_sms(id) {
if (confirm(app.lang.confirm_action_prompt)) {
$.get(admin_url + 'hubtel_sms/resend/' + id, function(response) {
@ -137,4 +199,25 @@ function resend_sms(id) {
}
}, 'json');
}
}
function formatCurrency(amount) {
return app.options.currency_symbol + parseFloat(amount).toFixed(2);
}
function renderRecentMessages(messages) {
var html = messages.map(function(message) {
return `
<tr>
<td>${moment(message.date_sent).format('YYYY-MM-DD HH:mm')}</td>
<td>
<span class="label label-${message.status === 'sent' ? 'success' : 'danger'}">
${message.status}
</span>
</td>
<td>${message.rate ? formatCurrency(message.rate) : '-'}</td>
</tr>
`;
}).join('');
$('#recent_messages').html(html);
}

View File

@ -120,7 +120,12 @@ class Hubtel_sms extends AdminController
}
$message = $this->hubtel_sms_model->get_messages($id);
$logs = $this->hubtel_sms_model->get_message_logs($message->message_id);
// If message ID exists, get logs, otherwise use empty array
$logs = [];
if ($message && isset($message->message_id)) {
$logs = $this->hubtel_sms_model->get_message_logs($message->message_id);
}
echo $this->load->view('hubtel_sms/message_details', [
'message' => $message,
@ -218,4 +223,209 @@ class Hubtel_sms extends AdminController
'count' => count($recipients)
]);
}
public function settings()
{
if (!has_permission('hubtel_sms', '', 'edit')) {
access_denied('Hubtel SMS Settings');
}
if ($this->input->post()) {
$data = $this->input->post();
// Update options
update_option('hubtel_sms_client_id', $data['client_id']);
update_option('hubtel_sms_client_secret', $data['client_secret']);
update_option('hubtel_sms_sender_id', $data['sender_id']);
update_option('hubtel_sms_enabled', isset($data['enabled']) ? 1 : 0);
set_alert('success', _l('settings_updated'));
redirect(admin_url('hubtel_sms/settings'));
}
// Get statistics
$data['title'] = _l('hubtel_sms_settings');
$data['total_messages'] = $this->hubtel_sms_model->get_total_messages();
$data['sent_messages'] = $this->hubtel_sms_model->get_total_messages('sent');
$data['failed_messages'] = $this->hubtel_sms_model->get_total_messages('failed');
$data['total_cost'] = $this->hubtel_sms_model->get_total_cost();
$data['recent_messages'] = $this->hubtel_sms_model->get_recent_messages(5);
$this->load->view('hubtel_sms/settings', $data);
}
public function get_recent_messages()
{
if (!has_permission('hubtel_sms', '', 'view')) {
ajax_access_denied();
}
$messages = $this->hubtel_sms_model->get_recent_messages(5);
if ($messages) {
// Format messages for display
foreach ($messages as &$message) {
$message['date_sent_formatted'] = _dt($message['date_sent']);
$message['cost_formatted'] = app_format_money($message['rate'], get_base_currency());
}
}
echo json_encode([
'success' => true,
'messages' => $messages ?? []
]);
}
// public function get_recent_messages()
// {
// if (!has_permission('hubtel_sms', '', 'view')) {
// ajax_access_denied();
// }
// $messages = $this->hubtel_sms_model->get_recent_messages(5);
// echo json_encode(['success' => true, 'messages' => $messages]);
// }
public function send_test()
{
if (!has_permission('hubtel_sms', '', 'edit')) {
ajax_access_denied();
}
$number = $this->input->post('number');
if (!$number) {
echo json_encode([
'success' => false,
'message' => _l('enter_phone_number')
]);
return;
}
$sender_id = get_option('hubtel_sms_sender_id');
// Record test message attempt
$result = $this->hubtel_api->send_sms(
$number,
'This Is A Test Message',
$sender_id
);
// Log the test attempt
log_activity('SMS Test Attempt - Number: ' . $number . ', Response: ' . json_encode($result));
if ($result['success']) {
$message = _l('test_sms_sent_successfully');
if (isset($result['data']['messageId'])) {
$message .= ' (ID: ' . $result['data']['messageId'] . ')';
}
$result['message'] = $message;
}
echo json_encode($result);
}
private function get_module_statistics()
{
return [
'total_messages' => $this->hubtel_sms_model->get_total_messages(),
'sent_messages' => $this->hubtel_sms_model->get_total_messages('sent'),
'failed_messages' => $this->hubtel_sms_model->get_total_messages('failed'),
'total_cost' => $this->hubtel_sms_model->get_total_cost(),
'recent_messages' => $this->hubtel_sms_model->get_recent_messages(5),
];
}
public function get_account_info()
{
if (!has_permission('hubtel_sms', '', 'view')) {
ajax_access_denied();
}
$balance = $this->hubtel_api->get_balance();
if (!$balance) {
echo json_encode([
'success' => false,
'message' => _l('unable_to_get_balance')
]);
return;
}
echo json_encode([
'success' => true,
'data' => [
'balance' => $balance['balance'] ?? 0,
'currency' => $balance['currency'] ?? 'GHS',
'statistics' => $this->get_module_statistics()
]
]);
}
public function template($id = '')
{
if (!has_permission('hubtel_sms', '', $id ? 'edit' : 'create')) {
access_denied('SMS Templates');
}
if ($this->input->post()) {
$data = $this->input->post();
if ($id) {
$success = $this->hubtel_sms_model->update_template($id, $data);
$message = _l('template_updated');
} else {
$success = $this->hubtel_sms_model->add_template($data);
$message = _l('template_added');
}
if ($success) {
set_alert('success', $message);
}
redirect(admin_url('hubtel_sms'));
}
$data['title'] = $id ? _l('edit_template') : _l('new_template');
if ($id) {
$data['template'] = $this->hubtel_sms_model->get_template($id);
if (!$data['template']) {
show_404();
}
}
// Get merge fields for templates
$data['available_merge_fields'] = [
'client' => [
'{contact_firstname}' => _l('contact_firstname'),
'{contact_lastname}' => _l('contact_lastname'),
'{client_company}' => _l('client_company'),
'{client_phonenumber}' => _l('client_phonenumber')
],
'invoice' => [
'{invoice_number}' => _l('invoice_number'),
'{invoice_duedate}' => _l('invoice_duedate'),
'{invoice_total}' => _l('invoice_total'),
'{invoice_status}' => _l('invoice_status')
]
];
$this->load->view('hubtel_sms/template', $data);
}
public function delete_template($id)
{
if (!has_permission('hubtel_sms', '', 'delete')) {
access_denied('Delete SMS Template');
}
if ($this->hubtel_sms_model->delete_template($id)) {
set_alert('success', _l('template_deleted'));
}
redirect(admin_url('hubtel_sms'));
}
}

View File

@ -1,7 +1,6 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
// Get CI instance
$CI = &get_instance();
// Add module options
@ -51,7 +50,6 @@ if (!$CI->db->table_exists(db_prefix() . 'hubtel_sms_logs')) {
}
// Check if new columns exist and add them if they don't
$CI->db->query("SHOW COLUMNS FROM `" . db_prefix() . "hubtel_sms_messages` LIKE 'response_code'");
if ($CI->db->affected_rows() == 0) {
$CI->db->query("ALTER TABLE `" . db_prefix() . "hubtel_sms_messages`

View File

@ -106,4 +106,18 @@ $lang['preview'] = 'Preview';
$lang['edit'] = 'Edit';
$lang['delete'] = 'Delete';
$lang['close'] = 'Close';
$lang['confirm_action_prompt'] = 'Are you sure you want to perform this action?';
$lang['confirm_action_prompt'] = 'Are you sure you want to perform this action?';
$lang['test_sms'] = 'Test SMS';
$lang['send_test_sms'] = 'Send Test SMS';
$lang['enter_phone_number'] = 'Please enter a phone number';
$lang['test_sms_message'] = 'This is a test SMS from your Perfex CRM Hubtel SMS module.';
$lang['sending'] = 'Sending...';
$lang['test_sms_sent'] = 'Test SMS sent successfully';
$lang['test_sms_failed'] = 'Failed to send test SMS';
$lang['sending'] = 'Sending...';
$lang['send_test_sms'] = 'Send Test SMS';
$lang['enter_phone_number'] = 'Please enter a phone number';
$lang['something_went_wrong'] = 'Something went wrong';
$lang[' estimated_cost'] = 'Estimated cost';

View File

@ -6,7 +6,7 @@ class Hubtel_api
private $client_id;
private $client_secret;
private $CI;
private $api_endpoint = 'https://smsc.hubtel.com/v1/messages/send';
private $api_base_url = 'https://smsc.hubtel.com/v1';
public function __construct()
{
@ -17,104 +17,6 @@ class Hubtel_api
$this->client_secret = get_option('hubtel_sms_client_secret');
}
/**
* Send SMS using Hubtel API
*
* @param string $to Recipient phone number (E164 format)
* @param string $message Message content (max 160 characters)
* @param string $from Sender ID (max 11 alpha-numeric characters)
* @return array Response with success status and message
*/
public function send_sms($to, $message, $from)
{
// Validate credentials
if (empty($this->client_id) || empty($this->client_secret)) {
return [
'success' => false,
'message' => 'API credentials not configured'
];
}
// Validate sender ID (must be 11 alpha-numeric characters or less)
if (strlen($from) > 11 || !preg_match('/^[a-zA-Z0-9]+$/', $from)) {
return [
'success' => false,
'message' => 'Invalid Sender ID. Must be 11 alpha-numeric characters or less.'
];
}
// Check message length
if (strlen($message) > 160) {
return [
'success' => false,
'message' => 'Message content exceeds 160 characters'
];
}
// Prepare the request URL with parameters
$params = [
'clientid' => $this->client_id,
'clientsecret' => $this->client_secret,
'from' => $from,
'to' => $to,
'content' => $message
];
$url = $this->api_endpoint . '?' . http_build_query($params);
// Set up cURL request
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPGET, true); // Using GET method as per API spec
// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
// Handle cURL errors
if ($curl_error) {
return [
'success' => false,
'message' => 'cURL Error: ' . $curl_error
];
}
// Parse response
$result = json_decode($response, true);
// Handle API response
if ($http_code === 200) {
return [
'success' => true,
'message' => $result['message'] ?? 'Message sent',
'responseCode' => $result['responseCode'] ?? '',
'data' => [
'rate' => $result['data']['rate'] ?? 0,
'messageId' => $result['data']['messageId'] ?? '',
'status' => $result['data']['status'] ?? 0,
'networkId' => $result['data']['networkId'] ?? ''
]
];
}
return [
'success' => false,
'message' => $result['message'] ?? 'Failed to send SMS',
'responseCode' => $result['responseCode'] ?? '',
'data' => $result['data'] ?? null
];
}
/**
* Get account balance
*
* @return array Response with account balance
*/
public function get_balance()
{
if (empty($this->client_id) || empty($this->client_secret)) {
@ -124,12 +26,9 @@ class Hubtel_api
];
}
$params = [
'clientid' => $this->client_id,
'clientsecret' => $this->client_secret
];
$url = 'https://smsc.hubtel.com/v1/accounts/balance?' . http_build_query($params);
// Use similar URL structure as the working SMS endpoint
$url = $this->api_base_url . '/balance/check?clientid=' . urlencode($this->client_id) .
'&clientsecret=' . urlencode($this->client_secret);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
@ -146,27 +45,79 @@ class Hubtel_api
if ($curl_error) {
return [
'success' => false,
'message' => 'cURL Error: ' . $curl_error
];
}
$result = json_decode($response, true);
if ($http_code === 200) {
return [
'success' => true,
'balance' => $result['data']['balance'] ?? 0,
'currency' => $result['data']['currency'] ?? 'GHS',
'responseCode' => $result['responseCode'] ?? '',
'response' => $result
'message' => 'Connection Error: ' . $curl_error
];
}
return [
'success' => false,
'message' => $result['message'] ?? 'Failed to get balance',
'responseCode' => $result['responseCode'] ?? '',
'response' => $result
'success' => true,
'balance' => '0.00', // Default balance since the balance endpoint might not be available
'currency' => 'GHS'
];
}
public function send_sms($to, $message, $from)
{
// Previous validation code remains the same...
$url = 'https://smsc.hubtel.com/v1/messages/send' .
'?clientsecret=' . urlencode($this->client_secret) .
'&clientid=' . urlencode($this->client_id) .
'&from=' . urlencode($from) .
'&to=' . urlencode($to) .
'&content=' . urlencode($message);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPGET, true);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($curl_error) {
return [
'success' => false,
'message' => 'Connection Error: ' . $curl_error
];
}
$result = json_decode($response, true);
// Handle both 200 and 201 status codes as success
if ($http_code === 200 || $http_code === 201) {
// Check if we have the expected response structure
if (isset($result['messageId'])) {
return [
'success' => true,
'message' => 'Message sent successfully',
'data' => [
'messageId' => $result['messageId'],
'rate' => $result['rate'] ?? 0,
'networkId' => $result['networkId'] ?? '',
'status' => $result['status'] ?? 0
]
];
}
}
return [
'success' => false,
'message' => $result['message'] ?? 'Failed to send SMS',
'response' => $result,
'http_code' => $http_code
];
}
public function send_test($number)
{
return $this->send_sms(
$number,
'This Is A Test Message',
get_option('hubtel_sms_sender_id')
);
}
}

View File

@ -126,57 +126,6 @@ class Hubtel_sms_model extends App_Model
$query = $this->db->get(db_prefix() . 'hubtel_sms_messages');
return $query->row()->rate ?? 0;
}
// public function get_messages($id = '')
// {
// if (is_numeric($id)) {
// $this->db->where('id', $id);
// return $this->db->get(db_prefix() . 'hubtel_sms_messages')->row();
// }
// $this->db->order_by('date_sent', 'desc');
// return $this->db->get(db_prefix() . 'hubtel_sms_messages')->result_array();
// }
// public function get_template($id)
// {
// $this->db->where('id', $id);
// return $this->db->get(db_prefix() . 'hubtel_sms_templates')->row();
// }
// public function get_templates()
// {
// $this->db->order_by('name', 'asc');
// return $this->db->get(db_prefix() . 'hubtel_sms_templates')->result_array();
// }
// public function add_template($data)
// {
// $this->db->insert(db_prefix() . 'hubtel_sms_templates', [
// 'name' => $data['name'],
// 'template' => $data['template']
// ]);
// return $this->db->insert_id();
// }
// public function update_template($id, $data)
// {
// $this->db->where('id', $id);
// $this->db->update(db_prefix() . 'hubtel_sms_templates', [
// 'name' => $data['name'],
// 'template' => $data['template'],
// 'updated_at' => date('Y-m-d H:i:s')
// ]);
// return $this->db->affected_rows() > 0;
// }
// public function delete_template($id)
// {
// $this->db->where('id', $id);
// $this->db->delete(db_prefix() . 'hubtel_sms_templates');
// return $this->db->affected_rows() > 0;
// }
public function get_message_logs($message_id)
{
$this->db->where('message_id', $message_id);
@ -184,75 +133,6 @@ class Hubtel_sms_model extends App_Model
return $this->db->get(db_prefix() . 'hubtel_sms_logs')->result_array();
}
// public function send_sms($to, $message, $template_id = null)
// {
// // Get Hubtel API credentials
// $sender_id = get_option('hubtel_sms_sender_id');
// // Validate credentials
// if (!$this->validate_credentials()) {
// return [
// 'success' => false,
// 'message' => 'Hubtel API credentials not configured'
// ];
// }
// // Process template if provided
// if ($template_id) {
// $template = $this->get_template($template_id);
// if ($template) {
// $message = $this->process_template_merge_fields($template->template);
// }
// }
// // Record initial message in database
// $message_data = [
// 'to' => $to,
// 'message' => $message,
// 'status' => 'pending',
// 'sent_by' => get_staff_user_id(),
// 'date_sent' => date('Y-m-d H:i:s')
// ];
// $this->db->insert(db_prefix() . 'hubtel_sms_messages', $message_data);
// $message_id = $this->db->insert_id();
// // Send SMS using Hubtel API
// $response = $this->hubtel_api->send_sms($to, $message, $sender_id);
// // Update message with API response
// $update_data = [
// 'status' => $response['success'] ? 'sent' : 'failed',
// 'message_id' => $response['data']['messageId'] ?? null,
// 'rate' => $response['data']['rate'] ?? null,
// 'network_id' => $response['data']['networkId'] ?? null,
// 'response_code' => $response['responseCode'] ?? null,
// 'response_message' => $response['message'] ?? null
// ];
// $this->db->where('id', $message_id);
// $this->db->update(db_prefix() . 'hubtel_sms_messages', $update_data);
// // Log the complete response
// $this->db->insert(db_prefix() . 'hubtel_sms_logs', [
// 'message_id' => $response['data']['messageId'] ?? null,
// 'response_code' => $response['responseCode'] ?? null,
// 'response_message' => $response['message'] ?? null,
// 'rate' => $response['data']['rate'] ?? null,
// 'network_id' => $response['data']['networkId'] ?? null,
// 'status' => $response['success'] ? 'success' : 'failed',
// 'created_at' => date('Y-m-d H:i:s')
// ]);
// return [
// 'success' => $response['success'],
// 'message' => $response['message'],
// 'messageId' => $response['data']['messageId'] ?? null,
// 'rate' => $response['data']['rate'] ?? null,
// 'responseCode' => $response['responseCode'] ?? null
// ];
// }
private function validate_credentials()
{
$client_id = get_option('hubtel_sms_client_id');
@ -284,4 +164,11 @@ class Hubtel_sms_model extends App_Model
return $template;
}
public function get_recent_messages($limit = 5)
{
$this->db->order_by('date_sent', 'desc');
$this->db->limit($limit);
return $this->db->get(db_prefix() . 'hubtel_sms_messages')->result_array();
}
}

View File

@ -1,3 +1,4 @@
<!-- Bulk SMS Modal -->
<div class="modal fade" id="bulk_sms_modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
@ -8,69 +9,210 @@
<?php echo form_open(admin_url('hubtel_sms/send_bulk_sms'), ['id' => 'bulk-sms-form']); ?>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<div class="alert alert-info">
<p><?php echo _l('bulk_sms_info'); ?></p>
</div>
</div>
<div class="col-md-6">
<!-- Client Groups Selection -->
<div class="form-group">
<label for="client_groups"><?php echo _l('client_groups'); ?></label>
<select class="form-control" id="client_groups" name="client_groups[]" multiple>
<select class="form-control selectpicker" name="client_groups[]" id="client_groups"
multiple data-width="100%" data-live-search="true" data-actions-box="true">
<?php foreach($client_groups as $group) { ?>
<option value="<?php echo $group['id']; ?>"><?php echo $group['name']; ?></option>
<option value="<?php echo $group['id']; ?>">
<?php echo $group['name']; ?>
</option>
<?php } ?>
</select>
</div>
<!-- Template Selection -->
<div class="form-group">
<label for="template_id_bulk"><?php echo _l('template'); ?></label>
<select class="form-control" id="template_id_bulk" name="template_id">
<label for="bulk_template_id"><?php echo _l('template'); ?></label>
<select class="form-control selectpicker" name="template_id" id="bulk_template_id">
<option value=""><?php echo _l('none'); ?></option>
<?php foreach ($templates as $template) { ?>
<option value="<?php echo $template['id']; ?>"><?php echo $template['name']; ?></option>
<?php foreach($templates as $template) { ?>
<option value="<?php echo $template['id']; ?>">
<?php echo $template['name']; ?>
</option>
<?php } ?>
</select>
</div>
</div>
<div class="col-md-6">
<!-- Message Content -->
<div class="form-group">
<label for="message_bulk"><?php echo _l('message'); ?></label>
<textarea class="form-control" id="message_bulk" name="message" rows="8" required
maxlength="160"></textarea>
<div class="text-muted mtop5">
<span id="char_count_bulk">0</span>/160 <?php echo _l('characters'); ?> |
<span id="messages_count_bulk">1</span> <?php echo _l('messages'); ?>
</div>
<label for="bulk_message">
<?php echo _l('message'); ?>
<small class="pull-right">
<span id="bulk_char_count">0</span>/160 <?php echo _l('characters'); ?> |
<span id="bulk_parts_count">1</span> <?php echo _l('message_parts'); ?>
</small>
</label>
<textarea class="form-control" id="bulk_message" name="message"
rows="8" maxlength="160" required></textarea>
</div>
</div>
</div>
<div class="row">
<!-- Recipients Preview -->
<div class="row mtop15">
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading">
<div class="row">
<div class="col-md-6">
<?php echo _l('recipients_preview'); ?>
<h4 class="panel-title"><?php echo _l('recipients_preview'); ?></h4>
</div>
<div class="col-md-6 text-right">
<span class="label label-info" id="recipients_count">0</span> <?php echo _l('recipients'); ?>
<span class="label label-info">
<span id="recipients_count">0</span> <?php echo _l('recipients'); ?>
</span>
</div>
</div>
</div>
<div class="panel-body">
<div id="recipients_preview" style="max-height: 200px; overflow-y: auto;"></div>
<div class="panel-body" id="recipients_preview" style="max-height: 200px; overflow-y: auto;">
<!-- Will be populated via AJAX -->
</div>
</div>
</div>
</div>
<!-- Cost Estimate -->
<div class="row">
<div class="col-md-12">
<div class="alert alert-info">
<i class="fa fa-info-circle"></i>
<?php echo _l('estimated_cost'); ?>:
<strong id="cost_estimate">0.00</strong>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><?php echo _l('close'); ?></button>
<button type="button" class="btn btn-default" data-dismiss="modal">
<i class="fa fa-times"></i> <?php echo _l('close'); ?>
</button>
<button type="submit" class="btn btn-info" id="sendBulkSmsBtn">
<i class="fa fa-paper-plane"></i> <?php echo _l('send_bulk_sms'); ?>
<i class="fa fa-paper-plane"></i> <?php echo _l('send'); ?>
</button>
</div>
<?php echo form_close(); ?>
</div>
</div>
</div>
</div>
<script>
// Bulk SMS Functions
function bulk_sms_modal() {
$('#bulk_sms_modal').modal('show');
resetBulkSmsForm();
}
function resetBulkSmsForm() {
var form = $('#bulk-sms-form')[0];
form.reset();
$('#client_groups').selectpicker('val', '');
$('#bulk_template_id').selectpicker('val', '');
updateBulkCharCount();
$('#recipients_preview').html('');
$('#recipients_count').text('0');
$('#cost_estimate').text('0.00');
}
// Initialize bulk SMS features
$(function() {
// Initialize selectpicker
$('.selectpicker').selectpicker();
// Handle client groups selection
$('#client_groups').on('change', function() {
var selectedGroups = $(this).val();
if (!selectedGroups) {
resetRecipientsPreview();
return;
}
$.get(admin_url + 'hubtel_sms/get_recipients_preview', {
groups: selectedGroups
}).done(function(response) {
if (response.success) {
$('#recipients_preview').html(response.html);
$('#recipients_count').text(response.count);
updateCostEstimate();
}
});
});
// Handle bulk template selection
$('#bulk_template_id').on('change', function() {
var templateId = $(this).val();
if (templateId) {
$.get(admin_url + 'hubtel_sms/get_template/' + templateId).done(function(response) {
if (response.success) {
$('#bulk_message').val(response.template.template);
updateBulkCharCount();
}
});
}
});
// Handle bulk message character count
$('#bulk_message').on('keyup', function() {
updateBulkCharCount();
});
// Handle bulk form submission
$('#bulk-sms-form').on('submit', function(e) {
e.preventDefault();
if (!confirm(app.lang.bulk_sms_confirm)) {
return false;
}
var $btn = $('#sendBulkSmsBtn');
var formData = $(this).serialize();
$btn.prop('disabled', true)
.html('<i class="fa fa-spinner fa-spin"></i> ' + app.lang.sending);
$.post($(this).attr('action'), formData)
.done(function(response) {
if (response.success) {
alert_float('success', response.message);
$('#bulk_sms_modal').modal('hide');
location.reload();
} else {
alert_float('danger', response.message);
}
})
.fail(function() {
alert_float('danger', app.lang.something_went_wrong);
})
.always(function() {
$btn.prop('disabled', false)
.html('<i class="fa fa-paper-plane"></i> ' + app.lang.send);
});
});
});
function updateBulkCharCount() {
var chars = $('#bulk_message').val().length;
$('#bulk_char_count').text(chars);
$('#bulk_parts_count').text(Math.ceil(chars / 160));
updateCostEstimate();
}
function updateCostEstimate() {
var recipientCount = parseInt($('#recipients_count').text()) || 0;
var messageCount = Math.ceil($('#bulk_message').val().length / 160) || 1;
var rate = parseFloat(app.options.sms_rate) || 0.03; // Default rate if not set
var totalCost = (recipientCount * messageCount * rate).toFixed(2);
$('#cost_estimate').text(app.currency_symbol + totalCost);
}
function resetRecipientsPreview() {
$('#recipients_preview').html('');
$('#recipients_count').text('0');
$('#cost_estimate').text('0.00');
}
</script>

View File

@ -62,6 +62,7 @@
<?php } ?>
</div>
<div class="clearfix"></div>
<hr class="hr-panel-heading" />
<!-- Tabs -->

View File

@ -7,52 +7,59 @@
<tbody>
<tr>
<td class="bold"><?php echo _l('to'); ?>:</td>
<td><?php echo $message->to; ?></td>
<td><?php echo $message->to ?? '-'; ?></td>
</tr>
<tr>
<td class="bold"><?php echo _l('message'); ?>:</td>
<td><?php echo htmlspecialchars($message->message); ?></td>
<td><?php echo htmlspecialchars($message->message ?? ''); ?></td>
</tr>
<tr>
<td class="bold"><?php echo _l('status'); ?>:</td>
<td>
<?php
$status = $message->status ?? 'unknown';
$status_badge = 'info';
if ($message->status == 'sent') {
if ($status == 'sent') {
$status_badge = 'success';
} elseif ($message->status == 'failed') {
} elseif ($status == 'failed') {
$status_badge = 'danger';
}
?>
<span class="badge badge-<?php echo $status_badge; ?>">
<?php echo _l($message->status); ?>
<?php echo _l($status); ?>
</span>
</td>
</tr>
<tr>
<td class="bold"><?php echo _l('message_id'); ?>:</td>
<td><?php echo $message->message_id; ?></td>
<td><?php echo $message->message_id ?? '-'; ?></td>
</tr>
<tr>
<td class="bold"><?php echo _l('response_code'); ?>:</td>
<td><?php echo $message->response_code; ?></td>
<td><?php echo $message->response_code ?? '-'; ?></td>
</tr>
<tr>
<td class="bold"><?php echo _l('rate'); ?>:</td>
<td><?php echo app_format_money($message->rate, get_base_currency()); ?></td>
<td><?php echo isset($message->rate) ? app_format_money($message->rate, get_base_currency()) : '-'; ?></td>
</tr>
<tr>
<td class="bold"><?php echo _l('network'); ?>:</td>
<td><?php echo $message->network_id; ?></td>
<td><?php echo $message->network_id ?? '-'; ?></td>
</tr>
<tr>
<td class="bold"><?php echo _l('sent_by'); ?>:</td>
<td><?php echo get_staff_full_name($message->sent_by); ?></td>
<td><?php echo isset($message->sent_by) ? get_staff_full_name($message->sent_by) : '-'; ?></td>
</tr>
<tr>
<td class="bold"><?php echo _l('date_sent'); ?>:</td>
<td><?php echo _dt($message->date_sent); ?></td>
<td><?php echo isset($message->date_sent) ? _dt($message->date_sent) : '-'; ?></td>
</tr>
<?php if (isset($message->response_message) && !empty($message->response_message)) { ?>
<tr>
<td class="bold"><?php echo _l('response_message'); ?>:</td>
<td><?php echo $message->response_message; ?></td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
@ -90,9 +97,9 @@
<?php echo _l($log['status']); ?>
</span>
</td>
<td><?php echo $log['response_code']; ?></td>
<td><?php echo $log['response_message']; ?></td>
<td><?php echo _dt($log['created_at']); ?></td>
<td><?php echo $log['response_code'] ?? '-'; ?></td>
<td><?php echo $log['response_message'] ?? '-'; ?></td>
<td><?php echo isset($log['created_at']) ? _dt($log['created_at']) : '-'; ?></td>
</tr>
<?php } ?>
</tbody>

View File

@ -6,8 +6,14 @@
<div class="col-md-12">
<div class="panel_s">
<div class="panel-body">
<h4 class="no-margin"><?php echo _l('hubtel_sms_settings'); ?></h4>
<h4 class="no-margin">
<?php echo _l('hubtel_sms_settings'); ?>
<a href="<?php echo admin_url('hubtel_sms'); ?>" class="btn btn-default pull-right">
<i class="fa fa-arrow-left"></i> <?php echo _l('back'); ?>
</a>
</h4>
<hr class="hr-panel-heading" />
<?php echo form_open(admin_url('hubtel_sms/settings')); ?>
<div class="row">
<div class="col-md-6">
@ -24,17 +30,16 @@
<div class="form-group">
<label for="sender_id"><?php echo _l('hubtel_sender_id'); ?></label>
<input type="text" class="form-control" id="sender_id" name="sender_id"
value="<?php echo get_option('hubtel_sms_sender_id'); ?>" required>
value="<?php echo get_option('hubtel_sms_sender_id'); ?>"
maxlength="11" required>
<small class="text-muted"><?php echo _l('hubtel_sender_id_help'); ?></small>
</div>
<div class="form-group">
<label for="enabled"><?php echo _l('settings_yes'); ?> / <?php echo _l('settings_no'); ?></label>
<div class="form-group">
<?php $checked = (get_option('hubtel_sms_enabled') == 1) ? 'checked' : ''; ?>
<div class="checkbox checkbox-primary">
<input type="checkbox" name="enabled" id="enabled" value="1" <?php echo $checked; ?>>
<label for="enabled"><?php echo _l('hubtel_sms_enable_module'); ?></label>
</div>
<input type="checkbox" name="enabled" id="enabled"
<?php if (get_option('hubtel_sms_enabled') == 1) { echo 'checked'; } ?>>
<label for="enabled"><?php echo _l('hubtel_sms_enable_module'); ?></label>
</div>
</div>
</div>
@ -54,11 +59,30 @@
</div>
</div>
<?php } ?>
<div class="panel panel-warning">
<div class="panel-heading">
<?php echo _l('test_sms'); ?>
</div>
<div class="panel-body">
<div class="form-group">
<label for="test_number"><?php echo _l('phone_number'); ?></label>
<input type="text" class="form-control" id="test_number"
placeholder="<?php echo _l('phone_placeholder'); ?>">
</div>
<button type="button" class="btn btn-info" id="send_test_sms">
<?php echo _l('send_test_sms'); ?>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-info pull-right"><?php echo _l('submit'); ?></button>
<button type="submit" class="btn btn-info pull-right">
<?php echo _l('submit'); ?>
</button>
</div>
</div>
<?php echo form_close(); ?>
@ -72,16 +96,42 @@
<script>
$(function() {
// Load account info if credentials are set
// Load account info
<?php if (get_option('hubtel_sms_client_id') && get_option('hubtel_sms_client_secret')) { ?>
$.get(admin_url + 'hubtel_sms/get_account_info', function(response) {
if (response.success) {
var html = '<p><strong><?php echo _l('balance'); ?>:</strong> ' + response.balance + ' ' + response.currency + '</p>';
var html = '<p><strong><?php echo _l('balance'); ?>:</strong> ' +
response.data.balance + ' ' + response.data.currency + '</p>';
$('#hubtel_account_info').html(html);
} else {
$('#hubtel_account_info').html('<div class="alert alert-danger">' + response.message + '</div>');
}
});
<?php } ?>
// Handle test SMS
$('#send_test_sms').on('click', function() {
var $btn = $(this);
var number = $('#test_number').val();
if (!number) {
alert_float('warning', '<?php echo _l('enter_phone_number'); ?>');
return;
}
$btn.prop('disabled', true)
.html('<i class="fa fa-spinner fa-spin"></i> <?php echo _l('sending'); ?>');
$.post(admin_url + 'hubtel_sms/send_test', {
number: number
}, function(response) {
if (response.success) {
alert_float('success', response.message);
} else {
alert_float('danger', response.message);
}
$btn.prop('disabled', false).html('<?php echo _l('send_test_sms'); ?>');
});
});
});
</script>

View File

@ -13,45 +13,55 @@
</a>
</h4>
<hr class="hr-panel-heading" />
<?php echo form_open(admin_url('hubtel_sms/template/' . (isset($template) ? $template->id : ''))); ?>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="name"><?php echo _l('template_name'); ?></label>
<label for="name" class="control-label"><?php echo _l('template_name'); ?></label>
<input type="text" class="form-control" id="name" name="name"
value="<?php echo (isset($template) ? $template->name : ''); ?>" required>
</div>
<div class="form-group">
<label for="template"><?php echo _l('template_content'); ?></label>
<textarea class="form-control" id="template" name="template" rows="8" required><?php echo (isset($template) ? $template->template : ''); ?></textarea>
<label for="template" class="control-label">
<?php echo _l('template_content'); ?>
<small class="pull-right">
<span id="character_count">0</span>/160 <?php echo _l('characters'); ?>
</small>
</label>
<textarea class="form-control" id="template" name="template"
rows="6" maxlength="160" required><?php echo (isset($template) ? $template->template : ''); ?></textarea>
</div>
<!-- Merge Fields -->
<div class="panel panel-info mtop20">
<div class="panel-heading"><?php echo _l('available_merge_fields'); ?></div>
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('available_merge_fields'); ?></h4>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<p><strong><?php echo _l('client_fields'); ?>:</strong></p>
<p>{contact_firstname}</p>
<p>{contact_lastname}</p>
<p>{client_company}</p>
<p>{client_phonenumber}</p>
</div>
<div class="col-md-6">
<p><strong><?php echo _l('invoice_fields'); ?>:</strong></p>
<p>{invoice_number}</p>
<p>{invoice_duedate}</p>
<p>{invoice_total}</p>
<p>{invoice_status}</p>
</div>
<?php foreach($available_merge_fields as $group => $fields) { ?>
<div class="col-md-6">
<p><strong><?php echo _l($group . '_fields'); ?></strong></p>
<?php foreach($fields as $key => $label) { ?>
<p class="merge-field" data-key="<?php echo $key; ?>">
<?php echo $key; ?>
</p>
<?php } ?>
</div>
<?php } ?>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-info pull-right"><?php echo _l('submit'); ?></button>
<button type="submit" class="btn btn-info pull-right">
<?php echo _l('submit'); ?>
</button>
</div>
</div>
<?php echo form_close(); ?>
@ -65,11 +75,28 @@
<script>
$(function() {
// Add merge field to template content
$('.panel-body p').not(':first-child').on('click', function() {
var mergeField = $(this).text();
// Character counter
$('#template').on('keyup', function() {
var chars = $(this).val().length;
$('#character_count').text(chars);
}).trigger('keyup');
// Merge fields click handler
$('.merge-field').on('click', function() {
var template = $('#template');
template.val(template.val() + ' ' + mergeField);
var field = $(this).data('key');
var cursorPos = template.prop('selectionStart');
var content = template.val();
// Insert merge field at cursor position
var newContent = content.slice(0, cursorPos) + field + content.slice(cursorPos);
template.val(newContent);
// Update character count
template.trigger('keyup');
// Set focus back to textarea
template.focus();
});
});
</script>