Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
ca4a290a72 | |||
837df68e89 | |||
f16b4caeef | |||
5ed64ed548 | |||
a70827a6b6 | |||
143f2b2402 | |||
717cb65516 | |||
3a49bf9afa | |||
21a5212120 | |||
f5ec9276bd | |||
b71d6aa44d | |||
015b57ddfd | |||
2a327ddd3d | |||
fd6a88f443 | |||
1524ae841f | |||
427ad9d085 | |||
eba008c916 | |||
3d75acc1be | |||
cec3f6b007 | |||
479b1d55e4 | |||
9ba7a1c003 | |||
dc570270a5 | |||
5242f5a262 | |||
b2eb51fff8 | |||
aa8c6c0b34 | |||
909c867ba5 | |||
4ea01347b2 | |||
f57c730219 | |||
6d4e964bf1 | |||
712224be37 | |||
ce6b63f3a3 | |||
c954394f5c | |||
64e8157c1c | |||
e5db5b605f |
86
system/mpesa/verifyPayment.php
Normal file
86
system/mpesa/verifyPayment.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
function stkQuery()
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
$rawData = file_get_contents('php://input');
|
||||
$postData = json_decode($rawData, true);
|
||||
|
||||
// Check if CheckoutRequestID is set
|
||||
if (!isset($postData['CheckoutRequestID'])) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'missing CheckoutRequestID fields']);
|
||||
return;
|
||||
}
|
||||
|
||||
$CheckoutRequestID = $postData['CheckoutRequestID'];
|
||||
|
||||
$consumerKey = '3AmVP1WFDQn7GrDH8GcSSKxcAvnJdZGC'; // Fill with your app Consumer Key
|
||||
$consumerSecret = '71Lybl6jUtxM0F35'; // Fill with your app Secret
|
||||
|
||||
$headers = ['Content-Type:application/json; charset=utf8'];
|
||||
|
||||
$access_token_url = 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
|
||||
$curl = curl_init($access_token_url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
||||
curl_setopt($curl, CURLOPT_HEADER, FALSE);
|
||||
curl_setopt($curl, CURLOPT_USERPWD, $consumerKey.':'.$consumerSecret);
|
||||
$result = curl_exec($curl);
|
||||
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
$result = json_decode($result);
|
||||
|
||||
$access_token = $result->access_token;
|
||||
|
||||
date_default_timezone_set('Africa/Nairobi');
|
||||
$query_url = 'https://api.safaricom.co.ke/mpesa/stkpushquery/v1/query';
|
||||
$BusinessShortCode = '4122323';
|
||||
$Passkey = 'aaebecea73082fa56af852606106b1316d5b4dfa2f12d0088800b0b88e4bb6e3';
|
||||
$Timestamp = date('YmdHis');
|
||||
|
||||
// ENCRYPT DATA TO GET PASSWORD
|
||||
$Password = base64_encode($BusinessShortCode . $Passkey . $Timestamp);
|
||||
|
||||
// THIS IS THE UNIQUE ID THAT WAS GENERATED WHEN STK REQUEST INITIATED SUCCESSFULLY
|
||||
$queryheader = ['Content-Type:application/json', 'Authorization:Bearer ' . $access_token];
|
||||
|
||||
// Initiating the transaction
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $query_url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $queryheader); // Setting custom header
|
||||
$curl_post_data = array(
|
||||
'BusinessShortCode' => $BusinessShortCode,
|
||||
'Password' => $Password,
|
||||
'Timestamp' => $Timestamp,
|
||||
'CheckoutRequestID' => $CheckoutRequestID
|
||||
);
|
||||
$data_string = json_encode($curl_post_data);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
|
||||
$curl_response = curl_exec($curl);
|
||||
$data_to = json_decode($curl_response, true);
|
||||
|
||||
// Handle response
|
||||
if (isset($data_to['ResultCode'])) {
|
||||
$ResultCode = $data_to['ResultCode'];
|
||||
if ($ResultCode == '1037') {
|
||||
$message = "1037 Timeout in completing transaction";
|
||||
} elseif ($ResultCode == '1032') {
|
||||
$message = "1032 Transaction has been cancelled by user";
|
||||
} elseif ($ResultCode == '1') {
|
||||
$message = "1 The balance is insufficient for the transaction";
|
||||
} elseif ($ResultCode == '0') {
|
||||
$message = "0 The transaction is successful";
|
||||
} else {
|
||||
$message = "Unknown Result Code: $ResultCode";
|
||||
}
|
||||
} else {
|
||||
$message = "Error in the response received from the M-Pesa API";
|
||||
}
|
||||
|
||||
// Sending the response back
|
||||
echo json_encode([
|
||||
'message' => $message,
|
||||
'result' => $data_to
|
||||
]);
|
||||
}
|
||||
?>
|
BIN
system/plugin/.DS_Store
vendored
Normal file
BIN
system/plugin/.DS_Store
vendored
Normal file
Binary file not shown.
2
system/plugin/.gitattributes
vendored
Normal file
2
system/plugin/.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
420
system/plugin/CreateHotspotUser.php
Normal file
420
system/plugin/CreateHotspotUser.php
Normal file
@ -0,0 +1,420 @@
|
||||
<?php
|
||||
function Alloworigins()
|
||||
{
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
exit;
|
||||
}
|
||||
$requestUri = $_SERVER['REQUEST_URI'];
|
||||
$queryString = parse_url($requestUri, PHP_URL_QUERY);
|
||||
$type = null;
|
||||
if ($queryString) {
|
||||
parse_str($queryString, $queryParameters);
|
||||
if (isset($queryParameters['type'])) {
|
||||
$type = $queryParameters['type'];
|
||||
if ($type === "grant") {
|
||||
CreateHostspotUser();
|
||||
exit;
|
||||
} elseif ($type === "verify") {
|
||||
VerifyHotspot();
|
||||
exit;
|
||||
} elseif ($type === "reconnect") {
|
||||
ReconnectUser();
|
||||
exit;
|
||||
} elseif ($type === "voucher") {
|
||||
ReconnectVoucher();
|
||||
exit;
|
||||
} else {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'The parameter is not present in the URL.']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ReconnectVoucher() {
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$rawData = file_get_contents('php://input');
|
||||
$postData = json_decode($rawData, true);
|
||||
|
||||
if (!isset($postData['voucher_code'], $postData['account_id'])) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'Missing accountId or voucherCode field']);
|
||||
return;
|
||||
}
|
||||
|
||||
$accountId = $postData['account_id'];
|
||||
$voucherCode = $postData['voucher_code'];
|
||||
|
||||
$voucher = ORM::for_table('tbl_voucher')
|
||||
->where('code', $voucherCode)
|
||||
->where('status', '0')
|
||||
->find_one();
|
||||
|
||||
if (!$voucher) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'Resultcode' => '1',
|
||||
'voucher' => 'Not Found',
|
||||
'message' => 'Invalid Voucher code'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($voucher['status'] == '1') {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'Resultcode' => '3',
|
||||
'voucher' => 'Used',
|
||||
'message' => 'Voucher code is already used'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$planId = $voucher['id_plan'];
|
||||
$routername = $voucher['routers'];
|
||||
|
||||
$router = ORM::for_table('tbl_routers')
|
||||
->where('name', $routername)
|
||||
->find_one();
|
||||
|
||||
if (!$router) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'Router not found'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$routerId = $router['id'];
|
||||
|
||||
if (!ORM::for_table('tbl_plans')->where('id', $planId)->count() || !ORM::for_table('tbl_routers')->where('id', $routerId)->count()) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'Unable to process your request, please refresh the page'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$user = ORM::for_table('tbl_customers')->where('username', $accountId)->find_one();
|
||||
if (!$user) {
|
||||
// Create a new user if not exists
|
||||
$user = ORM::for_table('tbl_customers')->create();
|
||||
$user->username = $accountId;
|
||||
$user->password = '1234';
|
||||
$user->fullname = $accountId;
|
||||
$user->email = $accountId . '@gmail.com';
|
||||
$user->phonenumber = $accountId;
|
||||
$user->pppoe_password = '1234';
|
||||
$user->address = '';
|
||||
$user->service_type = 'Hotspot';
|
||||
}
|
||||
|
||||
$user->router_id = $routerId;
|
||||
$user->save();
|
||||
|
||||
// Update the voucher with the user ID
|
||||
$voucher->user = $user->id;
|
||||
$voucher->status = '1'; // Mark as used
|
||||
$voucher->save();
|
||||
|
||||
if (Package::rechargeUser($user->id, $routername, $planId, 'Voucher', $voucherCode)) {
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'Resultcode' => '2',
|
||||
'voucher' => 'activated',
|
||||
'message' => 'Voucher code has been activated',
|
||||
'username' => $user->username
|
||||
]);
|
||||
} else {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'Failed to recharge user package'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function ReconnectUser()
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
$rawData = file_get_contents('php://input');
|
||||
$postData = json_decode($rawData, true);
|
||||
if (!$postData) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'Invalid JSON DATA']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!isset($postData['mpesa_code'])) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'missing required fields']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$mpesaCode = $postData['mpesa_code'];
|
||||
|
||||
// Query the payment gateway table
|
||||
$payment = ORM::for_table('tbl_payment_gateway')
|
||||
->where('gateway_trx_id', $mpesaCode)
|
||||
->find_one();
|
||||
|
||||
if (!$payment) {
|
||||
$data = array(['status' => 'error', "Resultcode" => "1", 'user' => "Not Found", 'message' => 'Invalid Mpesa Transaction code']);
|
||||
echo json_encode($data);
|
||||
exit();
|
||||
}
|
||||
|
||||
$username = $payment['username'];
|
||||
|
||||
// Query the user recharges table
|
||||
$recharge = ORM::for_table('tbl_user_recharges')
|
||||
->where('username', $username)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
if ($recharge) {
|
||||
$status = $recharge['status'];
|
||||
if ($status == 'on') {
|
||||
$data = array(
|
||||
"Resultcode" => "2",
|
||||
"user" => "Active User",
|
||||
"username" => $username,
|
||||
"tyhK" => "1234", // Replace with the actual password or token
|
||||
"Message" => "We have verified your transaction under the Mpesa Transaction $mpesaCode. Please don't leave this page as we are redirecting you.",
|
||||
"Status" => "success"
|
||||
);
|
||||
} elseif ($status == "off") {
|
||||
$data = array(
|
||||
"Resultcode" => "3",
|
||||
"user" => "Expired User",
|
||||
"Message" => "We have verified your transaction under the Mpesa Transaction $mpesaCode. But your Package is already Expired. Please buy a new Package.",
|
||||
"Status" => "danger"
|
||||
);
|
||||
} else {
|
||||
$data = array(
|
||||
"Message" => "Unexpected status value",
|
||||
"Status" => "error"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$data = array(
|
||||
"Message" => "Recharge information not found",
|
||||
"Status" => "error"
|
||||
);
|
||||
}
|
||||
|
||||
echo json_encode($data);
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
function VerifyHotspot() {
|
||||
header('Content-Type: application/json');
|
||||
$rawData = file_get_contents('php://input');
|
||||
$postData = json_decode($rawData, true);
|
||||
|
||||
if (!$postData) {
|
||||
echo json_encode(['Resultcode' => 'error', 'Message' => 'Invalid JSON data']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($postData['account_id'])) {
|
||||
echo json_encode(['Resultcode' => 'error', 'Message' => 'Missing required fields']);
|
||||
return;
|
||||
}
|
||||
|
||||
$accountId = $postData['account_id'];
|
||||
$user = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $accountId)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
if ($user) {
|
||||
$status = $user->status;
|
||||
$mpesacode = $user->gateway_trx_id;
|
||||
$res = $user->pg_paid_response;
|
||||
|
||||
if ($status == 2 && !empty($mpesacode)) {
|
||||
echo json_encode([
|
||||
"Resultcode" => "3",
|
||||
"Message" => "We have received your transaction under the Mpesa Transaction $mpesacode. Please do not leave this page as we are redirecting you.",
|
||||
"Status" => "success"
|
||||
]);
|
||||
} elseif ($res == "Not enough balance") {
|
||||
echo json_encode([
|
||||
"Resultcode" => "2",
|
||||
"Message" => "Insufficient Balance for the transaction",
|
||||
"Status" => "danger"
|
||||
]);
|
||||
} elseif ($res == "Wrong Mpesa pin") {
|
||||
echo json_encode([
|
||||
"Resultcode" => "2",
|
||||
"Message" => "You entered Wrong Mpesa pin, please resubmit",
|
||||
"Status" => "danger"
|
||||
]);
|
||||
} elseif ($status == 4) {
|
||||
echo json_encode([
|
||||
"Resultcode" => "2",
|
||||
"Message" => "You cancelled the transaction, you can enter phone number again to activate",
|
||||
"Status" => "info"
|
||||
]);
|
||||
} elseif (empty($mpesacode)) {
|
||||
echo json_encode([
|
||||
"Resultcode" => "1",
|
||||
"Message" => "A payment pop up has been sent to your phone. Please enter PIN to continue (Please do not leave or reload the page until redirected).",
|
||||
"Status" => "primary"
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode([
|
||||
"Resultcode" => "error",
|
||||
"Message" => "User not found"
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function CreateHostspotUser()
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
$rawData = file_get_contents('php://input');
|
||||
$postData = json_decode($rawData, true);
|
||||
if (!$postData) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'Invalid JSON DATA' . $postData . ' n tes ']);
|
||||
} else {
|
||||
$phone = $postData['phone_number'];
|
||||
$planId = $postData['plan_id'];
|
||||
$routerId = $postData['router_id'];
|
||||
$accountId = $postData['account_id'];
|
||||
|
||||
|
||||
|
||||
if (!isset( $postData['phone_number'], $postData['plan_id'], $postData['router_id'], $postData['account_id'])) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'missing required fields' . $postData, 'phone' => $phone, 'planId' => $planId, 'routerId' => $routerId, 'accountId' => $accountId]);
|
||||
} else {
|
||||
$phone = (substr($phone, 0, 1) == '+') ? str_replace('+', '', $phone) : $phone;
|
||||
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^0/', '254', $phone) : $phone;
|
||||
$phone = (substr($phone, 0, 1) == '7') ? preg_replace('/^7/', '2547', $phone) : $phone; //cater for phone number prefix 2547XXXX
|
||||
$phone = (substr($phone, 0, 1) == '1') ? preg_replace('/^1/', '2541', $phone) : $phone; //cater for phone number prefix 2541XXXX
|
||||
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^01/', '2541', $phone) : $phone;
|
||||
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^07/', '2547', $phone) : $phone;
|
||||
if (strlen($phone) !== 12) {
|
||||
echo json_encode(['status' => 'error', 'code' => 1, 'message' => 'Phone number ' . $phone . ' is invalid. Please confirm.']);
|
||||
}
|
||||
if (strlen($phone) == 12 && !empty($planId) && !empty($routerId)) {
|
||||
$PlanExist = ORM::for_table('tbl_plans')->where('id', $planId)->count() > 0;
|
||||
$RouterExist = ORM::for_table('tbl_routers')->where('id', $routerId)->count() > 0;
|
||||
if (!$PlanExist || !$RouterExist)
|
||||
echo json_encode(["status" => "error", "message" => "Unable to process your request, please refresh the page."]);
|
||||
}
|
||||
$Userexist = ORM::for_table('tbl_customers')->where('username', $accountId)->find_one();
|
||||
if ($Userexist) {
|
||||
$Userexist->router_id = $routerId;
|
||||
$Userexist->save();
|
||||
InitiateStkpush($phone, $planId, $accountId, $routerId);
|
||||
} else {
|
||||
try {
|
||||
$defpass = '1234';
|
||||
$defaddr = 'netXtreme';
|
||||
$defmail = $phone . '@gmail.com';
|
||||
$createUser = ORM::for_table('tbl_customers')->create();
|
||||
$createUser->username = $accountId;
|
||||
$createUser->password = $defpass;
|
||||
$createUser->fullname = $phone;
|
||||
$createUser->router_id = $routerId;
|
||||
$createUser->phonenumber = $phone;
|
||||
$createUser->pppoe_password = $defpass;
|
||||
$createUser->address = $defaddr;
|
||||
$createUser->email = $defmail;
|
||||
$createUser->service_type = 'Hotspot';
|
||||
if ($createUser->save()) {
|
||||
InitiateStkpush($phone, $planId, $accountId, $routerId);
|
||||
} else {
|
||||
echo json_encode(["status" => "error", "message" => "There was a system error when registering user, please contact support."]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(["status" => "error", "message" => "Error creating user: " . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function InitiateStkpush($phone, $planId, $accountId, $routerId)
|
||||
{
|
||||
$gateway = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'payment_gateway')
|
||||
->find_one();
|
||||
$gateway = ($gateway) ? $gateway->value : null;
|
||||
if ($gateway == "MpesatillStk") {
|
||||
$url = U . "plugin/initiatetillstk";
|
||||
} elseif ($gateway == "BankStkPush") {
|
||||
$url = U . "plugin/initiatebankstk";
|
||||
} elseif ($gateway == "mpesa") {
|
||||
$url = U . "plugin/initiatempesa";
|
||||
} else {
|
||||
$url = null; // or handle the default case appropriately
|
||||
}
|
||||
$Planname = ORM::for_table('tbl_plans')
|
||||
->where('id', $planId)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
$Findrouter = ORM::for_table('tbl_routers')
|
||||
->where('id', $routerId)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
$rname = $Findrouter->name;
|
||||
$price = $Planname->price;
|
||||
$Planname = $Planname->name_plan;
|
||||
$Checkorders = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $accountId)
|
||||
->where('status', 1)
|
||||
->order_by_desc('id')
|
||||
->find_many();
|
||||
if ($Checkorders) {
|
||||
foreach ($Checkorders as $Dorder) {
|
||||
$Dorder->delete();
|
||||
}
|
||||
}
|
||||
try {
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $accountId;
|
||||
$d->gateway = $gateway;
|
||||
$d->plan_id = $planId;
|
||||
$d->plan_name = $Planname;
|
||||
$d->routers_id = $routerId;
|
||||
$d->routers = $rname;
|
||||
$d->price = $price;
|
||||
$d->payment_method = $gateway;
|
||||
$d->payment_channel = $gateway;
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = $url;
|
||||
$d->status = 1;
|
||||
$d->save();
|
||||
} catch (Exception $e) {
|
||||
error_log('Error saving payment gateway record: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
SendSTKcred($phone, $url, $accountId);
|
||||
}
|
||||
|
||||
function SendSTKcred($phone, $url, $accountId )
|
||||
{
|
||||
$link = $url;
|
||||
$fields = array(
|
||||
'username' => $accountId,
|
||||
'phone' => $phone,
|
||||
'channel' => 'Yes',
|
||||
);
|
||||
$postvars = http_build_query($fields);
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $link);
|
||||
curl_setopt($ch, CURLOPT_POST, count($fields));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postvars);
|
||||
$result = curl_exec($ch);
|
||||
}
|
||||
|
||||
Alloworigins();
|
636
system/plugin/c2b.php
Normal file
636
system/plugin/c2b.php
Normal file
@ -0,0 +1,636 @@
|
||||
<?php
|
||||
|
||||
register_menu("Mpesa C2B Settings", true, "c2b_settings", 'SETTINGS', '', '', "");
|
||||
register_menu("Mpesa Transactions", true, "c2b_overview", 'AFTER_MESSAGE', 'fa fa-paypal', '', "");
|
||||
|
||||
try {
|
||||
$db = ORM::get_db();
|
||||
$tableCheckQuery = "CREATE TABLE IF NOT EXISTS tbl_mpesa_transactions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
TransID VARCHAR(255) NOT NULL,
|
||||
TransactionType VARCHAR(255) NOT NULL,
|
||||
TransTime VARCHAR(255) NOT NULL,
|
||||
TransAmount DECIMAL(10, 2) NOT NULL,
|
||||
BusinessShortCode VARCHAR(255) NOT NULL,
|
||||
BillRefNumber VARCHAR(255) NOT NULL,
|
||||
OrgAccountBalance DECIMAL(10, 2) NOT NULL,
|
||||
MSISDN VARCHAR(255) NOT NULL,
|
||||
FirstName VARCHAR(255) NOT NULL,
|
||||
CustomerID VARCHAR(255) NOT NULL,
|
||||
PackageName VARCHAR(255) NOT NULL,
|
||||
PackagePrice VARCHAR(255) NOT NULL,
|
||||
TransactionStatus VARCHAR(255) NOT NULL,
|
||||
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
)";
|
||||
$db->exec($tableCheckQuery);
|
||||
} catch (PDOException $e) {
|
||||
echo "Error creating the table: " . $e->getMessage();
|
||||
} catch (Exception $e) {
|
||||
echo "An unexpected error occurred: " . $e->getMessage();
|
||||
}
|
||||
|
||||
function c2b_overview()
|
||||
{
|
||||
global $ui, $config;
|
||||
_admin();
|
||||
$ui->assign('_title', 'Mpesa C2B Payment Overview');
|
||||
$ui->assign('_system_menu', '');
|
||||
$admin = Admin::_info();
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
// Check user type for access
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
exit;
|
||||
}
|
||||
|
||||
$query = ORM::for_table('tbl_mpesa_transactions')->order_by_desc('TransTime');
|
||||
$payments = $query->find_many();
|
||||
|
||||
if (
|
||||
(empty($config['mpesa_c2b_consumer_key']) || empty($config['mpesa_c2b_consumer_secret']) || empty($config['mpesa_c2b_business_code']))
|
||||
&& !$config['c2b_registered']
|
||||
) {
|
||||
$ui->assign('message', '<em>' . Lang::T("You haven't registered your validation and verification URLs. Please register URLs by clicking ") . ' <a href="' . APP_URL . '/index.php?_route=plugin/c2b_settings"> Register URL </a>' . '</em>');
|
||||
}
|
||||
$ui->assign('payments', $payments);
|
||||
$ui->assign('xheader', '<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.5/css/jquery.dataTables.css">');
|
||||
$ui->display('c2b_overview.tpl');
|
||||
}
|
||||
|
||||
function c2b_settings()
|
||||
{
|
||||
global $ui, $admin, $config;
|
||||
$ui->assign('_title', Lang::T("Mpesa C2B Settings [Offline Payment]"));
|
||||
$ui->assign('_system_menu', 'settings');
|
||||
$admin = Admin::_info();
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
if (_post('save') == 'save') {
|
||||
$mpesa_c2b_consumer_key = _post('mpesa_c2b_consumer_key');
|
||||
$mpesa_c2b_consumer_secret = _post('mpesa_c2b_consumer_secret');
|
||||
$mpesa_c2b_business_code = _post('mpesa_c2b_business_code');
|
||||
$mpesa_c2b_env = _post('mpesa_c2b_env');
|
||||
$mpesa_c2b_api = _post('mpesa_c2b_api');
|
||||
$mpesa_c2b_low_fee = _post('mpesa_c2b_low_fee') ? 1 : 0;
|
||||
$mpesa_c2b_bill_ref = _post('mpesa_c2b_bill_ref');
|
||||
|
||||
$errors = [];
|
||||
if (empty($mpesa_c2b_consumer_key)) {
|
||||
$errors[] = Lang::T('Mpesa C2B Consumer Key is required.');
|
||||
}
|
||||
if (empty($mpesa_c2b_consumer_secret)) {
|
||||
$errors[] = Lang::T('Mpesa C2B Consumer Secret is required.');
|
||||
}
|
||||
if (empty($mpesa_c2b_business_code)) {
|
||||
$errors[] = Lang::T('Mpesa C2B Business Code is required.');
|
||||
}
|
||||
if (empty($mpesa_c2b_env)) {
|
||||
$errors[] = Lang::T('Mpesa C2B Environment is required.');
|
||||
}
|
||||
if (empty($mpesa_c2b_api)) {
|
||||
$errors[] = Lang::T('Mpesa C2B API URL is required.');
|
||||
}
|
||||
|
||||
if (empty($mpesa_c2b_bill_ref)) {
|
||||
$errors[] = Lang::T('Mpesa Bill Ref Number Type is required.');
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$ui->assign('message', implode('<br>', $errors));
|
||||
$ui->display('c2b_settings.tpl');
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'mpesa_c2b_consumer_key' => $mpesa_c2b_consumer_key,
|
||||
'mpesa_c2b_consumer_secret' => $mpesa_c2b_consumer_secret,
|
||||
'mpesa_c2b_business_code' => $mpesa_c2b_business_code,
|
||||
'mpesa_c2b_env' => $mpesa_c2b_env,
|
||||
'mpesa_c2b_api' => $mpesa_c2b_api,
|
||||
'mpesa_c2b_low_fee' => $mpesa_c2b_low_fee,
|
||||
'mpesa_c2b_bill_ref' => $mpesa_c2b_bill_ref,
|
||||
];
|
||||
|
||||
// Update or insert settings in the database
|
||||
foreach ($settings as $key => $value) {
|
||||
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
|
||||
if ($d) {
|
||||
$d->value = $value;
|
||||
$d->save();
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = $key;
|
||||
$d->value = $value;
|
||||
$d->save();
|
||||
}
|
||||
}
|
||||
|
||||
if ($admin) {
|
||||
_log('[' . $admin['username'] . ']: ' . Lang::T('Settings Saved Successfully'));
|
||||
}
|
||||
r2(U . 'plugin/c2b_settings', 's', Lang::T('Settings Saved Successfully'));
|
||||
}
|
||||
|
||||
if (!empty($config['mpesa_c2b_consumer_key'] && $config['mpesa_c2b_consumer_secret'] && $config['mpesa_c2b_business_code']) && !$config['c2b_registered']) {
|
||||
$ui->assign('message', '<em>' . Lang::T("You haven't registered your validation and verification URLs, Please register URLs by clicking ") . ' <a href="' . APP_URL . '/index.php?_route=plugin/c2b_settings"> Register URL </a>' . '</em>');
|
||||
}
|
||||
$ui->assign('_c', $config);
|
||||
$ui->assign('companyName', $config['CompanyName']);
|
||||
$ui->display('c2b_settings.tpl');
|
||||
}
|
||||
|
||||
function c2b_generateAccessToken()
|
||||
{
|
||||
global $config;
|
||||
$mpesa_c2b_env = $config['mpesa_c2b_env'] ?? null;
|
||||
$mpesa_c2b_consumer_key = $config['mpesa_c2b_consumer_key'] ?? null;
|
||||
$mpesa_c2b_consumer_secret = $config['mpesa_c2b_consumer_secret'] ?? null;
|
||||
$access_token_url = match ($mpesa_c2b_env) {
|
||||
"live" => 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
|
||||
"sandbox" => 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
|
||||
};
|
||||
$headers = ['Content-Type:application/json; charset=utf8'];
|
||||
$curl = curl_init($access_token_url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
||||
curl_setopt($curl, CURLOPT_HEADER, FALSE);
|
||||
curl_setopt($curl, CURLOPT_USERPWD, "$mpesa_c2b_consumer_key:$mpesa_c2b_consumer_secret");
|
||||
$result = curl_exec($curl);
|
||||
$result = json_decode($result);
|
||||
if (isset($result->access_token)) {
|
||||
return $result->access_token;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function c2b_registerUrl()
|
||||
{
|
||||
global $config;
|
||||
if (
|
||||
(empty($config['mpesa_c2b_consumer_key']) || empty($config['mpesa_c2b_consumer_secret']) || empty($config['mpesa_c2b_business_code']))
|
||||
&& !$config['c2b_registered']
|
||||
) {
|
||||
r2(U . 'plugin/c2b_settings', 'e', Lang::T('Please setup your M-Pesa C2B settings first'));
|
||||
exit;
|
||||
}
|
||||
$access_token = c2b_generateAccessToken();
|
||||
switch ($access_token) {
|
||||
case null:
|
||||
r2(U . 'plugin/c2b_settings', 'e', Lang::T('Failed to generate access token'));
|
||||
exit;
|
||||
default:
|
||||
$BusinessShortCode = $config['mpesa_c2b_business_code'] ?? null;
|
||||
$mpesa_c2b_env = $config['mpesa_c2b_env'] ?? null;
|
||||
$confirmationUrl = U . 'plugin/c2b_confirmation';
|
||||
$validationUrl = U . 'plugin/c2b_validation';
|
||||
$mpesa_c2b_api = $config['mpesa_c2b_api'] ?? null;
|
||||
$registerurl = match ($mpesa_c2b_env) {
|
||||
"live" => match ($mpesa_c2b_api) {
|
||||
"v1" => 'https://api.safaricom.co.ke/mpesa/c2b/v1/registerurl',
|
||||
"v2" => 'https://api.safaricom.co.ke/mpesa/c2b/v2/registerurl',
|
||||
},
|
||||
"sandbox" => 'https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl',
|
||||
};
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $registerurl);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type:application/json',
|
||||
"Authorization:Bearer $access_token"
|
||||
]);
|
||||
$data = [
|
||||
'ShortCode' => $BusinessShortCode,
|
||||
'ResponseType' => 'Completed',
|
||||
'ConfirmationURL' => $confirmationUrl,
|
||||
'ValidationURL' => $validationUrl
|
||||
];
|
||||
$data_string = json_encode($data);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
|
||||
$curl_response = curl_exec($curl);
|
||||
$data = json_decode($curl_response);
|
||||
if (isset($data->ResponseCode) && $data->ResponseCode == 0) {
|
||||
try {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = 'c2b_registered';
|
||||
$d->value = '1';
|
||||
$d->save();
|
||||
} catch (Exception $e) {
|
||||
_log("Failed to save M-Pesa C2B URL to database.\n\n" . $e->getMessage());
|
||||
sendTelegram("Failed to save M-Pesa C2B URL to database.\n\n" . $e->getMessage());
|
||||
}
|
||||
sendTelegram("M-Pesa C2B URL registered successfully");
|
||||
r2(U . 'plugin/c2b_settings', 's', "M-Pesa C2B URL registered successfully");
|
||||
} else {
|
||||
$errorMessage = $data->errorMessage;
|
||||
sendTelegram("Resister M-Pesa C2B URL Failed\n\n" . json_encode($curl_response, JSON_PRETTY_PRINT));
|
||||
r2(U . 'plugin/c2b_settings', 'e', "Failed to register M-Pesa C2B URL Error $errorMessage");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function c2b_webhook_log($data)
|
||||
{
|
||||
$logFile = 'pages/mpesa-webhook.html';
|
||||
$logEntry = date('Y-m-d H:i:s') . "<pre>" . htmlspecialchars($data, ENT_QUOTES, 'UTF-8') . "</pre>\n";
|
||||
|
||||
if (file_put_contents($logFile, $logEntry, FILE_APPEND) === false) {
|
||||
sendTelegram("Failed to write to log file: $logFile");
|
||||
}
|
||||
}
|
||||
|
||||
function c2b_isValidSafaricomIP($ip)
|
||||
{
|
||||
$config = c2b_config();
|
||||
$safaricomIPs = [
|
||||
'196.201.214.0/24',
|
||||
'196.201.213.0/24',
|
||||
'196.201.212.0/24',
|
||||
'172.69.79.0/24',
|
||||
'172.69.0.0/24',
|
||||
'0.0.0.0/0',
|
||||
];
|
||||
if ($config['mpesa_c2b_env'] == 'sandbox') {
|
||||
$safaricomIPs[] = '::1';
|
||||
}
|
||||
|
||||
foreach ($safaricomIPs as $range) {
|
||||
if (c2b_ipInRange($ip, $range)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function c2b_ipInRange($ip, $range)
|
||||
{
|
||||
list($subnet, $bits) = explode('/', $range);
|
||||
$ip = ip2long($ip);
|
||||
$subnet = ip2long($subnet);
|
||||
$mask = -1 << (32 - $bits);
|
||||
$subnet &= $mask;
|
||||
return ($ip & $mask) == $subnet;
|
||||
}
|
||||
|
||||
function c2b_confirmation()
|
||||
{
|
||||
|
||||
global $config;
|
||||
header("Content-Type: application/json");
|
||||
|
||||
$clientIP = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if (!c2b_isValidSafaricomIP($clientIP)) {
|
||||
c2b_logAndNotify("Unauthorized request from IP: {$clientIP}");
|
||||
http_response_code(403);
|
||||
echo json_encode(["ResultCode" => 1, "ResultDesc" => "Unauthorized"]);
|
||||
return;
|
||||
}
|
||||
|
||||
$mpesaResponse = file_get_contents('php://input');
|
||||
if ($mpesaResponse === false) {
|
||||
c2b_logAndNotify("Failed to get input stream.");
|
||||
return;
|
||||
}
|
||||
|
||||
c2b_webhook_log('Received webhook request');
|
||||
c2b_webhook_log($mpesaResponse);
|
||||
|
||||
$content = json_decode($mpesaResponse);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
c2b_logAndNotify("Failed to decode JSON response: " . json_last_error_msg());
|
||||
return;
|
||||
}
|
||||
c2b_webhook_log('Decoded JSON data successfully');
|
||||
|
||||
if (!class_exists('Package')) {
|
||||
c2b_logAndNotify("Error: Package class does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($config['mpesa_c2b_bill_ref'])) {
|
||||
switch ($config['mpesa_c2b_bill_ref']) {
|
||||
case 'phone':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('phonenumber', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
case 'username':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('username', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
case 'id':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('id', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
default:
|
||||
$customer = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$customer) {
|
||||
sendTelegram("Validation failed: No account found for BillRefNumber: $content->BillRefNumber");
|
||||
_log("Validation failed: No account found for BillRefNumber: $content->BillRefNumber");
|
||||
echo json_encode(["ResultCode" => "C2B00012", "ResultDesc" => "Invalid Account Number"]);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_log("Configuration error: mpesa_c2b_bill_ref not set.");
|
||||
sendTelegram("Configuration error: mpesa_c2b_bill_ref not set.");
|
||||
}
|
||||
|
||||
|
||||
$bills = c2b_billing($customer->id);
|
||||
if (!$bills) {
|
||||
c2b_logAndNotify("No matching bill found for BillRefNumber: {$content->BillRefNumber}");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($bills as $bill) {
|
||||
c2b_handleBillPayment($content, $customer, $bill);
|
||||
}
|
||||
|
||||
echo json_encode(["ResultCode" => 0, "ResultDesc" => "Accepted"]);
|
||||
}
|
||||
|
||||
|
||||
function c2b_handleBillPayment($content, $customer, $bill)
|
||||
{
|
||||
$amountToPay = $bill['price'];
|
||||
$amountPaid = $content->TransAmount;
|
||||
$channel_mode = "Mpesa C2B - {$content->TransID}";
|
||||
$customerBalance = $customer->balance;
|
||||
$currentBalance = $customerBalance + $amountPaid;
|
||||
$customerID = $customer->id;
|
||||
|
||||
try {
|
||||
$transaction = c2b_storeTransaction($content, $bill['namebp'], $amountToPay, $customerID);
|
||||
} catch (Exception $e) {
|
||||
c2b_handleException("Failed to save transaction", $e);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($currentBalance >= $amountToPay) {
|
||||
$excessAmount = $currentBalance - $amountToPay;
|
||||
try {
|
||||
$result = Package::rechargeUser($customer->id, $bill['routers'], $bill['plan_id'], 'mpesa', $channel_mode);
|
||||
if (!$result) {
|
||||
c2b_logAndNotify("Mpesa Payment Successful, but failed to activate the package for customer {$customer->username}.");
|
||||
} else {
|
||||
if ($excessAmount > 0) {
|
||||
$customer->balance = $excessAmount;
|
||||
$customer->save();
|
||||
} else {
|
||||
$customer->balance = 0;
|
||||
$customer->save();
|
||||
}
|
||||
c2b_sendPaymentSuccessMessage($customer, $amountPaid, $bill['namebp']);
|
||||
$transaction->transactionStatus = 'Completed';
|
||||
$transaction->save();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
c2b_handleException("Error during package activation", $e);
|
||||
}
|
||||
} else {
|
||||
c2b_updateCustomerBalance($customer, $currentBalance, $amountPaid);
|
||||
$neededToActivate = $amountToPay - $currentBalance;
|
||||
c2b_sendBalanceUpdateMessage($customer, $amountPaid, $currentBalance, $neededToActivate);
|
||||
$transaction->transactionStatus = 'Completed';
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function c2b_storeTransaction($content, $packageName, $packagePrice, $customerID)
|
||||
{
|
||||
ORM::get_db()->beginTransaction();
|
||||
try {
|
||||
$transaction = ORM::for_table('tbl_mpesa_transactions')
|
||||
->where('TransID', $content->TransID)
|
||||
->find_one();
|
||||
|
||||
if ($transaction) {
|
||||
// Update existing transaction
|
||||
$transaction->TransactionType = $content->TransactionType;
|
||||
$transaction->TransTime = $content->TransTime;
|
||||
$transaction->TransAmount = $content->TransAmount;
|
||||
$transaction->BusinessShortCode = $content->BusinessShortCode;
|
||||
$transaction->BillRefNumber = $content->BillRefNumber;
|
||||
$transaction->OrgAccountBalance = $content->OrgAccountBalance;
|
||||
$transaction->MSISDN = $content->MSISDN;
|
||||
$transaction->FirstName = $content->FirstName;
|
||||
$transaction->PackageName = $packageName;
|
||||
$transaction->PackagePrice = $packagePrice;
|
||||
$transaction->customerID = $customerID;
|
||||
$transaction->transactionStatus = 'Pending';
|
||||
} else {
|
||||
// Create new transaction
|
||||
$transaction = ORM::for_table('tbl_mpesa_transactions')->create();
|
||||
$transaction->TransID = $content->TransID;
|
||||
$transaction->TransactionType = $content->TransactionType;
|
||||
$transaction->TransTime = $content->TransTime;
|
||||
$transaction->TransAmount = $content->TransAmount;
|
||||
$transaction->BusinessShortCode = $content->BusinessShortCode;
|
||||
$transaction->BillRefNumber = $content->BillRefNumber;
|
||||
$transaction->OrgAccountBalance = $content->OrgAccountBalance;
|
||||
$transaction->MSISDN = $content->MSISDN;
|
||||
$transaction->FirstName = $content->FirstName;
|
||||
$transaction->PackageName = $packageName;
|
||||
$transaction->PackagePrice = $packagePrice;
|
||||
$transaction->customerID = $customerID;
|
||||
$transaction->transactionStatus = 'Pending';
|
||||
}
|
||||
$transaction->save();
|
||||
ORM::get_db()->commit();
|
||||
return $transaction;
|
||||
} catch (Exception $e) {
|
||||
ORM::get_db()->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
function c2b_logAndNotify($message)
|
||||
{
|
||||
_log($message);
|
||||
sendTelegram($message);
|
||||
}
|
||||
|
||||
function c2b_handleException($message, $e)
|
||||
{
|
||||
$fullMessage = "$message: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine();
|
||||
c2b_logAndNotify($fullMessage);
|
||||
}
|
||||
|
||||
function c2b_updateCustomerBalance($customer, $newBalance, $amountPaid)
|
||||
{
|
||||
try {
|
||||
$customer->balance = $newBalance;
|
||||
$customer->save();
|
||||
c2b_logAndNotify("Payment of KES {$amountPaid} has been added to the balance of customer {$customer->username}.");
|
||||
} catch (Exception $e) {
|
||||
c2b_handleException("Failed to update customer balance", $e);
|
||||
}
|
||||
}
|
||||
|
||||
function c2b_sendPaymentSuccessMessage($customer, $amountPaid, $packageName)
|
||||
{
|
||||
$config = c2b_config();
|
||||
$message = "Dear {$customer->fullname}, your payment of KES {$amountPaid} has been received and your plan {$packageName} has been successfully activated. Thank you for choosing {$config['CompanyName']}.";
|
||||
c2b_sendNotification($customer, $message);
|
||||
}
|
||||
|
||||
function c2b_sendBalanceUpdateMessage($customer, $amountPaid, $currentBalance, $neededToActivate)
|
||||
{
|
||||
$config = c2b_config();
|
||||
$message = "Dear {$customer->fullname}, your payment of KES {$amountPaid} has been received and added to your account balance. Your current balance is KES {$currentBalance}.";
|
||||
if ($neededToActivate > 0) {
|
||||
$message .= " To activate your package, you need to add KES {$neededToActivate} more to your account.";
|
||||
} else {
|
||||
$message .= " Your current balance is sufficient to activate your package.";
|
||||
}
|
||||
$message .= "\n" . $config['CompanyName'];
|
||||
c2b_sendNotification($customer, $message);
|
||||
}
|
||||
|
||||
function c2b_sendNotification($customer, $message)
|
||||
{
|
||||
try {
|
||||
Message::sendSMS($customer->phonenumber, $message);
|
||||
Message::sendWhatsapp($customer->phonenumber, $message);
|
||||
} catch (Exception $e) {
|
||||
c2b_handleException("Failed to send SMS/WhatsApp message", $e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function c2b_validation()
|
||||
{
|
||||
header("Content-Type: application/json");
|
||||
$mpesaResponse = file_get_contents('php://input');
|
||||
|
||||
$config = c2b_config();
|
||||
$content = json_decode($mpesaResponse);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
sendTelegram("Failed to decode JSON response.");
|
||||
_log("Failed to decode JSON response.");
|
||||
echo json_encode(["ResultCode" => "C2B00016", "ResultDesc" => "Invalid JSON format"]);
|
||||
return;
|
||||
}
|
||||
|
||||
$BillRefNumber = $content->BillRefNumber;
|
||||
$TransAmount = $content->TransAmount;
|
||||
|
||||
if (isset($config['mpesa_c2b_bill_ref'])) {
|
||||
switch ($config['mpesa_c2b_bill_ref']) {
|
||||
case 'phone':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('phonenumber', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
case 'username':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('username', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
case 'id':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('id', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
default:
|
||||
$customer = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$customer) {
|
||||
sendTelegram("Validation failed: No account found for BillRefNumber: $BillRefNumber");
|
||||
_log("Validation failed: No account found for BillRefNumber: $BillRefNumber");
|
||||
echo json_encode(["ResultCode" => "C2B00012", "ResultDesc" => "Invalid Account Number"]);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_log("Configuration error: mpesa_c2b_bill_ref not set.");
|
||||
sendTelegram("Configuration error: mpesa_c2b_bill_ref not set.");
|
||||
}
|
||||
|
||||
|
||||
$bills = c2b_billing($customer->id);
|
||||
|
||||
if (!$bills) {
|
||||
sendTelegram("Validation failed: No bill found for BillRefNumber: $BillRefNumber");
|
||||
_log("Validation failed: No bill found for BillRefNumber: $BillRefNumber");
|
||||
echo json_encode(["ResultCode" => "C2B00012", "ResultDesc" => "Invalid Bill Reference"]);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($bills as $bill) {
|
||||
}
|
||||
|
||||
$billAmount = $bill['price'];
|
||||
if (!$config['mpesa_c2b_low_fee']) {
|
||||
if ($TransAmount < $billAmount) {
|
||||
sendTelegram("Validation failed: Insufficient amount. Transferred: $TransAmount, Required: $billAmount");
|
||||
_log("Validation failed: Insufficient amount. Transferred: $TransAmount, Required: $billAmount");
|
||||
echo json_encode(["ResultCode" => "C2B00013", "ResultDesc" => "Invalid or Insufficient Amount"]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sendTelegram("Validation successful for BillRefNumber: $BillRefNumber with amount: $TransAmount");
|
||||
_log("Validation successful for BillRefNumber: $BillRefNumber with amount: $TransAmount");
|
||||
echo json_encode(["ResultCode" => 0, "ResultDesc" => "Accepted"]);
|
||||
}
|
||||
|
||||
function c2b_billing($id)
|
||||
{
|
||||
$d = ORM::for_table('tbl_user_recharges')
|
||||
->selects([
|
||||
'customer_id',
|
||||
'username',
|
||||
'plan_id',
|
||||
'namebp',
|
||||
'recharged_on',
|
||||
'recharged_time',
|
||||
'expiration',
|
||||
'time',
|
||||
'status',
|
||||
'method',
|
||||
'plan_type',
|
||||
['tbl_user_recharges.routers', 'routers'],
|
||||
['tbl_user_recharges.type', 'type'],
|
||||
'admin_id',
|
||||
'prepaid'
|
||||
])
|
||||
->select('tbl_plans.price', 'price')
|
||||
->left_outer_join('tbl_plans', array('tbl_plans.id', '=', 'tbl_user_recharges.plan_id'))
|
||||
->where('customer_id', $id)
|
||||
->find_many();
|
||||
return $d;
|
||||
}
|
||||
|
||||
function c2b_config()
|
||||
{
|
||||
$result = ORM::for_table('tbl_appconfig')->find_many();
|
||||
foreach ($result as $value) {
|
||||
$config[$value['setting']] = $value['value'];
|
||||
}
|
||||
return $config;
|
||||
}
|
48
system/plugin/clear_cache.php
Normal file
48
system/plugin/clear_cache.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
register_menu("Clear System Cache", true, "clear_cache", 'SETTINGS', '');
|
||||
|
||||
function clear_cache()
|
||||
{
|
||||
global $ui;
|
||||
_admin();
|
||||
$ui->assign('_title', 'Clear Cache');
|
||||
$ui->assign('_system_menu', 'settings');
|
||||
$admin = Admin::_info();
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
// Check user type for access
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
exit;
|
||||
}
|
||||
|
||||
$compiledCacheDir = 'ui/compiled';
|
||||
$templateCacheDir = 'system/cache';
|
||||
|
||||
try {
|
||||
// Clear the compiled cache
|
||||
$files = scandir($compiledCacheDir);
|
||||
foreach ($files as $file) {
|
||||
if ($file !== '.' && $file !== '..' && is_file($compiledCacheDir . '/' . $file)) {
|
||||
unlink($compiledCacheDir . '/' . $file);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the template cache
|
||||
$templateCacheFiles = glob($templateCacheDir . '/*.{json,temp}', GLOB_BRACE);
|
||||
foreach ($templateCacheFiles as $file) {
|
||||
if (is_file($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache cleared successfully
|
||||
_log('[' . ($admin['fullname'] ?? 'Unknown Admin') . ']: ' . Lang::T(' Cleared the system cache '), $admin['user_type']);
|
||||
r2(U . 'dashboard', 's', Lang::T("Cache cleared successfully!"));
|
||||
} catch (Exception $e) {
|
||||
// Error occurred while clearing the cache
|
||||
_log('[' . ($admin['fullname'] ?? 'Unknown Admin') . ']: ' . Lang::T(' Error occurred while clearing the cache: ' . $e->getMessage()), $admin['user_type']);
|
||||
r2(U . 'dashboard', 'e', Lang::T("Error occurred while clearing the cache: ") . $e->getMessage());
|
||||
}
|
||||
}
|
755
system/plugin/download.php
Normal file
755
system/plugin/download.php
Normal file
@ -0,0 +1,755 @@
|
||||
<?php
|
||||
include '../../config.php';
|
||||
$mysqli = new mysqli($db_host, $db_user, $db_password, $db_name);
|
||||
|
||||
if ($mysqli->connect_error) {
|
||||
die("Connection failed: " . $mysqli->connect_error);
|
||||
}
|
||||
|
||||
// Function to get a setting value
|
||||
function getSettingValue($mysqli, $setting) {
|
||||
$query = $mysqli->prepare("SELECT value FROM tbl_appconfig WHERE setting = ?");
|
||||
$query->bind_param("s", $setting);
|
||||
$query->execute();
|
||||
$result = $query->get_result();
|
||||
if ($row = $result->fetch_assoc()) {
|
||||
return $row['value'];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Fetch hotspot title and description from tbl_appconfig
|
||||
$hotspotTitle = getSettingValue($mysqli, 'hotspot_title');
|
||||
$description = getSettingValue($mysqli, 'description');
|
||||
$phone = getSettingValue($mysqli, 'phone');
|
||||
$company = getSettingValue($mysqli, 'CompanyName');
|
||||
|
||||
// Fetch router name and router ID from tbl_appconfig
|
||||
$routerName = getSettingValue($mysqli, 'router_name');
|
||||
$routerId = getSettingValue($mysqli, 'router_id');
|
||||
|
||||
// Fetch available plans
|
||||
|
||||
$planQuery = "SELECT id, type, name_plan, price, validity, validity_unit FROM tbl_plans WHERE routers = ? AND type = 'Hotspot'";
|
||||
$planStmt = $mysqli->prepare($planQuery);
|
||||
$planStmt->bind_param("s", $routerName);
|
||||
$planStmt->execute();
|
||||
$planResult = $planStmt->get_result();
|
||||
|
||||
|
||||
$htmlContent = "";
|
||||
$htmlContent .= "<!DOCTYPE html>\n";
|
||||
$htmlContent .= "<html lang=\"en\">\n";
|
||||
$htmlContent .= "<head>\n";
|
||||
$htmlContent .= " <meta charset=\"UTF-8\">\n";
|
||||
$htmlContent .= " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n";
|
||||
$htmlContent .= " <title>$company</title>\n";
|
||||
$htmlContent .= " <script src=\"https://cdn.tailwindcss.com\"></script>\n";
|
||||
$htmlContent .= " <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css\">\n";
|
||||
$htmlContent .= " <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/glider-js@1.7.7/glider.min.css\" />\n";
|
||||
$htmlContent .= " <script src=\"https://cdn.jsdelivr.net/npm/glider-js@1.7.7/glider.min.js\"></script>\n";
|
||||
$htmlContent .= " <link rel=\"preconnect\" href=\"https://cdn.jsdelivr.net\">\n";
|
||||
$htmlContent .= " <link rel=\"preconnect\" href=\"https://cdnjs.cloudflare.com\" crossorigin>\n";
|
||||
$htmlContent .= " <link rel=\"stylesheet\" href=\"https://rsms.me/inter/inter.css\">\n";
|
||||
$htmlContent .= " <!-- <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\"> -->\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "</head>\n";
|
||||
$htmlContent .= "<body class=\"font-sans antialiased text-gray-900 bg-gray-900 font-inter\">\n";
|
||||
$htmlContent .= " <!-- Main Content -->\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-screen-2xl px-4 md:px-4\">\n";
|
||||
$htmlContent .= " <div class=\"max-h-34 relative mx-auto mt-4 flex max-w-lg flex-1 shrink-0 items-center justify-center overflow-hidden shadow-lg rounded-lg bg-blue-100\">\n";
|
||||
$htmlContent .= " <!-- overlay - start -->\n";
|
||||
$htmlContent .= " <!-- <div class=\"absolute inset-0 mix-blend-multiply\"></div> -->\n";
|
||||
$htmlContent .= " <!-- overlay - end -->\n";
|
||||
$htmlContent .= " <!-- text start -->\n";
|
||||
$htmlContent .= " <div class=\"relative flex flex-col items-center p-4 sm:max-w-xl\">\n";
|
||||
$htmlContent .= " <p class=\"mb-4 text-center text-2xl font-bold text-gray-800 sm:text-xl md:mb-2 \">$company HOTSPOT LOGIN </p>\n";
|
||||
$htmlContent .= " <ol class=\"text-base text-left text-gray-800 mb-1 list-decimal pl-6\">\n";
|
||||
$htmlContent .= " <li>Click on your preferred package</li>\n";
|
||||
$htmlContent .= " <li>Enter Mpesa No.</li>\n";
|
||||
$htmlContent .= " <li>Enter pin</li>\n";
|
||||
$htmlContent .= " <li>Wait to be connected</li>\n";
|
||||
$htmlContent .= " </ol>\n";
|
||||
$htmlContent .= " <p class=\"mb-4 text-center text-lg font-medium text-gray-700 sm:text-1xl md:mb-1 md:text-xl\"> For any enquiries contact : $phone</p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <!-- text end -->\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " <div class=\"py-2 sm:py-4 lg:py-4\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-screen-2xl px-4 md:px-4\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-lg\">\n";
|
||||
$htmlContent .= " <div class=\"flex flex-col gap-4\">\n";
|
||||
$htmlContent .= " <button type=\"button\" class=\"flex items-center justify-center gap-2 rounded-lg bg-blue-500 px-8 py-3 text-center text-sm font-semibold text-white outline-none ring-blue-300 transition duration-100 hover:bg-blue-600 focus-visible:ring active:bg-blue-700 md:text-base\" onclick=\"redeemVoucher()\">\n";
|
||||
$htmlContent .= " <svg class=\"w-5 h-5 mr-2\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n";
|
||||
$htmlContent .= " <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7\"></path>\n";
|
||||
$htmlContent .= " </svg>\n";
|
||||
$htmlContent .= " Click here to Redeem Voucher\n";
|
||||
$htmlContent .= " </button>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " <div class=\"py-2 sm:py-4 lg:py-6\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-screen-2xl px-4 md:px-8\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-lg grid grid-cols-2 sm:grid-cols-3 gap-1 p-1\" id=\"cards-container\">\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
// New HTML content added
|
||||
$htmlContent .= " <div class=\"container mx-auto px-4 mb-2\">\n";
|
||||
$htmlContent .= " <div class=\"max-w-md mx-auto bg-white rounded-lg overflow-hidden md:max-w-lg\">\n";
|
||||
$htmlContent .= " <div class=\"p-3\">\n";
|
||||
$htmlContent .= " <h3 class=\"text-2xl font-semibold text-gray-900 mb-3 text-center\">Enter code to reconnect</h3>\n";
|
||||
$htmlContent .= " <div class=\"mb-6\">\n";
|
||||
$htmlContent .= " <label for=\"mpesaCodeInput\" class=\"block text-gray-700 text-sm font-bold mb-2\">Code or
|
||||
message:</label>\n";
|
||||
$htmlContent .= " <input type=\"text\" id=\"mpesaCodeInput\" name=\"mpesa_code\" placeholder=\"Enter Code or Full Message\" class=\"w-full rounded-lg border bg-gray-50 px-3 py-2 text-gray-800 outline-none ring-indigo-300 transition duration-100 focus:ring\">\n";
|
||||
$htmlContent .= " <button id=\"reconnectBtn\" class=\"w-full mt-3 rounded-lg bg-blue-500 px-4 py-2 text-white font-semibold hover:bg-red-600 transition duration-100\">Reconnect</button>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"p-1\">\n";
|
||||
$htmlContent .= " <div class=\"w-full p-3\">\n";
|
||||
$htmlContent .= " <div class=\"text-center\">\n";
|
||||
$htmlContent .= " <h3 class=\"text-2xl text-gray-900\">Already Have an Active Package?</h3>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <form id=\"loginForm\" class=\"form\" name=\"login\" action=\"$(link-login-only)\" method=\"post\" $(if chap-id)onSubmit=\"return doLogin()\" $(endif)>\n";
|
||||
$htmlContent .= " <input type=\"hidden\" name=\"dst\" value=\"$(link-orig)\" />\n";
|
||||
$htmlContent .= " <input type=\"hidden\" name=\"popup\" value=\"true\" />\n";
|
||||
$htmlContent .= " <div class=\"mb-4\">\n";
|
||||
$htmlContent .= " <label class=\"block text-gray-700 text-sm font-bold mb-2\" for=\"username\">enter username or account number.</label>\n";
|
||||
$htmlContent .= " <div>\n";
|
||||
$htmlContent .= " <input id=\"usernameInput\" name=\"username\" type=\"text\" value=\"\" placeholder=\"eg. ACC123456\" class=\"w-full rounded-lg border bg-gray-50 px-3 py-2 text-gray-800 outline-none ring-indigo-300 transition duration-100 focus:ring\" />\n";
|
||||
$htmlContent .= " <button id=\"submitBtn\" class=\"w-full mt-3 flex items-center justify-center gap-2 rounded-lg bg-blue-500 px-8 py-3 text-center text-sm font-semibold text-white outline-none ring-red-300 transition duration-100 hover:bg-red-600 focus-visible:ring active:bg-red-700 md:text-base\" type=\"button\" onclick=\"submitLogin()\">\n";
|
||||
$htmlContent .= " Connect\n";
|
||||
$htmlContent .= " </button>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <input type=\"hidden\" name=\"password\" value=\"1234\">\n";
|
||||
$htmlContent .= " </form>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-screen-2xl px-4 md:px-8\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto mb-4 max-w-lg\">\n";
|
||||
$htmlContent .= " <div class=\"border-t py-4\">\n";
|
||||
$htmlContent .= " <p class=\"text-xs text-red-700 text-center\">© " . date("Y") . ". Powered by NestICT</p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= "</body>\n";
|
||||
|
||||
// Add the closing script section as well, if necessary
|
||||
$htmlContent .= "<script>\n";
|
||||
// Add any required JavaScript here
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "function fetchData() {\n";
|
||||
$htmlContent .= " let domain = '" . APP_URL . "/';\n";
|
||||
$htmlContent .= " let siteUrl = domain + \"/index.php?_route=plugin/hotspot_plan\";\n";
|
||||
$htmlContent .= " let request = new XMLHttpRequest();\n";
|
||||
$htmlContent .= " const routerName = encodeURIComponent(\"$routerName\");\n";
|
||||
$htmlContent .= " const dataparams = `routername=\${routerName}`;\n";
|
||||
$htmlContent .= " request.open(\"POST\", siteUrl, true);\n";
|
||||
$htmlContent .= " request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\n";
|
||||
$htmlContent .= " request.onload = () => {\n";
|
||||
$htmlContent .= " if (request.readyState === XMLHttpRequest.DONE) {\n";
|
||||
$htmlContent .= " if (request.status === 200) {\n";
|
||||
$htmlContent .= " let fetchedData = JSON.parse(request.responseText);\n";
|
||||
$htmlContent .= " populateCards(fetchedData);\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.log(`Error \${request.status}: \${request.statusText}`);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " };\n";
|
||||
$htmlContent .= " request.onerror = () => {\n";
|
||||
$htmlContent .= " console.error(\"Network error\");\n";
|
||||
$htmlContent .= " };\n";
|
||||
$htmlContent .= " request.send(dataparams);\n";
|
||||
$htmlContent .= "}\n";
|
||||
|
||||
$htmlContent .= "function populateCards(data) {\n";
|
||||
$htmlContent .= " var cardsContainer = document.getElementById('cards-container');\n";
|
||||
$htmlContent .= " cardsContainer.innerHTML = ''; // Clear existing content\n";
|
||||
|
||||
$htmlContent .= " // Sort the plans by price in ascending order\n";
|
||||
$htmlContent .= " data.data.forEach(router => {\n";
|
||||
$htmlContent .= " // Sort hotspot plans by price\n";
|
||||
$htmlContent .= " router.plans_hotspot.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));\n";
|
||||
|
||||
$htmlContent .= " router.plans_hotspot.forEach(item => {\n";
|
||||
$htmlContent .= " var cardDiv = document.createElement('div');\n";
|
||||
$htmlContent .= " cardDiv.className = 'bg-white border border-black rounded-lg shadow-md overflow-hidden transition duration-300 hover:shadow-lg flex flex-col items-center justify-between mx-auto mb-4 w-40';\n";
|
||||
$htmlContent .= " cardDiv.innerHTML = `\n";
|
||||
$htmlContent .= " <div class=\"bg-red-500 text-white w-full py-1\">\n";
|
||||
$htmlContent .= " <h2 class=\"text-sm font-medium uppercase text-center\" style=\"font-size: clamp(0.75rem, 1.5vw, 1rem); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\n";
|
||||
$htmlContent .= " \${item.planname}\n";
|
||||
$htmlContent .= " </h2>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"px-4 py-2 flex-grow\">\n";
|
||||
$htmlContent .= " <p class=\"text-2xl font-bold text-red-600 mb-1\">\n";
|
||||
$htmlContent .= " <span class=\"text-lg font-medium text-black\">\${item.currency}</span>\n";
|
||||
$htmlContent .= " \${item.price}\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " <p class=\"text-sm text-black mb-2\">\n";
|
||||
$htmlContent .= " Valid for \${item.validity} \${item.timelimit}\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " <hr class=\"border-black mb-2\">\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"px-4 py-2 flex-shrink-0\">\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"inline-block bg-gray-900 text-white hover:bg-red-600 font-semibold py-1 px-4 rounded-lg transition duration-300 text-md\"\n";
|
||||
$htmlContent .= " onclick=\"handlePhoneNumberSubmission('\${item.planId}', '\${item.routerId}'); return false;\"\n";
|
||||
$htmlContent .= " data-plan-id=\"\${item.planId}\"\n";
|
||||
$htmlContent .= " data-router-id=\"\${item.routerId}\">\n";
|
||||
$htmlContent .= " Buy\n";
|
||||
$htmlContent .= " </a>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " `;\n";
|
||||
$htmlContent .= " cardsContainer.appendChild(cardDiv);\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "}\n";
|
||||
$htmlContent .= "fetchData();\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script src=\"https://cdn.jsdelivr.net/npm/sweetalert2@11\"></script>\n";
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= " function formatPhoneNumber(phoneNumber) {\n";
|
||||
$htmlContent .= " if (phoneNumber.startsWith('+')) {\n";
|
||||
$htmlContent .= " phoneNumber = phoneNumber.substring(1);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " if (phoneNumber.startsWith('0')) {\n";
|
||||
$htmlContent .= " phoneNumber = '254' + phoneNumber.substring(1);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " if (phoneNumber.match(/^(7|1)/)) {\n";
|
||||
$htmlContent .= " phoneNumber = '254' + phoneNumber;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " return phoneNumber;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " function setCookie(name, value, days) {\n";
|
||||
$htmlContent .= " var expires = \"\";\n";
|
||||
$htmlContent .= " if (days) {\n";
|
||||
$htmlContent .= " var date = new Date();\n";
|
||||
$htmlContent .= " date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));\n";
|
||||
$htmlContent .= " expires = \"; expires=\" + date.toUTCString();\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " document.cookie = name + \"=\" + (value || \"\") + expires + \"; path=/\";\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " function getCookie(name) {\n";
|
||||
$htmlContent .= " var nameEQ = name + \"=\";\n";
|
||||
$htmlContent .= " var ca = document.cookie.split(';');\n";
|
||||
$htmlContent .= " for (var i = 0; i < ca.length; i++) {\n";
|
||||
$htmlContent .= " var c = ca[i];\n";
|
||||
$htmlContent .= " while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n";
|
||||
$htmlContent .= " if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " return null;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " function generateAccountId() {\n";
|
||||
$htmlContent .= " return 'ACN' + Math.floor(10000 + Math.random() * 90000); // Generate a random number between 10000 and 99999\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
|
||||
$htmlContent .= "var loginTimeout; // Variable to store the timeout ID\n";
|
||||
$htmlContent .= "function handlePhoneNumberSubmission(planId, routerId, price) {\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= ' var msg = "You are about to pay Kshs: ' . $item['price'] . '. Enter phone number below and click pay now to initialize payment";' . "\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " const regexp = /\\\${([^{}]+)}/g;\n";
|
||||
$htmlContent .= " let result = msg.replace(regexp, function(ignore, key) {\n";
|
||||
$htmlContent .= " return eval(key);\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " swal.fire({\n";
|
||||
$htmlContent .= " title: 'Enter Your Number',\n";
|
||||
$htmlContent .= " input: 'number',\n";
|
||||
$htmlContent .= " inputAttributes: {\n";
|
||||
$htmlContent .= " required: 'true'\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " inputValidator: function(value) {\n";
|
||||
$htmlContent .= " if (value === '') {\n";
|
||||
$htmlContent .= " return 'You need to write your phonenumber!';\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " text: result,\n";
|
||||
$htmlContent .= " showCancelButton: true,\n";
|
||||
$htmlContent .= " confirmButtonColor: '#3085d6',\n";
|
||||
$htmlContent .= " cancelButtonColor: '#d33',\n";
|
||||
$htmlContent .= " confirmButtonText: 'Pay Now',\n";
|
||||
$htmlContent .= " showLoaderOnConfirm: true,\n";
|
||||
$htmlContent .= " preConfirm: (phoneNumber) => {\n";
|
||||
$htmlContent .= " var formattedPhoneNumber = formatPhoneNumber(phoneNumber);\n";
|
||||
$htmlContent .= " var accountId = getCookie('accountId');\n";
|
||||
$htmlContent .= " if (!accountId) {\n";
|
||||
$htmlContent .= " accountId = generateAccountId(); // Generate a new account ID\n";
|
||||
$htmlContent .= " setCookie('accountId', accountId, 7); // Set account ID as a cookie\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " document.getElementById('usernameInput').value = accountId; // Use account ID as the new username\n";
|
||||
$htmlContent .= " console.log(\"Phone number for autofill:\", formattedPhoneNumber);\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " return fetch('" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=grant', {\n";
|
||||
$htmlContent .= " method: 'POST',\n";
|
||||
$htmlContent .= " headers: {'Content-Type': 'application/json'},\n";
|
||||
$htmlContent .= " body: JSON.stringify({phone_number: formattedPhoneNumber, plan_id: planId, router_id: routerId, account_id: accountId}),\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(response => {\n";
|
||||
$htmlContent .= " if (!response.ok) throw new Error('Network response was not ok');\n";
|
||||
$htmlContent .= " return response.json();\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(data => {\n";
|
||||
$htmlContent .= " if (data.status === 'error') throw new Error(data.message);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'info',\n";
|
||||
$htmlContent .= " title: 'Processing..',\n";
|
||||
$htmlContent .= " html: `A payment request has been sent to your phone. Please wait while we process your payment.`,\n";
|
||||
$htmlContent .= " showConfirmButton: false,\n";
|
||||
$htmlContent .= " allowOutsideClick: false,\n";
|
||||
$htmlContent .= " didOpen: () => {\n";
|
||||
$htmlContent .= " Swal.showLoading();\n";
|
||||
$htmlContent .= " checkPaymentStatus(formattedPhoneNumber);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " return formattedPhoneNumber;\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .catch(error => {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'error',\n";
|
||||
$htmlContent .= " title: 'Oops...',\n";
|
||||
$htmlContent .= " text: error.message,\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " allowOutsideClick: () => !Swal.isLoading()\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "}\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= "function checkPaymentStatus(phoneNumber) {\n";
|
||||
$htmlContent .= " let checkInterval = setInterval(() => {\n";
|
||||
$htmlContent .= " $.ajax({\n";
|
||||
$htmlContent .= " url: '" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=verify',\n";
|
||||
$htmlContent .= " method: 'POST',\n";
|
||||
$htmlContent .= " data: JSON.stringify({account_id: document.getElementById('usernameInput').value}),\n";
|
||||
$htmlContent .= " contentType: 'application/json',\n";
|
||||
$htmlContent .= " dataType: 'json',\n";
|
||||
$htmlContent .= " success: function(data) {\n";
|
||||
$htmlContent .= " console.log('Raw Response:', data); // Debugging\n";
|
||||
$htmlContent .= " if (data.Resultcode === '3') { // Success\n";
|
||||
$htmlContent .= " clearInterval(checkInterval);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'success',\n";
|
||||
$htmlContent .= " title: 'Payment Successful',\n";
|
||||
$htmlContent .= " text: data.Message,\n";
|
||||
$htmlContent .= " showConfirmButton: false\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " if (loginTimeout) {\n";
|
||||
$htmlContent .= " clearTimeout(loginTimeout);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " loginTimeout = setTimeout(function() {\n";
|
||||
$htmlContent .= " document.getElementById('loginForm').submit();\n";
|
||||
$htmlContent .= " }, 2000);\n";
|
||||
$htmlContent .= " } else if (data.Resultcode === '2') { // Error\n";
|
||||
$htmlContent .= " clearInterval(checkInterval);\n";
|
||||
$htmlContent .= " let iconType = data.Status === 'danger' ? 'error' : data.Status;\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: iconType,\n";
|
||||
$htmlContent .= " title: 'Payment Issue',\n";
|
||||
$htmlContent .= " text: data.Message,\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " } else if (data.Resultcode === '1') { // Primary\n";
|
||||
$htmlContent .= " // Continue checking\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " error: function(xhr, textStatus, errorThrown) {\n";
|
||||
$htmlContent .= " console.log('Error: ' + errorThrown);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }, 2000);\n";
|
||||
$htmlContent .= "\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " setTimeout(() => {\n";
|
||||
$htmlContent .= " clearInterval(checkInterval);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'warning',\n";
|
||||
$htmlContent .= " title: 'Timeout',\n";
|
||||
$htmlContent .= " text: 'Payment verification timed out. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }, 600000); // Stop checking after 60 seconds\n";
|
||||
$htmlContent .= "}\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "</script>\n";
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "var loginTimeout; // Variable to store the timeout ID\n";
|
||||
$htmlContent .= "function redeemVoucher() {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Redeem Voucher',\n";
|
||||
$htmlContent .= " input: 'text',\n";
|
||||
$htmlContent .= " inputPlaceholder: 'Enter voucher code',\n";
|
||||
$htmlContent .= " inputValidator: function(value) {\n";
|
||||
$htmlContent .= " if (!value) {\n";
|
||||
$htmlContent .= " return 'You need to enter a voucher code!';\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " confirmButtonColor: '#3085d6',\n";
|
||||
$htmlContent .= " cancelButtonColor: '#d33',\n";
|
||||
$htmlContent .= " confirmButtonText: 'Redeem',\n";
|
||||
$htmlContent .= " showLoaderOnConfirm: true,\n";
|
||||
$htmlContent .= " preConfirm: (voucherCode) => {\n";
|
||||
$htmlContent .= " var accountId = voucherCode;\n";
|
||||
$htmlContent .= " if (!accountId) {\n";
|
||||
$htmlContent .= " accountId = voucherCode;\n";
|
||||
$htmlContent .= " setCookie('accountId', accountId, 7);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " return fetch('" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=voucher', {\n";
|
||||
$htmlContent .= " method: 'POST',\n";
|
||||
$htmlContent .= " headers: {'Content-Type': 'application/json'},\n";
|
||||
$htmlContent .= " body: JSON.stringify({voucher_code: voucherCode, account_id: accountId}),\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(response => {\n";
|
||||
$htmlContent .= " if (!response.ok) throw new Error('Network response was not ok');\n";
|
||||
$htmlContent .= " return response.json();\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(data => {\n";
|
||||
$htmlContent .= " if (data.status === 'error') throw new Error(data.message);\n";
|
||||
$htmlContent .= " return data;\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " allowOutsideClick: () => !Swal.isLoading()\n";
|
||||
$htmlContent .= " }).then((result) => {\n";
|
||||
$htmlContent .= " if (result.isConfirmed) {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'success',\n";
|
||||
$htmlContent .= " title: 'Voucher Redeemed',\n";
|
||||
$htmlContent .= " text: result.value.message,\n";
|
||||
$htmlContent .= " showConfirmButton: false,\n";
|
||||
$htmlContent .= " allowOutsideClick: false,\n";
|
||||
$htmlContent .= " didOpen: () => {\n";
|
||||
$htmlContent .= " Swal.showLoading();\n";
|
||||
$htmlContent .= " var username = result.value.username;\n";
|
||||
$htmlContent .= " console.log('Received username from server:', username);\n";
|
||||
$htmlContent .= " var usernameInput = document.querySelector('input[name=\"username\"]');\n";
|
||||
$htmlContent .= " if (usernameInput) {\n";
|
||||
$htmlContent .= " console.log('Found username input element.');\n";
|
||||
$htmlContent .= " usernameInput.value = username;\n";
|
||||
$htmlContent .= " loginTimeout = setTimeout(function() {\n";
|
||||
$htmlContent .= " var loginForm = document.getElementById('loginForm');\n";
|
||||
$htmlContent .= " if (loginForm) {\n";
|
||||
$htmlContent .= " loginForm.submit();\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.error('Login form not found.');\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'error',\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Login form not found. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }, 2000);\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.error('Username input element not found.');\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'error',\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Username input not found. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }).catch(error => {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'error',\n";
|
||||
$htmlContent .= " title: 'Oops...',\n";
|
||||
$htmlContent .= " text: error.message,\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "}\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "var loginTimeout; // Variable to store the timeout ID\n";
|
||||
$htmlContent .= "document.addEventListener('DOMContentLoaded', function() {\n";
|
||||
$htmlContent .= " document.getElementById('reconnectBtn').addEventListener('click', function() {\n";
|
||||
$htmlContent .= " var mpesaCode = document.getElementById('mpesaCodeInput').value;\n";
|
||||
$htmlContent .= " var firstWord = mpesaCode.split(' ')[0]; // Get the first word in the MPESA code\n";
|
||||
$htmlContent .= " fetch('" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=reconnect', {\n";
|
||||
$htmlContent .= " method: 'POST',\n";
|
||||
$htmlContent .= " headers: {'Content-Type': 'application/json'},\n";
|
||||
$htmlContent .= " body: JSON.stringify({mpesa_code: firstWord}),\n"; // Sending only the first word of the MPESA code\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(response => response.json())\n";
|
||||
$htmlContent .= " .then(data => {\n";
|
||||
$htmlContent .= " if (data.Status === 'success') {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'success',\n";
|
||||
$htmlContent .= " title: 'Reconnection Successful',\n";
|
||||
$htmlContent .= " text: data.Message,\n";
|
||||
$htmlContent .= " showConfirmButton: false,\n";
|
||||
$htmlContent .= " allowOutsideClick: false,\n";
|
||||
$htmlContent .= " didOpen: () => {\n";
|
||||
$htmlContent .= " Swal.showLoading();\n";
|
||||
$htmlContent .= " var username = data.username; // Replace with actual JSON field name\n";
|
||||
$htmlContent .= " console.log('Received username from server:', username);\n";
|
||||
$htmlContent .= " var usernameInput = document.querySelector('input[name=\"username\"]');\n";
|
||||
$htmlContent .= " if (usernameInput) {\n";
|
||||
$htmlContent .= " console.log('Found username input element.');\n";
|
||||
$htmlContent .= " usernameInput.value = username;\n";
|
||||
$htmlContent .= " loginTimeout = setTimeout(function() {\n";
|
||||
$htmlContent .= " var loginForm = document.getElementById('loginForm');\n";
|
||||
$htmlContent .= " if (loginForm) {\n";
|
||||
$htmlContent .= " loginForm.submit();\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.error('Login form not found.');\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'error',\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Login form not found. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }, 2000);\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.error('Username input element not found.');\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'error',\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Username input not found. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'error',\n";
|
||||
$htmlContent .= " title: 'Reconnection Failed',\n";
|
||||
$htmlContent .= " text: data.Message,\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .catch(error => {\n";
|
||||
$htmlContent .= " console.error('Error:', error);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'error',\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Failed to reconnect. Please try again later.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "});\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js\"></script>\n";
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "document.addEventListener('DOMContentLoaded', function() {\n";
|
||||
$htmlContent .= " // Ensure the button is correctly targeted by its ID.\n";
|
||||
$htmlContent .= " var submitBtn = document.getElementById('submitBtn');\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " // Add a click event listener to the \"Login Now\" button.\n";
|
||||
$htmlContent .= " submitBtn.addEventListener('click', function(event) {\n";
|
||||
$htmlContent .= " event.preventDefault(); // Prevent the default button action.\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " // Optional: Log to console for debugging purposes.\n";
|
||||
$htmlContent .= " console.log(\"Login Now button clicked.\");\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " // Direct form submission, bypassing the doLogin function for simplicity.\n";
|
||||
$htmlContent .= " var form = document.getElementById('loginForm');\n";
|
||||
$htmlContent .= " form.submit(); // Submit the form directly.\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "});\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "</script>\n";
|
||||
$htmlContent .= "</html>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$planStmt->close();
|
||||
$mysqli->close();
|
||||
// Check if the download parameter is set
|
||||
if (isset($_GET['download']) && $_GET['download'] == '1') {
|
||||
// Prepare the HTML content for download
|
||||
// ... build your HTML content ...
|
||||
|
||||
// Specify the filename for the download
|
||||
$filename = "login.html";
|
||||
|
||||
// Send headers to force download
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename='.basename($filename));
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: ' . strlen($htmlContent));
|
||||
|
||||
// Output the content
|
||||
echo $htmlContent;
|
||||
|
||||
// Prevent any further output
|
||||
exit;
|
||||
}
|
||||
|
||||
// Regular page content goes here
|
||||
// ... HTML and PHP code to display the page ...
|
||||
|
||||
|
12
system/plugin/error_log
Normal file
12
system/plugin/error_log
Normal file
@ -0,0 +1,12 @@
|
||||
[06-Jul-2024 15:05:25 UTC] PHP Fatal error: Uncaught Error: Undefined constant "request" in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php:154
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php on line 154
|
||||
[06-Jul-2024 15:05:28 UTC] PHP Fatal error: Uncaught Error: Undefined constant "request" in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php:154
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php on line 154
|
||||
[06-Jul-2024 17:35:47 UTC] PHP Fatal error: Uncaught Error: Undefined constant "request" in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php:154
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php on line 154
|
79
system/plugin/hotspot_plan.php
Normal file
79
system/plugin/hotspot_plan.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
// Assuming you have ORM or database access configured correctly
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['routername'])) {
|
||||
// Example of fetching data (simplified)
|
||||
$routerName = $_POST['routername'];
|
||||
|
||||
// Fetch routers and plans from database (replace with your actual ORM or database queries)
|
||||
$routers = ORM::for_table('tbl_routers')->find_many();
|
||||
$plans_hotspot = ORM::for_table('tbl_plans')->where('type', 'Hotspot')->find_many();
|
||||
|
||||
// Fetch bandwidth limits for all plans
|
||||
$bandwidth_limits = ORM::for_table('tbl_bandwidth')->find_many();
|
||||
$bandwidth_map = [];
|
||||
foreach ($bandwidth_limits as $limit) {
|
||||
$bandwidth_map[$limit['plan_id']] = [
|
||||
'downlimit' => $limit['rate_down'],
|
||||
'uplimit' => $limit['rate_up'],
|
||||
];
|
||||
}
|
||||
|
||||
// Fetch currency from tbl_appconfig using the correct column names
|
||||
$currency_config = ORM::for_table('tbl_appconfig')->where('setting', 'currency_code')->find_one();
|
||||
$currency = $currency_config ? $currency_config->value : 'Ksh'; // Default to 'Ksh' if not found
|
||||
|
||||
// Initialize empty data array to store router-specific plans
|
||||
$data = [];
|
||||
|
||||
// Process each router to filter and collect hotspot plans
|
||||
foreach ($routers as $router) {
|
||||
if ($router['name'] === $routerName) { // Check if router name matches POSTed routername
|
||||
$routerData = [
|
||||
'name' => $router['name'],
|
||||
'router_id' => $router['id'],
|
||||
'description' => $router['description'],
|
||||
'plans_hotspot' => [],
|
||||
];
|
||||
|
||||
// Filter and collect hotspot plans associated with the router
|
||||
foreach ($plans_hotspot as $plan) {
|
||||
if ($router['name'] == $plan['routers']) {
|
||||
$plan_id = $plan['id'];
|
||||
$bandwidth_data = isset($bandwidth_map[$plan_id]) ? $bandwidth_map[$plan_id] : [];
|
||||
|
||||
// Construct payment link using $_url
|
||||
$paymentlink = "https://codevibeisp.co.ke/index.php?_route=plugin/hotspot_pay&routerName={$router['name']}&planId={$plan['id']}&routerId={$router['id']}";
|
||||
|
||||
// Prepare plan data to be sent in JSON response
|
||||
$routerData['plans_hotspot'][] = [
|
||||
'plantype' => $plan['type'],
|
||||
'planname' => $plan['name_plan'],
|
||||
'currency' => $currency,
|
||||
'price' => $plan['price'],
|
||||
'validity' => $plan['validity'],
|
||||
'device' => $plan['shared_users'],
|
||||
'datalimit' => $plan['data_limit'],
|
||||
'timelimit' => $plan['validity_unit'] ?? null,
|
||||
'downlimit' => $bandwidth_data['downlimit'] ?? null,
|
||||
'uplimit' => $bandwidth_data['uplimit'] ?? null,
|
||||
'paymentlink' => $paymentlink,
|
||||
'planId' => $plan['id'],
|
||||
'routerName' => $router['name'],
|
||||
'routerId' => $router['id']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Add router data to $data array
|
||||
$data[] = $routerData;
|
||||
}
|
||||
}
|
||||
|
||||
// Respond with JSON data
|
||||
// header('Content-Type: application/json');
|
||||
// header('Access-Control-Allow-Origin: *'); // Adjust this based on your CORS requirements
|
||||
echo json_encode(['data' => $data], JSON_PRETTY_PRINT);
|
||||
exit();
|
||||
}
|
||||
?>
|
229
system/plugin/hotspot_settings.php
Normal file
229
system/plugin/hotspot_settings.php
Normal file
@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
|
||||
$conn = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_password);
|
||||
function hotspot_settings() {
|
||||
global $ui, $conn;
|
||||
_admin();
|
||||
$admin = Admin::_info();
|
||||
$ui->assign('_title', 'Hotspot Dashboard');
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
// Check if form is submitted
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Update Hotspot Title
|
||||
$newHotspotTitle = isset($_POST['hotspot_title']) ? trim($_POST['hotspot_title']) : '';
|
||||
if (!empty($newHotspotTitle)) {
|
||||
$updateStmt = $conn->prepare("UPDATE tbl_appconfig SET value = ? WHERE setting = 'hotspot_title'");
|
||||
$updateStmt->execute([$newHotspotTitle]);
|
||||
}
|
||||
|
||||
// Add similar logic for FAQ fields here
|
||||
// FAQ Headline 1 Posting To Database
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newFaqHeadline1 = isset($_POST['frequently_asked_questions_headline1']) ? trim($_POST['frequently_asked_questions_headline1']) : '';
|
||||
if (!empty($newFaqHeadline1)) {
|
||||
$updateFaqStmt1 = $conn->prepare("UPDATE tbl_appconfig SET value = ? WHERE setting = 'frequently_asked_questions_headline1'");
|
||||
$updateFaqStmt1->execute([$newFaqHeadline1]);
|
||||
}
|
||||
}
|
||||
|
||||
// FAQ Headline 2 Posting To Database
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newFaqHeadline1 = isset($_POST['frequently_asked_questions_headline2']) ? trim($_POST['frequently_asked_questions_headline2']) : '';
|
||||
if (!empty($newFaqHeadline1)) {
|
||||
$updateFaqStmt1 = $conn->prepare("UPDATE tbl_appconfig SET value = ? WHERE setting = 'frequently_asked_questions_headline2'");
|
||||
$updateFaqStmt1->execute([$newFaqHeadline1]);
|
||||
}
|
||||
}
|
||||
|
||||
// FAQ Headline 3 Posting To Database
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newFaqHeadline1 = isset($_POST['frequently_asked_questions_headline3']) ? trim($_POST['frequently_asked_questions_headline3']) : '';
|
||||
if (!empty($newFaqHeadline1)) {
|
||||
$updateFaqStmt1 = $conn->prepare("UPDATE tbl_appconfig SET value = ? WHERE setting = 'frequently_asked_questions_headline3'");
|
||||
$updateFaqStmt1->execute([$newFaqHeadline1]);
|
||||
}
|
||||
}
|
||||
|
||||
// FAQ Answer 1 Posting To Database
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newFaqHeadline1 = isset($_POST['frequently_asked_questions_answer1']) ? trim($_POST['frequently_asked_questions_answer1']) : '';
|
||||
if (!empty($newFaqHeadline1)) {
|
||||
$updateFaqStmt1 = $conn->prepare("UPDATE tbl_appconfig SET value = ? WHERE setting = 'frequently_asked_questions_answer1'");
|
||||
$updateFaqStmt1->execute([$newFaqHeadline1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FAQ Answer 2 Posting To Database
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newFaqHeadline1 = isset($_POST['frequently_asked_questions_answer2']) ? trim($_POST['frequently_asked_questions_answer2']) : '';
|
||||
if (!empty($newFaqHeadline1)) {
|
||||
$updateFaqStmt1 = $conn->prepare("UPDATE tbl_appconfig SET value = ? WHERE setting = 'frequently_asked_questions_answer2'");
|
||||
$updateFaqStmt1->execute([$newFaqHeadline1]);
|
||||
}
|
||||
}
|
||||
|
||||
// FAQ Answer 3 Posting To Database
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newFaqHeadline1 = isset($_POST['frequently_asked_questions_answer3']) ? trim($_POST['frequently_asked_questions_answer3']) : '';
|
||||
if (!empty($newFaqHeadline1)) {
|
||||
$updateFaqStmt1 = $conn->prepare("UPDATE tbl_appconfig SET value = ? WHERE setting = 'frequently_asked_questions_answer3'");
|
||||
$updateFaqStmt1->execute([$newFaqHeadline1]);
|
||||
}
|
||||
}
|
||||
|
||||
// FAQ Description Posting To Database
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newFaqHeadline1 = isset($_POST['description']) ? trim($_POST['description']) : '';
|
||||
if (!empty($newFaqHeadline1)) {
|
||||
$updateFaqStmt1 = $conn->prepare("UPDATE tbl_appconfig SET value = ? WHERE setting = 'description'");
|
||||
$updateFaqStmt1->execute([$newFaqHeadline1]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Get router name from user input
|
||||
$routerName = isset($_POST['router_name']) ? trim($_POST['router_name']) : '';
|
||||
|
||||
if (!empty($routerName)) {
|
||||
// Fetch the router ID based on the router name
|
||||
$routerStmt = $conn->prepare("SELECT id FROM tbl_routers WHERE name = :router_name");
|
||||
$routerStmt->execute(['router_name' => $routerName]);
|
||||
$router = $routerStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($router) {
|
||||
// Update router_id in tbl_appconfig
|
||||
$updateRouterIdStmt = $conn->prepare("UPDATE tbl_appconfig SET value = :router_id WHERE setting = 'router_id'");
|
||||
$updateRouterIdStmt->execute(['router_id' => $router['id']]);
|
||||
|
||||
// Update router_name in tbl_appconfig
|
||||
$updateRouterNameStmt = $conn->prepare("UPDATE tbl_appconfig SET value = :router_name WHERE setting = 'router_name'");
|
||||
$updateRouterNameStmt->execute(['router_name' => $routerName]);
|
||||
} else {
|
||||
// Handle the case where no matching router is found
|
||||
// For example, you can set an error message or take any other appropriate action
|
||||
}
|
||||
}
|
||||
// Other form handling code (if any)
|
||||
}
|
||||
|
||||
|
||||
// Redirect with a success message
|
||||
r2(U . "plugin/hotspot_settings", 's', "Settings Saved");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Fetch the current hotspot title from the database
|
||||
$stmt = $conn->prepare("SELECT value FROM tbl_appconfig WHERE setting = 'hotspot_title'");
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$hotspotTitle = $result ? $result['value'] : '';
|
||||
|
||||
// Assign the fetched title to the template
|
||||
$ui->assign('hotspot_title', $hotspotTitle);
|
||||
|
||||
|
||||
|
||||
|
||||
// Fetch the current faq headline 1 from the database
|
||||
$stmt = $conn->prepare("SELECT value FROM tbl_appconfig WHERE setting = 'frequently_asked_questions_headline1'");
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$headline1 = $result ? $result['value'] : '';
|
||||
|
||||
// Assign the fetched title to the template
|
||||
$ui->assign('frequently_asked_questions_headline1', $headline1);
|
||||
|
||||
|
||||
// Fetch the current faq headline 2 from the database
|
||||
$stmt = $conn->prepare("SELECT value FROM tbl_appconfig WHERE setting = 'frequently_asked_questions_headline2'");
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$headline2 = $result ? $result['value'] : '';
|
||||
|
||||
// Assign the fetched title to the template
|
||||
$ui->assign('frequently_asked_questions_headline2', $headline2);
|
||||
|
||||
|
||||
|
||||
// Fetch the current faq headline 3 from the database
|
||||
$stmt = $conn->prepare("SELECT value FROM tbl_appconfig WHERE setting = 'frequently_asked_questions_headline3'");
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$headline3 = $result ? $result['value'] : '';
|
||||
|
||||
// Assign the fetched title to the template
|
||||
$ui->assign('frequently_asked_questions_headline3', $headline3);
|
||||
|
||||
|
||||
// Fetch the current faq Answer1 from the database
|
||||
$stmt = $conn->prepare("SELECT value FROM tbl_appconfig WHERE setting = 'frequently_asked_questions_answer1'");
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$answer1 = $result ? $result['value'] : '';
|
||||
|
||||
// Assign the fetched title to the template
|
||||
$ui->assign('frequently_asked_questions_answer1', $answer1);
|
||||
|
||||
// Fetch the current faq Answer2 from the database
|
||||
$stmt = $conn->prepare("SELECT value FROM tbl_appconfig WHERE setting = 'frequently_asked_questions_answer2'");
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$answer2 = $result ? $result['value'] : '';
|
||||
|
||||
// Assign the fetched title to the template
|
||||
$ui->assign('frequently_asked_questions_answer2', $answer2);
|
||||
|
||||
// Fetch the current faq Answer 3 from the database
|
||||
$stmt = $conn->prepare("SELECT value FROM tbl_appconfig WHERE setting = 'frequently_asked_questions_answer3'");
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$answer3 = $result ? $result['value'] : '';
|
||||
|
||||
// Assign the fetched title to the template
|
||||
$ui->assign('frequently_asked_questions_answer3', $answer3);
|
||||
|
||||
// Fetch the current faq description from the database
|
||||
$stmt = $conn->prepare("SELECT value FROM tbl_appconfig WHERE setting = 'description'");
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$description = $result ? $result['value'] : '';
|
||||
|
||||
// Assign the fetched title to the template
|
||||
$ui->assign('description', $description);
|
||||
|
||||
|
||||
|
||||
/// Fetch the current router name from the database for display in the form
|
||||
$routerIdStmt = $conn->prepare("SELECT value FROM tbl_appconfig WHERE setting = 'router_id'");
|
||||
$routerIdStmt->execute();
|
||||
$routerIdResult = $routerIdStmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($routerIdResult) {
|
||||
$routerStmt = $conn->prepare("SELECT name FROM tbl_routers WHERE id = :router_id");
|
||||
$routerStmt->execute(['router_id' => $routerIdResult['value']]);
|
||||
$router = $routerStmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($router) {
|
||||
$ui->assign('router_name', $router['name']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Render the template
|
||||
$ui->display('hotspot_settings.tpl');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
BIN
system/plugin/index.html
Normal file
BIN
system/plugin/index.html
Normal file
Binary file not shown.
137
system/plugin/initiatePaybillStk.php
Normal file
137
system/plugin/initiatePaybillStk.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
function initiatePaybillPayment()
|
||||
{
|
||||
// Ensure POST variables are set and sanitize input
|
||||
$username = isset($_POST['username']) ? filter_var($_POST['username'], FILTER_SANITIZE_STRING) : null;
|
||||
$phone = isset($_POST['phone']) ? filter_var($_POST['phone'], FILTER_SANITIZE_STRING) : null;
|
||||
|
||||
if (!$username || !$phone) {
|
||||
echo "<script>toastr.error('Invalid input data');</script>";
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize phone number
|
||||
$phone = preg_replace(['/^\+/', '/^0/', '/^7/', '/^1/'], ['', '254', '2547', '2541'], $phone);
|
||||
|
||||
// Retrieve bank details from the database
|
||||
$bankaccount = ORM::for_table('tbl_appconfig')->where('setting', 'PaybillAcc')->find_one();
|
||||
$bankname = ORM::for_table('tbl_appconfig')->where('setting', 'PaybillName')->find_one();
|
||||
$bankaccount = $bankaccount ? $bankaccount->value : null;
|
||||
$bankname = $bankname ? $bankname->value : null;
|
||||
|
||||
if (!$bankaccount || !$bankname) {
|
||||
echo "<script>toastr.error('Could not complete the payment req, please contact admin');</script>";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for existing user details
|
||||
$CheckId = ORM::for_table('tbl_customers')->where('username', $username)->order_by_desc('id')->find_one();
|
||||
$CheckUser = ORM::for_table('tbl_customers')->where('phonenumber', $phone)->find_many();
|
||||
$UserId = $CheckId ? $CheckId->id : null;
|
||||
|
||||
if ($CheckUser) {
|
||||
ORM::for_table('tbl_customers')->where('phonenumber', $phone)->where_not_equal('id', $UserId)->delete_many();
|
||||
}
|
||||
|
||||
// Retrieve payment gateway record
|
||||
$PaymentGatewayRecord = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $username)
|
||||
->where('status', 1)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
if (!$PaymentGatewayRecord) {
|
||||
echo "<script>toastr.error('Could not complete the payment req, please contact administrator');</script>";
|
||||
return;
|
||||
}
|
||||
|
||||
// Update user phone number
|
||||
$ThisUser = ORM::for_table('tbl_customers')->where('username', $username)->order_by_desc('id')->find_one();
|
||||
if ($ThisUser) {
|
||||
$ThisUser->phonenumber = $phone;
|
||||
$ThisUser->save();
|
||||
}
|
||||
|
||||
$amount = $PaymentGatewayRecord->price;
|
||||
|
||||
// Safaricom API credentials
|
||||
$consumerKey = 'YOUR_CONSUMER_KEY';
|
||||
$consumerSecret = 'YOUR_CONSUMER_SECRET';
|
||||
|
||||
// Get access token
|
||||
$access_token_url = 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
|
||||
$curl = curl_init($access_token_url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type:application/json; charset=utf8']);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
||||
curl_setopt($curl, CURLOPT_USERPWD, "$consumerKey:$consumerSecret");
|
||||
$result = curl_exec($curl);
|
||||
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
curl_close($curl);
|
||||
|
||||
if ($status !== 200) {
|
||||
echo "<script>toastr.error('Failed to get access token');</script>";
|
||||
return;
|
||||
}
|
||||
|
||||
$result = json_decode($result);
|
||||
$access_token = $result->access_token;
|
||||
|
||||
// Initiate Paybill payment
|
||||
$paybill_url = 'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest';
|
||||
$Timestamp = date("YmdHis");
|
||||
$BusinessShortCode = 'YOUR_BUSINESS_SHORTCODE';
|
||||
$Passkey = 'YOUR_PASSKEY';
|
||||
$Password = base64_encode($BusinessShortCode . $Passkey . $Timestamp);
|
||||
$CallBackURL = U . 'callback/PaybillCallback';
|
||||
|
||||
$curl_post_data = [
|
||||
'BusinessShortCode' => $BusinessShortCode,
|
||||
'Password' => $Password,
|
||||
'Timestamp' => $Timestamp,
|
||||
'TransactionType' => 'CustomerPayBillOnline',
|
||||
'Amount' => $amount,
|
||||
'PartyA' => $phone,
|
||||
'PartyB' => $BusinessShortCode,
|
||||
'PhoneNumber' => $phone,
|
||||
'CallBackURL' => $CallBackURL,
|
||||
'AccountReference' => $bankaccount,
|
||||
'TransactionDesc' => 'PayBill Payment'
|
||||
];
|
||||
|
||||
$curl = curl_init($paybill_url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type:application/json', 'Authorization:Bearer ' . $access_token]);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($curl_post_data));
|
||||
$curl_response = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
|
||||
$mpesaResponse = json_decode($curl_response);
|
||||
$responseCode = $mpesaResponse->ResponseCode;
|
||||
$resultDesc = $mpesaResponse->resultDesc;
|
||||
$MerchantRequestID = $mpesaResponse->MerchantRequestID;
|
||||
$CheckoutRequestID = $mpesaResponse->CheckoutRequestID;
|
||||
|
||||
if ($responseCode == "0") {
|
||||
date_default_timezone_set('Africa/Nairobi');
|
||||
$now = date("Y-m-d H:i:s");
|
||||
|
||||
$PaymentGatewayRecord->pg_paid_response = $resultDesc;
|
||||
$PaymentGatewayRecord->username = $username;
|
||||
$PaymentGatewayRecord->checkout = $CheckoutRequestID;
|
||||
$PaymentGatewayRecord->payment_method = 'Mpesa PayBill';
|
||||
$PaymentGatewayRecord->payment_channel = 'Mpesa PayBill';
|
||||
$PaymentGatewayRecord->save();
|
||||
|
||||
if (!empty($_POST['channel'])) {
|
||||
echo json_encode(["status" => "success", "message" => "Enter Pin to complete"]);
|
||||
} else {
|
||||
echo "<script>toastr.success('Enter Mpesa Pin to complete');</script>";
|
||||
}
|
||||
} else {
|
||||
echo "<script>toastr.error('We could not complete the payment for you, please contact administrator');</script>";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
304
system/plugin/initiatebankstk.php
Normal file
304
system/plugin/initiatebankstk.php
Normal file
@ -0,0 +1,304 @@
|
||||
<?php
|
||||
|
||||
function initiatebankstk()
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$username=$_POST['username'];
|
||||
$phone=$_POST['phone'];
|
||||
|
||||
|
||||
|
||||
$phone = (substr($phone, 0,1) == '+') ? str_replace('+', '', $phone) : $phone;
|
||||
$phone = (substr($phone, 0,1) == '0') ? preg_replace('/^0/', '254', $phone) : $phone;
|
||||
$phone = (substr($phone, 0,1) == '7') ? preg_replace('/^7/', '2547', $phone) : $phone; //cater for phone number prefix 2547XXXX
|
||||
$phone = (substr($phone, 0,1) == '1') ? preg_replace('/^1/', '2541', $phone) : $phone; //cater for phone number prefix 2541XXXX
|
||||
$phone = (substr($phone, 0,1) == '0') ? preg_replace('/^01/', '2541', $phone) : $phone;
|
||||
$phone = (substr($phone, 0,1) == '0') ? preg_replace('/^07/', '2547', $phone) : $phone;
|
||||
|
||||
|
||||
|
||||
|
||||
$bankaccount = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'Stkbankacc')
|
||||
->find_one();
|
||||
|
||||
$bankname = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'Stkbankname')
|
||||
->find_one();
|
||||
|
||||
$bankaccount = ($bankaccount) ? $bankaccount->value : null;
|
||||
$bankname = ($bankname) ? $bankname->value : null;
|
||||
|
||||
// echo $bankname;
|
||||
|
||||
|
||||
$CheckId = ORM::for_table('tbl_customers')
|
||||
->where('username', $username)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
$CheckUser = ORM::for_table('tbl_customers')
|
||||
->where('phonenumber', $phone)
|
||||
->find_many();
|
||||
|
||||
$UserId=$CheckId->id;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (empty($bankaccount) || empty($bankname)) {
|
||||
|
||||
|
||||
echo $error="<script>toastr.error('Could not complete the payment req, please contact admin');</script>";
|
||||
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$getpaybill = ORM::for_table('tbl_banks')
|
||||
->where('name', $bankname)
|
||||
->find_one();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$paybill=$getpaybill->paybill;
|
||||
|
||||
|
||||
|
||||
// echo $paybill;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$cburl = U . 'callback/BankStkPush' ;
|
||||
|
||||
|
||||
$PaymentGatewayRecord = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $username)
|
||||
->where('status', 1) // Add this line to filter by status
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
|
||||
|
||||
$ThisUser= ORM::for_table('tbl_customers')
|
||||
->where('username', $username)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
|
||||
|
||||
$ThisUser->phonenumber=$phone;
|
||||
// $ThisUser->username=$phone;
|
||||
$ThisUser->save();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$amount=$PaymentGatewayRecord->price;
|
||||
|
||||
if(!$PaymentGatewayRecord){
|
||||
|
||||
echo $error="<script>toastr.error('Could not complete the payment req, please contact administrator');</script>";
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$consumerKey = '3AmVP1WFDQn7GrDH8GcSSKxcAvnJdZGC'; //Fill with your app Consumer Key
|
||||
$consumerSecret = '71Lybl6jUtxM0F35'; // Fill with your app Secret
|
||||
|
||||
$headers = ['Content-Type:application/json; charset=utf8'];
|
||||
|
||||
$access_token_url = 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
|
||||
|
||||
$curl = curl_init($access_token_url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
||||
curl_setopt($curl, CURLOPT_HEADER, FALSE);('');
|
||||
|
||||
curl_setopt($curl, CURLOPT_USERPWD, $consumerKey.':'.$consumerSecret);
|
||||
$result = curl_exec($curl);
|
||||
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
$result = json_decode($result);
|
||||
|
||||
$access_token = $result->access_token;
|
||||
|
||||
// echo $access_token;
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
|
||||
// Initiate Stk push
|
||||
|
||||
$stk_url = 'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest';
|
||||
$PartyA = $phone; // This is your phone number,
|
||||
$AccountReference = $bankaccount;
|
||||
$TransactionDesc = 'TestMapayment';
|
||||
$Amount = $amount;
|
||||
$BusinessShortCode='4122323';
|
||||
$Passkey='aaebecea73082fa56af852606106b1316d5b4dfa2f12d0088800b0b88e4bb6e3';
|
||||
$Timestamp = date("YmdHis",time());
|
||||
$Password = base64_encode($BusinessShortCode.$Passkey.$Timestamp);
|
||||
$CallBackURL = $cburl;
|
||||
|
||||
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $stk_url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Authorization:Bearer '.$access_token)); //setting custom header
|
||||
|
||||
|
||||
$curl_post_data = array(
|
||||
//Fill in the request parameters with valid values
|
||||
'BusinessShortCode' => $BusinessShortCode,
|
||||
'Password' => $Password,
|
||||
'Timestamp' => $Timestamp,
|
||||
'TransactionType' => 'CustomerPayBillOnline',
|
||||
'Amount' => $Amount,
|
||||
'PartyA' => $PartyA,
|
||||
'PartyB' => $paybill,
|
||||
'PhoneNumber' => $PartyA,
|
||||
'CallBackURL' => $CallBackURL,
|
||||
'AccountReference' => $AccountReference,
|
||||
'TransactionDesc' => $TransactionDesc
|
||||
);
|
||||
|
||||
$data_string = json_encode($curl_post_data);
|
||||
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
|
||||
|
||||
$curl_response = curl_exec($curl);
|
||||
//print_r($curl_response);
|
||||
|
||||
// echo $curl_response;
|
||||
// die;
|
||||
|
||||
$mpesaResponse = json_decode($curl_response);
|
||||
|
||||
|
||||
|
||||
|
||||
$responseCode = $mpesaResponse->ResponseCode;
|
||||
$resultDesc = $mpesaResponse->resultDesc;
|
||||
$MerchantRequestID = $mpesaResponse->MerchantRequestID;
|
||||
$CheckoutRequestID = $mpesaResponse->CheckoutRequestID;
|
||||
|
||||
|
||||
if($responseCode=="0"){
|
||||
date_default_timezone_set('Africa/Nairobi');
|
||||
$now=date("Y-m-d H:i:s");
|
||||
|
||||
// $username=$phone;
|
||||
|
||||
$PaymentGatewayRecord->pg_paid_response = $resultDesc;
|
||||
$PaymentGatewayRecord->username = $username;
|
||||
$PaymentGatewayRecord->checkout = $CheckoutRequestID;
|
||||
$PaymentGatewayRecord->payment_method = 'Mpesa Stk Push';
|
||||
$PaymentGatewayRecord->payment_channel = 'Mpesa Stk Push';
|
||||
$PaymentGatewayRecord->save();
|
||||
|
||||
|
||||
|
||||
if(!empty($_POST['channel'])){
|
||||
|
||||
echo json_encode(["status" => "success", "message" => "Enter Pin to complete"]);
|
||||
|
||||
}else{
|
||||
echo $error="<script>toastr.success('Enter Mpesa Pin to complete');</script>";
|
||||
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
echo $error="<script>toastr.error('We could not complete the payment for you, please contact administrator');</script>";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
?>
|
148
system/plugin/initiatempesa.php
Normal file
148
system/plugin/initiatempesa.php
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
function initiatempesa()
|
||||
{
|
||||
$username = $_POST['username'];
|
||||
$phone = $_POST['phone'];
|
||||
$phone = (substr($phone, 0, 1) == '+') ? str_replace('+', '', $phone) : $phone;
|
||||
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^0/', '254', $phone) : $phone;
|
||||
$phone = (substr($phone, 0, 1) == '7') ? preg_replace('/^7/', '2547', $phone) : $phone; //cater for phone number prefix 2547XXXX
|
||||
$phone = (substr($phone, 0, 1) == '1') ? preg_replace('/^1/', '2541', $phone) : $phone; //cater for phone number prefix 2541XXXX
|
||||
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^01/', '2541', $phone) : $phone;
|
||||
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^07/', '2547', $phone) : $phone;
|
||||
$CheckId = ORM::for_table('tbl_customers')
|
||||
->where('username', $username)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
$CheckUser = ORM::for_table('tbl_customers')
|
||||
->where('phonenumber', $phone)
|
||||
->find_many();
|
||||
$UserId = $CheckId->id;
|
||||
|
||||
$CallBackURL = U . 'callback/mpesa';
|
||||
$PaymentGatewayRecord = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $username)
|
||||
->where('status', 1) // Add this line to filter by status
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
$ThisUser = ORM::for_table('tbl_customers')
|
||||
->where('username', $username)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
$ThisUser->phonenumber = $phone;
|
||||
$ThisUser->save();
|
||||
$amount = $PaymentGatewayRecord->price;
|
||||
if (!$PaymentGatewayRecord) {
|
||||
echo json_encode(["status" => "error", "message" => "Could not complete the payment req, please contact administrator"]);
|
||||
}
|
||||
// Get the M-Pesa mpesa_env
|
||||
$mpesa_env = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_env')
|
||||
->find_one();
|
||||
$mpesa_env = ($mpesa_env) ? $mpesa_env->value : null;
|
||||
// Get the M-Pesa consumer key
|
||||
$mpesa_consumer_key = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_consumer_key')
|
||||
->find_one();
|
||||
$mpesa_consumer_key = ($mpesa_consumer_key) ? $mpesa_consumer_key->value : null;
|
||||
// Get the M-Pesa consumer secret
|
||||
$mpesa_consumer_secret = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_consumer_secret')
|
||||
->find_one();
|
||||
$mpesa_consumer_secret = ($mpesa_consumer_secret) ? $mpesa_consumer_secret->value : null;
|
||||
$mpesa_business_code = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_business_code')
|
||||
->find_one();
|
||||
$mpesa_business_code = ($mpesa_business_code) ? $mpesa_business_code->value : null;
|
||||
$mpesa_shortcode_type = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_shortcode_type')
|
||||
->find_one();
|
||||
if ($mpesa_shortcode_type == 'BuyGoods') {
|
||||
$mpesa_buygoods_till_number = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_buygoods_till_number')
|
||||
->find_one();
|
||||
$mpesa_buygoods_till_number = ($mpesa_buygoods_till_number) ? $mpesa_buygoods_till_number->value : null;
|
||||
$PartyB = $mpesa_buygoods_till_number;
|
||||
$Type_of_Transaction = 'CustomerBuyGoodsOnline';
|
||||
} else {
|
||||
$PartyB = $mpesa_business_code;
|
||||
$Type_of_Transaction = 'CustomerPayBillOnline';
|
||||
}
|
||||
$Passkey = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_pass_key')
|
||||
->find_one();
|
||||
$Passkey = ($Passkey) ? $Passkey->value : null;
|
||||
$Time_Stamp = date("Ymdhis");
|
||||
$password = base64_encode($mpesa_business_code . $Passkey . $Time_Stamp);
|
||||
if ($mpesa_env == "live") {
|
||||
$OnlinePayment = 'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest';
|
||||
$Token_URL = 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
|
||||
} elseif ($mpesa_env == "sandbox") {
|
||||
$OnlinePayment = 'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest';
|
||||
$Token_URL = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
|
||||
} else {
|
||||
return json_encode(["Message" => "invalid application status"]);
|
||||
};
|
||||
$headers = ['Content-Type:application/json; charset=utf8'];
|
||||
$curl = curl_init($Token_URL);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
||||
curl_setopt($curl, CURLOPT_HEADER, FALSE);
|
||||
curl_setopt($curl, CURLOPT_USERPWD, $mpesa_consumer_key . ':' . $mpesa_consumer_secret);
|
||||
$result = curl_exec($curl);
|
||||
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
$result = json_decode($result);
|
||||
$access_token = $result->access_token;
|
||||
curl_close($curl);
|
||||
$password = base64_encode($mpesa_business_code . $Passkey . $Time_Stamp);
|
||||
$stkpushheader = ['Content-Type:application/json', 'Authorization:Bearer ' . $access_token];
|
||||
//INITIATE CURL
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $OnlinePayment);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $stkpushheader); //setting custom header
|
||||
$curl_post_data = array(
|
||||
//Fill in the request parameters with valid values
|
||||
'BusinessShortCode' => $mpesa_business_code,
|
||||
'Password' => $password,
|
||||
'Timestamp' => $Time_Stamp,
|
||||
'TransactionType' => $Type_of_Transaction,
|
||||
'Amount' => $amount,
|
||||
'PartyA' => $phone,
|
||||
'PartyB' => $PartyB,
|
||||
'PhoneNumber' => $phone,
|
||||
'CallBackURL' => $CallBackURL,
|
||||
'AccountReference' => $username,
|
||||
'TransactionDesc' => 'Payment for ' . $username
|
||||
);
|
||||
$data_string = json_encode($curl_post_data);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
|
||||
$curl_response = curl_exec($curl);
|
||||
$curl_Tranfer2_response = json_decode($curl_response);
|
||||
if (isset($curl_Tranfer2_response->ResponseCode) && $curl_Tranfer2_response->ResponseCode == "0") {
|
||||
$resultDesc = $curl_Tranfer2_response->resultDesc;
|
||||
$CheckoutRequestID = $curl_Tranfer2_response->CheckoutRequestID;
|
||||
date_default_timezone_set('Africa/Nairobi');
|
||||
$now = date("Y-m-d H:i:s");
|
||||
// $username=$phone;
|
||||
$PaymentGatewayRecord->pg_paid_response = $resultDesc;
|
||||
$PaymentGatewayRecord->username = $username;
|
||||
$PaymentGatewayRecord->checkout = $CheckoutRequestID;
|
||||
$PaymentGatewayRecord->payment_method = 'Mpesa Stk Push';
|
||||
$PaymentGatewayRecord->payment_channel = 'Mpesa Stk Push';
|
||||
$saveGateway = $PaymentGatewayRecord->save();
|
||||
if ($saveGateway) {
|
||||
if (!empty($_POST['channel'])) {
|
||||
echo json_encode(["status" => "success", "message" => "Enter Mpesa Pin to complete $mpesa_business_code $Type_of_Transaction , Party B: $PartyB, Amount: $amount, Phone: $phone, CheckoutRequestID: $CheckoutRequestID"]);
|
||||
} else {
|
||||
echo "<script>toastr.success('Enter Mpesa Pin to complete');</script>";
|
||||
}
|
||||
} else {
|
||||
echo json_encode(["status" => "error", "message" => "Failed to save the payment gateway record"]);
|
||||
}
|
||||
} else {
|
||||
$errorMessage = $curl_Tranfer2_response->errorMessage;
|
||||
echo json_encode(["status" => "error", "message" => $errorMessage]);
|
||||
}
|
||||
}
|
232
system/plugin/initiatetillstk.php
Normal file
232
system/plugin/initiatetillstk.php
Normal file
@ -0,0 +1,232 @@
|
||||
<?php
|
||||
function initiatetillstk()
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
$username=$_POST['username'];
|
||||
$phone=$_POST['phone'];
|
||||
|
||||
|
||||
|
||||
|
||||
$phone = (substr($phone, 0,1) == '+') ? str_replace('+', '', $phone) : $phone;
|
||||
$phone = (substr($phone, 0,1) == '0') ? preg_replace('/^0/', '254', $phone) : $phone;
|
||||
$phone = (substr($phone, 0,1) == '7') ? preg_replace('/^7/', '2547', $phone) : $phone; //cater for phone number prefix 2547XXXX
|
||||
$phone = (substr($phone, 0,1) == '1') ? preg_replace('/^1/', '2541', $phone) : $phone; //cater for phone number prefix 2541XXXX
|
||||
$phone = (substr($phone, 0,1) == '0') ? preg_replace('/^01/', '2541', $phone) : $phone;
|
||||
$phone = (substr($phone, 0,1) == '0') ? preg_replace('/^07/', '2547', $phone) : $phone;
|
||||
|
||||
|
||||
$consumer_key = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_till_consumer_key')
|
||||
->find_one();
|
||||
|
||||
$consumer_secret = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_till_consumer_secret')
|
||||
->find_one();
|
||||
|
||||
$consumer_secret = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_till_consumer_secret')
|
||||
->find_one();
|
||||
|
||||
$BusinessShortCode= ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_till_shortcode_code')
|
||||
->find_one();
|
||||
|
||||
$PartyB= ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_till_partyb')
|
||||
->find_one();
|
||||
|
||||
|
||||
$LipaNaMpesaPasskey= ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'mpesa_till_pass_key')
|
||||
->find_one();
|
||||
|
||||
|
||||
|
||||
$consumer_key = ($consumer_key) ? $consumer_key->value : null;
|
||||
$consumer_secret = ($consumer_secret) ? $consumer_secret->value : null;
|
||||
$BusinessShortCode = ($BusinessShortCode) ? $BusinessShortCode->value : null;
|
||||
$PartyB = ($PartyB) ? $PartyB->value : null;
|
||||
$LipaNaMpesaPasskey = ($LipaNaMpesaPasskey) ? $LipaNaMpesaPasskey->value : null;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$cburl = U . 'callback/MpesatillStk' ;
|
||||
|
||||
|
||||
//
|
||||
|
||||
$CheckId = ORM::for_table('tbl_customers')
|
||||
->where('username', $username)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
$CheckUser = ORM::for_table('tbl_customers')
|
||||
->where('phonenumber', $phone)
|
||||
->find_many();
|
||||
|
||||
$UserId=$CheckId->id;
|
||||
|
||||
|
||||
|
||||
$PaymentGatewayRecord = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $username)
|
||||
->where('status', 1) // Add this line to filter by status
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
$ThisUser= ORM::for_table('tbl_customers')
|
||||
->where('username', $username)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
|
||||
|
||||
$ThisUser->phonenumber=$phone;
|
||||
// $ThisUser->username=$phone;
|
||||
$ThisUser->save();
|
||||
|
||||
|
||||
$amount=$PaymentGatewayRecord->price;
|
||||
|
||||
if(!$PaymentGatewayRecord){
|
||||
|
||||
echo $error="<script>toastr.success('Unable to proess payment, please reload the page');</script>";
|
||||
die();
|
||||
|
||||
}
|
||||
|
||||
|
||||
$TransactionType = 'CustomerBuyGoodsOnline';
|
||||
$tokenUrl = 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials';
|
||||
$phone= $phone;
|
||||
$lipaOnlineUrl = 'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest';
|
||||
// $amount= '1';
|
||||
$CallBackURL = $cburl;
|
||||
date_default_timezone_set('Africa/Nairobi');
|
||||
$timestamp = date("YmdHis");
|
||||
$password = base64_encode($BusinessShortCode . $LipaNaMpesaPasskey . $timestamp);
|
||||
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $tokenUrl);
|
||||
$credentials = base64_encode($consumer_key . ':' . $consumer_secret);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Authorization: Basic ' . $credentials));
|
||||
curl_setopt($curl, CURLOPT_HEADER, false);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
$curl_response = curl_exec($curl);
|
||||
|
||||
$token = json_decode($curl_response)->access_token;
|
||||
$curl2 = curl_init();
|
||||
curl_setopt($curl2, CURLOPT_URL, $lipaOnlineUrl);
|
||||
curl_setopt($curl2, CURLOPT_HTTPHEADER, array('Content-Type:application/json', 'Authorization:Bearer ' . $token));
|
||||
|
||||
|
||||
|
||||
$curl2_post_data = [
|
||||
'BusinessShortCode' => $BusinessShortCode,
|
||||
'Password' => $password,
|
||||
'Timestamp' => $timestamp,
|
||||
'TransactionType' => $TransactionType,
|
||||
'Amount' => $amount,
|
||||
'PartyA' => $phone,
|
||||
'PartyB' => $PartyB,
|
||||
'PhoneNumber' => $phone,
|
||||
'CallBackURL' => $CallBackURL,
|
||||
'AccountReference' => 'Payment For Goods',
|
||||
'TransactionDesc' => 'Payment for goods',
|
||||
];
|
||||
|
||||
$data2_string = json_encode($curl2_post_data);
|
||||
|
||||
curl_setopt($curl2, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl2, CURLOPT_POST, true);
|
||||
curl_setopt($curl2, CURLOPT_POSTFIELDS, $data2_string);
|
||||
curl_setopt($curl2, CURLOPT_HEADER, false);
|
||||
curl_setopt($curl2, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_setopt($curl2, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
$curl_response = curl_exec($curl2);
|
||||
|
||||
$curl_response1 = curl_exec($curl);
|
||||
//($curl_response);
|
||||
|
||||
//echo $curl_response;
|
||||
|
||||
$mpesaResponse = json_decode($curl_response);
|
||||
|
||||
|
||||
//echo $phone;
|
||||
|
||||
$responseCode = $mpesaResponse->ResponseCode;
|
||||
$MerchantRequestID = $mpesaResponse->MerchantRequestID;
|
||||
$CheckoutRequestID = $mpesaResponse->CheckoutRequestID;
|
||||
$resultDesc = $mpesaResponse->CustomerMessage;
|
||||
// file_put_contents('stk.log',$curl_response,FILE_APPEND);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// echo $cburl;
|
||||
|
||||
$responseCode = $responseCode;
|
||||
if($responseCode=="0"){
|
||||
date_default_timezone_set('Africa/Nairobi');
|
||||
$now=date("Y-m-d H:i:s");
|
||||
|
||||
|
||||
// $username=$phone;
|
||||
|
||||
|
||||
$PaymentGatewayRecord->pg_paid_response = $resultDesc;
|
||||
$PaymentGatewayRecord->checkout = $CheckoutRequestID;
|
||||
$PaymentGatewayRecord->username = $username;
|
||||
$PaymentGatewayRecord->payment_method = 'Mpesa Stk Push';
|
||||
$PaymentGatewayRecord->payment_channel = 'Mpesa Stk Push';
|
||||
$PaymentGatewayRecord->save();
|
||||
|
||||
if(!empty($_POST['channel'])){
|
||||
|
||||
echo json_encode(["status" => "success", "message" => "Enter Pin to complete","phone"=> $phone]);
|
||||
|
||||
}else{
|
||||
echo $error="<script>toastr.success('Enter Mpesa Pin to complete');</script>";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}else{
|
||||
|
||||
echo "There is an issue with the transaction, please wait for 0 seconds then try again";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
46
system/plugin/log.php
Normal file
46
system/plugin/log.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
use PEAR2\Net\RouterOS\Client;
|
||||
use PEAR2\Net\RouterOS\Request;
|
||||
|
||||
// Fungsi untuk menampilkan log monitor
|
||||
register_menu("Router Logs", true, "log_ui", 'NETWORK');
|
||||
|
||||
function log_ui() {
|
||||
global $ui, $routes;
|
||||
_admin();
|
||||
$ui->assign('_title', 'Log Mikrotik');
|
||||
$ui->assign('_system_menu', 'Log Mikrotik');
|
||||
$admin = Admin::_info();
|
||||
$ui->assign('_admin', $admin);
|
||||
$routers = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
|
||||
$routerId = $routes['2'] ?? ($routers ? $routers[0]['id'] : null); // Memastikan ada router yang aktif
|
||||
$logs = fetchLogs($routerId); // Mengambil log dari router yang dipilih
|
||||
$ui->assign('logs', $logs);
|
||||
|
||||
$ui->display('log.tpl');
|
||||
}
|
||||
|
||||
// Fungsi untuk mengambil logs dari MikroTik
|
||||
function fetchLogs($routerId) {
|
||||
if (!$routerId) {
|
||||
return []; // Mengembalikan array kosong jika router tidak tersedia
|
||||
}
|
||||
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
if (!$mikrotik) {
|
||||
return []; // Mengembalikan array kosong jika router tidak ditemukan
|
||||
}
|
||||
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$request = new Request('/log/print');
|
||||
$response = $client->sendSync($request);
|
||||
|
||||
$logs = [];
|
||||
foreach ($response as $entry) {
|
||||
$logs[] = $entry->getIterator()->getArrayCopy(); // Mengumpulkan data dari setiap entry
|
||||
}
|
||||
|
||||
return $logs;
|
||||
}
|
418
system/plugin/pppoe_monitor.php
Normal file
418
system/plugin/pppoe_monitor.php
Normal file
@ -0,0 +1,418 @@
|
||||
<?php
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
|
||||
|
||||
function pppoe()
|
||||
{
|
||||
global $ui, $routes;
|
||||
_admin();
|
||||
$ui->assign('_title', 'PPPoE Monitor');
|
||||
$ui->assign('_system_menu', 'PPPoE Monitor');
|
||||
$admin = Admin::_info();
|
||||
$ui->assign('_admin', $admin);
|
||||
$routers = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
|
||||
$router = $routes['2'] ?? $routers[0]['id'];
|
||||
$ui->assign('routers', $routers);
|
||||
$ui->assign('router', $router);
|
||||
$ui->assign('interfaces', pppoe_monitor_router_getInterface());
|
||||
|
||||
$ui->display('pppoe_monitor.tpl');
|
||||
}
|
||||
|
||||
function pppoe_monitor_router_getInterface()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'] ?? null;
|
||||
|
||||
if (!$routerId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
|
||||
if (!$mikrotik) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$interfaces = $client->sendSync(new RouterOS\Request('/interface/print'));
|
||||
|
||||
$interfaceList = [];
|
||||
foreach ($interfaces as $interface) {
|
||||
$name = $interface->getProperty('name');
|
||||
$interfaceList[] = $name; // Jangan menghapus karakter < dan > dari nama interface
|
||||
}
|
||||
|
||||
return $interfaceList;
|
||||
}
|
||||
|
||||
function pppoe_get_combined_users() {
|
||||
global $routes;
|
||||
$router = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
|
||||
if (!$mikrotik) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['error' => 'Router not found']);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
// Fetch PPP online users
|
||||
$pppUsers = $client->sendSync(new RouterOS\Request('/ppp/active/print'));
|
||||
$interfaceTraffic = $client->sendSync(new RouterOS\Request('/interface/print'));
|
||||
$pppSecrets = $client->sendSync(new RouterOS\Request('/ppp/secret/print'));
|
||||
|
||||
|
||||
$interfaceData = [];
|
||||
foreach ($interfaceTraffic as $interface) {
|
||||
$name = $interface->getProperty('name');
|
||||
if (empty($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$interfaceData[$name] = [
|
||||
'status' => $interface->getProperty('running') === 'true' ? 'Connected' : 'Disconnected',
|
||||
'txBytes' => intval($interface->getProperty('tx-byte')),
|
||||
'rxBytes' => intval($interface->getProperty('rx-byte')),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$pppUserList = [];
|
||||
foreach ($pppUsers as $pppUser) {
|
||||
$username = $pppUser->getProperty('name');
|
||||
if (empty($username)) {
|
||||
continue;
|
||||
}
|
||||
$address = $pppUser->getProperty('address');
|
||||
$uptime = $pppUser->getProperty('uptime');
|
||||
$service = $pppUser->getProperty('service');
|
||||
$callerid = $pppUser->getProperty('caller-id');
|
||||
$bytes_in = $pppUser->getProperty('limit-bytes-in');
|
||||
$bytes_out = $pppUser->getProperty('limit-bytes-out');
|
||||
$id = $pppUser->getProperty('.id');
|
||||
|
||||
$interfaceName = "<pppoe-$username>";
|
||||
|
||||
if (isset($interfaceData[$interfaceName])) {
|
||||
$trafficData = $interfaceData[$interfaceName];
|
||||
$txBytes = $trafficData['txBytes'];
|
||||
$rxBytes = $trafficData['rxBytes'];
|
||||
$status = $trafficData['status'];
|
||||
} else {
|
||||
$txBytes = 0;
|
||||
$rxBytes = 0;
|
||||
$status = 'Disconnected';
|
||||
}
|
||||
|
||||
// Get device information
|
||||
$manufacturer = "Unknown";
|
||||
if ($callerid) {
|
||||
$manufacturer = get_manufacturer_from_mac($callerid);
|
||||
}
|
||||
|
||||
// Check if MAC is bound in ppp secrets
|
||||
$isBound = false;
|
||||
foreach ($pppSecrets as $secret) {
|
||||
if ($secret->getProperty('name') === $username && $secret->getProperty('caller-id') === $callerid) {
|
||||
$isBound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$pppUserList[$username] = [
|
||||
'id' => $id,
|
||||
'username' => $username,
|
||||
'address' => $address,
|
||||
'uptime' => $uptime,
|
||||
'service' => $service,
|
||||
'caller_id' => $callerid,
|
||||
'bytes_in' => $bytes_in,
|
||||
'bytes_out' => $bytes_out,
|
||||
'tx' => pppoe_monitor_router_formatBytes($txBytes),
|
||||
'rx' => pppoe_monitor_router_formatBytes($rxBytes),
|
||||
'total' => pppoe_monitor_router_formatBytes($txBytes + $rxBytes),
|
||||
'status' => $status,
|
||||
'manufacturer' => $manufacturer,
|
||||
'is_bound' => $isBound
|
||||
];
|
||||
}
|
||||
|
||||
// Convert the user list to a regular array for JSON encoding
|
||||
$userList = array_values($pppUserList);
|
||||
|
||||
// Return the combined user list as JSON
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($userList);
|
||||
} catch (Exception $e) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
function get_manufacturer_from_mac($mac_address) {
|
||||
// Normalize the MAC address
|
||||
$mac_address = strtoupper(preg_replace('/[^A-F0-9]/', '', $mac_address));
|
||||
|
||||
// Check if MAC address is valid (at least 6 hex characters required)
|
||||
if (strlen($mac_address) < 6) {
|
||||
return 'Invalid MAC address';
|
||||
}
|
||||
|
||||
// Construct the API URL
|
||||
$url = "https://www.macvendorlookup.com/api/v2/{$mac_address}";
|
||||
|
||||
// Initialize cURL session
|
||||
$ch = curl_init();
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // For testing purposes, handle SSL properly in production
|
||||
|
||||
// Execute cURL request
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// Check for cURL errors
|
||||
if (curl_errno($ch)) {
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
return "Error: $error";
|
||||
}
|
||||
|
||||
// Get HTTP response code
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
// Check if API returned a valid response
|
||||
if ($http_code === 204) {
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
// Decode JSON response
|
||||
$data = json_decode($response, true);
|
||||
|
||||
// Check if the response contains manufacturer information
|
||||
if (isset($data[0]['company'])) {
|
||||
return trim($data[0]['company']);
|
||||
} else {
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
function pppoe_monitor_router_formatMaxLimit($max_limit) {
|
||||
$limits = explode('/', $max_limit);
|
||||
if (count($limits) == 2) {
|
||||
$downloadLimit = intval($limits[0]);
|
||||
$uploadLimit = intval($limits[1]);
|
||||
$formattedDownloadLimit = ceil($downloadLimit / (1024 * 1024)) . ' MB';
|
||||
$formattedUploadLimit = ceil($uploadLimit / (1024 * 1024)) . ' MB';
|
||||
return $formattedDownloadLimit . '/' . $formattedUploadLimit;
|
||||
}
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
// Fungsi untuk menghitung total data yang digunakan per harinya
|
||||
|
||||
function pppoe_monitor_router_formatBytes($bytes, $precision = 2)
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
$bytes /= pow(1024, $pow);
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
function pppoe_monitor_router_traffic()
|
||||
{
|
||||
$interface = $_GET["interface"]; // Ambil interface dari parameter GET
|
||||
|
||||
// Contoh koneksi ke MikroTik menggunakan library tertentu (misalnya menggunakan ORM dan MikroTik API wrapper)
|
||||
global $routes;
|
||||
$router = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
try {
|
||||
$results = $client->sendSync(
|
||||
(new RouterOS\Request('/interface/monitor-traffic'))
|
||||
->setArgument('interface', $interface)
|
||||
->setArgument('once', '')
|
||||
);
|
||||
|
||||
$rows = array();
|
||||
$rows2 = array();
|
||||
$labels = array();
|
||||
|
||||
foreach ($results as $result) {
|
||||
$ftx = $result->getProperty('tx-bits-per-second');
|
||||
$frx = $result->getProperty('rx-bits-per-second');
|
||||
|
||||
// Timestamp dalam milidetik (millisecond)
|
||||
$timestamp = time() * 1000;
|
||||
|
||||
$rows[] = $ftx;
|
||||
$rows2[] = $frx;
|
||||
$labels[] = $timestamp; // Tambahkan timestamp ke dalam array labels
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'labels' => $labels,
|
||||
'rows' => array(
|
||||
'tx' => $rows,
|
||||
'rx' => $rows2
|
||||
)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$result = array('error' => $e->getMessage());
|
||||
}
|
||||
|
||||
// Set header untuk respons JSON
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
}
|
||||
|
||||
function pppoe_monitor_router_online()
|
||||
{
|
||||
global $routes;
|
||||
$router = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$pppUsers = $client->sendSync(new RouterOS\Request('/ppp/active/print'));
|
||||
|
||||
$pppoeInterfaces = [];
|
||||
|
||||
foreach ($pppUsers as $pppUser) {
|
||||
$username = $pppUser->getProperty('name');
|
||||
$interfaceName = "<pppoe-$username>"; // Tambahkan karakter < dan >
|
||||
|
||||
// Ensure interface name is not empty and it's not already in the list
|
||||
if (!empty($interfaceName) && !in_array($interfaceName, $pppoeInterfaces)) {
|
||||
$pppoeInterfaces[] = $interfaceName;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the list of PPPoE interfaces
|
||||
return $pppoeInterfaces;
|
||||
}
|
||||
|
||||
function pppoe_monitor_router_delete_ppp_user()
|
||||
{
|
||||
global $routes;
|
||||
$router = $routes['2'];
|
||||
$id = $_POST['id']; // Ambil .id dari POST data
|
||||
|
||||
// Cek apakah ID ada di POST data
|
||||
if (empty($id)) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => 'ID is missing.']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ambil detail router dari database
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
|
||||
if (!$mikrotik) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => 'Router not found.']);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dapatkan klien MikroTik
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
if (!$client) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => 'Failed to connect to the router.']);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Buat permintaan untuk menghapus koneksi aktif PPPoE
|
||||
$request = new RouterOS\Request('/ppp/active/remove');
|
||||
$request->setArgument('.id', $id); // Gunakan .id yang sesuai
|
||||
$client->sendSync($request);
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => true, 'message' => 'PPPoE user successfully deleted.']);
|
||||
} catch (Exception $e) {
|
||||
// Log error untuk debugging
|
||||
error_log('Failed to delete PPPoE user: ' . $e->getMessage());
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => 'Failed to delete PPPoE user: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// NEW FUNCTIONS:
|
||||
|
||||
// Fungsi untuk menghitung total data yang digunakan per harinya
|
||||
function pppoe_monitor_router_daily_data_usage()
|
||||
{
|
||||
global $routes;
|
||||
$router = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
// Ambil semua pengguna aktif PPPoE
|
||||
$pppUsers = $client->sendSync(new RouterOS\Request('/ppp/active/print'));
|
||||
$interfaceTraffic = $client->sendSync(new RouterOS\Request('/interface/print'));
|
||||
|
||||
// Array untuk menyimpan data penggunaan harian
|
||||
$daily_usage = [];
|
||||
|
||||
// Looping untuk setiap pengguna PPPoE
|
||||
foreach ($pppUsers as $pppUser) {
|
||||
$username = $pppUser->getProperty('name');
|
||||
$interfaceName = "<pppoe-$username>"; // Nama interface sesuai format PPPoE
|
||||
|
||||
// Ambil data traffic untuk interface ini
|
||||
$interfaceData = [];
|
||||
foreach ($interfaceTraffic as $interface) {
|
||||
$name = $interface->getProperty('name');
|
||||
if ($name === $interfaceName) {
|
||||
$interfaceData = [
|
||||
'txBytes' => intval($interface->getProperty('tx-byte')),
|
||||
'rxBytes' => intval($interface->getProperty('rx-byte'))
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Hitung total penggunaan harian
|
||||
$txBytes = $interfaceData['txBytes'] ?? 0;
|
||||
$rxBytes = $interfaceData['rxBytes'] ?? 0;
|
||||
$totalDataMB = ($txBytes + $rxBytes) / (1024 * 1024); // Konversi ke MB
|
||||
|
||||
// Ambil tanggal dari waktu saat ini
|
||||
$date = date('Y-m-d', time());
|
||||
|
||||
// Jika belum ada data untuk tanggal ini, inisialisasi
|
||||
if (!isset($daily_usage[$date])) {
|
||||
$daily_usage[$date] = [
|
||||
'total' => 0,
|
||||
'users' => []
|
||||
];
|
||||
}
|
||||
|
||||
// Tambahkan penggunaan harian untuk pengguna ini
|
||||
$daily_usage[$date]['total'] += $totalDataMB;
|
||||
$daily_usage[$date]['users'][] = [
|
||||
'username' => $username,
|
||||
'tx' => pppoe_monitor_router_formatBytes($txBytes),
|
||||
'rx' => pppoe_monitor_router_formatBytes($rxBytes),
|
||||
'total' => pppoe_monitor_router_formatBytes($txBytes + $rxBytes)
|
||||
];
|
||||
}
|
||||
|
||||
// Kembalikan hasil dalam format JSON
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($daily_usage); // $daily_usage adalah array yang berisi data harian dalam format yang sesuai
|
||||
}
|
||||
// Fungsi untuk mendapatkan pengguna terbatas pada MikroTik
|
BIN
system/plugin/ui/.DS_Store
vendored
Normal file
BIN
system/plugin/ui/.DS_Store
vendored
Normal file
Binary file not shown.
162
system/plugin/ui/c2b_overview.tpl
Normal file
162
system/plugin/ui/c2b_overview.tpl
Normal file
@ -0,0 +1,162 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<style>
|
||||
/* Styles for overall layout and responsiveness */
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
font-family: 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-top: 20px;
|
||||
background-color: #d8dfe5;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
max-width: 98%;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Styles for table and pagination */
|
||||
.table {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.table th {
|
||||
vertical-align: middle;
|
||||
border-color: #dee2e6;
|
||||
background-color: #343a40;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.table td {
|
||||
vertical-align: middle;
|
||||
border-color: #dee2e6;
|
||||
}
|
||||
|
||||
.table-striped tbody tr:nth-of-type(odd) {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.table-hover tbody tr:hover {
|
||||
background-color: rgba(0, 0, 0, 0.075);
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
.pagination .page-item .page-link {
|
||||
color: #007bff;
|
||||
background-color: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
margin: 0 2px;
|
||||
padding: 6px 12px;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
.pagination .page-item .page-link:hover {
|
||||
background-color: #e9ecef;
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.pagination .page-item.active .page-link {
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
margin-right: 5px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
{if isset($message)}
|
||||
<div class="alert alert-{if $notify_t == 's'}success{else}danger{/if}">
|
||||
<button type="button" class="close" data-dismiss="alert">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<div>{$message}</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="col-md-14">
|
||||
<!-- LINE CHART -->
|
||||
<div class="box box-info">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{Lang::T('Payment History')}</h3>
|
||||
|
||||
<div class="box-tools pull-right">
|
||||
<button type="button" class="btn bg-teal btn-sm" data-widget="collapse"><i class="fa fa-refresh"></i>
|
||||
</button>
|
||||
<a href="{$app_url}/pages/mpesa-webhook.html" class="btn bg-teal btn-sm"><i class="fa fa-file"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="container">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped" id="payments-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Customer Name')}</th>
|
||||
<th>{Lang::T('Transaction Type')}</th>
|
||||
<th>{Lang::T('Transaction Time')}</th>
|
||||
<th>{Lang::T('Amount Paid')}</th>
|
||||
<th>{Lang::T('Package Name')}</th>
|
||||
<th>{Lang::T('Package Price')}</th>
|
||||
<th>{Lang::T('Status')}</th>
|
||||
<th>{Lang::T('Bill Ref Number')}</th>
|
||||
<th>{Lang::T('Company Balance')}</th>
|
||||
<th>{Lang::T('Date')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $payments as $payment}
|
||||
<tr>
|
||||
<td><a href="{$app_url}/index.php?_route=customers/view/{$payment.CustomerID}">{$payment.FirstName}</a></td>
|
||||
<td>{$payment.TransactionType}</td>
|
||||
<td>{$payment.TransTime}</td>
|
||||
<td>{$payment.TransAmount}</td>
|
||||
<td>{$payment.PackageName}</td>
|
||||
<td>{$payment.PackagePrice}</td>
|
||||
<td><span
|
||||
class="label {if $payment.TransactionStatus == Completed}label-success {elseif $payment.TransactionStatus == Pending}label-warning {/if}">{$payment.TransactionStatus}</span>
|
||||
</td>
|
||||
<td>{$payment.BillRefNumber}</td>
|
||||
<td>{$payment.OrgAccountBalance}</td>
|
||||
<td>{$payment.CreatedAt}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<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>
|
||||
var $j = jQuery.noConflict();
|
||||
|
||||
$j(document).ready(function () {
|
||||
$j('#payments-table').DataTable({
|
||||
"pagingType": "full_numbers"
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
234
system/plugin/ui/c2b_settings.tpl
Normal file
234
system/plugin/ui/c2b_settings.tpl
Normal file
@ -0,0 +1,234 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||
<style>
|
||||
.styled-form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.styled-btn {
|
||||
color: #28a745;
|
||||
border: 1px solid #28a745;
|
||||
background-color: #fff;
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.styled-btn:hover {
|
||||
background-color: #28a745;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.styled-small-text {
|
||||
color: blue;
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* Hidden checkbox */
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* Slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked+.slider {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:focus+.slider {
|
||||
box-shadow: 0 0 1px #2196F3;
|
||||
}
|
||||
|
||||
input:checked+.slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
</style>
|
||||
|
||||
{if isset($message)}
|
||||
<div class="alert alert-{if $notify_t == 's'}success{else}danger{/if}">
|
||||
<button type="button" class="close" data-dismiss="alert">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<div>{$message}</div>
|
||||
</div>
|
||||
{/if}
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}plugin/c2b_settings">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="panel-heading">{Lang::T('M-Pesa C2B Payment Gateway')}</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group col-6">
|
||||
<label class="col-md-3 control-label">{Lang::T('M-Pesa C2B Environment')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" name="mpesa_c2b_env" id="mpesa_c2b_env">
|
||||
<option value="sandbox" {if $_c['mpesa_c2b_env']=='sandbox' }selected{/if}>
|
||||
{Lang::T('SandBox or
|
||||
Testing')}</option>
|
||||
<option value="live" {if $_c['mpesa_c2b_env']=='live' }selected{/if}>{Lang::T('Live or
|
||||
Production')}
|
||||
</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">
|
||||
<font color="red"><b>{Lang::T('Sandbox')}</b></font> {Lang::T('is for testing purpose,
|
||||
please switch to')} <font color="green"><b>{Lang::T('Live')}</b></font> {Lang::T('in
|
||||
production.')}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-6">
|
||||
<label class="col-md-3 control-label">M-Pesa C2B Consumer Key</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="mpesa_c2b_consumer_key"
|
||||
name="mpesa_c2b_consumer_key" placeholder="xxxxxxxxxxxxxxxxx"
|
||||
value="{$_c['mpesa_c2b_consumer_key']}">
|
||||
<small class="form-text text-muted"><a href="https://developer.safaricom.co.ke/MyApps"
|
||||
target="_blank">https://developer.safaricom.co.ke/MyApps</a></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-6">
|
||||
<label class="col-md-3 control-label">M-Pesa C2B Consumer Secret</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="mpesa_c2b_consumer_secret"
|
||||
name="mpesa_c2b_consumer_secret" placeholder="xxxxxxxxxxxxxxxxx"
|
||||
value="{$_c['mpesa_c2b_consumer_secret']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label class="col-md-3 control-label">M-Pesa C2B Business Shortcode</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="mpesa_c2b_business_code"
|
||||
name="mpesa_c2b_business_code" placeholder="xxxxxxx" maxlength="7"
|
||||
value="{$_c['mpesa_c2b_business_code']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label class="col-md-3 control-label">M-Pesa C2B Version</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" name="mpesa_c2b_api">
|
||||
<option value="v1" {if $_c['mpesa_c2b_api']=='v1' }selected{/if}>v1</option>
|
||||
<option value="v2" {if $_c['mpesa_c2b_api']=='v2' }selected{/if}>v2</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Select the version of the API you want to
|
||||
use.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label class="col-md-3 control-label">Bill Ref Number Type</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" name="mpesa_c2b_bill_ref">
|
||||
<option value="phone" {if $_c['mpesa_c2b_bill_ref']=='phone' }selected{/if}>Phone Number</option>
|
||||
<option value="username" {if $_c['mpesa_c2b_bill_ref']=='username' }selected{/if}>Username</option>
|
||||
<option value="id" {if $_c['mpesa_c2b_bill_ref']=='id' }selected{/if}>Account ID</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">How will the system identify your customers. BillRefNumber must be a unique identity</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label class="col-md-3 control-label">{Lang::T('Accept Insufficient Fee')}</label>
|
||||
<div class="col-md-6">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="mpesa_c2b_low_fee" value="1"
|
||||
name="mpesa_c2b_low_fee" {if $_c['mpesa_c2b_low_fee']==1}checked{/if}>
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{if $_c['c2b_registered'] && $_c['mpesa_c2b_env']!='sandbox'}
|
||||
<div class="form-group col-12 styled-form-group">
|
||||
<label class="col-md-3 control-label">Register C2B URL</label>
|
||||
<div class="col-md-6">
|
||||
<button class="btn styled-btn">URLs Already Registered</button>
|
||||
</div>
|
||||
</div>
|
||||
{else}
|
||||
<div class="form-group col-12 styled-form-group">
|
||||
<label class="col-md-3 control-label">Register C2B URL</label>
|
||||
<div class="col-md-6">
|
||||
<a href="{$_url}plugin/c2b_registerUrl" class="btn styled-btn">Click to Register Mpesa
|
||||
C2B URL</a>
|
||||
<small class="form-text text-muted styled-small-text">Click only after you have saved
|
||||
the changes.</small>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="form-group col-6">
|
||||
<div class="col-lg-offset-3 col-lg-10">
|
||||
<button class="btn btn-primary waves-effect waves-light" name="save" value="save"
|
||||
type="submit">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bs-callout bs-callout-info" id="callout-navbar-role">
|
||||
<h4><b>Accept Insufficient Fee</b>:</h4>
|
||||
<p> If Enable the money customer sent will be convert to customer balance, but if disabled the system will reject it. <br> It requires Validation URL</p>
|
||||
<h4><b>Note</b>:</h4>
|
||||
<p> Before click on Register URL <br>
|
||||
Make sure you have fill the required fields <br>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
toggleTillNumberInput();
|
||||
$('#mpesa_c2b_transaction').on('change', function () {
|
||||
toggleTillNumberInput();
|
||||
});
|
||||
function toggleTillNumberInput() {
|
||||
if ($('#mpesa_c2b_transaction').val() === 'BuyGoods') {
|
||||
$('#tillNumberContainer').show();
|
||||
} else {
|
||||
$('#tillNumberContainer').hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{include file="sections/footer.tpl"}
|
193
system/plugin/ui/captive_portal_settings.tpl
Normal file
193
system/plugin/ui/captive_portal_settings.tpl
Normal file
@ -0,0 +1,193 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-success">
|
||||
Captive Portal Settings
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-success dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="{$_url}plugin/captive_portal_settings">{Lang::T('General Settings')}</a></li>
|
||||
<li>
|
||||
<a href="{$_url}plugin/captive_portal_slider"
|
||||
>{Lang::T('Manage Sliders')}</a
|
||||
>
|
||||
</li>
|
||||
<li><a href="#">{Lang::T('Manage Advertisements')}</a></li>
|
||||
<li><a href="#">{Lang::T('Manage Authorizations')}</a></li>
|
||||
<li><a href="#">{Lang::T('Reports')}</a></li>
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a
|
||||
href="{$_url}plugin/captive_portal_login"
|
||||
target="”_blank”"
|
||||
>Preview Member Landing Page</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="{$_url}plugin/captive_portal_download_login"
|
||||
target="”_blank”"
|
||||
> Download Login Page </a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li>
|
||||
<a href="{$_url}plugin/captive_portal_overview"><i class="fa fa-dashboard"></i> Captive Portal</a>
|
||||
</li>
|
||||
<li class="active"> General Settings</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="content">
|
||||
<div class="table-responsive">
|
||||
<div class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a href="#tab_1" data-toggle="tab">{Lang::T('General Settings')}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab_2" data-toggle="tab">{Lang::T('Customization')}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab_3" data-toggle="tab">{Lang::T('Slider Settings')}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab_4" data-toggle="tab">{Lang::T('Advertisement Settings')}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab_5" data-toggle="tab">{Lang::T('Trial Authorization Settings')}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab_6" data-toggle="tab">{Lang::T('Pages Settings')}</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div style="overflow-x:auto;" class="tab-pane active" id="tab_1">
|
||||
<div class="box-body no-padding" id="">
|
||||
<form method="POST" action="" enctype="multipart/form-data">
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<label for="">Hotspot Page Title</label>
|
||||
<input type="text" class="form-control" name="title" id="title" value="{$settings.hotspot_title}" required>
|
||||
<small class="form-text text-muted">Hotspot Title will be display on Login Page Head Tag</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="">Hotspot Name</label>
|
||||
<input type="text" class="form-control" name="name" id="name" value="{$settings.hotspot_name}" required>
|
||||
<small class="form-text text-muted">Hotspot Name will be display on Login Page Nav Bar if Logo is not available</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="favicon">Favicon</label>
|
||||
<input type="file" class="form-control" name="favicon" id="favicon" accept="image/x-icon, image/png, image/jpeg, image/gif" onchange="previewImage('favicon', 'favicon-preview')">
|
||||
<small class="form-text text-muted">Favicon will be display on Login Page browser tab, its placed in head section</small>
|
||||
<br>
|
||||
<img id="favicon-preview" src="{$settings.favicon}" alt="Favicon Preview" style="max-width: 32px; max-height: 32px;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="logo">Logo</label>
|
||||
<input type="file" class="form-control" name="logo" id="logo" accept="image/png, image/jpeg, image/svg+xml" onchange="previewImage('logo', 'logo-preview')">
|
||||
<small class="form-text text-muted">Logo will be display on Login Page Nav Bar section</small>
|
||||
<br>
|
||||
<img id="logo-preview" src="{$settings.logo}" alt="Logo Preview" style="max-width: 200px; max-height: 200px;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="">{Lang::T('Allow Free Trial')}</label>
|
||||
<div class="form-group">
|
||||
<select name="trial" id="trial" class="form-control">
|
||||
<option value="no" {if {$settings.hotspot_trial}=='no' }selected="selected" {/if}>No
|
||||
</option>
|
||||
<option value="yes" {if {$settings.hotspot_trial}=='yes' }selected="selected" {/if}>Yes
|
||||
</option>
|
||||
</select>
|
||||
<small class="form-text text-muted"><ul>
|
||||
<li>Choose No if you dont want to allow Free Trial </li>
|
||||
<li>Make sure you enable free trial in Mikrotik Router</li>
|
||||
<li>free trial button won't display
|
||||
on captive portal preview, but will work if you connect from hotspot</li>
|
||||
</ul></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="">{Lang::T('Allow Member Login')}</label>
|
||||
<div class="form-group">
|
||||
<select name="member" id="member" class="form-control">
|
||||
<option value="no" {if {$settings.hotspot_member}=='no' }selected="selected" {/if}>No
|
||||
</option>
|
||||
<option value="yes" {if {$settings.hotspot_member}=='yes' }selected="selected" {/if}>Yes
|
||||
</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Choose No If you want to disable Member Login</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<a href="{$_url}plugin/captive_portal_overview" class="btn btn-default">Cancel</a>
|
||||
<button type="submit" class="btn btn-info pull-right">Save Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- /.tab-pane -->
|
||||
<div class="tab-pane" style="overflow-x:auto;" id="tab_2">
|
||||
<div class="box-body no-padding" id="">
|
||||
This feature will be available on Pro Version
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- /.tab-pane -->
|
||||
<div style="overflow-x:auto;" class="tab-pane" id="tab_3">
|
||||
<div class="box-body no-padding" id="">
|
||||
This feature will be available on Pro Version
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow-x:auto;" class="tab-pane" id="tab_4">
|
||||
<div class="box-body no-padding" id="">
|
||||
This feature will be available on Pro Version
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow-x:auto;" class="tab-pane" id="tab_5">
|
||||
<div class="box-body no-padding" id="">
|
||||
This feature will be available on Pro Version
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow-x:auto;" class="tab-pane" id="tab_6">
|
||||
<div class="box-body no-padding" id="">
|
||||
This feature will be available on Pro Version
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<pre><b>USAGE:</b>
|
||||
|
||||
<br>Upload your sliders in Slider Setting
|
||||
<br>Go General Settings and setup as per your requirements
|
||||
<br>Then download your the login.html by clicking on download login page
|
||||
<br>Then upload the downloaded login.html file to your mikrotik router
|
||||
<br>Make sure you add your webiste URL in mikrotik hotspot wall garden
|
||||
<br>If your website is https i will suggest you to add certificate to your router
|
||||
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
var portalLink = "https://github.com/focuslinkstech";
|
||||
$('#version').html('Captive Portal Plugin by: <a href="' + portalLink + '">Focuslinks Tech</a>');
|
||||
});
|
||||
</script>
|
||||
{include file="sections/footer.tpl"}
|
559
system/plugin/ui/download.php
Normal file
559
system/plugin/ui/download.php
Normal file
@ -0,0 +1,559 @@
|
||||
<?php
|
||||
include '../../config.php';
|
||||
$mysqli = new mysqli($db_host, $db_user, $db_password, $db_name);
|
||||
|
||||
if ($mysqli->connect_error) {
|
||||
die("Connection failed: " . $mysqli->connect_error);
|
||||
}
|
||||
|
||||
// Function to get a setting value
|
||||
function getSettingValue($mysqli, $setting) {
|
||||
$query = $mysqli->prepare("SELECT value FROM tbl_appconfig WHERE setting = ?");
|
||||
$query->bind_param("s", $setting);
|
||||
$query->execute();
|
||||
$result = $query->get_result();
|
||||
if ($row = $result->fetch_assoc()) {
|
||||
return $row['value'];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Fetch hotspot title and description from tbl_appconfig
|
||||
$hotspotTitle = getSettingValue($mysqli, 'hotspot_title');
|
||||
$description = getSettingValue($mysqli, 'description');
|
||||
$phone = getSettingValue($mysqli, 'phone');
|
||||
$company = getSettingValue($mysqli, 'CompanyName');
|
||||
|
||||
// Fetch settings
|
||||
$settings = [];
|
||||
$settings['frequently_asked_questions_headline1'] = getSettingValue($mysqli, 'frequently_asked_questions_headline1');
|
||||
$settings['frequently_asked_questions_answer1'] = getSettingValue($mysqli, 'frequently_asked_questions_answer1');
|
||||
$settings['frequently_asked_questions_headline2'] = getSettingValue($mysqli, 'frequently_asked_questions_headline2');
|
||||
$settings['frequently_asked_questions_answer2'] = getSettingValue($mysqli, 'frequently_asked_questions_answer2');
|
||||
$settings['frequently_asked_questions_headline3'] = getSettingValue($mysqli, 'frequently_asked_questions_headline3');
|
||||
$settings['frequently_asked_questions_answer3'] = getSettingValue($mysqli, 'frequently_asked_questions_answer3');
|
||||
|
||||
// Fetch router name and router ID from tbl_appconfig
|
||||
$routerName = getSettingValue($mysqli, 'router_name');
|
||||
$routerId = getSettingValue($mysqli, 'router_id');
|
||||
|
||||
// Fetch available plans
|
||||
$planQuery = "SELECT id, name_plan, price, validity, validity_unit FROM tbl_plans WHERE routers = ? AND type = 'Hotspot'";
|
||||
$planStmt = $mysqli->prepare($planQuery);
|
||||
$planStmt->bind_param("s", $routerName);
|
||||
$planStmt->execute();
|
||||
$planResult = $planStmt->get_result();
|
||||
|
||||
// Initialize HTML content variable
|
||||
$htmlContent = "<!DOCTYPE html>\n";
|
||||
$htmlContent .= "<html lang=\"en\">\n";
|
||||
$htmlContent .= "<head>\n";
|
||||
$htmlContent .= " <meta charset=\"UTF-8\">\n";
|
||||
$htmlContent .= " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n";
|
||||
$htmlContent .= " <title>" . htmlspecialchars($hotspotTitle) . " Hotspot Template - Index</title>\n";
|
||||
$htmlContent .= " <script src=\"https://cdn.tailwindcss.com\"></script>\n";
|
||||
$htmlContent .= " <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css\">\n";
|
||||
$htmlContent .= " <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/glider-js@1.7.7/glider.min.css\" />\n";
|
||||
$htmlContent .= " <script src=\"https://cdn.jsdelivr.net/npm/glider-js@1.7.7/glider.min.js\"></script>\n";
|
||||
$htmlContent .= " <link rel=\"preconnect\" href=\"https://cdn.jsdelivr.net\">\n";
|
||||
$htmlContent .= " <link rel=\"preconnect\" href=\"https://cdnjs.cloudflare.com\" crossorigin>\n";
|
||||
$htmlContent .= " <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\">\n";
|
||||
$htmlContent .= "</head>\n";
|
||||
|
||||
|
||||
$htmlContent .= "<body class=\"font-sans antialiased text-gray-900\">\n";
|
||||
$htmlContent .= " <!-- Sticky Header -->\n";
|
||||
$htmlContent .= " <header class=\"bg-pink-600 text-white fixed w-full z-10\">\n";
|
||||
$htmlContent .= " <div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-5\">\n";
|
||||
$htmlContent .= " <div class=\"flex items-center justify-between h-16\">\n";
|
||||
$htmlContent .= " <!-- Logo and title area -->\n";
|
||||
$htmlContent .= " <div class=\"flex items-center\">\n";
|
||||
$htmlContent .= " <img src=\"logo.png\" alt=\"Your Company Logo\" class=\"h-8 w-8 mr-2\">\n";
|
||||
$htmlContent .= " <h1 class=\"text-xl font-bold\">" . htmlspecialchars($hotspotTitle) . " Hotspot Login Page</h1>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <!-- Navigation Links -->\n";
|
||||
$htmlContent .= " <div class=\"block\">\n";
|
||||
$htmlContent .= " <div class=\"ml-10 flex items-baseline space-x-4\">\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"text-teal-200 hover:text-white px-3 py-2 rounded-md text-sm font-medium\">Already Have an Account? Login</a>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </header>\n";
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " <!-- Main content -->\n";
|
||||
$htmlContent .= " <main class=\"pt-24\">\n";
|
||||
$htmlContent .= " <section class=\"bg-white\">\n";
|
||||
$htmlContent .= " <div class=\"max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8\">\n";
|
||||
$htmlContent .= " <h2 class=\"text-3xl font-extrabold text-gray-900 mb-6\">" . htmlspecialchars($description) . "</h2>\n";
|
||||
$htmlContent .= " <!-- Pricing Section -->\n";
|
||||
$htmlContent .= " <div class=\"mt-10\">\n";
|
||||
$htmlContent .= " <div class=\"text-center\">\n";
|
||||
$htmlContent .= " <h3 class=\"text-2xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-3xl sm:leading-9\">\n";
|
||||
$htmlContent .= " CHECK OUR PRICING\n";
|
||||
$htmlContent .= " </h3>\n";
|
||||
$htmlContent .= " <p class=\"mt-4 max-w-2xl text-xl leading-7 text-gray-500 lg:mx-auto\">\n";
|
||||
$htmlContent .= " Choose the plan that fits your needs.\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </section>\n";
|
||||
$htmlContent .= " </main>\n";
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<div class=\"mt-10 max-w-7xl mx-auto grid grid-cols-2 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-5\">\n";
|
||||
|
||||
while ($plan = $planResult->fetch_assoc()) {
|
||||
$htmlContent .= " <div class=\"flex flex-col rounded-lg shadow-xl overflow-hidden transform transition duration-500 hover:scale-105\">\n";
|
||||
$htmlContent .= " <div class=\"px-4 py-5 bg-gradient-to-tr from-pink-50 to-pink-200 text-center\">\n";
|
||||
$htmlContent .= " <span class=\"inline-flex px-3 py-1 rounded-full text-xs font-semibold tracking-wide uppercase bg-pink-800 text-pink-50\">\n";
|
||||
$htmlContent .= htmlspecialchars($plan['name_plan']) . "\n";
|
||||
$htmlContent .= " </span>\n";
|
||||
$htmlContent .= " <div class=\"mt-4 text-4xl leading-none font-extrabold text-pink-800\">\n";
|
||||
$htmlContent .= " <span class=\"text-lg font-medium text-pink-600\">ksh</span>\n";
|
||||
$htmlContent .= htmlspecialchars($plan['price']) . "\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <p class=\"mt-2 text-md leading-5 text-pink-700 text-center\">\n";
|
||||
$htmlContent .= htmlspecialchars($plan['validity']) . " " . htmlspecialchars($plan['validity_unit']) . " Unlimited\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"px-4 pt-4 pb-6 bg-pink-500 text-center\">\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"inline-block text-pink-800 bg-pink-50 hover:bg-pink-100 focus:outline-none focus:ring-4 focus:ring-pink-500 focus:ring-opacity-50 transform transition duration-150 ease-in-out rounded-lg font-semibold px-3 py-2 text-xs shadow-lg cursor-pointer\"\n";
|
||||
$htmlContent .= " onclick=\"handlePhoneNumberSubmission(this.getAttribute('data-plan-id'), this.getAttribute('data-router-id')); return false;\" data-plan-id=\"" . $plan['id'] . "\" data-router-id=\"" . $routerId . "\">\n";
|
||||
$htmlContent .= " Click Here To Connect\n";
|
||||
$htmlContent .= " </a>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
}
|
||||
|
||||
$htmlContent .= "</div>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<!-- Testimonials Section -->\n";
|
||||
$htmlContent .= "<div class=\"mt-10 mx-auto px-4 sm:px-6 lg:px-8\">\n";
|
||||
$htmlContent .= " <h3 class=\"text-center text-2xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-3xl sm:leading-9\">\n";
|
||||
$htmlContent .= " What Our Users Say\n";
|
||||
$htmlContent .= " </h3>\n";
|
||||
$htmlContent .= " <div class=\"glider-contain mt-6\">\n";
|
||||
$htmlContent .= " <div class=\"glider\">\n";
|
||||
// Testimonial 1
|
||||
$htmlContent .= " <div class=\"bg-white rounded-lg shadow-md overflow-hidden\">\n";
|
||||
$htmlContent .= " <img class=\"w-full h-48 object-cover object-center\" src=\"assets/img/testimonials/testimonials-3.jpg\" alt=\"Testimonial from Otieno Peter\">\n";
|
||||
$htmlContent .= " <div class=\"p-4\">\n";
|
||||
$htmlContent .= " <div class=\"uppercase tracking-wide text-sm text-indigo-500 font-semibold\">Otieno Peter</div>\n";
|
||||
$htmlContent .= " <p class=\"mt-2 text-gray-500\">\"Switching to this service has been a game changer for me. The connection is reliable and fast, making my online work seamless and efficient.\"</p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
// Testimonial 2
|
||||
$htmlContent .= " <div class=\"bg-white rounded-lg shadow-md overflow-hidden\">\n";
|
||||
$htmlContent .= " <img class=\"w-full h-48 object-cover object-center\" src=\"assets/img/testimonials/testimonials-2.jpg\" alt=\"Testimonial from Kiveu\">\n";
|
||||
$htmlContent .= " <div class=\"p-4\">\n";
|
||||
$htmlContent .= " <div class=\"uppercase tracking-wide text-sm text-indigo-500 font-semibold\">Kiveu</div>\n";
|
||||
$htmlContent .= " <p class=\"mt-2 text-gray-500\">\"I've experienced unparalleled support and service. The team goes above and beyond to ensure customer satisfaction. Highly recommend!\"</p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
// Testimonial 3
|
||||
$htmlContent .= " <div class=\"bg-white rounded-lg shadow-md overflow-hidden\">\n";
|
||||
$htmlContent .= " <img class=\"w-full h-48 object-cover object-center\" src=\"assets/img/testimonials/testimonials-1.jpg\" alt=\"Testimonial from Anonymous User\">\n";
|
||||
$htmlContent .= " <div class=\"p-4\">\n";
|
||||
$htmlContent .= " <div class=\"uppercase tracking-wide text-sm text-indigo-500 font-semibold\">Anonymous User</div>\n";
|
||||
$htmlContent .= " <p class=\"mt-2 text-gray-500\">\"Their commitment to quality and speed is evident. My internet experience has been fantastic ever since I made the switch.\"</p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <!-- Add Arrows -->\n";
|
||||
$htmlContent .= " <button aria-label=\"Previous\" class=\"glider-prev\">«</button>\n";
|
||||
$htmlContent .= " <button aria-label=\"Next\" class=\"glider-next\">»</button>\n";
|
||||
$htmlContent .= " <div role=\"tablist\" class=\"dots\"></div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= "</div>\n";
|
||||
|
||||
// Glider.js script for the Testimonials Section
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= " new Glider(document.querySelector('.glider'), {\n";
|
||||
$htmlContent .= " slidesToShow: 1,\n";
|
||||
$htmlContent .= " slidesToScroll: 1,\n";
|
||||
$htmlContent .= " draggable: true,\n";
|
||||
$htmlContent .= " dots: '.dots',\n";
|
||||
$htmlContent .= " arrows: {\n";
|
||||
$htmlContent .= " prev: '.glider-prev',\n";
|
||||
$htmlContent .= " next: '.glider-next'\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " responsive: [\n";
|
||||
$htmlContent .= " {\n";
|
||||
$htmlContent .= " breakpoint: 775,\n";
|
||||
$htmlContent .= " settings: {\n";
|
||||
$htmlContent .= " slidesToShow: 2,\n";
|
||||
$htmlContent .= " slidesToScroll: 2,\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " {\n";
|
||||
$htmlContent .= " breakpoint: 1024,\n";
|
||||
$htmlContent .= " settings: {\n";
|
||||
$htmlContent .= " slidesToShow: 3,\n";
|
||||
$htmlContent .= " slidesToScroll: 3,\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " ]\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<!-- FAQ Section -->\n";
|
||||
$htmlContent .= "<div class=\"mt-10 mx-auto px-4 sm:px-6 lg:px-8\">\n";
|
||||
$htmlContent .= " <div class=\"text-center\">\n";
|
||||
$htmlContent .= " <h3 class=\"text-2xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-3xl sm:leading-9\">\n";
|
||||
$htmlContent .= " FREQUENTLY ASKED QUESTIONS Will Be Here\n";
|
||||
$htmlContent .= " </h3>\n";
|
||||
$htmlContent .= " <p class=\"mt-4 max-w-2xl text-xl leading-7 text-gray-500 lg:mx-auto\">\n";
|
||||
$htmlContent .= " Everything you need to know before getting started.\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"mt-6\">\n";
|
||||
$htmlContent .= " <dl class=\"space-y-6\">\n";
|
||||
|
||||
// FAQ 1
|
||||
$htmlContent .= " <div class=\"bg-white rounded-lg shadow-md\">\n";
|
||||
$htmlContent .= " <dt class=\"p-4 cursor-pointer text-lg leading-6 font-medium text-gray-900\" onclick=\"toggleFAQ('faq1')\">" . htmlspecialchars($settings['frequently_asked_questions_headline1']) . "</dt>\n";
|
||||
$htmlContent .= " <dd id=\"faq1\" class=\"p-4 hidden text-base text-gray-500\">" . htmlspecialchars($settings['frequently_asked_questions_answer1']) . "</dd>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
// FAQ 2
|
||||
$htmlContent .= " <div class=\"bg-white rounded-lg shadow-md\">\n";
|
||||
$htmlContent .= " <dt class=\"p-4 cursor-pointer text-lg leading-6 font-medium text-gray-900\" onclick=\"toggleFAQ('faq2')\">" . htmlspecialchars($settings['frequently_asked_questions_headline2']) . "</dt>\n";
|
||||
$htmlContent .= " <dd id=\"faq2\" class=\"p-4 hidden text-base text-gray-500\">" . htmlspecialchars($settings['frequently_asked_questions_answer2']) . "</dd>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
// FAQ 3
|
||||
$htmlContent .= " <div class=\"bg-white rounded-lg shadow-md\">\n";
|
||||
$htmlContent .= " <dt class=\"p-4 cursor-pointer text-lg leading-6 font-medium text-gray-900\" onclick=\"toggleFAQ('faq3')\">" . htmlspecialchars($settings['frequently_asked_questions_headline3']) . "</dt>\n";
|
||||
$htmlContent .= " <dd id=\"faq3\" class=\"p-4 hidden text-base text-gray-500\">" . htmlspecialchars($settings['frequently_asked_questions_answer3']) . "</dd>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
$htmlContent .= " </dl>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= "</div>\n";
|
||||
|
||||
|
||||
$htmlContent .= "<div class=\"container mx-auto px-4\">\n";
|
||||
$htmlContent .= " <div class=\"max-w-md mx-auto bg-white rounded-lg overflow-hidden md:max-w-lg\">\n";
|
||||
$htmlContent .= " <div class=\"md:flex\">\n";
|
||||
$htmlContent .= " <div class=\"w-full p-5\">\n";
|
||||
$htmlContent .= " <div class=\"text-center\">\n";
|
||||
$htmlContent .= " <h3 class=\"text-2xl text-gray-900\">Already Have an Active Package?</h3>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
$htmlContent .= " <form id=\"loginForm\" class=\"form\" name=\"login\" action=\"$(link-login-only)\" method=\"post\" $(if chap-id)onSubmit=\"return doLogin()\"$(endif)>\n";
|
||||
$htmlContent .= " <input type=\"hidden\" name=\"dst\" value=\"$(link-orig)\" />\n";
|
||||
$htmlContent .= " <input type=\"hidden\" name=\"popup\" value=\"true\" />\n";
|
||||
$htmlContent .= " <div class=\"mb-4\">\n";
|
||||
$htmlContent .= " <label class=\"block text-gray-700 text-sm font-bold mb-2\" for=\"username\">Username</label>\n";
|
||||
$htmlContent .= " <input id=\"usernameInput\" class=\"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline\" name=\"username\" type=\"text\" value=\"\" placeholder=\"Username\">\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"mb-6\">\n";
|
||||
$htmlContent .= " <label class=\"block text-gray-700 text-sm font-bold mb-2\" for=\"password\">Password</label>\n";
|
||||
$htmlContent .= " <input id=\"passwordInput\" class=\"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline\" name=\"password\" type=\"password\" placeholder=\"******************\">\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"flex items-center justify-between\">\n";
|
||||
$htmlContent .= " <button id=\"submitBtn\" class=\"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline\" type=\"button\">\n";
|
||||
$htmlContent .= " Click Here To Connect\n";
|
||||
$htmlContent .= " </button>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </form>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= "</div>\n";
|
||||
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "document.addEventListener('DOMContentLoaded', function() {\n";
|
||||
$htmlContent .= " function autofillLogin() {\n";
|
||||
$htmlContent .= " var phoneNumber = '2547xxxxxxx';\n";
|
||||
$htmlContent .= " var password = '1234';\n";
|
||||
$htmlContent .= " document.querySelector('input[name=\"username\"]').value = phoneNumber;\n";
|
||||
$htmlContent .= " document.querySelector('input[name=\"password\"]').value = password;\n";
|
||||
$htmlContent .= " setTimeout(function() {\n";
|
||||
$htmlContent .= " document.querySelector('button[type=\"submit\"]').click();\n";
|
||||
$htmlContent .= " }, 15000);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " autofillLogin();\n";
|
||||
$htmlContent .= "});\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "function toggleFAQ(faqId) {\n";
|
||||
$htmlContent .= " var element = document.getElementById(faqId);\n";
|
||||
$htmlContent .= " if (element.style.display === \"block\") {\n";
|
||||
$htmlContent .= " element.style.display = \"none\";\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " element.style.display = \"block\";\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "}\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "</section>\n";
|
||||
$htmlContent .= "</main>\n";
|
||||
|
||||
$htmlContent .= "<!-- Footer -->\n";
|
||||
$htmlContent .= "<footer class=\"bg-blue-900 text-white\">\n";
|
||||
$htmlContent .= " <div class=\"max-w-7xl mx-auto px-4 py-12 sm:px-6 lg:px-8\">\n";
|
||||
$htmlContent .= " <div class=\"lg:grid lg:grid-cols-3 lg:gap-8\">\n";
|
||||
$htmlContent .= " <div class=\"lg:col-span-1\">\n";
|
||||
$htmlContent .= " <h2 class=\"text-sm font-semibold uppercase tracking-wider\">\n";
|
||||
$htmlContent .= " Contact Us\n";
|
||||
$htmlContent .= " </h2>\n";
|
||||
$htmlContent .= " <ul class=\"mt-4 space-y-4\">\n";
|
||||
$htmlContent .= " <li>\n";
|
||||
$htmlContent .= " <span class=\"block\">Address</span>\n";
|
||||
$htmlContent .= " </li>\n";
|
||||
$htmlContent .= " <li>\n";
|
||||
$htmlContent .= " <span class=\"block\">Email: contact@" . htmlspecialchars($company) . "</span>\n";
|
||||
$htmlContent .= " </li>\n";
|
||||
$htmlContent .= " <li>\n";
|
||||
$htmlContent .= " <span class=\"block\">Phone: " . htmlspecialchars($phone) . "</span>\n";
|
||||
$htmlContent .= " </li>\n";
|
||||
$htmlContent .= " </ul>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
$htmlContent .= " <div class=\"lg:col-span-1\">\n";
|
||||
$htmlContent .= " <h2 class=\"text-sm font-semibold uppercase tracking-wider\">\n";
|
||||
$htmlContent .= " Quick Links\n";
|
||||
$htmlContent .= " </h2>\n";
|
||||
$htmlContent .= " <ul class=\"mt-4 space-y-4\">\n";
|
||||
$htmlContent .= " <li><a href=\"#\" class=\"hover:underline\">About Us</a></li>\n";
|
||||
$htmlContent .= " <li><a href=\"#\" class=\"hover:underline\">Our Services</a></li>\n";
|
||||
$htmlContent .= " <li><a href=\"#\" class=\"hover:underline\">FAQ</a></li>\n";
|
||||
$htmlContent .= " <li><a href=\"#\" class=\"hover:underline\">Support</a></li>\n";
|
||||
$htmlContent .= " </ul>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
$htmlContent .= " <div class=\"lg:col-span-1\">\n";
|
||||
$htmlContent .= " <h2 class=\"text-sm font-semibold uppercase tracking-wider\">\n";
|
||||
$htmlContent .= " Follow Us\n";
|
||||
$htmlContent .= " </h2>\n";
|
||||
$htmlContent .= " <div class=\"mt-4 space-x-4\">\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"hover:text-gray-400\"><i class=\"fab fa-facebook-f\"></i></a>\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"hover:text-gray-400\"><i class=\"fab fa-twitter\"></i></a>\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"hover:text-gray-400\"><i class=\"fab fa-instagram\"></i></a>\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"hover:text-gray-400\"><i class=\"fab fa-linkedin-in\"></i></a>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
$htmlContent .= " <div class=\"mt-8 border-t border-gray-700 pt-8 md:flex md:items-center md:justify-between\">\n";
|
||||
$htmlContent .= " <div class=\"flex space-x-6 md:order-2\">\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"text-gray-400 hover:text-gray-300\"><span class=\"sr-only\">Facebook</span><i class=\"fab fa-facebook-f\"></i></a>\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"text-gray-400 hover:text-gray-300\"><span class=\"sr-only\">Instagram</span><i class=\"fab fa-instagram\"></i></a>\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"text-gray-400 hover:text-gray-300\"><span class=\"sr-only\">Twitter</span><i class=\"fab fa-twitter\"></i></a>\n";
|
||||
$htmlContent .= " <a href=\"#\" class=\"text-gray-400 hover:text-gray-300\"><span class=\"sr-only\">LinkedIn</span><i class=\"fab fa-linkedin-in\"></i></a>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= "<p class=\"mt-8 text-base leading-6 text-gray-400 md:mt-0 md:order-1\">\n";
|
||||
$htmlContent .= " © 2024 " . htmlspecialchars($company) . " All rights reserved.\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= "</footer>\n";
|
||||
|
||||
|
||||
$htmlContent .= "<script src=\"https://cdn.jsdelivr.net/npm/sweetalert2@11\"></script>\n";
|
||||
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= " function formatPhoneNumber(phoneNumber) {\n";
|
||||
$htmlContent .= " if (phoneNumber.startsWith('+')) {\n";
|
||||
$htmlContent .= " phoneNumber = phoneNumber.substring(1);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " if (phoneNumber.startsWith('0')) {\n";
|
||||
$htmlContent .= " phoneNumber = '254' + phoneNumber.substring(1);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " if (phoneNumber.match(/^(7|1)/)) {\n";
|
||||
$htmlContent .= " phoneNumber = '254' + phoneNumber;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " return phoneNumber;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " function handlePhoneNumberSubmission(planId, routerId) {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Enter Your Phone Number',\n";
|
||||
$htmlContent .= " input: 'text',\n";
|
||||
$htmlContent .= " inputPlaceholder: 'Your phone number here',\n";
|
||||
$htmlContent .= " inputAttributes: {\n";
|
||||
$htmlContent .= " autocapitalize: 'off'\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " showCancelButton: true,\n";
|
||||
$htmlContent .= " confirmButtonColor: '#3085d6',\n";
|
||||
$htmlContent .= " cancelButtonColor: '#d33',\n";
|
||||
$htmlContent .= " confirmButtonText: 'Submit',\n";
|
||||
$htmlContent .= " showLoaderOnConfirm: true,\n";
|
||||
$htmlContent .= " backdrop: `\n";
|
||||
$htmlContent .= " rgba(0,0,123,0.4)\n";
|
||||
$htmlContent .= " url(\"https://sweetalert2.github.io/images/nyan-cat.gif\")\n";
|
||||
$htmlContent .= " center left\n";
|
||||
$htmlContent .= " no-repeat\n";
|
||||
$htmlContent .= " `,\n";
|
||||
$htmlContent .= " preConfirm: (phoneNumber) => {\n";
|
||||
$htmlContent .= " var formattedPhoneNumber = formatPhoneNumber(phoneNumber);\n";
|
||||
$htmlContent .= " document.getElementById('usernameInput').value = formattedPhoneNumber;\n";
|
||||
$htmlContent .= " console.log(\"Phone number for autofill:\", formattedPhoneNumber);\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " return fetch('" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=grant', {\n";
|
||||
$htmlContent .= " method: 'POST',\n";
|
||||
$htmlContent .= " headers: {'Content-Type': 'application/json'},\n";
|
||||
$htmlContent .= " body: JSON.stringify({phone_number: formattedPhoneNumber, plan_id: planId, router_id: routerId}),\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(response => {\n";
|
||||
$htmlContent .= " if (!response.ok) throw new Error('Network response was not ok');\n";
|
||||
$htmlContent .= " return response.json();\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(data => {\n";
|
||||
$htmlContent .= " if (data.status === 'error') throw new Error(data.message);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Connecting in 35 Secs...',\n";
|
||||
$htmlContent .= " html: `Remaining time is <b>\${formattedPhoneNumber}</b>.<br>A payment request has been sent to <b>\${formattedPhoneNumber}</b>. Dont click anything until you are connected. Still on this page after the timer ended? Scroll down and Click Login Now`,\n";
|
||||
$htmlContent .= " timer: 35000, // Adjusted for 35 seconds\n";
|
||||
$htmlContent .= " timerProgressBar: true,\n";
|
||||
$htmlContent .= " didOpen: () => {\n";
|
||||
$htmlContent .= " Swal.showLoading();\n";
|
||||
$htmlContent .= " const timer = Swal.getPopup().querySelector(\"b\");\n";
|
||||
$htmlContent .= " timerInterval = setInterval(() => {\n";
|
||||
$htmlContent .= " timer.textContent = `\${Swal.getTimerLeft()}`;\n";
|
||||
$htmlContent .= " }, 100);\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " willClose: () => {\n";
|
||||
$htmlContent .= " clearInterval(timerInterval);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }).then((result) => {\n";
|
||||
$htmlContent .= " if (result.dismiss === Swal.DismissReason.timer) {\n";
|
||||
$htmlContent .= " console.log('I was closed by the timer');\n";
|
||||
$htmlContent .= " document.getElementById('submitBtn').click();\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " return formattedPhoneNumber; \n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .catch(error => {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'error',\n";
|
||||
$htmlContent .= " title: 'Oops...',\n";
|
||||
$htmlContent .= " text: error.message,\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " allowOutsideClick: () => !Swal.isLoading()\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " function FetchAjax(phoneNumber) {\n";
|
||||
$htmlContent .= " refreshData();\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " function refreshData() {\n";
|
||||
$htmlContent .= " function refreshDataInternal() {\n";
|
||||
$htmlContent .= " $.ajax({\n";
|
||||
$htmlContent .= " url: '" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=verify',\n";
|
||||
$htmlContent .= " method: \"POST\",\n";
|
||||
$htmlContent .= " data: {phone_number: document.getElementById('usernameInput').value},\n";
|
||||
$htmlContent .= " dataType: \"json\",\n";
|
||||
$htmlContent .= " success: function(data) {\n";
|
||||
$htmlContent .= " // Response handling code\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " error: function(xhr, textStatus, errorThrown) {\n";
|
||||
$htmlContent .= " console.log(\"Error: \" + errorThrown);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " var refreshInterval = setInterval(refreshDataInternal, 2000);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " document.addEventListener('DOMContentLoaded', function() {\n";
|
||||
$htmlContent .= " var submitBtn = document.getElementById('submitBtn');\n";
|
||||
$htmlContent .= " if (submitBtn) {\n";
|
||||
$htmlContent .= " submitBtn.addEventListener('click', function(event) {\n";
|
||||
$htmlContent .= " event.preventDefault();\n";
|
||||
$htmlContent .= " document.getElementById('loginForm').submit();\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js\"></script>\n";
|
||||
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "document.addEventListener('DOMContentLoaded', function() {\n";
|
||||
$htmlContent .= " // Ensure the button is correctly targeted by its ID.\n";
|
||||
$htmlContent .= " var submitBtn = document.getElementById('submitBtn');\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " // Add a click event listener to the \"Login Now\" button.\n";
|
||||
$htmlContent .= " submitBtn.addEventListener('click', function(event) {\n";
|
||||
$htmlContent .= " event.preventDefault(); // Prevent the default button action.\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " // Optional: Log to console for debugging purposes.\n";
|
||||
$htmlContent .= " console.log(\"Login Now button clicked.\");\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " // Direct form submission, bypassing the doLogin function for simplicity.\n";
|
||||
$htmlContent .= " var form = document.getElementById('loginForm');\n";
|
||||
$htmlContent .= " form.submit(); // Submit the form directly.\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "});\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
$htmlContent .= "</html>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$planStmt->close();
|
||||
$mysqli->close();
|
||||
// Check if the download parameter is set
|
||||
if (isset($_GET['download']) && $_GET['download'] == '1') {
|
||||
// Prepare the HTML content for download
|
||||
// ... build your HTML content ...
|
||||
|
||||
// Specify the filename for the download
|
||||
$filename = "login.html";
|
||||
|
||||
// Send headers to force download
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename='.basename($filename));
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: ' . strlen($htmlContent));
|
||||
|
||||
// Output the content
|
||||
echo $htmlContent;
|
||||
|
||||
// Prevent any further output
|
||||
exit;
|
||||
}
|
||||
|
||||
// Regular page content goes here
|
||||
// ... HTML and PHP code to display the page ...
|
||||
|
||||
|
117
system/plugin/ui/hotspot_settings.tpl
Normal file
117
system/plugin/ui/hotspot_settings.tpl
Normal file
@ -0,0 +1,117 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-success">
|
||||
Hotspot Settings
|
||||
</button>
|
||||
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="{$_url}plugin/hotspot_settings">{Lang::T('General Settings')}</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="{$_url}plugin/captive_portal_login" target="_blank">Preview Hotspot Login Page</a></li>
|
||||
<li><a href="{$app_url}/system/plugin/download.php?download=1" target="_blank">Download Login Page</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{$app_url}/system/plugin/download.php?download=1"><i class="fa fa-dashboard"></i> Click Here To Download Login Page</a></li>
|
||||
<li class="active">Hotspot Settings</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="content">
|
||||
<div class="table-responsive">
|
||||
<div class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#tab_1" data-toggle="tab">{Lang::T('General Settings')}</a></li>
|
||||
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tab_1" style="overflow-x:auto;">
|
||||
<div class="box-body no-padding" id="">
|
||||
<form method="POST" action="" enctype="multipart/form-data">
|
||||
<div class="box-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hotspot_title">Hotspot Page Title</label>
|
||||
<input type="text" class="form-control" name="hotspot_title" id="hotspot_title" value="{$hotspot_title}" required>
|
||||
<small class="form-text text-muted">In this field, you can enter the name of your ISP company. It will appear as the main title on the hotspot page.</small>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">Brief Description Of Company/Tagline</label>
|
||||
<input type="text" class="form-control" name="description" id="description" value="{$description}" required>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="router_name">Router Name:</label>
|
||||
<input type="text" class="form-control" name="router_name" id="router_name" value="{$router_name}" required>
|
||||
<small class="form-text text-muted">This is the most important part of the form. Go to Network and then Routers, and copy the exact router name.</small>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- FAQ fields -->
|
||||
<div class="form-group">
|
||||
<label for="frequently_asked_questions_headline1">FAQ Headline 1</label>
|
||||
<input type="text" class="form-control" name="frequently_asked_questions_headline1" id="frequently_asked_questions_headline1" value="{$frequently_asked_questions_headline1}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="frequently_asked_questions_answer1">FAQ Answer 1</label>
|
||||
<textarea class="form-control" id="frequently_asked_questions_answer1" name="frequently_asked_questions_answer1" required>{$frequently_asked_questions_answer1}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="frequently_asked_questions_headline2">FAQ Headline 2</label>
|
||||
<input type="text" class="form-control" id="frequently_asked_questions_headline2" name="frequently_asked_questions_headline2" value="{$frequently_asked_questions_headline2}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="frequently_asked_questions_answer2">FAQ Answer 2</label>
|
||||
<textarea class="form-control" id="frequently_asked_questions_answer2" name="frequently_asked_questions_answer2" required>{$frequently_asked_questions_answer2}</textarea>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="frequently_asked_questions_headline3">FAQ Headline 3</label>
|
||||
<input type="text" class="form-control" name="frequently_asked_questions_headline3" id="frequently_asked_questions_headline3" value="{$frequently_asked_questions_headline3}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="frequently_asked_questions_answer3">FAQ Answer 3</label>
|
||||
<textarea class="form-control" id="frequently_asked_questions_answer3" name="frequently_asked_questions_answer3" required>{$frequently_asked_questions_answer3}</textarea>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Save Changes button -->
|
||||
|
||||
<button type="submit" class="btn btn-info pull-right">Save Changes</button>
|
||||
</form>
|
||||
|
||||
<div class="tab-pane" id="tab_6" style="overflow-x:auto;">
|
||||
<div class="box-body no-padding" id="">
|
||||
<!-- Content for Pages Settings tab -->
|
||||
This feature will be Coming Soon
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<pre><b>USAGE:</b>
|
||||
<br>Make sure you change this custom Settings and personalize them.
|
||||
<br>Then download the <strong style="color: black; background-color: yellow;">login.html</strong> by clicking on download login page.
|
||||
<br>Then upload the downloaded <strong style="color: black; background-color: yellow;">login.html</strong> file to your Mikrotik router.
|
||||
<br>Make sure you add your website URL in Mikrotik hotspot wall garden. <strong style="color: black; background-color: yellow;">login.html</strong>
|
||||
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
BIN
system/plugin/ui/index.html
Normal file
BIN
system/plugin/ui/index.html
Normal file
Binary file not shown.
154
system/plugin/ui/log.tpl
Normal file
154
system/plugin/ui/log.tpl
Normal file
@ -0,0 +1,154 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Log UI</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
</head>
|
||||
{include file="sections/header.tpl"}
|
||||
<body class="bg-gray-100 font-sans leading-normal tracking-normal">
|
||||
<div class="container mx-auto mt-8 bg-white rounded-lg shadow-lg p-6">
|
||||
<form class="mb-4" method="post" role="form" action="{$_url}plugin/log_ui">
|
||||
<ul class="nav nav-tabs flex border-b">
|
||||
{foreach $routers as $r}
|
||||
<li class="mr-1" role="presentation" {if $r['id']==$router}class="active"{/if}>
|
||||
<a class="bg-white inline-block py-2 px-4 text-blue-500 hover:text-blue-800 font-semibold" href="{$_url}plugin/log_ui/{$r['id']}">{$r['name']}</a>
|
||||
</li>
|
||||
{/foreach}
|
||||
</ul>
|
||||
</form>
|
||||
|
||||
<div class="flex flex-wrap mb-4">
|
||||
<div class="w-full md:w-8/12">
|
||||
<label class="block">
|
||||
Show entries
|
||||
<select name="data_length" aria-controls="data" class="form-control form-control-sm" onchange="updatePerPage(this.value)">
|
||||
<option value="5" {if $per_page == 5}selected{/if}>5</option>
|
||||
<option value="10" {if $per_page == 10}selected{/if}>10</option>
|
||||
<option value="25" {if $per_page == 25}selected{/if}>25</option>
|
||||
<option value="50" {if $per_page == 50}selected{/if}>50</option>
|
||||
<option value="100" {if $per_page == 100}selected{/if}>100</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="w-full md:w-4/12">
|
||||
<label class="block">
|
||||
Search:
|
||||
<input type="search" id="logSearch" class="form-control form-control-sm" placeholder="Search logs" aria-controls="data" onkeyup="filterLogs()">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="min-w-full bg-white table-auto">
|
||||
<thead>
|
||||
<tr class="bg-gray-200">
|
||||
<th class="w-1/3 py-2 px-4">Time</th>
|
||||
<th class="w-1/3 py-2 px-4">Topic</th>
|
||||
<th class="w-1/3 py-2 px-4">Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="logTableBody">
|
||||
{assign var=current_page value=$smarty.get.page|default:1}
|
||||
{assign var=per_page value=$smarty.get.per_page|default:10}
|
||||
{assign var=start_index value=($current_page - 1) * $per_page}
|
||||
|
||||
{foreach from=$logs|array_reverse item=log name=logLoop}
|
||||
{if $smarty.foreach.logLoop.index >= $start_index && $smarty.foreach.logLoop.index < ($start_index + $per_page)}
|
||||
<tr class="log-entry">
|
||||
<td class="border px-4 py-2">{$log.time}</td>
|
||||
<td class="border px-4 py-2">{$log.topics}</td>
|
||||
<td class="border px-4 py-2 log-message">
|
||||
{if $log.message|lower|strpos:'failed' !== false}
|
||||
<span class="text-red-700 text-bold">{$log.message}</span>
|
||||
{elseif $log.message|lower|strpos:'trying' !== false}
|
||||
<span class="text-yellow-700">{$log.message}</span>
|
||||
{elseif $log.message|lower|strpos:'logged in' !== false}
|
||||
<span class="text-green-700">{$log.message}</span>
|
||||
{elseif $log.message|lower|strpos:'login failed' !== false}
|
||||
<span class="text-blue-700">{$log.message}</span>
|
||||
{else}
|
||||
<span class="text-gray-700">{$log.message}</span>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{assign var=total_logs value=$logs|@count}
|
||||
{assign var=last_page value=ceil($total_logs / $per_page)}
|
||||
|
||||
<nav class="mt-4">
|
||||
<ul class="pagination flex justify-center">
|
||||
{if $current_page > 1}
|
||||
<li>
|
||||
<a class="page-link border py-2 px-4 mx-1" href="index.php?_route=plugin/log_ui&page=1&per_page={$per_page}" aria-label="First">««</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="page-link border py-2 px-4 mx-1" href="index.php?_route=plugin/log_ui&page={$current_page-1}&per_page={$per_page}" aria-label="Previous">«</a>
|
||||
</li>
|
||||
{/if}
|
||||
|
||||
{assign var=max_links value=5}
|
||||
{assign var=start_page value=max(1, $current_page - floor($max_links / 2))}
|
||||
{assign var=end_page value=min($last_page, $start_page + $max_links - 1)}
|
||||
|
||||
{if $start_page > 1}
|
||||
<li>
|
||||
<a class="page-link border py-2 px-4 mx-1" href="index.php?_route=plugin/log_ui&page={$start_page-1}&per_page={$per_page}" aria-label="Previous">…</a>
|
||||
</li>
|
||||
{/if}
|
||||
|
||||
{foreach from=range($start_page, $end_page) item=page}
|
||||
<li>
|
||||
<a class="page-link border py-2 px-4 mx-1 {if $page == $current_page}bg-blue-500 text-white{/if}" href="index.php?_route=plugin/log_ui&page={$page}&per_page={$per_page}">{$page}</a>
|
||||
</li>
|
||||
{/foreach}
|
||||
|
||||
{if $end_page < $last_page}
|
||||
<li>
|
||||
<a class="page-link border py-2 px-4 mx-1" href="index.php?_route=plugin/log_ui&page={$end_page+1}&per_page={$per_page}" aria-label="Next">…</a>
|
||||
</li>
|
||||
{/if}
|
||||
|
||||
{if $current_page < $last_page}
|
||||
<li>
|
||||
<a class="page-link border py-2 px-4 mx-1" href="index.php?_route=plugin/log_ui&page={$current_page+1}&per_page={$per_page}" aria-label="Next">»</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="page-link border py-2 px-4 mx-1" href="index.php?_route=plugin/log_ui&page={$last_page}&per_page={$per_page}" aria-label="Last">»»</a>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function updatePerPage(value) {
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
urlParams.set('per_page', value);
|
||||
urlParams.set('page', 1); // Reset to first page
|
||||
window.location.search = urlParams.toString();
|
||||
}
|
||||
|
||||
function filterLogs() {
|
||||
var input = document.getElementById('logSearch').value.toLowerCase();
|
||||
var table = document.getElementById('logTableBody');
|
||||
var tr = table.getElementsByClassName('log-entry');
|
||||
|
||||
for (var i = 0; i < tr.length; i++) {
|
||||
var logMessage = tr[i].getElementsByClassName('log-message')[0].textContent || tr[i].getElementsByClassName('log-message')[0].innerText;
|
||||
if (logMessage.toLowerCase().indexOf(input) > -1) {
|
||||
tr[i].style.display = '';
|
||||
} else {
|
||||
tr[i].style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
||||
</body>
|
||||
</html>
|
34
system/plugin/ui/port_tester.tpl
Normal file
34
system/plugin/ui/port_tester.tpl
Normal file
@ -0,0 +1,34 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}plugin/port_tester">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="panel-heading">Testing port external</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">Port</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" id="port" name="port" value="{$port}" placeholder="8728" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success waves-effect waves-light" type="submit">Test It</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{if $result != ''}
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30">
|
||||
<div class="panel-heading">Result</div>
|
||||
<div class="panel-body">
|
||||
{Lang::nl2br($result)}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
943
system/plugin/ui/pppoe_monitor.tpl
Normal file
943
system/plugin/ui/pppoe_monitor.tpl
Normal file
@ -0,0 +1,943 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.10.23/js/jquery.dataTables.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.23/css/jquery.dataTables.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/1.7.1/css/buttons.dataTables.min.css">
|
||||
<script src="https://cdn.datatables.net/buttons/1.7.1/js/dataTables.buttons.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></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/dataTables.bootstrap5.min.js"></script>
|
||||
|
||||
<style>
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 15% auto;
|
||||
padding: 0px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
max-width: 600px;
|
||||
}
|
||||
.close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
.card-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
.card-header {
|
||||
padding: .75rem 1.25rem;
|
||||
margin-bottom: 0;
|
||||
background-color: none;
|
||||
border-bottom: 1px solid rgba(0,0,0,.125);
|
||||
}
|
||||
.card-title {
|
||||
margin-bottom: .75rem;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.table-responsive {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||
}
|
||||
.table {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
color: #212529;
|
||||
}
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
}
|
||||
#ppp-table_wrapper {
|
||||
padding: 15px;
|
||||
}
|
||||
#ppp-table th, #ppp-table td {
|
||||
text-align: center;
|
||||
padding: 6px;
|
||||
}
|
||||
.table-striped tbody tr:nth-of-type(odd) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.panel-default {
|
||||
border-color: #ddd;
|
||||
}
|
||||
.panel-heading {
|
||||
background-color: #f5f5f5;
|
||||
border-color: #ddd;
|
||||
}
|
||||
.nav-tabs {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.nav-tabs > li > a {
|
||||
border-radius: 0;
|
||||
color: #555;
|
||||
background-color: #f9f9f9;
|
||||
border-color: #ddd;
|
||||
}
|
||||
.nav-tabs > li.active > a,
|
||||
.nav-tabs > li.active > a:focus,
|
||||
.nav-tabs > li.active > a:hover {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
border: 1px solid #ddd;
|
||||
border-bottom-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
.table-striped tbody tr:nth-of-type(odd) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.table th {
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
padding: 8px;
|
||||
}
|
||||
.table-striped > tbody > tr > td {
|
||||
background-color: #fff;
|
||||
}
|
||||
.status-connect {
|
||||
color: #5cb85c;
|
||||
}
|
||||
.status-disconnect {
|
||||
color: #d9534f;
|
||||
}
|
||||
.modalsupport {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgb(0,0,0);
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.modalsupport-content {
|
||||
background-color: #fefefe;
|
||||
margin: auto;
|
||||
padding: 0px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
|
||||
text-align: center;
|
||||
}
|
||||
.modalsupport-close {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.modalsupport-close:hover,
|
||||
.modalsupport-close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.card {
|
||||
border: none;
|
||||
}
|
||||
.card-header {
|
||||
background-color: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
.card-body {
|
||||
padding: 20px;
|
||||
}
|
||||
.donate-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.modalsupport img {
|
||||
width: 100px;
|
||||
height: auto;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.dataSize {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.action-icons i {
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
color: #007bff;
|
||||
}
|
||||
.action-icons i:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
.modal-title {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.table-bordered {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.table-bordered th, .table-bordered td {
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 5px;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
.advanced-search-container {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.form-inline .form-group {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.dataTables_filter {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.panel-default {
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
.panel-heading {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
.panel-body {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.table th, .table td {
|
||||
font-size: 15px; /* Mengurangi ukuran font pada tabel */
|
||||
}
|
||||
}
|
||||
.traffic-icon {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.traffic-icon-green {
|
||||
background-color: green;
|
||||
}
|
||||
.traffic-icon-yellow {
|
||||
background-color: yellow;
|
||||
}
|
||||
.traffic-icon-red {
|
||||
background-color: red;
|
||||
}
|
||||
.text-left {
|
||||
text-align: left !important;
|
||||
}
|
||||
</style>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<!-- Form dan navigasi tabs -->
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}plugin/pppoe">
|
||||
<div class="form-group">
|
||||
<label for="routerSelect" class="col-sm-2 control-label">Select Router</label>
|
||||
<div class="col-sm-10">
|
||||
<select id="routerSelect" name="router" class="form-control" onchange="window.location.href=this.value;">
|
||||
{foreach $routers as $r}
|
||||
<option value="{$_url}plugin/pppoe/{$r['id']}" {if $r['id'] == $router}selected{/if}>
|
||||
{$r['name']}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="advanced-search-container">
|
||||
<form id="advancedSearchForm" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="searchUsername">Username:</label>
|
||||
<input type="text" class="form-control" id="searchUsername" placeholder="Enter username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="searchStatus">Status:</label>
|
||||
<select class="form-control" id="searchStatus">
|
||||
<option value="">Any</option>
|
||||
<option value="Connected">Connected</option>
|
||||
<option value="Disconnected">Disconnected</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="table-responsive">
|
||||
<div class="panel-body">
|
||||
<table class="table table-striped" id="ppp-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Username</th>
|
||||
<th>IP Address</th>
|
||||
<th>Uptime</th>
|
||||
<th>Service</th>
|
||||
<th>Caller ID</th>
|
||||
<th>Device</th>
|
||||
<th>Download</th>
|
||||
<th>Upload</th>
|
||||
<th>Total Usage</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Isi tabel akan dimasukkan melalui JavaScript -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="detailsModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close">×</span>
|
||||
<div class="container-fluid mt-5">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="modal-title">Data Usage for <span id="modalUsername"></span></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive mt-4">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<input type="hidden" id="interface" value="">
|
||||
<th id="tabletx"><i class="fa fa-download"></i></th>
|
||||
<th id="tablerx"><i class="fa fa-upload"></i></th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div id="chart" class="mt-3"></div>
|
||||
<div id="dailyChart" class="mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var $j = jQuery.noConflict();
|
||||
|
||||
$j(document).ready(function() {
|
||||
var table = $j('#ppp-table').DataTable({
|
||||
responsive: true,
|
||||
columns: [
|
||||
{ data: 'id', visible: false },
|
||||
{
|
||||
data: 'username',
|
||||
className: 'text-left',
|
||||
render: function(data, type, row) {
|
||||
return '<div style="width: 150px;"><i class="traffic-icon traffic-icon-green"></i> ' + data + '</div>';
|
||||
}
|
||||
},
|
||||
{ data: 'address' },
|
||||
{ data: 'uptime' },
|
||||
{ data: 'service' },
|
||||
{ data: 'caller_id' },
|
||||
{ data: 'manufacturer' }, // New column for device/manufacturer
|
||||
{ data: 'tx', className: 'dataSize' },
|
||||
{ data: 'rx', className: 'dataSize' },
|
||||
{ data: 'total', className: 'dataSize' },
|
||||
{
|
||||
data: 'status',
|
||||
render: function(data) {
|
||||
if (data === 'Connected') {
|
||||
return '<small class="label bg-green">Connected</small>';
|
||||
} else if (data === 'Disconnected') {
|
||||
return '<small class="label bg-red">Disconnected</small>';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
data: null,
|
||||
render: function(data, type, row) {
|
||||
return '<div class="action-icons" style="display: flex; align-items: center;">' +
|
||||
'<i class="fa fa-area-chart view-details" style="color: blue; cursor: pointer;" title="View Traffic" data-username="' + row.username + '" data-id="' + row.id + '"></i> ' +
|
||||
'<i class="fa fa-retweet reconnect-button" style="color: red; cursor: pointer;" title="Reconnect" data-username="' + row.username + '" data-id="' + row.id + '"></i> ' +
|
||||
'<button class="btn btn-sm ' + (row.is_bound ? 'btn-danger' : 'btn-success') + ' bind-button" data-username="' + row.username + '" data-caller-id="' + row.caller_id + '" data-is-bound="' + row.is_bound + '">' +
|
||||
(row.is_bound ? 'Unbind' : 'Bind') + '</button>' +
|
||||
'</div>';
|
||||
}
|
||||
}
|
||||
],
|
||||
order: [[0, 'asc']],
|
||||
pageLength: 10,
|
||||
lengthMenu: [[10, 25, 50, -1], [10, 25, 50, 'All']],
|
||||
dom: 'Bfrtip',
|
||||
buttons: ['reset', 'pageLength'],
|
||||
paging: true,
|
||||
info: true,
|
||||
searching: true,
|
||||
ajax: {
|
||||
url: '{$_url}plugin/pppoe_get_combined_users/{$router}',
|
||||
dataSrc: ''
|
||||
}
|
||||
});
|
||||
|
||||
// Fungsi untuk mendapatkan batas maksimum
|
||||
function getMaxLimit(data) {
|
||||
if (data.hasOwnProperty('max_limit')) {
|
||||
return data.max_limit.toString();
|
||||
} else {
|
||||
return 'N/A';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle view details icon clicks
|
||||
$j('#ppp-table tbody').on('click', '.view-details', function(e) {
|
||||
e.preventDefault();
|
||||
var username = $j(this).data('username');
|
||||
var id = $j(this).data('id');
|
||||
|
||||
viewDetails(id, username);
|
||||
});
|
||||
|
||||
// Handle reconnect icon clicks
|
||||
$j('#ppp-table tbody').on('click', '.reconnect-button', function(e) {
|
||||
e.preventDefault();
|
||||
var username = $j(this).data('username');
|
||||
var id = $j(this).data('id');
|
||||
|
||||
reconnect(id, username);
|
||||
});
|
||||
|
||||
// Function to handle view details
|
||||
function viewDetails(id, username) {
|
||||
console.log("Viewing details for:", username);
|
||||
$j('#modalUsername').text(username);
|
||||
|
||||
$j.ajax({
|
||||
url: '{$_url}plugin/pppoe_get_combined_users',
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
var user = response.find(function(item) {
|
||||
return (item.username && item.username.toString().toLowerCase() === username.toString().toLowerCase());
|
||||
});
|
||||
|
||||
if (username !== null && user !== null && user.username !== null) {
|
||||
var interfaceValue = '<pppoe-' + user.username + '>';
|
||||
$j('#interface').val(interfaceValue);
|
||||
$j('#selectedInterface').text(interfaceValue);
|
||||
$j('#detailsModal').css('display', 'block');
|
||||
createChart();
|
||||
createDailyChart(username); // Pass the username to createDailyChart
|
||||
} else {
|
||||
alert('User not found.');
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
alert('Failed to retrieve user data.');
|
||||
console.error('AJAX error:', textStatus, errorThrown);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to handle reconnect
|
||||
function reconnect(id, username) {
|
||||
if (confirm('Are you sure you want to disconnect user ' + username + '?')) {
|
||||
$.ajax({
|
||||
url: '{$_url}plugin/pppoe_monitor_router_delete_ppp_user/{$router}', // Perbaiki URL AJAX
|
||||
method: 'POST',
|
||||
data: { id: id, username: username },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
alert('User ' + username + ' has been disconnected.');
|
||||
setTimeout(function() {
|
||||
table.ajax.reload();
|
||||
}, 2000);
|
||||
} else {
|
||||
alert('Failed to disconnect user ' + username + ': ' + (response.message || 'Unknown error'));
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
alert('Failed to disconnect user ' + username + ': ' + (errorThrown || 'Unknown error'));
|
||||
console.error('AJAX error:', textStatus, errorThrown);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Close modal on click of close button
|
||||
$j('.close').click(function() {
|
||||
$j('#detailsModal').css('display', 'none');
|
||||
});
|
||||
|
||||
// Close modal on click outside the modal
|
||||
$j(window).click(function(event) {
|
||||
if (event.target == document.getElementById('detailsModal')) {
|
||||
$j('#detailsModal').css('display', 'none');
|
||||
}
|
||||
});
|
||||
|
||||
// Handle advanced search form submission
|
||||
$j(document).ready(function() {
|
||||
$j('#advancedSearchForm').on('submit', function(e) {
|
||||
e.preventDefault(); // Mencegah pengiriman form secara default
|
||||
|
||||
// Mendapatkan nilai dari input
|
||||
var username = $j('#searchUsername').val();
|
||||
var status = $j('#searchStatus').val();
|
||||
|
||||
// Melakukan pencarian dan menggambar ulang tabel
|
||||
table.column(1).search(username).draw(); // Kolom 1 untuk username
|
||||
table.column(9).search(status).draw(); // Kolom 9 untuk status
|
||||
});
|
||||
|
||||
// Menambahkan ikon search ke dalam tombol
|
||||
var searchButton = $j('<button type="submit" class="btn btn-primary"><i class="fas fa-search"></i></button>');
|
||||
$j('#advancedSearchForm').append(searchButton);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var chart;
|
||||
var chartData = {
|
||||
txData: [],
|
||||
rxData: []
|
||||
};
|
||||
|
||||
function createChart() {
|
||||
var options = {
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'area',
|
||||
animations: {
|
||||
enabled: true,
|
||||
easing: 'linear',
|
||||
speed: 200,
|
||||
animateGradually: {
|
||||
enabled: true,
|
||||
delay: 150
|
||||
},
|
||||
dynamicAnimation: {
|
||||
enabled: true,
|
||||
speed: 200
|
||||
}
|
||||
},
|
||||
events: {
|
||||
mounted: function() {
|
||||
updateTrafficValues();
|
||||
setInterval(updateTrafficValues, 3000);
|
||||
}
|
||||
}
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth'
|
||||
},
|
||||
series: [
|
||||
{ name: 'Download', data: chartData.txData },
|
||||
{ name: 'Upload', data: chartData.rxData }
|
||||
],
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
formatter: function(value) {
|
||||
return new Date(value).toLocaleTimeString();
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
text: 'Real Time Data Usage'
|
||||
},
|
||||
labels: {
|
||||
formatter: function(value) {
|
||||
return formatBytes(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: 'HH:mm:ss'
|
||||
},
|
||||
y: {
|
||||
formatter: function(value) {
|
||||
return formatBytes(value) + 'ps';
|
||||
}
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
formatter: function(value) {
|
||||
return formatBytes(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
chart = new ApexCharts(document.querySelector("#chart"), options);
|
||||
chart.render();
|
||||
}
|
||||
|
||||
var dailyChart; // Declare dailyChart variable globally
|
||||
|
||||
function createDailyChart(username) {
|
||||
var currentDate = new Date();
|
||||
var startOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1).getTime();
|
||||
var endOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getTime();
|
||||
|
||||
generateDailyData(username)
|
||||
.then(dailyData => {
|
||||
var dailyTotals = dailyData.download.map((item, index) => ({
|
||||
x: item.x,
|
||||
y: item.y + dailyData.upload[index].y
|
||||
}));
|
||||
|
||||
if (dailyChart) {
|
||||
dailyChart.destroy();
|
||||
}
|
||||
|
||||
var options = {
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'bar',
|
||||
animations: {
|
||||
enabled: true,
|
||||
easing: 'linear',
|
||||
speed: 800,
|
||||
animateGradually: {
|
||||
enabled: true,
|
||||
delay: 150
|
||||
},
|
||||
dynamicAnimation: {
|
||||
enabled: true,
|
||||
speed: 200
|
||||
}
|
||||
},
|
||||
toolbar: {
|
||||
show: true,
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
columnWidth: '15%',
|
||||
endingShape: 'rounded'
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
stroke: {
|
||||
show: true,
|
||||
width: 2,
|
||||
colors: ['transparent']
|
||||
},
|
||||
series: [{
|
||||
name: 'Download',
|
||||
data: dailyData.upload
|
||||
}, {
|
||||
name: 'Upload',
|
||||
data: dailyData.download
|
||||
}, {
|
||||
name: 'Daily Totals',
|
||||
data: dailyTotals
|
||||
}],
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
min: startOfMonth,
|
||||
max: endOfMonth,
|
||||
labels: {
|
||||
formatter: function(value) {
|
||||
return new Date(value).toLocaleDateString();
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
text: 'Total Usage'
|
||||
},
|
||||
labels: {
|
||||
formatter: function(value) {
|
||||
return formatBytesPerSecond(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
fill: {
|
||||
opacity: 1
|
||||
},
|
||||
tooltip: {
|
||||
y: {
|
||||
formatter: function(val) {
|
||||
return formatBytes(val);
|
||||
}
|
||||
}
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
plotOptions: {
|
||||
bar: {
|
||||
columnWidth: '100%'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
dailyChart = new ApexCharts(document.querySelector("#dailyChart"), options);
|
||||
dailyChart.render();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Failed to fetch daily usage data:", error);
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================== NEW FITUR ==========================================//
|
||||
function generateDailyData(username, startDate, endDate) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$j.ajax({
|
||||
url: '{$_url}plugin/pppoe_monitor_router_daily_data_usage/{$router}',
|
||||
data: {
|
||||
username: username,
|
||||
start_date: startDate,
|
||||
end_date: endDate
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
console.log("Raw data from server for username", username, ":", data);
|
||||
|
||||
var dailyData = {
|
||||
download: [],
|
||||
upload: []
|
||||
};
|
||||
|
||||
// Iterate over dates in data and find the correct user data
|
||||
for (var date in data) {
|
||||
var users = data[date].users;
|
||||
|
||||
// Handle username as number case
|
||||
var userData = users.find(user => user.username === username || user.username == parseInt(username));
|
||||
|
||||
if (userData) {
|
||||
var rxBytes = convertToBytes(userData.rx);
|
||||
var txBytes = convertToBytes(userData.tx);
|
||||
|
||||
// Store data in dailyData based on date
|
||||
dailyData.download.push({ x: new Date(date).getTime(), y: rxBytes });
|
||||
dailyData.upload.push({ x: new Date(date).getTime(), y: txBytes });
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Filtered daily data for username", username, ":", dailyData);
|
||||
resolve(dailyData);
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
console.error("AJAX Error in generateDailyData:", textStatus, errorThrown);
|
||||
console.log("Status:", xhr.status);
|
||||
console.log("Response Text:", xhr.responseText);
|
||||
reject(errorThrown);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function convertToBytes(value) {
|
||||
let [number, unit] = value.split(' ');
|
||||
number = parseFloat(number);
|
||||
switch (unit) {
|
||||
case 'GB':
|
||||
return number * 1024 * 1024 * 1024;
|
||||
case 'MB':
|
||||
return number * 1024 * 1024;
|
||||
case 'KB':
|
||||
return number * 1024;
|
||||
default:
|
||||
return number;
|
||||
}
|
||||
}
|
||||
// ========================================== NEW FITUR ==========================================//
|
||||
function formatBytesPerSecond(bytes) {
|
||||
if (bytes === 0) {
|
||||
return '0 Bps';
|
||||
}
|
||||
var k = 1024;
|
||||
var sizes = ['Bps', 'KBps', 'MBps', 'GBps', 'TBps', 'PBps', 'EBps', 'ZBps', 'YBps'];
|
||||
var i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
var formattedValue = parseFloat((bytes / Math.pow(k, i)).toFixed(2));
|
||||
return formattedValue + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// Fungsi untuk mengubah ukuran dalam byte menjadi format yang lebih mudah dibaca
|
||||
function formatBytes(bytes, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// Function to update traffic values and icons
|
||||
function updateTrafficValues() {
|
||||
var username = $j('#modalUsername').text().trim();
|
||||
var interfaceValue = $j('#interface').val();
|
||||
|
||||
if (!username || !interfaceValue) {
|
||||
console.error("Username or interface is undefined or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
$j.ajax({
|
||||
url: '{$_url}plugin/pppoe_monitor_router_traffic/{$router}',
|
||||
dataType: 'json',
|
||||
data: { username: username, interface: interfaceValue },
|
||||
success: function(data) {
|
||||
var timestamp = new Date().getTime();
|
||||
var txData = parseInt(data.rows.tx[0]) || 0;
|
||||
var rxData = parseInt(data.rows.rx[0]) || 0;
|
||||
|
||||
// Log data tx dan rx untuk debugging
|
||||
console.log('txData:', txData, 'rxData:', rxData);
|
||||
|
||||
// Update chart data
|
||||
chartData.txData.push({ x: timestamp, y: txData });
|
||||
chartData.rxData.push({ x: timestamp, y: rxData });
|
||||
|
||||
var maxDataPoints = 10;
|
||||
if (chartData.txData.length > maxDataPoints) {
|
||||
chartData.txData.shift();
|
||||
chartData.rxData.shift();
|
||||
}
|
||||
|
||||
// Update series on the chart
|
||||
chart.updateSeries([
|
||||
{ name: 'Download', data: chartData.txData },
|
||||
{ name: 'Upload', data: chartData.rxData }
|
||||
]);
|
||||
|
||||
// Find the icon element for the specific user based on username
|
||||
var userRow = $j('#ppp-table tbody tr').filter(function() {
|
||||
return $j(this).find('td').eq(1).text().trim() === username;
|
||||
});
|
||||
|
||||
var iconElement = userRow.find('.traffic-icon');
|
||||
|
||||
// Define thresholds for traffic levels
|
||||
var thresholdHigh = 2000; // Adjust these values as needed
|
||||
var thresholdMedium = 1500; // Adjust these values as needed
|
||||
|
||||
// Adjust icon color based on traffic levels
|
||||
if (txData > thresholdHigh || rxData > thresholdHigh) {
|
||||
iconElement.removeClass('traffic-icon-green traffic-icon-yellow').addClass('traffic-icon-red');
|
||||
} else if (txData > thresholdMedium || rxData > thresholdMedium) {
|
||||
iconElement.removeClass('traffic-icon-green traffic-icon-red').addClass('traffic-icon-yellow');
|
||||
} else {
|
||||
iconElement.removeClass('traffic-icon-yellow traffic-icon-red').addClass('traffic-icon-green');
|
||||
}
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
console.error("Status: " + textStatus);
|
||||
console.error("Error: " + errorThrown);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to update traffic icons based on table data
|
||||
function updateTrafficIcons(response) {
|
||||
$j('#ppp-table tbody tr').each(function(index) {
|
||||
var row = table.row(this).data();
|
||||
if (row) {
|
||||
var txValue = parseInt(row.tx, 10);
|
||||
var rxValue = parseInt(row.rx, 10);
|
||||
var iconElement = $j(this).find('.traffic-icon');
|
||||
|
||||
var maxLimit = row.max_limit;
|
||||
if (maxLimit === '1M/2M') {
|
||||
if (txValue >= 2 * 1024 * 1024 || rxValue >= 2 * 1024 * 1024) {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-red');
|
||||
} else if (txValue >= 1.5 * 1024 * 1024 || rxValue >= 1.5 * 1024 * 1024) {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-yellow');
|
||||
} else {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-green');
|
||||
}
|
||||
} else {
|
||||
// Default logic for other max limits
|
||||
if (txValue >= 2 * 1024 * 1024 || rxValue >= 2 * 1024 * 1024) {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-red');
|
||||
} else if (txValue >= 1.5 * 1024 * 1024 || rxValue >= 1.5 * 1024 * 1024) {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-yellow');
|
||||
} else {
|
||||
iconElement.removeClass().addClass('traffic-icon traffic-icon-green');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Donation Popup
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setTimeout(function() {
|
||||
document.getElementById('donationPopup').style.display = 'flex';
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
document.getElementById('donationPopup').querySelector('.modalsupport-close').addEventListener('click', function() {
|
||||
document.getElementById('donationPopup').style.display = 'none';
|
||||
});
|
||||
|
||||
window.addEventListener('click', function(event) {
|
||||
if (event.target === document.getElementById('donationPopup')) {
|
||||
document.getElementById('donationPopup').style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('donationPopup').querySelector('.donate-button').addEventListener('click', function() {
|
||||
window.open('https://buymeacoffee.com/kevindonisaputra', '_blank');
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
{include file="sections/footer.tpl"}
|
BIN
system/plugin/user.png
Normal file
BIN
system/plugin/user.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 281 KiB |
8
system/uploads/_sysfrm_tmp_/index.html
Normal file
8
system/uploads/_sysfrm_tmp_/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
BIN
system/uploads/admin.default.png
Normal file
BIN
system/uploads/admin.default.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
8
system/uploads/index.html
Normal file
8
system/uploads/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
BIN
system/uploads/logo.default.png
Normal file
BIN
system/uploads/logo.default.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
system/uploads/logo.png
Normal file
BIN
system/uploads/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
10
system/uploads/notifications.default.json
Normal file
10
system/uploads/notifications.default.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"expired": "Hello [[name]], your internet package [[package]] has been expired.",
|
||||
"balance_send": "You sent [[balance]] to [[name]].",
|
||||
"balance_received": "You have received [[balance]] from [[name]].",
|
||||
"reminder_7_day": "Hello *[[name]]*, \r\nyour internet package *[[package]]* will be expired in 7 days.",
|
||||
"reminder_3_day": "Hello *[[name]]*, \r\nyour internet package *[[package]]* will be expired in 3 days.",
|
||||
"reminder_1_day": "Hello *[[name]]*,\r\n your internet package *[[package]]* will be expired tomorrow.",
|
||||
"invoice_paid": "*[[company_name]]*\r\n[[address]]\r\n[[phone]]\r\n\r\n\r\nINVOICE: *[[invoice]]*\r\nDate : [[date]]\r\n[[payment_gateway]] [[payment_channel]]\r\n\r\n\r\nType : *[[type]]*\r\nPackage : *[[plan_name]]*\r\nPrice : *[[plan_price]]*\r\n\r\nUsername : *[[user_name]]*\r\nPassword : ***********\r\n\r\nExpired : *[[expired_date]]*\r\n\r\n====================\r\n[[footer]]",
|
||||
"invoice_balance": "*[[company_name]]*\r\n[[address]]\r\n[[phone]]\r\n\r\n\r\nINVOICE: *[[invoice]]*\r\nDate : [[date]]\r\n[[payment_gateway]] [[payment_channel]]\r\n\r\n\r\nType : *[[type]]*\r\nPackage : *[[plan_name]]*\r\nPrice : *[[plan_price]]*\r\n\r\n====================\r\n[[footer]]"
|
||||
}
|
1
system/uploads/notifications.json
Normal file
1
system/uploads/notifications.json
Normal file
@ -0,0 +1 @@
|
||||
{"expired":"Dear Customer, your subscription of [[package]] has expired.\r\nONLY Home\/Office Users pay using:\r\nPay Bill:4355580\r\nAcc. No:[[username]]\r\nPrice:[[price]]","reminder_7_day":"","reminder_3_day":"","reminder_1_day":"Hello, Your Internet will Expire in 1 Day, Kindly make subscribe to continue using our Services. \r\n","invoice_paid":"Successfully Purchased [[plan_name]]. Expiry: [[expired_date]]. \r\nIf not connected use MPESA message or Account No.:[[user_name]] on the login page.","invoice_balance":""}
|
205
system/uploads/paymentgateway/BankStkPush.php
Normal file
205
system/uploads/paymentgateway/BankStkPush.php
Normal file
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
|
||||
function BankStkPush_validate_config()
|
||||
{
|
||||
global $config;
|
||||
if (empty($config['Stkbankacc']) || empty($config['Stkbankname']) ) {
|
||||
sendTelegram("Bank Stk payment gateway not configured");
|
||||
r2(U . 'order/balance', 'w', Lang::T("Admin has not yet setup the payment gateway, please tell admin"));
|
||||
}
|
||||
}
|
||||
|
||||
function BankStkPush_show_config()
|
||||
{
|
||||
global $ui, $config;
|
||||
$ui->assign('_title', 'Bank Stk Push - ' . $config['CompanyName']);
|
||||
$ui->display('bankstkpush.tpl');
|
||||
}
|
||||
|
||||
function BankStkPush_save_config()
|
||||
{
|
||||
global $admin, $_L;
|
||||
$bankacc = _post('account');
|
||||
$bankname = _post('bankname');
|
||||
$d = ORM::for_table('tbl_appconfig')->where('setting', 'Stkbankacc')->find_one();
|
||||
if ($d) {
|
||||
$d->value = $bankacc;
|
||||
$d->save();
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = 'Stkbankacc';
|
||||
$d->value = $bankacc;
|
||||
$d->save();
|
||||
}
|
||||
$d = ORM::for_table('tbl_appconfig')->where('setting', 'Stkbankname')->find_one();
|
||||
if ($d) {
|
||||
$d->value = $bankname;
|
||||
$d->save();
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = 'Stkbankname';
|
||||
$d->value = $bankname;
|
||||
$d->save();
|
||||
}
|
||||
|
||||
_log('[' . $admin['username'] . ']: Stk Bank details ' . $_L['Settings_Saved_Successfully'], 'Admin', $admin['id']);
|
||||
|
||||
r2(U . 'paymentgateway/BankStkPush', 's', $_L['Settings_Saved_Successfully']);
|
||||
}
|
||||
|
||||
|
||||
function BankStkPush_create_transaction($trx, $user )
|
||||
{
|
||||
$url=(U. "plugin/initiatebankstk");
|
||||
|
||||
$d = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->where('status', 1)
|
||||
->find_one();
|
||||
$d->gateway_trx_id = '';
|
||||
$d->payment_method = 'Bank Stk Push';
|
||||
$d->pg_url_payment = $url;
|
||||
$d->pg_request = '';
|
||||
$d->expired_date = date('Y-m-d H:i:s', strtotime("+5 minutes"));
|
||||
$d->save();
|
||||
|
||||
r2(U . "order/view/" . $d['id'], 's', Lang::T("Create Transaction Success, Please click pay now to process payment"));
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
function BankStkPush_payment_notification()
|
||||
{
|
||||
$captureLogs = file_get_contents("php://input");
|
||||
|
||||
$analizzare = json_decode($captureLogs);
|
||||
/// sleep(10);
|
||||
file_put_contents('back.log',$captureLogs,FILE_APPEND);
|
||||
$response_code = $analizzare->Body->stkCallback->ResultCode;
|
||||
$resultDesc = ($analizzare->Body->stkCallback->ResultDesc);
|
||||
$merchant_req_id = ($analizzare->Body->stkCallback->MerchantRequestID);
|
||||
$checkout_req_id = ($analizzare->Body->stkCallback->CheckoutRequestID);
|
||||
|
||||
$amount_paid = ($analizzare->Body->stkCallback->CallbackMetadata->Item['0']->Value);//get the amount value
|
||||
$mpesa_code = ($analizzare->Body->stkCallback->CallbackMetadata->Item['1']->Value);//mpesa transaction code..
|
||||
$sender_phone = ($analizzare->Body->stkCallback->CallbackMetadata->Item['4']->Value);//Telephone Number
|
||||
|
||||
$PaymentGatewayRecord = ORM::for_table('tbl_payment_gateway')
|
||||
->where('checkout', $checkout_req_id)
|
||||
->where('status', 1) // Add this line to filter by status
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
$uname=$PaymentGatewayRecord->username;
|
||||
$plan_id=$PaymentGatewayRecord->plan_id;
|
||||
$mac_address=$PaymentGatewayRecord->mac_address;
|
||||
$user=$PaymentGatewayRecord;
|
||||
|
||||
$userid = ORM::for_table('tbl_customers')
|
||||
->where('username', $uname)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
$userid->username=$uname;
|
||||
$userid->save();
|
||||
$plans = ORM::for_table('tbl_plans')
|
||||
->where('id', $plan_id)
|
||||
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
if ($response_code=="1032")
|
||||
{
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$PaymentGatewayRecord->paid_date = $now;
|
||||
$PaymentGatewayRecord->status = 4;
|
||||
$PaymentGatewayRecord->save();
|
||||
|
||||
exit();
|
||||
}
|
||||
if($response_code=="1037"){
|
||||
$PaymentGatewayRecord->status = 1;
|
||||
$PaymentGatewayRecord->pg_paid_response = 'User failed to enter pin';
|
||||
$PaymentGatewayRecord->save();
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
if($response_code=="1"){
|
||||
$PaymentGatewayRecord->status = 1;
|
||||
$PaymentGatewayRecord->pg_paid_response = 'Not enough balance';
|
||||
$PaymentGatewayRecord->save();
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
if($response_code=="2001"){
|
||||
$PaymentGatewayRecord->status = 1;
|
||||
$PaymentGatewayRecord->pg_paid_response = 'Wrong Mpesa pin';
|
||||
$PaymentGatewayRecord->save();
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
if($response_code=="0"){
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$date = date('Y-m-d');
|
||||
$time= date('H:i:s');
|
||||
|
||||
$check_mpesa = ORM::for_table('tbl_payment_gateway')
|
||||
->where('gateway_trx_id', $mpesa_code)
|
||||
->find_one();
|
||||
|
||||
if($check_mpesa){
|
||||
echo "double callback, ignore one";
|
||||
|
||||
die;
|
||||
}
|
||||
|
||||
$plan_type=$plans->type;
|
||||
$UserId=$userid->id;
|
||||
|
||||
if (!Package::rechargeUser($UserId, $user['routers'], $user['plan_id'], $user['gateway'], $mpesa_code)){
|
||||
$PaymentGatewayRecord->status = 2;
|
||||
$PaymentGatewayRecord->paid_date = $now;
|
||||
$PaymentGatewayRecord->gateway_trx_id = $mpesa_code;
|
||||
$PaymentGatewayRecord->save();
|
||||
$username = $PaymentGatewayRecord->username;
|
||||
|
||||
// Check if a transaction with the same gateway_trx_id already exists
|
||||
$existingTransaction = ORM::for_table('tbl_transactions')
|
||||
->where('mpesacode', $mpesa_code)
|
||||
->find_one();
|
||||
|
||||
if (!$existingTransaction) {
|
||||
// Save transaction data to tbl_transactions
|
||||
$transaction = ORM::for_table('tbl_transactions')->create();
|
||||
$transaction->invoice = $PaymentGatewayRecord->gateway_trx_id; // Set invoice to gateway_trx_id value
|
||||
$transaction->username = $PaymentGatewayRecord->username;
|
||||
$transaction->plan_name = $PaymentGatewayRecord->plan_name;
|
||||
$transaction->price = $amount_paid;
|
||||
$transaction->recharged_on = $date;
|
||||
$transaction->recharged_time = $time;
|
||||
$transaction->expiration = $now;
|
||||
$transaction->time = $now;
|
||||
$transaction->method = $PaymentGatewayRecord->payment_method;
|
||||
$transaction->routers = 0;
|
||||
$transaction->Type = 'Balance';
|
||||
$transaction->mpesacode = $mpesa_code;
|
||||
$transaction->save();
|
||||
} else {
|
||||
error_log("Duplicate transaction entry detected for gateway_trx_id: " . $PaymentGatewayRecord->gateway_trx_id);
|
||||
}
|
||||
} else {
|
||||
// Update tbl_recharges
|
||||
$PaymentGatewayRecord->status = 2;
|
||||
$PaymentGatewayRecord->paid_date = $now;
|
||||
$PaymentGatewayRecord->gateway_trx_id = $mpesa_code;
|
||||
$PaymentGatewayRecord->save();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
234
system/uploads/paymentgateway/flutterwave.php
Normal file
234
system/uploads/paymentgateway/flutterwave.php
Normal file
@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
*
|
||||
* Payment Gateway flutterwave.com
|
||||
*
|
||||
* created by @foculinkstech
|
||||
*
|
||||
**/
|
||||
|
||||
|
||||
function flutterwave_validate_config()
|
||||
{
|
||||
global $config;
|
||||
if (empty($config['flutterwave_secret_key'])) {
|
||||
Message::sendTelegram("flutterwave payment gateway not configured");
|
||||
r2(U . 'order/package', 'w', Lang::T("Admin has not yet setup flutterwave payment gateway, please tell admin"));
|
||||
}
|
||||
}
|
||||
|
||||
function flutterwave_show_config()
|
||||
{
|
||||
global $ui;
|
||||
$ui->assign('_title', 'Flutterwave - Payment Gateway');
|
||||
$ui->assign('cur', json_decode(file_get_contents('system/paymentgateway/flutterwave_currency.json'), true));
|
||||
$ui->assign('channel', json_decode(file_get_contents('system/paymentgateway/flutterwave_channel.json'), true));
|
||||
$ui->display('flutterwave.tpl');
|
||||
}
|
||||
|
||||
|
||||
function flutterwave_save_config()
|
||||
{
|
||||
global $admin, $_L;
|
||||
$flutterwave_secret_key = _post('flutterwave_secret_key');
|
||||
$flutterwave_currency = _post('flutterwave_currency');
|
||||
$d = ORM::for_table('tbl_appconfig')->where('setting', 'flutterwave_secret_key')->find_one();
|
||||
if ($d) {
|
||||
$d->value = $flutterwave_secret_key;
|
||||
$d->save();
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = 'flutterwave_secret_key';
|
||||
$d->value = $flutterwave_secret_key;
|
||||
$d->save();
|
||||
}
|
||||
$d = ORM::for_table('tbl_appconfig')->where('setting', 'flutterwave_currency')->find_one();
|
||||
if ($d) {
|
||||
$d->value = $flutterwave_currency;
|
||||
$d->save();
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = 'flutterwave_currency';
|
||||
$d->value = $flutterwave_currency;
|
||||
$d->save();
|
||||
}
|
||||
$d = ORM::for_table('tbl_appconfig')->where('setting', 'flutterwave_channel')->find_one();
|
||||
if ($d) {
|
||||
$d->value = implode(',', $_POST['flutterwave_channel']);
|
||||
$d->save();
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = 'flutterwave_channel';
|
||||
$d->value = implode(',', $_POST['flutterwave_channel']);
|
||||
$d->save();
|
||||
}
|
||||
_log('[' . $admin['username'] . ']: Flutterwave ' . $_L['Settings_Saved_Successfully'], 'Admin', $admin['id']);
|
||||
|
||||
r2(U . 'paymentgateway/flutterwave', 's', $_L['Settings_Saved_Successfully']);
|
||||
}
|
||||
|
||||
function flutterwave_create_transaction($trx, $user)
|
||||
{
|
||||
global $config;
|
||||
$txref = uniqid('trx');
|
||||
$json = [
|
||||
'tx_ref' => $txref,
|
||||
'amount' => $trx['price'],
|
||||
'currency' => $config['flutterwave_currency'],
|
||||
'payment_options' => explode(',', $config['flutterwave_channel']),
|
||||
'customer' => [
|
||||
'email' => (empty($user['email'])) ? $user['username'] . '@' . $_SERVER['HTTP_HOST'] : $user['email'],
|
||||
'name' => $user['fullname'],
|
||||
'phonenumber' => $user['phonenumber']
|
||||
],
|
||||
'meta' => [
|
||||
'price' => $trx['price'],
|
||||
'username' => $user['username'],
|
||||
'trxid' => $trx['id']
|
||||
],
|
||||
|
||||
'customizations' => [
|
||||
'title' => $trx['plan_name'],
|
||||
'description' => $trx['plan_name'],
|
||||
],
|
||||
|
||||
'redirect_url' => U . 'callback/flutterwave'
|
||||
];
|
||||
// die(json_encode($json,JSON_PRETTY_PRINT));
|
||||
|
||||
$result = json_decode(Http::postJsonData(flutterwave_get_server() . 'payments', $json,[
|
||||
'Authorization: Bearer ' . $config['flutterwave_secret_key'],
|
||||
'Cache-Control: no-cahe'
|
||||
],
|
||||
),
|
||||
true);
|
||||
|
||||
//die(json_encode($result,JSON_PRETTY_PRINT));
|
||||
|
||||
if ($result['status'] == 'error') {
|
||||
Message::sendTelegram("Flutterwave payment failed\n\n" . json_encode($result, JSON_PRETTY_PRINT));
|
||||
r2(U . 'order/package', 'e', Lang::T("Failed to create transaction.\n".$result['message']));
|
||||
}
|
||||
$d = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->where('status', 1)
|
||||
->find_one();
|
||||
$d->gateway_trx_id = $txref;
|
||||
$d->pg_url_payment = $result['data']['link'];
|
||||
$d->pg_request = json_encode($result);
|
||||
$d->expired_date = date('Y-m-d H:i:s', strtotime("+ 6 HOUR"));
|
||||
$d->save();
|
||||
|
||||
header('Location: ' . $result['data']['link']);
|
||||
exit();
|
||||
|
||||
r2(U . "order/view/" . $d['id'], 's', Lang::T("Create Transaction Success"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
function flutterwave_payment_notification()
|
||||
{
|
||||
global $config;
|
||||
if(isset($_GET['status']))
|
||||
|
||||
{
|
||||
//* check payment status
|
||||
if($_GET['status'] == 'cancelled')
|
||||
{
|
||||
// die(json_encode($txref,JSON_PRETTY_PRINT));
|
||||
Message::sendTelegram("Flutterwave Payment Cancelled: \n\n");
|
||||
r2(U . 'order/package', 'e', Lang::T("Flutterwave Payment Cancelled."));
|
||||
}
|
||||
elseif($_GET['status'] == 'successful')
|
||||
{
|
||||
|
||||
$txid = $_GET['transaction_id'];
|
||||
$result = json_decode(Http::getData(flutterwave_get_server() . 'transactions/' . $txid. '/verify', [
|
||||
'Authorization: Bearer ' . $config['flutterwave_secret_key'],
|
||||
'Cache-Control: no-cahe'
|
||||
]), true);
|
||||
//die(json_encode($result,JSON_PRETTY_PRINT));
|
||||
{
|
||||
$id = $result['data']['id'];
|
||||
$amountPaid = $result['data']['charged_amount'];
|
||||
$amountToPay = $result['data']['meta']['price'];
|
||||
$username = $result['data']['meta']['username'];
|
||||
$trxid = $result['data']['meta']['trxid'];
|
||||
if($amountPaid >= $amountToPay)
|
||||
{
|
||||
// die(json_encode($trxid,JSON_PRETTY_PRINT));
|
||||
// echo 'Payment successful';
|
||||
$d = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $username)
|
||||
->where('status', 1)
|
||||
->find_one();
|
||||
$d->gateway_trx_id = $id;
|
||||
$d->save();
|
||||
r2(U . 'order/view/'.$trxid.'/check');
|
||||
// r2(U . 'order/package', 's', Lang::T("Flutterwave Payment Completed."));
|
||||
exit();
|
||||
//* Continue to give item to the user
|
||||
}
|
||||
else
|
||||
{
|
||||
// echo 'Fraud transactio detected';
|
||||
r2(U . 'order/package', 'e', Lang::T("Fraud transactions detected."));
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function flutterwave_get_status($trx, $user)
|
||||
{
|
||||
global $config;
|
||||
$trans_id = $trx['gateway_trx_id'];
|
||||
$result = json_decode(Http::getData(flutterwave_get_server() . 'transactions/' . $trx['gateway_trx_id']. '/verify', [
|
||||
'Authorization: Bearer ' . $config['flutterwave_secret_key'],
|
||||
'Cache-Control: no-cahe'
|
||||
]), true);
|
||||
//die(json_encode($result,JSON_PRETTY_PRINT));
|
||||
if ($result['status'] == 'error') {
|
||||
r2(U . "order/view/" . $trx['id'], 'w', Lang::T("Transaction still unpaid."));
|
||||
} else if (in_array($result['status'], ['success']) && $trx['status'] != 2) {
|
||||
if (!Package::rechargeUser($user['id'], $trx['routers'], $trx['plan_id'], $trx['gateway'], 'Flutterwave')) {
|
||||
r2(U . "order/view/" . $trx['id'], 'd', Lang::T("Failed to activate your Package, please try again later."));
|
||||
}
|
||||
$trx->pg_paid_response = json_encode($result);
|
||||
$trx->payment_method = 'Flutterwave';
|
||||
$trx->payment_channel = $result['data']['payment_type'];
|
||||
$trx->paid_date = date('Y-m-d H:i:s', strtotime( $result['data']['created_at']));
|
||||
$trx->status = 2;
|
||||
$trx->save();
|
||||
|
||||
r2(U . "order/view/" . $trx['id'], 's', Lang::T("Transaction successful."));
|
||||
} else if ($result['status'] == 'EXPIRED') {
|
||||
$trx->pg_paid_response = json_encode($result);
|
||||
$trx->status = 3;
|
||||
$trx->save();
|
||||
r2(U . "order/view/" . $trx['id'], 'd', Lang::T("Transaction expired."));
|
||||
} else if ($trx['status'] == 2) {
|
||||
r2(U . "order/view/" . $trx['id'], 'd', Lang::T("Transaction has been paid.."));
|
||||
}else{
|
||||
Message::sendTelegram("flutterwave_get_status: unknown result\n\n".json_encode($result, JSON_PRETTY_PRINT));
|
||||
r2(U . "order/view/" . $trx['id'], 'd', Lang::T("Unknown Command."));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function flutterwave_get_server()
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'Live') {
|
||||
return 'https://api.flutterwave.com/v3/';
|
||||
} else {
|
||||
return 'https://api.flutterwave.com/v3/';
|
||||
}
|
||||
}
|
35
system/uploads/paymentgateway/flutterwave_channel.json
Normal file
35
system/uploads/paymentgateway/flutterwave_channel.json
Normal file
@ -0,0 +1,35 @@
|
||||
[
|
||||
|
||||
{
|
||||
"id": "card",
|
||||
"name": "Card Payment"
|
||||
},
|
||||
{
|
||||
"id": "ussd",
|
||||
"name": "USSD"
|
||||
},
|
||||
{
|
||||
"id": "account",
|
||||
"name": "Bank Account"
|
||||
},
|
||||
{
|
||||
"id": "banktransfer",
|
||||
"name": "Bank Transfer"
|
||||
},
|
||||
{
|
||||
"id": "nqr",
|
||||
"name": "QR payment"
|
||||
},
|
||||
{
|
||||
"id": "mpesa",
|
||||
"name": "M-Pesa"
|
||||
},
|
||||
{
|
||||
"id": "mobilemoneyghana",
|
||||
"name": "Mobile money Ghana"
|
||||
},
|
||||
{
|
||||
"id": "credit",
|
||||
"name": "Credit payment"
|
||||
}
|
||||
]
|
30
system/uploads/paymentgateway/flutterwave_currency.json
Normal file
30
system/uploads/paymentgateway/flutterwave_currency.json
Normal file
@ -0,0 +1,30 @@
|
||||
[
|
||||
{
|
||||
"id": "NGN",
|
||||
"name": "Nigerian Naira"
|
||||
},
|
||||
{
|
||||
"id": "GHC",
|
||||
"name": "Ghana Cedis"
|
||||
},
|
||||
{
|
||||
"id": "KES",
|
||||
"name": "Kenyan Shilling"
|
||||
},
|
||||
{
|
||||
"id": "ZAR",
|
||||
"name": "South African Rand"
|
||||
},
|
||||
{
|
||||
"id": "GBP",
|
||||
"name": "British Pound Sterling"
|
||||
},
|
||||
{
|
||||
"id": "USD",
|
||||
"name": "United States Dollar"
|
||||
},
|
||||
{
|
||||
"id": "TZS",
|
||||
"name": "Tanzanian Shilling"
|
||||
}
|
||||
]
|
BIN
system/uploads/paymentgateway/index.html
Normal file
BIN
system/uploads/paymentgateway/index.html
Normal file
Binary file not shown.
BIN
system/uploads/sms/index.html
Normal file
BIN
system/uploads/sms/index.html
Normal file
Binary file not shown.
8
system/uploads/system/index.html
Normal file
8
system/uploads/system/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
BIN
system/uploads/user.default.jpg
Normal file
BIN
system/uploads/user.default.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
ui/ui/Ass/.DS_Store
vendored
Normal file
BIN
ui/ui/Ass/.DS_Store
vendored
Normal file
Binary file not shown.
30929
ui/ui/Ass/css/style.css
Normal file
30929
ui/ui/Ass/css/style.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
ui/ui/Ass/icons/.DS_Store
vendored
Normal file
BIN
ui/ui/Ass/icons/.DS_Store
vendored
Normal file
Binary file not shown.
258
ui/ui/hotspot-edit.tpl
Normal file
258
ui/ui/hotspot-edit.tpl
Normal file
@ -0,0 +1,258 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">{Lang::T('Edit Service Plan')} || {$d['name_plan']}</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}services/edit-post">
|
||||
<input type="hidden" name="id" value="{$d['id']}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Status')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="enabled" value="1" {if $d['enabled'] == 1}checked{/if}> Enable
|
||||
<input type="radio" name="enabled" value="0" {if $d['enabled'] == 0}checked{/if}> Disable
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="prepaid" onclick="prePaid()" value="yes"
|
||||
{if $d['prepaid'] == yes}checked{/if}>
|
||||
Prepaid
|
||||
<input type="radio" name="prepaid" onclick="postPaid()" value="no"
|
||||
{if $d['prepaid'] == no}checked{/if}> Postpaid
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="plan_type" value="Personal"
|
||||
{if $d['plan_type'] == 'Personal'}checked{/if}>
|
||||
Personal
|
||||
<input type="radio" name="plan_type" value="Business"
|
||||
{if $d['plan_type'] == 'Business'}checked{/if}> Business
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{if $_c['radius_enable'] and $d['is_radius']}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Radius</label>
|
||||
<div class="col-md-10">
|
||||
<label class="label label-primary">RADIUS</label>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name" name="name" maxlength="40"
|
||||
value="{$d['name_plan']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" id="Unlimited" name="typebp" value="Unlimited"
|
||||
{if $d['typebp'] eq 'Unlimited'} checked {/if}> {Lang::T('Unlimited')}
|
||||
<input type="radio" id="Limited" {if $_c['radius_enable'] and $d['is_radius']}disabled{/if}
|
||||
name="typebp" value="Limited" {if $d['typebp'] eq 'Limited'} checked {/if}>
|
||||
{Lang::T('Limited')}
|
||||
</div>
|
||||
</div>
|
||||
<div {if $d['typebp'] eq 'Unlimited'} style="display:none;" {/if} id="Type">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Limit Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" id="Time_Limit" name="limit_type" value="Time_Limit"
|
||||
{if $d['limit_type'] eq 'Time_Limit'} checked {/if}> {Lang::T('Time Limit')}
|
||||
<input type="radio" id="Data_Limit" name="limit_type" value="Data_Limit"
|
||||
{if $d['limit_type'] eq 'Data_Limit'} checked {/if}> {Lang::T('Data Limit')}
|
||||
<input type="radio" id="Both_Limit" name="limit_type" value="Both_Limit"
|
||||
{if $d['limit_type'] eq 'Both_Limit'} checked {/if}> {Lang::T('Both Limit')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div {if $d['typebp'] eq 'Unlimited'} style="display:none;"
|
||||
{elseif ($d['time_limit']) eq '0'}
|
||||
style="display:none;" {/if} id="TimeLimit">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Time Limit')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="time_limit" name="time_limit"
|
||||
value="{$d['time_limit']}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="time_unit" name="time_unit">
|
||||
<option value="Hrs" {if $d['time_unit'] eq 'Hrs'} selected {/if}>{Lang::T('Hrs')}
|
||||
</option>
|
||||
<option value="Mins" {if $d['time_unit'] eq 'Mins'} selected {/if}>{Lang::T('Mins')}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div {if $d['typebp'] eq 'Unlimited'} style="display:none;"
|
||||
{elseif ($d['data_limit']) eq '0'}
|
||||
style="display:none;" {/if} id="DataLimit">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Data Limit')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="data_limit" name="data_limit"
|
||||
value="{$d['data_limit']}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="data_unit" name="data_unit">
|
||||
<option value="MB" {if $d['data_unit'] eq 'MB'} selected {/if}>MBs</option>
|
||||
<option value="GB" {if $d['data_unit'] eq 'GB'} selected {/if}>GBs</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}bandwidth/add">{Lang::T('Bandwidth Name')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="id_bw" name="id_bw" class="form-select" style="height: 52px; background-color: white;">
|
||||
{foreach $b as $bs}
|
||||
<option value="{$bs['id']}" {if $d['id_bw'] eq $bs['id']} selected {/if}>
|
||||
{$bs['name_bw']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Price')}</label>
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="basic-addon1">{$_c['currency_code']}</span>
|
||||
<input type="number" class="form-control" name="price" value="{$d['price']}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Shared Users')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="sharedusers" name="sharedusers"
|
||||
value="{$d['shared_users']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Validity')}</label>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="validity" name="validity"
|
||||
value="{$d['validity']}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="validity_unit" name="validity_unit">
|
||||
{if $d['prepaid'] == yes}
|
||||
<option value="Mins" {if $d['validity_unit'] eq 'Mins'} selected {/if}>{Lang::T('Mins')}
|
||||
</option>
|
||||
<option value="Hrs" {if $d['validity_unit'] eq 'Hrs'} selected {/if}>{Lang::T('Hrs')}
|
||||
</option>
|
||||
<option value="Days" {if $d['validity_unit'] eq 'Days'} selected {/if}>{Lang::T('Days')}
|
||||
</option>
|
||||
<option value="Months" {if $d['validity_unit'] eq 'Months'} selected {/if}>
|
||||
{Lang::T('Months')}</option>
|
||||
{else}
|
||||
<option value="Period" {if $d['validity_unit'] eq 'Period'} selected {/if}>
|
||||
{Lang::T('Period')}</option>
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('1 Period = 1 Month, Expires the 20th of each month')}
|
||||
</p>
|
||||
</div>
|
||||
<span id="routerChoose" class="{if $d['is_radius']}hidden{/if}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}routers/add">{Lang::T('Router Name')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="routers" name="routers"
|
||||
value="{$d['routers']}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<legend>{Lang::T('Expired Action')} <sub>{Lang::T('Optional')}</sub></legend>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}pool/add">{Lang::T('Expired IP Pool')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="pool_expired" name="pool_expired" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value=''>{Lang::T('Select Pool')}</option>
|
||||
{foreach $p as $ps}
|
||||
<option value="{$ps['pool_name']}" {if $d['pool_expired'] eq $ps['pool_name']} selected
|
||||
{/if}>{$ps['pool_name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{* <div class="form-group row" id="AddressList">
|
||||
<label class="col-md-2 control-label">{Lang::T('Address List')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="list_expired" id="list_expired" value="{$d['list_expired']}">
|
||||
</div>
|
||||
</div> *}
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button class="btn btn-success" type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a href="{$_url}services/hotspot">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var preOpt = `<option value="Mins">{Lang::T('Mins')}</option>
|
||||
<option value="Hrs">{Lang::T('Hrs')}</option>
|
||||
<option value="Days">{Lang::T('Days')}</option>
|
||||
<option value="Months">{Lang::T('Months')}</option>`;
|
||||
var postOpt = `<option value="Period">{Lang::T('Period')}</option>`;
|
||||
function prePaid() {
|
||||
$("#validity_unit").html(preOpt);
|
||||
}
|
||||
|
||||
function postPaid() {
|
||||
$("#validity_unit").html(postOpt);
|
||||
}
|
||||
</script>
|
||||
|
||||
{if $_c['radius_enable'] && $d['is_radius']}
|
||||
{literal}
|
||||
<script>
|
||||
function isRadius(cek) {
|
||||
if (cek.checked) {
|
||||
$("#routerChoose").addClass('hidden');
|
||||
document.getElementById("routers").required = false;
|
||||
document.getElementById("Limited").disabled = true;
|
||||
} else {
|
||||
document.getElementById("Limited").disabled = false;
|
||||
document.getElementById("routers").required = true;
|
||||
$("#routerChoose").removeClass('hidden');
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
$.ajax({
|
||||
url: "index.php?_route=autoload/pool",
|
||||
data: "routers=radius",
|
||||
cache: false,
|
||||
success: function(msg) {
|
||||
$("#pool_expired").html(msg);
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
</script>
|
||||
{/literal}
|
||||
{/if}
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
8
ui/ui/index.html
Normal file
8
ui/ui/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
815
ui/ui/indexmain.tpl
Normal file
815
ui/ui/indexmain.tpl
Normal file
@ -0,0 +1,815 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<!-- Add Project -->
|
||||
<div class="modal fade" id="addProjectSidebar">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Create Project</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label class="text-black font-w500">Project Name</label>
|
||||
<input type="text" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="text-black font-w500">Dadeline</label>
|
||||
<div class="cal-icon"><input type="date" class="form-control"><i
|
||||
class="far fa-calendar-alt"></i></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="text-black font-w500">Client Name</label>
|
||||
<input type="text" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-primary">CREATE</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-secondary card-border" style="background:#3444d5 !important;"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center">
|
||||
<div class="media-body me-3">
|
||||
<h2 class="count num-text text-black font-w700">78</h2>
|
||||
<span class="fs-14">Total Project Handled</span>
|
||||
</div>
|
||||
<svg width="36" height="36" viewBox="0 0 36 36" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M34.422 13.9831C34.3341 13.721 34.1756 13.4884 33.9638 13.3108C33.7521 13.1332 33.4954 13.0175 33.222 12.9766L23.649 11.5141L19.353 2.36408C19.2319 2.10638 19.0399 1.88849 18.7995 1.73587C18.5591 1.58325 18.2803 1.5022 17.9955 1.5022C17.7108 1.5022 17.4319 1.58325 17.1915 1.73587C16.9511 1.88849 16.7592 2.10638 16.638 2.36408L12.342 11.5141L2.76902 12.9766C2.49635 13.0181 2.24042 13.1341 2.02937 13.3117C1.81831 13.4892 1.6603 13.7215 1.57271 13.9831C1.48511 14.2446 1.47133 14.5253 1.53287 14.7941C1.59441 15.063 1.72889 15.3097 1.92152 15.5071L8.89802 22.6501L7.24802 32.7571C7.20299 33.0345 7.23679 33.3189 7.34555 33.578C7.45431 33.8371 7.63367 34.0605 7.86319 34.2226C8.09271 34.3847 8.36315 34.4791 8.64371 34.495C8.92426 34.5109 9.20365 34.4477 9.45002 34.3126L18 29.5906L26.55 34.3126C26.7964 34.4489 27.0761 34.5131 27.3573 34.4978C27.6384 34.4826 27.9096 34.3885 28.1398 34.2264C28.37 34.0643 28.5499 33.8406 28.659 33.5811C28.768 33.3215 28.8018 33.0365 28.7565 32.7586L27.1065 22.6516L34.0785 15.5071C34.2703 15.3091 34.4037 15.0622 34.4643 14.7933C34.5249 14.5245 34.5103 14.2441 34.422 13.9831Z"
|
||||
fill="#864AD1" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-warning card-border"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center">
|
||||
<div class="media-body me-3">
|
||||
<h2 class="count num-text text-black font-w700">214</h2>
|
||||
<span class="fs-14">Contacts You Have</span>
|
||||
</div>
|
||||
<svg width="36" height="36" viewBox="0 0 36 36" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M17.8935 22.5C23.6925 22.5 28.3935 17.799 28.3935 12C28.3935 6.20101 23.6925 1.5 17.8935 1.5C12.0945 1.5 7.39351 6.20101 7.39351 12C7.39351 17.799 12.0945 22.5 17.8935 22.5Z"
|
||||
fill="#FFB930" />
|
||||
<path
|
||||
d="M29.5605 21.3344C29.217 20.9909 28.851 20.6699 28.476 20.3564C27.2159 21.96 25.6078 23.2562 23.7733 24.1472C21.9388 25.0382 19.9259 25.5007 17.8864 25.4996C15.847 25.4986 13.8345 25.0342 12.0009 24.1414C10.1673 23.2486 8.56051 21.9507 7.30199 20.3459C5.447 21.8906 3.95577 23.8256 2.9347 26.013C1.91364 28.2003 1.3879 30.586 1.39499 32.9999C1.39499 33.3978 1.55303 33.7793 1.83433 34.0606C2.11564 34.3419 2.49717 34.4999 2.89499 34.4999H32.895C33.2928 34.4999 33.6743 34.3419 33.9557 34.0606C34.237 33.7793 34.395 33.3978 34.395 32.9999C34.4004 30.8324 33.9759 28.6854 33.146 26.683C32.3162 24.6807 31.0975 22.8627 29.5605 21.3344Z"
|
||||
fill="#FFB930" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-primary card-border"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center">
|
||||
<div class="media-body me-3">
|
||||
<h2 class="count num-text text-black font-w700">93</h2>
|
||||
<span class="fs-14">Total Unfinished Task</span>
|
||||
</div>
|
||||
<svg class="primary-icon" width="36" height="36" viewBox="0 0 36 36" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M11.9999 1.5H5.99994C3.51466 1.5 1.49994 3.51472 1.49994 6V29.8125C1.49994 32.2977 3.51466 34.3125 5.99994 34.3125H11.9999C14.4852 34.3125 16.4999 32.2977 16.4999 29.8125V6C16.4999 3.51472 14.4852 1.5 11.9999 1.5Z"
|
||||
fill="#20F174" />
|
||||
<path
|
||||
d="M30 1.5H24C21.5147 1.5 19.5 3.51472 19.5 6V12C19.5 14.4853 21.5147 16.5 24 16.5H30C32.4853 16.5 34.5 14.4853 34.5 12V6C34.5 3.51472 32.4853 1.5 30 1.5Z"
|
||||
fill="#20F174" />
|
||||
<path
|
||||
d="M30 19.5H24C21.5147 19.5 19.5 21.5147 19.5 24V30C19.5 32.4853 21.5147 34.5 24 34.5H30C32.4853 34.5 34.5 32.4853 34.5 30V24C34.5 21.5147 32.4853 19.5 30 19.5Z"
|
||||
fill="#20F174" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-lg-6 col-sm-6">
|
||||
<div class="card card-bd">
|
||||
<div class="bg-info card-border"></div>
|
||||
<div class="card-body box-style">
|
||||
<div class="media align-items-center">
|
||||
<div class="media-body me-3">
|
||||
<h2 class="count num-text text-black font-w700">12</h2>
|
||||
<span class="fs-14">Unread Messages</span>
|
||||
</div>
|
||||
<svg width="46" height="46" viewBox="0 0 46 46" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M34.4999 1.91663H11.4999C8.95917 1.91967 6.52338 2.93032 4.72682 4.72688C2.93026 6.52345 1.91961 8.95924 1.91656 11.5V26.8333C1.91935 29.0417 2.6834 31.1816 4.07994 32.8924C5.47648 34.6031 7.42011 35.7801 9.58323 36.225V42.1666C9.58318 42.5136 9.67733 42.8541 9.85564 43.1518C10.0339 43.4495 10.2897 43.6932 10.5957 43.8569C10.9016 44.0206 11.2463 44.0982 11.5929 44.0813C11.9395 44.0645 12.275 43.9539 12.5636 43.7613L23.5749 36.4166H34.4999C37.0406 36.4136 39.4764 35.4029 41.273 33.6064C43.0695 31.8098 44.0802 29.374 44.0832 26.8333V11.5C44.0802 8.95924 43.0695 6.52345 41.273 4.72688C39.4764 2.93032 37.0406 1.91967 34.4999 1.91663ZM30.6666 24.9166H15.3332C14.8249 24.9166 14.3374 24.7147 13.9779 24.3552C13.6185 23.9958 13.4166 23.5083 13.4166 23C13.4166 22.4916 13.6185 22.0041 13.9779 21.6447C14.3374 21.2852 14.8249 21.0833 15.3332 21.0833H30.6666C31.1749 21.0833 31.6624 21.2852 32.0219 21.6447C32.3813 22.0041 32.5832 22.4916 32.5832 23C32.5832 23.5083 32.3813 23.9958 32.0219 24.3552C31.6624 24.7147 31.1749 24.9166 30.6666 24.9166ZM34.4999 17.25H11.4999C10.9916 17.25 10.5041 17.048 10.1446 16.6886C9.78517 16.3291 9.58323 15.8416 9.58323 15.3333C9.58323 14.825 9.78517 14.3374 10.1446 13.978C10.5041 13.6186 10.9916 13.4166 11.4999 13.4166H34.4999C35.0082 13.4166 35.4957 13.6186 35.8552 13.978C36.2146 14.3374 36.4166 14.825 36.4166 15.3333C36.4166 15.8416 36.2146 16.3291 35.8552 16.6886C35.4957 17.048 35.0082 17.25 34.4999 17.25Z"
|
||||
fill="#3ECDFF" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-xxl-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-block border-0 pb-0">
|
||||
<div class="d-flex justify-content-between pb-3">
|
||||
<h4 class="mb-0 text-black fs-20">Project Created</h4>
|
||||
<div class="dropdown">
|
||||
<a href="javascript:void(0)" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
<path
|
||||
d="M12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
<path
|
||||
d="M12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-left">
|
||||
<a class="dropdown-item" href="javascript:void(0);">Edit</a>
|
||||
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="fs-36 text-black font-w600 me-4">25%</span>
|
||||
<div>
|
||||
<svg class="me-2" width="27" height="14" viewBox="0 0 27 14" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 13.435L13.435 0L26.8701 13.435H0Z" fill="#fc5130"></path>
|
||||
</svg>
|
||||
<span>last month $563,443</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body pb-0 px-2 pt-2">
|
||||
<div id="chartTimeline" class="timeline-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-6 col-sm-6">
|
||||
<div class="card">
|
||||
<div class="card-header border-0 pb-0">
|
||||
<h4 class="fs-20 mb-0 text-black">New Clients</h4>
|
||||
<div class="dropdown">
|
||||
<a href="javascript:void(0)" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
<path
|
||||
d="M12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
<path
|
||||
d="M12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-left">
|
||||
<a class="dropdown-item" href="javascript:void(0);">Edit</a>
|
||||
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body text-center pb-0 px-2 pt-2">
|
||||
<div id="widgetChart1" class="widgetChart1 dashboard-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-6 col-sm-6">
|
||||
<div class="card">
|
||||
<div class="card-header border-0 pb-0">
|
||||
<h4 class="fs-20 mb-0 text-black">Monthly Target</h4>
|
||||
<div class="dropdown">
|
||||
<a href="javascript:void(0)" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
<path
|
||||
d="M12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
<path
|
||||
d="M12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-left">
|
||||
<a class="dropdown-item" href="javascript:void(0);">Edit</a>
|
||||
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body text-center pt-0">
|
||||
<div id="radialChart" class="monthly-project-chart"></div>
|
||||
<span class="fs-14 text-black d-block op5">100 Projects/ monthy</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-xxl-12">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-6">
|
||||
<div class="card">
|
||||
<div class="card-header border-0">
|
||||
<h4 class="fs-16 text-black font-w500">Project Released</h4>
|
||||
<div class="d-flex align-items-center">
|
||||
<svg width="14" height="8" viewBox="0 0 14 8" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.90735e-06 0.499999L7 7.5L14 0.5" fill="#FF6746" />
|
||||
</svg>
|
||||
<span class="fs-28 font-w600 ms-2 text-black">4%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body text-center pb-0 p-0">
|
||||
<div id="widgetChart2" class="dashboard-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="card">
|
||||
<div
|
||||
class="card-body text-center d-flex align-items-center justify-content-between">
|
||||
<div class="d-inline-block position-relative donut-chart-sale">
|
||||
<span class="donut1"
|
||||
data-peity='{ "fill": ["#fc5130", "rgba(241, 241, 241,1)"], "innerRadius": 33, "radius": 10}'>3/8</span>
|
||||
<small class="text-primary">29%</small>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="fs-28 font-w600 mb-0 text-end text-black">567</h2>
|
||||
<p class="mb-0 fs-14 font-w400 text-black">Contacts Added</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-12">
|
||||
<div class="card overflow-hidden">
|
||||
<div class="card-body">
|
||||
<div class="text-center">
|
||||
<div class="profile-photo">
|
||||
<img src="assets/img/man.png" width="100"
|
||||
class="img-fluid rounded-circle" alt="">
|
||||
</div>
|
||||
<h3 class="mt-4 mb-1">Therichpost</h3>
|
||||
<p class="text-muted">Youtuber</p>
|
||||
<a class="btn btn-outline-primary btn-rounded mt-3 px-5"
|
||||
href="javascript:void(0)">Folllow</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer pt-0 pb-0 text-center">
|
||||
<div class="row">
|
||||
<div class="col-4 pt-3 pb-3 border-right">
|
||||
<h3 class="mb-1">150</h3><span>Follower</span>
|
||||
</div>
|
||||
<div class="col-4 pt-3 pb-3 border-right">
|
||||
<h3 class="mb-1">140</h3><span>Place Stay</span>
|
||||
</div>
|
||||
<div class="col-4 pt-3 pb-3">
|
||||
<h3 class="mb-1">45</h3><span>Reviews</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-12">
|
||||
<div class="card message-bx">
|
||||
<div class="card-header border-0 d-sm-flex d-block pb-0">
|
||||
<div>
|
||||
<h4 class="fs-20 mb-0 text-black mb-sm-0 mb-2">Recent Messages</h4>
|
||||
</div>
|
||||
<a href="#" class="btn btn-primary shadow-primary btn-rounded text-white">+ New
|
||||
Message</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="media mb-3 pb-3 border-bottom">
|
||||
<div class="image-bx me-sm-4 me-2">
|
||||
<img src="assets/img/hacker.png" alt="" class="rounded-circle img-1">
|
||||
<span class="active"></span>
|
||||
</div>
|
||||
<div
|
||||
class="media-body d-sm-flex justify-content-between d-block align-items-center">
|
||||
<div class="me-sm-3 me-0">
|
||||
<h6 class="fs-16 font-w600 mb-sm-2 mb-0"><a href="#"
|
||||
class="text-black">Laura Chyan</a></h6>
|
||||
<p class="text-black mb-1">Lorem ipsum dolor sit amet, consectetur
|
||||
adipiscing elit, sed do eiusmod tempor incididunt ut</p>
|
||||
<span class="fs-14">5m ago</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media mb-3 pb-3 border-bottom">
|
||||
<div class="image-bx me-sm-4 me-2">
|
||||
<img src="assets/img/gamer.png" alt="" class="rounded-circle img-1">
|
||||
</div>
|
||||
<div
|
||||
class="media-body d-sm-flex justify-content-between d-block align-items-center">
|
||||
<div class="me-sm-3 me-0">
|
||||
<h6 class="fs-16 font-w600 mb-sm-2 mb-0"><a href="#"
|
||||
class="text-black">Olivia Rellaq</a></h6>
|
||||
<p class="text-black mb-1">Lorem ipsum dolor sit amet, consectetur
|
||||
adipiscing elit, sed do eiusmod tempor incididunt ut</p>
|
||||
<span class="fs-14">41m ago</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media">
|
||||
<div class="image-bx me-sm-4 me-2">
|
||||
<img src="assets/img/man (1).png" alt="" class="rounded-circle img-1">
|
||||
<span class="active"></span>
|
||||
</div>
|
||||
<div
|
||||
class="media-body d-sm-flex justify-content-between d-block align-items-center">
|
||||
<div class="me-sm-3 me-0">
|
||||
<h6 class="fs-16 font-w600 mb-sm-2 mb-0"><a href="#"
|
||||
class="text-black">Keanu Tipes</a></h6>
|
||||
<p class="text-black mb-1">Nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse
|
||||
cillum...</p>
|
||||
<span class="fs-14">25m ago</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6 col-xxl-12">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header border-0 pb-0">
|
||||
<div class="me-2">
|
||||
<h4 class="fs-20 mb-0 font-w500 text-black">Upcoming Projects</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="border-bottom up-project-bx pb-4 mb-4">
|
||||
<span class="fs-16 text-primary mb-2 d-block sub-title font-w500">Yoast
|
||||
Esac</span>
|
||||
<div class="d-flex">
|
||||
<p class="font-w500 me-auto mb-2 title fs-20"><a href="#"
|
||||
class="text-black">Redesign Kripton Mobile App</a></p>
|
||||
<div class="dropdown mb-3">
|
||||
<a href="javascript:void(0)" data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
d="M12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
d="M12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-left">
|
||||
<a class="dropdown-item" href="javascript:void(0);">Edit</a>
|
||||
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3"><i class="far fa-calendar me-3"
|
||||
aria-hidden="true"></i>Created on Sep 8th, 2020</div>
|
||||
<div class="media align-items-center">
|
||||
<div class="power-ic me-3">
|
||||
<i class="fa fa-bolt" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="media-body">
|
||||
<p class="mb-1">Deadline</p>
|
||||
<span class="text-black font-w600">Tuesday, Sep 29th 2020</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-bottom up-project-bx pb-4 mb-4">
|
||||
<span class="fs-16 text-primary mb-2 d-block sub-title font-w500">Yoast
|
||||
Esac</span>
|
||||
<div class="d-flex">
|
||||
<p class="font-w500 me-auto title mb-2 fs-20"><a href="#"
|
||||
class="text-black">Build Branding Persona for Etza.id</a></p>
|
||||
<div class="dropdown mb-3">
|
||||
<a href="javascript:void(0)" data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
d="M12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
d="M12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-left">
|
||||
<a class="dropdown-item" href="javascript:void(0);">Edit</a>
|
||||
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3"><i class="far fa-calendar me-3"
|
||||
aria-hidden="true"></i>Created on Sep 8th, 2020</div>
|
||||
<div class="media align-items-center">
|
||||
<div class="power-ic me-3">
|
||||
<i class="fa fa-bolt" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="media-body">
|
||||
<p class="mb-1">Deadline</p>
|
||||
<span class="text-black font-w600">Tuesday, Sep 29th 2020</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="up-project-bx">
|
||||
<span class="fs-16 text-primary sub-title mb-2 d-block font-w500">Yoast
|
||||
Esac</span>
|
||||
<div class="d-flex">
|
||||
<p class="font-w500 me-auto title mb-2 fs-20"><a href="#"
|
||||
class="text-black">Manage SEO for Eclan Company Profile</a></p>
|
||||
<div class="dropdown mb-3">
|
||||
<a href="javascript:void(0)" data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
d="M12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
<path
|
||||
d="M12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20Z"
|
||||
stroke="#575757" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-left">
|
||||
<a class="dropdown-item" href="javascript:void(0);">Edit</a>
|
||||
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3"><i class="far fa-calendar me-3"
|
||||
aria-hidden="true"></i>Created on Sep 8th, 2020</div>
|
||||
<div class="media align-items-center">
|
||||
<div class="power-ic me-3">
|
||||
<i class="fa fa-bolt" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="media-body">
|
||||
<p class="mb-1">Deadline</p>
|
||||
<span class="text-black font-w600">Tuesday, Sep 29th 2020</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card kanbanPreview-bx">
|
||||
<div class="card-body">
|
||||
<div class="sub-card bg-secondary d-flex text-white">
|
||||
<div class="me-auto pe-2">
|
||||
<h4 class="fs-20 mb-0 font-w600 text-white">Quick To-Do List</h4>
|
||||
<span class="fs-14 op6 font-w200">Lorem ipsum dolor sit amet</span>
|
||||
</div>
|
||||
<a href="#" class="plus-icon"><i class="fa fa-plus"
|
||||
aria-hidden="true"></i></a>
|
||||
</div>
|
||||
<div class="sub-card">
|
||||
<span class="text-warning sub-title fs-14">Graphic Deisgner</span>
|
||||
<p class="font-w500"><a href="#" class="text-black">Visual Graphic for
|
||||
Presentation to Client</a></p>
|
||||
<div class="row justify-content-between align-items-center">
|
||||
<div class="col-6">
|
||||
<span>Aug 4, 2021</span>
|
||||
</div>
|
||||
<ul class="users col-6">
|
||||
<li><img src="assets/img/man (1).png" alt=""></li>
|
||||
<li><img
|
||||
src="https://cdn-icons-png.flaticon.com/512/921/921071.png">
|
||||
</li>
|
||||
<li><img
|
||||
src="https://cdn-icons-png.flaticon.com/512/921/921071.png">
|
||||
</li>
|
||||
<li><img src="https://cdn-icons-png.flaticon.com/512/1154/1154448.png"
|
||||
alt=""></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-card">
|
||||
<span class="text-primary sub-title fs-14">Database Engineer</span>
|
||||
<p class="font-w500"><a href="#" class="text-black">Build Database Design
|
||||
for Fasto Admin v2</a></p>
|
||||
<div class="row justify-content-between align-items-center">
|
||||
<div class="col-6">
|
||||
<span>Aug 4, 2021</span>
|
||||
</div>
|
||||
<ul class="users col-6">
|
||||
<li><img src="assets/img/man (1).png" alt=""></li>
|
||||
<li><img
|
||||
src="https://cdn-icons-png.flaticon.com/512/921/921071.png">
|
||||
</li>
|
||||
<li><img src="assets/img/man (1).png" alt=""></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-card">
|
||||
<span class="text-secondary sub-title fs-14">Digital Marketing</span>
|
||||
<p class="font-w500"><a href="#" class="text-black">Make Promotional Ads for
|
||||
Instagram Fasto’s</a></p>
|
||||
<div class="row justify-content-between align-items-center mb-4">
|
||||
<div class="col-6">
|
||||
<span>Aug 4, 2021</span>
|
||||
</div>
|
||||
<ul class="users col-6">
|
||||
<li><img src="assets/img/man (1).png" alt=""></li>
|
||||
<li><img
|
||||
src="https://cdn-icons-png.flaticon.com/512/921/921071.png">
|
||||
</li>
|
||||
<li><img src="assets/img/man (1).png" alt=""></li>
|
||||
</ul>
|
||||
</div>
|
||||
<span><i class="far fa-comment me-2"></i>2 Comment</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header border-0 pb-0">
|
||||
<h4 class="card-title">Timeline</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="DZ_W_TimeLine"
|
||||
class="widget-timeline dz-scroll height370 ps ps--active-y">
|
||||
<ul class="timeline">
|
||||
<li>
|
||||
<div class="timeline-badge primary"></div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>10 minutes ago</span>
|
||||
<h6 class="mb-0">Youtube, a video-sharing website, goes live
|
||||
<strong class="text-primary">$500</strong>.</h6>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge info">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>20 minutes ago</span>
|
||||
<h6 class="mb-0">New order placed <strong
|
||||
class="text-info">#XF-2356.</strong></h6>
|
||||
<p class="mb-0">Quisque a consequat ante Sit amet magna at
|
||||
volutapt...</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge danger">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>30 minutes ago</span>
|
||||
<h6 class="mb-0">john just buy your product <strong
|
||||
class="text-warning">Sell $250</strong></h6>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge success">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>15 minutes ago</span>
|
||||
<h6 class="mb-0">StumbleUpon is acquired by eBay. </h6>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge warning">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>20 minutes ago</span>
|
||||
<h6 class="mb-0">Mashable, a news website and blog, goes live.
|
||||
</h6>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge dark">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>20 minutes ago</span>
|
||||
<h6 class="mb-0">Mashable, a news website and blog, goes live.
|
||||
</h6>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge primary"></div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>10 minutes ago</span>
|
||||
<h6 class="mb-0">Youtube, a video-sharing website, goes live
|
||||
<strong class="text-primary">$500</strong>.</h6>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge info">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>20 minutes ago</span>
|
||||
<h6 class="mb-0">New order placed <strong
|
||||
class="text-info">#XF-2356.</strong></h6>
|
||||
<p class="mb-0">Quisque a consequat ante Sit amet magna at
|
||||
volutapt...</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge danger">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>30 minutes ago</span>
|
||||
<h6 class="mb-0">john just buy your product <strong
|
||||
class="text-warning">Sell $250</strong></h6>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge success">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>15 minutes ago</span>
|
||||
<h6 class="mb-0">StumbleUpon is acquired by eBay. </h6>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge warning">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>20 minutes ago</span>
|
||||
<h6 class="mb-0">Mashable, a news website and blog, goes live.
|
||||
</h6>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="timeline-badge dark">
|
||||
</div>
|
||||
<a class="timeline-panel text-muted" href="javascript:void(0);">
|
||||
<span>20 minutes ago</span>
|
||||
<h6 class="mb-0">Mashable, a news website and blog, goes live.
|
||||
</h6>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="ps__rail-x" style="left: 0px; bottom: 0px;">
|
||||
<div class="ps__thumb-x" tabindex="0" style="left: 0px; width: 0px;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="ps__rail-y" style="top: 0px; height: 370px; right: 0px;">
|
||||
<div class="ps__thumb-y" tabindex="0" style="top: 0px; height: 229px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card" style="display: grid; align-content: center;">
|
||||
<div class="card-body text-center ai-icon text-primary">
|
||||
<svg id="rocket-icon" class="my-2" viewBox="0 0 24 24" width="80" height="80"
|
||||
stroke="currentColor" stroke-width="1" fill="none" stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"></path>
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<path d="M16 10a4 4 0 0 1-8 0"></path>
|
||||
</svg>
|
||||
<h4 class="my-2">You don’t have badges yet</h4>
|
||||
<a href="javascript:void(0);" class="btn my-2 btn-primary btn-lg px-4"><i
|
||||
class="fa fa-usd"></i> Earn Budges</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-xl-3 col-xxl-3 col-sm-6">
|
||||
<div class="card">
|
||||
<div class="social-graph-wrapper widget-facebook">
|
||||
<span class="s-icon"><i class="fab fa-facebook-f"></i></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 border-end">
|
||||
<div class="pt-3 pb-3 pl-0 pr-0 text-center">
|
||||
<h4 class="m-1"><span class="count counter">89</span> k</h4>
|
||||
<p class="m-0">Friends</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="pt-3 pb-3 pl-0 pr-0 text-center">
|
||||
<h4 class="m-1"><span class="count counter">119</span> k</h4>
|
||||
<p class="m-0">Followers</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-sm-6">
|
||||
<div class="card">
|
||||
<div class="social-graph-wrapper widget-linkedin">
|
||||
<span class="s-icon"><i class="fab fa-linkedin-in"></i></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 border-end">
|
||||
<div class="pt-3 pb-3 pl-0 pr-0 text-center">
|
||||
<h4 class="m-1"><span class="count counter">89</span> k</h4>
|
||||
<p class="m-0">Friends</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="pt-3 pb-3 pl-0 pr-0 text-center">
|
||||
<h4 class="m-1"><span class="count counter">119</span> k</h4>
|
||||
<p class="m-0">Followers</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-sm-6">
|
||||
<div class="card">
|
||||
<div class="social-graph-wrapper widget-googleplus">
|
||||
<span class="s-icon"><i class="fab fa-google-plus-g"></i></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 border-end">
|
||||
<div class="pt-3 pb-3 pl-0 pr-0 text-center">
|
||||
<h4 class="m-1"><span class="count counter">89</span> k</h4>
|
||||
<p class="m-0">Friends</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="pt-3 pb-3 pl-0 pr-0 text-center">
|
||||
<h4 class="m-1"><span class="count counter">119</span> k</h4>
|
||||
<p class="m-0">Followers</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-xxl-3 col-sm-6">
|
||||
<div class="card">
|
||||
<div class="social-graph-wrapper widget-twitter">
|
||||
<span class="s-icon"><i class="fab fa-twitter"></i></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 border-end">
|
||||
<div class="pt-3 pb-3 pl-0 pr-0 text-center">
|
||||
<h4 class="m-1"><span class="count counter">89</span> k</h4>
|
||||
<p class="m-0">Friends</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="pt-3 pb-3 pl-0 pr-0 text-center">
|
||||
<h4 class="m-1"><span class="count counter">119</span> k</h4>
|
||||
<p class="m-0">Followers</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
21
ui/ui/invoice-customer.tpl
Normal file
21
ui/ui/invoice-customer.tpl
Normal file
@ -0,0 +1,21 @@
|
||||
{include file="sections/user-header.tpl"}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-md-offset-2">
|
||||
<div class="card card-hovered card-primary card-stacked mb30">
|
||||
<div class="card-header">{$in['invoice']}</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" action="{$_url}plan/print" target="_blank">
|
||||
<pre id="content">{$invoice}</pre>
|
||||
<input type="hidden" name="id" value="{$in['id']}">
|
||||
<a href="{$_url}voucher/list-activated" class="btn btn-default btn-sm"><i
|
||||
class="ion-reply-all"></i>{Lang::T('Finish')}</a>
|
||||
<a href="https://api.whatsapp.com/send/?text={$whatsapp}" target="_blank"
|
||||
class="btn btn-primary btn-sm">
|
||||
<i class="glyphicon glyphicon-share"></i> WhatsApp</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/user-footer.tpl"}
|
74
ui/ui/invoice-print.tpl
Normal file
74
ui/ui/invoice-print.tpl
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="https://laravel.com/img/favicon/favicon-16x16.png" type='image/x-icon'>
|
||||
<title>{Lang::T('Login')} - {$_c['CompanyName']}</title>
|
||||
<link rel="stylesheet" href="assets/vendor/chartist/css/chartist.min.css">
|
||||
<link href="assets/vendor/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet">
|
||||
<link href="assets/css/style.css" rel="stylesheet">
|
||||
<meta http-equiv="refresh" content="{$time}; url={$url}">
|
||||
<script type="text/javascript">
|
||||
function printpage() {
|
||||
window.print();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body topmargin="0" leftmargin="0" {if !$nuxprint} onload="printpage()" {/if}>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<table width="200">
|
||||
<tr>
|
||||
<td>
|
||||
{if $content}
|
||||
<pre style="border-style: none; background-color: white;">{$content}</pre>{else}
|
||||
<pre style="border-style: none; background-color: white;"><b>{Lang::pad($_c['CompanyName'],' ', 2)}</b>
|
||||
{Lang::pad($_c['address'],' ', 2)}
|
||||
{Lang::pad($_c['phone'],' ', 2)}
|
||||
{Lang::pad("", '=')}
|
||||
{Lang::pads("Invoice", $in['invoice'], ' ')}
|
||||
{Lang::pads(Lang::T('Date'), $date, ' ')}
|
||||
{Lang::pads(Lang::T('Sales'), $_admin['fullname'], ' ')}
|
||||
{Lang::pad("", '=')}
|
||||
{Lang::pads(Lang::T('Type'), $in['type'], ' ')}
|
||||
{Lang::pads(Lang::T('Plan Name'), $in['plan_name'], ' ')}
|
||||
{Lang::pads(Lang::T('Plan Price'), Lang::moneyFormat($in['price']), ' ')}
|
||||
{Lang::pad($in['method'], ' ', 2)}
|
||||
|
||||
{Lang::pads(Lang::T('Username'), $in['username'], ' ')}
|
||||
{Lang::pads(Lang::T('Password'), '**********', ' ')}
|
||||
{if $in['type'] != 'Balance'}
|
||||
{Lang::pads(Lang::T('Created On'), Lang::dateAndTimeFormat($in['recharged_on'],$in['recharged_time']), ' ')}
|
||||
{Lang::pads(Lang::T('Expires On'), Lang::dateAndTimeFormat($in['expiration'],$in['time']), ' ')}
|
||||
{/if}
|
||||
{Lang::pad("", '=')}
|
||||
{Lang::pad($_c['note'],' ', 2)}</pre>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{if $nuxprint}
|
||||
<a href="{$nuxprint}" class="btn btn-success text-black btn-sm" name="nux" value="print">
|
||||
<i class="fa fa-print"></i>
|
||||
Print Invoice
|
||||
<!-- <i class="fa fa-phone"></i> -->
|
||||
</a>
|
||||
<br>
|
||||
<iframe src="{$nuxprint}" style="height: 2px;"><iframe>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="ui/ui/scripts/jquery.min.js"></script>
|
||||
<script src="ui/ui/scripts/bootstrap.min.js"></script>
|
||||
{if isset($xfooter)}
|
||||
{$xfooter}
|
||||
{/if}
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
43
ui/ui/invoice.tpl
Normal file
43
ui/ui/invoice.tpl
Normal file
@ -0,0 +1,43 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-sm-12 col-md-offset-3">
|
||||
<div class="card card-hovered card-primary card-stacked mb30">
|
||||
<div class="card-header">{$in['invoice']}</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" action="{$_url}plan/print" target="_blank">
|
||||
<pre id="content"></pre>
|
||||
<textarea class="hidden" id="formcontent" name="content">{$invoice}</textarea>
|
||||
<input type="hidden" name="id" value="{$in['id']}">
|
||||
<div class="">
|
||||
<a href="{$_url}plan/list" class="btn btn-success btn-sm"><i
|
||||
class="fa fa-reply-all"></i>{Lang::T('Finish')}</a>
|
||||
<a href="https://api.whatsapp.com/send/?text={$whatsapp}" target="_blank"
|
||||
class="btn btn-primary btn-sm">
|
||||
<i class="fa fa-share"></i> WhatsApp</a>
|
||||
<a href="{$_url}plan/view/{$in['id']}/send" class="btn btn-info btn-sm"><i
|
||||
class="fa fa-envelope"></i> {Lang::T("Resend")}</a>
|
||||
<button type="submit" class="btn btn-info btn-sm"><i class="fa fa-print"></i>
|
||||
Print</button>
|
||||
<!-- <a href="nux://print?text={urlencode($invoice)}"
|
||||
class="btn btn-success btn-sm hidden-md hidden-lg">
|
||||
<i class="fa fa-phone"></i>
|
||||
Print Invoice
|
||||
</a>
|
||||
<a href="https://github.com/hotspotbilling/android-printer"
|
||||
class="btn btn-success btn-sm hidden-xs hidden-sm" target="_blank">
|
||||
<i class="fa fa-phone"></i>
|
||||
Print Invoice
|
||||
</a> -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var s5_taf_parent = window.location;
|
||||
document.getElementById('content').innerHTML = document.getElementById('formcontent').innerHTML;
|
||||
</script>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
31
ui/ui/language-add.tpl
Normal file
31
ui/ui/language-add.tpl
Normal file
@ -0,0 +1,31 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">{Lang::T('Translation')}</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}settings/lang-post">
|
||||
{foreach $langs as $lang}
|
||||
<div class="form-group">
|
||||
<div class="col-md-12">
|
||||
<small>{str_replace('_',' ', $lang@key)}</small>
|
||||
<input type="text" class="form-control" rows="1" name="{$lang@key}"
|
||||
placeholder="{$lang@key}" value="{$lang}">
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary"
|
||||
type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a href="{$_url}settings/localisation">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
71
ui/ui/logs-radius.tpl
Normal file
71
ui/ui/logs-radius.tpl
Normal file
@ -0,0 +1,71 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<!-- pool -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
Radius
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-xs" title="save" href="{$_url}logs/radius-csv"
|
||||
onclick="return confirm('This will export to CSV?')"><span class="glyphicon glyphicon-download"
|
||||
aria-hidden="true"></span> CSV</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="text-center"">
|
||||
<div class="col-md-12 mb-3">
|
||||
<form id="site-search" method="post" action="{$_url}logs/radius/">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<input type="text" name="q" class="form-control" value="{$q}"
|
||||
placeholder="{Lang::T('Search by Name')}...">
|
||||
<button class="btn btn-success input-group-btn" type="submit">{Lang::T('Search')}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<form class="form-inline" method="post" action="{$_url}logs/radius/">
|
||||
<div class="form-group">
|
||||
<div class="input-group has-error">
|
||||
<span class="input-group-text">Keep Logs </span>
|
||||
<input type="text" name="keep" class="form-control" placeholder="90" value="90">
|
||||
<span class="input-group-text">Days</span>
|
||||
<button type="submit" class="btn btn-danger btn-sm input-group-btn"
|
||||
onclick="return confirm('Clear old logs?')">Clean Logs
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-condensed table-hover border-primary">
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td width="30px">{$ds['id']}</td>
|
||||
<td width="200px">{Lang::dateTimeFormat($ds['authdate'])}</td>
|
||||
<td width="100px">{$ds['username']}</td>
|
||||
<td width="10px"><input type="password" value="{$ds['pass']}"
|
||||
style="width:200px;height: 36px; border: 0px; text-align: right;" class="pull-right form-control"
|
||||
onmouseleave="this.type = 'password'" onmouseenter="this.type = 'text'"
|
||||
onclick="this.select()"></td>
|
||||
<td>{$ds['reply']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
66
ui/ui/logs.tpl
Normal file
66
ui/ui/logs.tpl
Normal file
@ -0,0 +1,66 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<!-- pool -->
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="panel panel-hovered mb20 panel-primary">
|
||||
<div class="panel-heading">
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-xs" title="save" href="{$_url}logs/list-csv"
|
||||
onclick="return confirm('This will export to CSV?')"><span class="glyphicon glyphicon-download"
|
||||
aria-hidden="true"></span> CSV</a>
|
||||
</div>
|
||||
{/if}
|
||||
Activity Log
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="text-center" style="padding: 15px">
|
||||
<div class="col-md-4">
|
||||
<form id="site-search" method="post" action="{$_url}logs/list/">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<input type="text" name="q" class="form-control" value="{$q}"
|
||||
placeholder="{Lang::T('Search by Name')}...">
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-success" type="submit">{Lang::T('Search')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<form class="form-inline" method="post" action="{$_url}logs/list/">
|
||||
<div class="input-group has-error">
|
||||
<span class="input-group-addon">Keep Logs </span>
|
||||
<input type="text" name="keep" class="form-control" placeholder="90" value="90">
|
||||
<span class="input-group-addon">Days</span>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-danger btn-sm"
|
||||
onclick="return confirm('Clear old logs?')">Clean Logs</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-condensed">
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['id']}</td>
|
||||
<td>{Lang::dateTimeFormat($ds['date'])}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
<td>{$ds['ip']}</td>
|
||||
<td style="overflow-x: scroll;">{nl2br($ds['description'])}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{include file="sections/footer.tpl"}
|
209
ui/ui/main.css
Normal file
209
ui/ui/main.css
Normal file
@ -0,0 +1,209 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
background-color: #ecf0f3;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #000;
|
||||
font-size: 3rem;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 16rem;
|
||||
height: 4.5rem;
|
||||
border-radius: 2.5rem;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1rem;
|
||||
text-transform: uppercase;
|
||||
background-color: #ecf0f3;
|
||||
color: grey;
|
||||
border: none;
|
||||
outline: none;
|
||||
-webkit-box-shadow: 1rem 1rem 1.6rem #d1d9e6, -0.6rem -0.6rem 1.6rem #fff;
|
||||
box-shadow: 1rem 1rem 1.6rem #d1d9e6, -0.6rem -0.6rem 1.6rem #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:active {
|
||||
-webkit-box-shadow: inset 1rem 1rem 1.6rem #d1d9e6, inset -1rem -1rem 1.6rem #fff;
|
||||
box-shadow: inset 1rem 1rem 1.6rem #d1d9e6, inset -1rem -1rem 1.6rem #fff;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
width: 80rem;
|
||||
height: 60vh;
|
||||
background-color: #ecf0f3;
|
||||
-webkit-box-shadow: 1rem 1rem 1rem #d1d9e6, -0.5rem -0.5rem 1rem #fff;
|
||||
box-shadow: 1rem 1rem 1rem #d1d9e6, -0.5rem -0.5rem 1rem #fff;
|
||||
border-radius: 1.2rem;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.container .circle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 20rem;
|
||||
height: 20rem;
|
||||
border-radius: 50%;
|
||||
background-color: #ecf0f3;
|
||||
-webkit-box-shadow: 0.4rem 0.4rem 0.8rem #d1d9e6;
|
||||
box-shadow: 0.4rem 0.4rem 0.8rem #d1d9e6;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.container .top-left {
|
||||
top: 0;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.container .bottom-left {
|
||||
bottom: 0;
|
||||
-webkit-transform: translate(-70%, 70%) rotate(-80deg);
|
||||
transform: translate(-70%, 70%) rotate(-80deg);
|
||||
}
|
||||
|
||||
.container .side {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.container .left-side {
|
||||
width: 40%;
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
-webkit-box-shadow: 0.4rem 0.4rem 1rem #d1d9e6;
|
||||
box-shadow: 0.4rem 0.4rem 1rem #d1d9e6;
|
||||
}
|
||||
|
||||
.container .left-side .content p {
|
||||
margin: 1rem 0 2rem 0;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.container .right-side {
|
||||
width: 60%;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.container .right-side .content {
|
||||
width: 70%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container .right-side .content .icon-group {
|
||||
margin: 1rem 0 3rem 0;
|
||||
}
|
||||
|
||||
.container .right-side .content .icon-group .btn {
|
||||
margin: 0 1rem;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: #ecf0f3;
|
||||
-webkit-box-shadow: 0.3rem 0.3rem 0.6rem #d1d9e6, -0.3rem -0.3rem 0.6rem #fff;
|
||||
box-shadow: 0.3rem 0.3rem 0.6rem #d1d9e6, -0.3rem -0.3rem 0.6rem #fff;
|
||||
border-radius: 50%;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
color: #000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.container .right-side .content .icon-group .btn:hover {
|
||||
-webkit-box-shadow: inset 0.3rem 0.3rem 0.6rem #d1d9e6, inset -0.3rem -0.3rem 0.6rem #fff;
|
||||
box-shadow: inset 0.3rem 0.3rem 0.6rem #d1d9e6, inset -0.3rem -0.3rem 0.6rem #fff;
|
||||
}
|
||||
|
||||
.container .right-side .content .line {
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-pack: distribute;
|
||||
justify-content: space-around;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.container .right-side .content .line hr {
|
||||
width: 40%;
|
||||
border: 1px solid transparent;
|
||||
border-top: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.container .right-side .content .form form .form-control {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.container .right-side .content .form form .form-control i {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 4%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.container .right-side .content .form form .form-control input {
|
||||
width: 100%;
|
||||
height: 4rem;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: #ecf0f3;
|
||||
-webkit-box-shadow: inset 0.2rem 0.2rem 0.4rem #d1d9e6, inset -0.4rem -0.4rem 0.4rem #fff;
|
||||
box-shadow: inset 0.2rem 0.2rem 0.4rem #d1d9e6, inset -0.4rem -0.4rem 0.4rem #fff;
|
||||
padding-left: 2.5rem;
|
||||
border-radius: 0.8rem;
|
||||
color: #000;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.container .right-side .content .form form .form-control input:focus {
|
||||
-webkit-box-shadow: inset 0.4rem 0.4rem 0.4rem #d1d9e6, inset -0.4rem -0.4rem 0.4rem #fff;
|
||||
box-shadow: inset 0.4rem 0.4rem 0.4rem #d1d9e6, inset -0.4rem -0.4rem 0.4rem #fff;
|
||||
}
|
||||
|
||||
.container .right-side .content .form form button {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
/*# sourceMappingURL=main.css.map */
|
106
ui/ui/maintenance-mode.tpl
Normal file
106
ui/ui/maintenance-mode.tpl
Normal file
@ -0,0 +1,106 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
<style>
|
||||
/* Checkbox container */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* Hidden checkbox */
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* Slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
input:checked+.slider {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:focus+.slider {
|
||||
box-shadow: 0 0 1px #2196F3;
|
||||
}
|
||||
|
||||
input:checked+.slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
</style>
|
||||
<div class="container-fluid">
|
||||
<form class="form-horizontal" method="post" autocomplete="off" role="form" action="">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">{Lang::T('Maintenance Mode')}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{Lang::T('Status:')}</label>
|
||||
<div class="col-md-6">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="maintenance_mode" value="1" name="maintenance_mode" {if
|
||||
$_c['maintenance_mode']==1}checked{/if}>
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{Lang::T('Force Logout:')}</label>
|
||||
<div class="col-md-6">
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="maintenance_mode_logout" value="1"
|
||||
name="maintenance_mode_logout" {if $_c['maintenance_mode_logout']==1}checked{/if}>
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{Lang::T('End Date:')}</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" value="{$_c['maintenance_date']}" type="date" id="start_date"
|
||||
name="maintenance_date">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary waves-effect waves-light" name="save" value="save"
|
||||
type="submit">{Lang::T('Save')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
249
ui/ui/maintenance.tpl
Normal file
249
ui/ui/maintenance.tpl
Normal file
@ -0,0 +1,249 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{Lang::T('Site is down for maintenance')}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Noto Sans', sans-serif;
|
||||
color: #616161;
|
||||
background-color: #eeeeee;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1024px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.box {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
margin: auto;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 6px 18px 18px rgba(0, 0, 0, 0.08), -6px 18px 18px rgba(0, 0, 0, 0.08);
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.animation {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #f6921e;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.one,
|
||||
.two,
|
||||
.three {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.one {
|
||||
background: url('data:image/svg+xml,%3Csvg%20version%3D%221.1%22%0A%09%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20xmlns%3Aa%3D%22http%3A%2F%2Fns.adobe.com%2FAdobeSVGViewerExtensions%2F3.0%2F%22%0A%09%20x%3D%220px%22%20y%3D%220px%22%20width%3D%2281px%22%20height%3D%2280.5px%22%20viewBox%3D%220%200%2081%2080.5%22%20style%3D%22overflow%3Ascroll%3Benable-background%3Anew%200%200%2081%2080.5%3B%22%0A%09%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text%2Fcss%22%3E%0A%09.st0%7Bfill%3A%23383838%3B%7D%0A%3C%2Fstyle%3E%0A%3Cdefs%3E%0A%3C%2Fdefs%3E%0A%3Cpath%20class%3D%22st0%22%20d%3D%22M30.3%2C68.2c1.2%2C0.2%2C2.3%2C0.9%2C3.8%2C1.2c1.6%2C0.3%2C2.7%2C0.6%2C4%2C0.4l4.9%2C9.6c0.6%2C0.9%2C1.4%2C1.1%2C2.3%2C0.9l15.3-4.9%0A%09c0.5-0.3%2C1-1%2C0.9-2.3l-1.8-10.6c2-1.6%2C3.6-3.7%2C5.3-5.8l10.5%2C0.6c1.1%2C0.6%2C2.1-0.4%2C2.3-1.1L81%2C40.7c0.2-0.8-0.4-2.1-1.1-2.3l-10.2-3.8%0A%09c-0.3-2.5-1.4-4.8-2.5-7.5l5.9-8.5c0.6-1.1%2C0.4-1.9-0.2-2.9l-12-10.7c-0.3-0.5-1.6-0.3-2.5%2C0.3l-8%2C6.9c-1.2-0.2-2.3-0.9-3.8-1.2%0A%09c-1.6-0.3-2.7-0.6-4-0.4L37.7%2C1c-0.6-0.9-1.4-1.1-2.3-0.9L20.1%2C5c-0.5%2C0.3-1%2C1-0.9%2C2.3l1.8%2C10.6c-2%2C1.6-3.6%2C3.7-5.3%2C5.8L5.3%2C23%0A%09c-0.8-0.2-1.7%2C0.4-2%2C1.6L0%2C40.2c-0.2%2C0.8%2C0.4%2C2.1%2C1.1%2C2.3l9.8%2C3.7c0.7%2C2.6%2C1.4%2C5.2%2C2.5%2C7.5l-6%2C8.9c-0.6%2C0.7-0.4%2C2%2C0.3%2C2.5l12%2C10.7%0A%09c0.7%2C0.5%2C1.9%2C0.8%2C2.4%2C0.1L30.3%2C68.2z%20M26.7%2C37.3c1.6-7.4%2C9.1-12.3%2C16.5-10.8S55.6%2C35.7%2C54%2C43.1c-1.6%2C7.4-9.1%2C12.3-16.5%2C10.7%0A%09C30.1%2C52.3%2C25.1%2C44.7%2C26.7%2C37.3L26.7%2C37.3z%22%2F%3E%0A%3C%2Fsvg%3E');
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
margin-top: -10px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.two {
|
||||
background: url('data:image/svg+xml,%3Csvg%20version%3D%221.1%22%0A%09%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20xmlns%3Aa%3D%22http%3A%2F%2Fns.adobe.com%2FAdobeSVGViewerExtensions%2F3.0%2F%22%0A%09%20x%3D%220px%22%20y%3D%220px%22%20width%3D%22103px%22%20height%3D%22103.7px%22%20viewBox%3D%220%200%20103%20103.7%22%0A%09%20style%3D%22overflow%3Ascroll%3Benable-background%3Anew%200%200%20103%20103.7%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text%2Fcss%22%3E%0A%09.st0%7Bfill%3A%23F6921E%3B%7D%0A%3C%2Fstyle%3E%0A%3Cdefs%3E%0A%3C%2Fdefs%3E%0A%3Cpath%20class%3D%22st0%22%20d%3D%22M87.3%2C64.8c0.3-1.5%2C1.1-2.9%2C1.6-4.9c0.4-2%2C0.7-3.5%2C0.5-5.1l12.3-6.3c1.2-0.8%2C1.4-1.8%2C1.1-2.9l-6.3-19.6%0A%09c-0.4-0.6-1.3-1.3-2.9-1.1l-13.5%2C2.3c-2.1-2.5-4.7-4.7-7.4-6.8l0.8-13.4C74.3%2C5.8%2C73%2C4.5%2C72%2C4.3L52.1%2C0c-1-0.2-2.7%2C0.5-2.9%2C1.5%0A%09l-4.8%2C13c-3.2%2C0.4-6.1%2C1.8-9.5%2C3.2l-10.9-7.5c-1.4-0.8-2.5-0.5-3.7%2C0.3L6.5%2C25.8c-0.6%2C0.4-0.4%2C2%2C0.4%2C3.2l8.8%2C10.2%0A%09c-0.3%2C1.5-1.1%2C2.9-1.5%2C4.9c-0.4%2C2-0.7%2C3.5-0.6%2C5.1L1.2%2C55.4c-1.2%2C0.8-1.4%2C1.8-1.1%2C2.9l6.3%2C19.6c0.4%2C0.6%2C1.3%2C1.3%2C2.9%2C1.1l13.5-2.3%0A%09c2.1%2C2.5%2C4.7%2C4.7%2C7.4%2C6.8l-0.8%2C13.4c-0.2%2C1%2C0.6%2C2.2%2C2.1%2C2.5l20%2C4.2c1%2C0.2%2C2.7-0.5%2C2.9-1.5l4.7-12.6c3.3-0.9%2C6.6-1.7%2C9.5-3.2L80.1%2C94%0A%09c0.9%2C0.7%2C2.5%2C0.5%2C3.2-0.4L97%2C78.3c0.7-0.9%2C1-2.4%2C0.1-3.1L87.3%2C64.8z%20M47.8%2C69.5C38.3%2C67.5%2C32%2C57.8%2C34%2C48.3%0A%09c2-9.5%2C11.7-15.8%2C21.2-13.8c9.5%2C2%2C15.7%2C11.7%2C13.7%2C21.2C66.9%2C65.2%2C57.3%2C71.5%2C47.8%2C69.5L47.8%2C69.5z%22%2F%3E%0A%3C%2Fsvg%3E');
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.three {
|
||||
background: url('data:image/svg+xml,%3Csvg%20version%3D%221.1%22%0A%09%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20xmlns%3Aa%3D%22http%3A%2F%2Fns.adobe.com%2FAdobeSVGViewerExtensions%2F3.0%2F%22%0A%09%20x%3D%220px%22%20y%3D%220px%22%20width%3D%2281px%22%20height%3D%2280.5px%22%20viewBox%3D%220%200%2081%2080.5%22%20style%3D%22overflow%3Ascroll%3Benable-background%3Anew%200%200%2081%2080.5%3B%22%0A%09%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text%2Fcss%22%3E%0A%09.st0%7Bfill%3A%23383838%3B%7D%0A%3C%2Fstyle%3E%0A%3Cdefs%3E%0A%3C%2Fdefs%3E%0A%3Cpath%20class%3D%22st0%22%20d%3D%22M30.3%2C68.2c1.2%2C0.2%2C2.3%2C0.9%2C3.8%2C1.2c1.6%2C0.3%2C2.7%2C0.6%2C4%2C0.4l4.9%2C9.6c0.6%2C0.9%2C1.4%2C1.1%2C2.3%2C0.9l15.3-4.9%0A%09c0.5-0.3%2C1-1%2C0.9-2.3l-1.8-10.6c2-1.6%2C3.6-3.7%2C5.3-5.8l10.5%2C0.6c1.1%2C0.6%2C2.1-0.4%2C2.3-1.1L81%2C40.7c0.2-0.8-0.4-2.1-1.1-2.3l-10.2-3.8%0A%09c-0.3-2.5-1.4-4.8-2.5-7.5l5.9-8.5c0.6-1.1%2C0.4-1.9-0.2-2.9l-12-10.7c-0.3-0.5-1.6-0.3-2.5%2C0.3l-8%2C6.9c-1.2-0.2-2.3-0.9-3.8-1.2%0A%09c-1.6-0.3-2.7-0.6-4-0.4L37.7%2C1c-0.6-0.9-1.4-1.1-2.3-0.9L20.1%2C5c-0.5%2C0.3-1%2C1-0.9%2C2.3l1.8%2C10.6c-2%2C1.6-3.6%2C3.7-5.3%2C5.8L5.3%2C23%0A%09c-0.8-0.2-1.7%2C0.4-2%2C1.6L0%2C40.2c-0.2%2C0.8%2C0.4%2C2.1%2C1.1%2C2.3l9.8%2C3.7c0.7%2C2.6%2C1.4%2C5.2%2C2.5%2C7.5l-6%2C8.9c-0.6%2C0.7-0.4%2C2%2C0.3%2C2.5l12%2C10.7%0A%09c0.7%2C0.5%2C1.9%2C0.8%2C2.4%2C0.1L30.3%2C68.2z%20M26.7%2C37.3c1.6-7.4%2C9.1-12.3%2C16.5-10.8S55.6%2C35.7%2C54%2C43.1c-1.6%2C7.4-9.1%2C12.3-16.5%2C10.7%0A%09C30.1%2C52.3%2C25.1%2C44.7%2C26.7%2C37.3L26.7%2C37.3z%22%2F%3E%0A%3C%2Fsvg%3E');
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
margin-top: -50px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
@keyframes spin-one {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotate(-359deg);
|
||||
transform: rotate(-359deg);
|
||||
}
|
||||
}
|
||||
|
||||
.spin-one {
|
||||
-webkit-animation: spin-one 1.5s infinite linear;
|
||||
animation: spin-one 1.5s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes spin-two {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotate(-359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
|
||||
.spin-two {
|
||||
-webkit-animation: spin-two 2s infinite linear;
|
||||
animation: spin-two 2s infinite linear;
|
||||
}
|
||||
|
||||
.day,
|
||||
.hour,
|
||||
.minute,
|
||||
.second {
|
||||
font-size: 18px;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.day {
|
||||
background-color: #1abc9c;
|
||||
}
|
||||
|
||||
.hour {
|
||||
background-color: #3498db;
|
||||
}
|
||||
|
||||
.minute {
|
||||
background-color: #f1c40f;
|
||||
}
|
||||
|
||||
.second {
|
||||
background-color: #e74c3c;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.one,
|
||||
.two,
|
||||
.three {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.day,
|
||||
.hour,
|
||||
.minute,
|
||||
.second {
|
||||
font-size: 14px;
|
||||
padding: 8px;
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- partial:index.partial.html -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Noto+Sans:400,700" rel="stylesheet">
|
||||
|
||||
<div class="container">
|
||||
<div class="box">
|
||||
<div class="animation">
|
||||
<div class="one spin-one"></div>
|
||||
<div class="two spin-two"></div>
|
||||
<div class="three spin-one"></div>
|
||||
</div>
|
||||
<h1>{Lang::T('Site is temporarily unavailable.')}</h1>
|
||||
<p>{Lang::T('Scheduled maintenance is currently in progress. Please check back soon.')}</p>
|
||||
<p>{Lang::T('We apologize for any inconvenience.')} <br> — {Lang::T('The ')} {$companyName} {Lang::T('
|
||||
Team.')}</p>
|
||||
<br>
|
||||
{if $date} <div style="display: flex; flex-direction: row; justify-content: space-between;">
|
||||
<p class="day"></p>
|
||||
<p class="hour"></p>
|
||||
<p class="minute"></p>
|
||||
<p class="second"></p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{if $date}
|
||||
<script>
|
||||
const countDown = () => {
|
||||
const countDay = new Date('{$date}');
|
||||
const now = new Date();
|
||||
const counter = countDay - now;
|
||||
const second = 1000;
|
||||
const minute = second * 60;
|
||||
const hour = minute * 60;
|
||||
const day = hour * 24;
|
||||
const textDay = Math.floor(counter / day);
|
||||
const textHour = Math.floor((counter % day) / hour);
|
||||
const textMinute = Math.floor((counter % hour) / minute);
|
||||
const textSecond = Math.floor((counter % minute) / second)
|
||||
document.querySelector(".day").innerText = textDay + ' Days';
|
||||
document.querySelector(".hour").innerText = textHour + ' Hours';
|
||||
document.querySelector(".minute").innerText = textMinute + ' Minutes';
|
||||
document.querySelector(".second").innerText = textSecond + ' Seconds';
|
||||
}
|
||||
setInterval(countDown, 1000);
|
||||
</script>
|
||||
{/if}
|
||||
</body>
|
||||
|
||||
</html>
|
144
ui/ui/message-bulk.tpl
Normal file
144
ui/ui/message-bulk.tpl
Normal file
@ -0,0 +1,144 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Send Bulk Message')}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" id="bulkMessageForm" action="">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Group')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" name="group" id="group">
|
||||
<option value="all" selected>{Lang::T('All Customers')}</option>
|
||||
<option value="new">{Lang::T('New Customers')}</option>
|
||||
<option value="expired">{Lang::T('Expired Customers')}</option>
|
||||
<option value="active">{Lang::T('Active Customers')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" name="via" id="via">
|
||||
<option value="sms" selected>{Lang::T('SMS')}</option>
|
||||
<option value="wa">{Lang::T('WhatsApp')}</option>
|
||||
<option value="both">{Lang::T('SMS and WhatsApp')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Message per time')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" name="batch" id="batch">
|
||||
<option value="5">{Lang::T('5 Messages')}</option>
|
||||
<option value="10" selected>{Lang::T('10 Messages')}</option>
|
||||
<option value="15">{Lang::T('15 Messages')}</option>
|
||||
<option value="20">{Lang::T('20 Messages')}</option>
|
||||
<option value="20">{Lang::T('30 Messages')}</option>
|
||||
<option value="20">{Lang::T('40 Messages')}</option>
|
||||
<option value="20">{Lang::T('50 Messages')}</option>
|
||||
<option value="20">{Lang::T('60 Messages')}</option>
|
||||
</select>{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Delay')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" name="delay" id="delay">
|
||||
<option value="0" selected>{Lang::T('No Delay')}</option>
|
||||
<option value="5">{Lang::T('5 Seconds')}</option>
|
||||
<option value="10">{Lang::T('10 Seconds')}</option>
|
||||
<option value="15">{Lang::T('15 Seconds')}</option>
|
||||
<option value="20">{Lang::T('20 Seconds')}</option>
|
||||
</select>{Lang::T('Use at least 5 secs if you are sending to all customers to avoid being banned by your message provider')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="message" name="message"
|
||||
placeholder="{Lang::T('Compose your message...')}" rows="5"></textarea>
|
||||
<input name="test" type="checkcard"> {Lang::T('Testing [if checked no real message is sent]')}
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
{Lang::T('Use placeholders:')}
|
||||
<br>
|
||||
<b>[[name]]</b> - {Lang::T('Customer Name')}
|
||||
<br>
|
||||
<b>[[user_name]]</b> - {Lang::T('Customer Username')}
|
||||
<br>
|
||||
<b>[[phone]]</b> - {Lang::T('Customer Phone')}
|
||||
<br>
|
||||
<b>[[company_name]]</b> - {Lang::T('Your Company Name')}
|
||||
</p>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success mb-3" type="submit" name=send value=now>
|
||||
{Lang::T('Send Message')}</button>
|
||||
<a class="btn btn-outline-primary" href="{$_url}dashboard" class="btn btn-default">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if $batchStatus}
|
||||
<p><span class="label label-success">Total SMS Sent: {$totalSMSSent}</span> <span class="label label-danger">Total SMS
|
||||
Failed: {$totalSMSFailed}</span> <span class="label label-success">Total WhatsApp Sent:
|
||||
{$totalWhatsappSent}</span> <span class="label label-danger">Total WhatsApp Failed:
|
||||
{$totalWhatsappFailed}</span></p>
|
||||
{/if}
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Message Results</h3>
|
||||
</div>
|
||||
<!-- /.card-header -->
|
||||
<div class="table-responsive">
|
||||
<table id="messageResultsTable" class="table table-bordered table-striped table-condensed table-hover border-primary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Phone</th>
|
||||
<th>Message</th>
|
||||
<th>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>
|
||||
<!-- /.card-body -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.card -->
|
||||
</div>
|
||||
|
||||
<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>
|
||||
var $j = jQuery.noConflict();
|
||||
|
||||
$j(document).ready(function () {
|
||||
$j('#messageResultsTable').DataTable();
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
74
ui/ui/message-list.tpl
Normal file
74
ui/ui/message-list.tpl
Normal file
@ -0,0 +1,74 @@
|
||||
{include file="sections/header.tpl"}
|
||||
|
||||
<style>
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
margin-right: 5px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
{Lang::T('Manage Messages')}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<br>
|
||||
<div class="table-responsive table_mobile">
|
||||
<table id="messageTable" class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('From')}</th>
|
||||
<th>{Lang::T('To')}</th>
|
||||
<th>{Lang::T('Title')}</th>
|
||||
<th>{Lang::T('Date')}</th>
|
||||
<th>{Lang::T('Status')}</th>
|
||||
<th>{Lang::T('Actions')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $messages as $msg}
|
||||
<tr {if $msg.status == 0}class="info"{/if}>
|
||||
<td>{$msg.from_user}</td>
|
||||
<td>{$msg.to_user}</td>
|
||||
<td onclick="window.location.href = '{$_url}messages/view/{$msg.id}'" style="cursor:pointer;">{$msg.title}</td>
|
||||
<td>{Lang::dateTimeFormat($msg.date)}</td>
|
||||
<td>{if $msg.status == 0}{Lang::T('Not Sent')}{else}{Lang::T('Sent')}{/if}</td>
|
||||
<td align="center">
|
||||
<a href="{$_url}messages/view/{$msg.id}" class="btn btn-success btn-xs">{Lang::T('View')}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
var $j = jQuery.noConflict();
|
||||
|
||||
$j(document).ready(function() {
|
||||
$j('#messageTable').DataTable({
|
||||
"pagingType": "full_numbers",
|
||||
"lengthMenu": [
|
||||
[5, 10, 25, 50, 100, -1],
|
||||
[5, 10, 25, 50, 100, "All"]
|
||||
],
|
||||
"pageLength": 25,
|
||||
"order": [[3, 'desc']]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
66
ui/ui/message.tpl
Normal file
66
ui/ui/message.tpl
Normal file
@ -0,0 +1,66 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Send Personal Message')}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}message/send-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Customer')}</label>
|
||||
<div class="col-md-6">
|
||||
<select {if $cust}{else}id="personSelect" {/if} class="form-select" style="height: 52px; background-color: white;"
|
||||
name="id_customer" style="width: 100%"
|
||||
data-placeholder="{Lang::T('Select a customer')}...">
|
||||
{if $cust}
|
||||
<option value="{$cust['id']}">{$cust['username']} • {$cust['fullname']} •
|
||||
{$cust['email']}</option>
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" name="via" id="via">
|
||||
<option value="sms" selected> {Lang::T('SMS')}</option>
|
||||
<option value="wa"> {Lang::T('WhatsApp')}</option>
|
||||
<option value="both"> {Lang::T('SMS and WhatsApp')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="message" name="message"
|
||||
placeholder="{Lang::T('Compose your message...')}" rows="5"></textarea>
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
{Lang::T('Use placeholders:')}
|
||||
<br>
|
||||
<b>[[name]]</b> - {Lang::T('Customer Name')}
|
||||
<br>
|
||||
<b>[[user_name]]</b> - {Lang::T('Customer Username')}
|
||||
<br>
|
||||
<b>[[phone]]</b> - {Lang::T('Customer Phone')}
|
||||
<br>
|
||||
<b>[[company_name]]</b> - {Lang::T('Your Company Name')}
|
||||
</p>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success mb-3" type="submit">{Lang::T('Send Message')}</button>
|
||||
<a class="btn btn-outline-primary href="{$_url}dashboard" class="btn btn-default">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
78
ui/ui/onlinehotspot.tpl
Normal file
78
ui/ui/onlinehotspot.tpl
Normal file
@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Online Hotspot Users</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.11.5/datatables.min.css"/>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.datatables.net/v/dt/dt-1.11.5/datatables.min.js"></script>
|
||||
<style>
|
||||
table.dataTable thead th,
|
||||
table.dataTable thead td {
|
||||
padding: 10px 18px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
table.dataTable tbody td {
|
||||
padding: 10px 18px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
.disconnect-btn {
|
||||
padding: 5px 10px;
|
||||
background-color: #d53f8c;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<h1 class="text-2xl font-bold mb-4">Online Hotspot Users</h1>
|
||||
<table id="hotspotUsersTable" class="stripe hover" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Address</th>
|
||||
<th>Uptime</th>
|
||||
<th>Server</th>
|
||||
<th>MAC</th>
|
||||
<th>Session Time</th>
|
||||
<th>Rx Bytes</th>
|
||||
<th>Tx Bytes</th>
|
||||
<th>Total Bytes</th>
|
||||
<th>Action</th> <!-- Added column for action -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $onlineHotspotUsers as $user}
|
||||
<tr>
|
||||
<td>{$user.username}</td>
|
||||
<td>{$user.address}</td>
|
||||
<td>{$user.uptime}</td>
|
||||
<td>{$user.server}</td>
|
||||
<td>{$user.mac}</td>
|
||||
<td>{$user.session_time}</td>
|
||||
<td>{$user.rx_bytes}</td>
|
||||
<td>{$user.tx_bytes}</td>
|
||||
<td>{$user.total}</td>
|
||||
<td><button class="disconnect-btn" onclick="disconnectUser('{$user.username}')">Disconnect</button></td> <!-- Disconnect button -->
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#hotspotUsersTable').DataTable();
|
||||
});
|
||||
|
||||
function disconnectUser(username) {
|
||||
// You can perform disconnect action here, such as making an AJAX call to disconnect the user
|
||||
alert('Disconnecting user: ' + username);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
63
ui/ui/page-edit.tpl
Normal file
63
ui/ui/page-edit.tpl
Normal file
@ -0,0 +1,63 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card mb20 card-primary card-hovered">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{$pageHeader}</h3>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-danger btn-xs" title="Reset File" href="{$_url}pages/{$PageFile}-reset" onclick="return confirm('Reset File?')"><span
|
||||
class="fa fa-refresh" aria-hidden="true"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="myNiccard" style="width: 100%;"></div>
|
||||
<div id="card-edit" class="card-body">{$htmls}</div>
|
||||
{if $writeable}
|
||||
<div class="card-footer">
|
||||
<a href="javascript:saveIt()" class="btn btn-primary btn-block">SAVE</a>
|
||||
<br>
|
||||
<p class="help-block">{Lang::T("Sometimes you need to refresh 3 times until content change")}</p>
|
||||
<input type="text" class="form-control" onclick="this.select()" readonly
|
||||
value="{$app_url}/pages/{$PageFile}.html">
|
||||
</div>
|
||||
{else}
|
||||
<div class="card-footer">
|
||||
{Lang::T("Failed to save page, make sure i can write to folder pages, <i>chmod 664 pages/*.html<i>")}
|
||||
</div>
|
||||
{/if}
|
||||
{if $PageFile=='Voucher'}
|
||||
<div class="card-footer">
|
||||
<p class="help-block">
|
||||
<b>[[company_name]]</b> Your Company Name at Settings.<br>
|
||||
<b>[[price]]</b> Plan Price.<br>
|
||||
<b>[[voucher_code]]</b> Voucher Code.<br>
|
||||
<b>[[plan]]</b> Voucher Plan.<br>
|
||||
<b>[[counter]]</b> Counter.<br>
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form id="formpages" class="hidden" method="post" role="form" action="{$_url}pages/{$PageFile}-post">
|
||||
<textarea name="html" id="html"></textarea>
|
||||
</form>
|
||||
<script src="ui/ui/scripts/nicEdit.js"></script>
|
||||
{literal}
|
||||
<script type="text/javascript">
|
||||
var myNicEditor
|
||||
bkLib.onDomLoaded(function() {
|
||||
myNicEditor = new nicEditor({fullcard : true});
|
||||
myNicEditor.setcard('myNiccard');
|
||||
myNicEditor.addInstance('card-edit');
|
||||
});
|
||||
|
||||
function saveIt() {
|
||||
//alert(document.getElementById('card-edit').innerHTML);
|
||||
document.getElementById('html').value = nicEditors.findEditor('card-edit').getContent()
|
||||
document.getElementById('formpages').submit();
|
||||
}
|
||||
</script>
|
||||
{/literal}
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
20
ui/ui/pagination.tpl
Normal file
20
ui/ui/pagination.tpl
Normal file
@ -0,0 +1,20 @@
|
||||
{if $paginator}
|
||||
<nav aria-label="Page navigation example" style="margin-top: 6px;">
|
||||
<ul class="pagination justify-content-end" style="border-radius: 6px;">
|
||||
<li style="background: #D7DAE3;" {if empty($paginator['prev'])}class="disabled page-item" {/if}>
|
||||
<a class="page-link" href="{$paginator['url']}{$paginator['prev']}" aria-label="Previous">
|
||||
<span aria-hidden="true">{Lang::T('Prev')}</span>
|
||||
</a>
|
||||
</li>
|
||||
{foreach $paginator['pages'] as $page}
|
||||
<li class="page-item {if $paginator['page'] == $page}active{elseif $page == '...'}disabled{/if}"><a class="page-link"
|
||||
href="{$paginator['url']}{$page}">{$page}</a></li>
|
||||
{/foreach}
|
||||
<li style="background: #D7DAE3;" {if $paginator['page']>=$paginator['count']}class="disabled page-item" {/if}>
|
||||
<a class="page-link" href="{$paginator['url']}{$paginator['next']}" aria-label="Next">
|
||||
<span aria-hidden="true">{Lang::T('Next')}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{/if}
|
35
ui/ui/paymentgateway.tpl
Normal file
35
ui/ui/paymentgateway.tpl
Normal file
@ -0,0 +1,35 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<form method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-md-offset-3">
|
||||
<div class="card card-info card-hovered">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Payment Gateway')}</h3>
|
||||
</div>
|
||||
<div class="table-responsive card-body">
|
||||
<table class="table table-striped table-condensed">
|
||||
<tbody>
|
||||
{foreach $pgs as $pg}
|
||||
<tr>
|
||||
<td width="12" align="center" valign="center"><input type="checkbox" name="pgs[]"
|
||||
{if in_array($pg, $actives)}checked{/if} value="{$pg}"></td>
|
||||
<td><a href="{$_url}paymentgateway/{$pg}"
|
||||
class="btn btn-block btn-{if in_array($pg, $actives)}info{else}default{/if} text-left">{ucwords($pg)}</a>
|
||||
</td>
|
||||
<td width="12"><a href="{$_url}paymentgateway/delete/{$pg}"
|
||||
onclick="return confirm('{Lang::T('Delete')} {$pg}?')" class="btn btn-danger"><i
|
||||
class="fa fa-trash"></i></a></td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer"><button type="submit" class="btn btn-primary btn-block" name="save"
|
||||
value="actives">{Lang::T('Save Changes')}</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
69
ui/ui/plan-edit.tpl
Normal file
69
ui/ui/plan-edit.tpl
Normal file
@ -0,0 +1,69 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Edit Plan</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}plan/edit-post">
|
||||
<input type="hidden" name="id" value="{$d['id']}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Select Account')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="username" name="username"
|
||||
value="{$d['username']}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Service Plan')}</label>
|
||||
<div class="col-md-6">
|
||||
<select id="id_plan" name="id_plan" class="form-select" style="height: 52px; background-color: white;">
|
||||
{foreach $p as $ps}
|
||||
<option value="{$ps['id']}" {if $d['plan_id'] eq $ps['id']} selected {/if}>
|
||||
{if $ps['enabled'] neq 1}DISABLED PLAN • {/if}
|
||||
{if $ps['is_radius']=='1'}Radius{else}{$ps['routers']}{/if} • {$ps['name_plan']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Created On')}</label>
|
||||
<div class="col-md-4 mb-2">
|
||||
<input type="date" class="form-control" name="expiration" readonly
|
||||
value="{$d['recharged_on']}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" placeholder="00:00:00" readonly
|
||||
value="{$d['recharged_time']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Expires On')}</label>
|
||||
<div class="col-md-4 mb-2">
|
||||
<input type="date" class="form-control" id="expiration" name="expiration"
|
||||
value="{$d['expiration']}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" id="time" name="time" placeholder="00:00:00"
|
||||
value="{$d['time']}">
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success mb-3"
|
||||
type="submit">{Lang::T('Edit')}</button>
|
||||
Or <a href="{$_url}plan/list" class="btn btn-outline-primary">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
91
ui/ui/plan.tpl
Normal file
91
ui/ui/plan.tpl
Normal file
@ -0,0 +1,91 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Plans</h3>
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<div>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary" title="save" href="{$_url}plan/sync"
|
||||
onclick="return confirm('This will sync/send Caustomer active plan to Mikrotik?')"><span
|
||||
class="fa fa-refresh" aria-hidden="true"></span> sync</a>
|
||||
</div>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-info" title="save" href="{$_url}customers/csv"
|
||||
onclick="return confirm('This will export to CSV?')"><span class="fa fa-download"
|
||||
aria-hidden="true"></span> CSV</a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="md-whiteframe-z1 mb20 text-center row">
|
||||
<div class="col-md-8 mb-3">
|
||||
<form id="site-search" method="post" action="{$_url}plan/list/">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<input type="text" name="search" class="form-control"
|
||||
placeholder="{Lang::T('Search by Username')}..." value="{$search}">
|
||||
<button class="btn btn-success input-group-btn" type="submit">{Lang::T('Search')}</button>
|
||||
<!-- <div class="input-group-btn">
|
||||
</div> -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="{$_url}plan/recharge" class="btn btn-primary btn-block"><i
|
||||
class="fa fa-add"> </i> {Lang::T('Recharge Account')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table id="datatable" class="table table-bordered table-striped table-condensed table-hover border-primary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Type')}</th>
|
||||
<th>{Lang::T('Type')}</th>
|
||||
<th>{Lang::T('Created On')}</th>
|
||||
<th>{Lang::T('Expires On')}</th>
|
||||
<th>{Lang::T('Method')}</th>
|
||||
<th>{Lang::T('Routers')}</th>
|
||||
<th>{Lang::T('Manage')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr {if $ds['status']=='off'}class="danger" {/if}>
|
||||
<td><a href="{$_url}customers/viewu/{$ds['username']}">{$ds['username']}</a></td>
|
||||
<td>{$ds['namebp']}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
<td>{$ds['plan_type']}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['recharged_on'],$ds['recharged_time'])}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['expiration'],$ds['time'])}</td>
|
||||
<td>{$ds['method']}</td>
|
||||
<td>{$ds['routers']}</td>
|
||||
<td>
|
||||
<a href="{$_url}plan/edit/{$ds['id']}"
|
||||
class="btn btn-warning btn-xs mb-1">{Lang::T('Edit')}</a>
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<a href="{$_url}plan/delete/{$ds['id']}" id="{$ds['id']}"
|
||||
onclick="return confirm('{Lang::T('Delete')}?')"
|
||||
class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
92
ui/ui/plugin-manager.tpl
Normal file
92
ui/ui/plugin-manager.tpl
Normal file
@ -0,0 +1,92 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-primary card-hovered">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Plugin')}</h3>
|
||||
</div>
|
||||
<div class="card-body row">
|
||||
{foreach $plugins as $plugin}
|
||||
<div class="col-md-6">
|
||||
<div class="card kanbanPreview-bx">
|
||||
<div class="card-body">
|
||||
<div class="sub-card">
|
||||
<span class="text-warning sub-title fs-14">{$plugin['name']}</span>
|
||||
<p class="font-w300">{$plugin['description']}</p>
|
||||
<div class="row justify-content-between align-items-center">
|
||||
<div>
|
||||
<span>
|
||||
<center>
|
||||
<small><i>@{$plugin['author']} Last update: {$plugin['last_update']}</i></small>
|
||||
</center>
|
||||
</span>
|
||||
</div>
|
||||
<div class="btn-group btn-group-justified mb-2" role="group" aria-label="...">
|
||||
<a href="{$plugin['url']}" target="_blank" class="btn btn-primary"><i
|
||||
class="fa fa-globe"></i> Web</a>
|
||||
<a href="{$plugin['github']}" target="_blank" class="btn btn-info"><i
|
||||
class="fa fa-align-left"></i> Source</a>
|
||||
</div>
|
||||
<div class="btn-group btn-group-justified" role="group" aria-label="...">
|
||||
<a href="{$_url}pluginmanager/delete/plugin/{$plugin['id']}" onclick="return confirm('{Lang::T('Delete')}?')" class="btn btn-danger"><i
|
||||
class="fa fa-trash"></i> Delete</a>
|
||||
<a {if $zipExt } href="{$_url}pluginmanager/install/plugin/{$plugin['id']}"
|
||||
onclick="return confirm('Installing plugin will take some time to complete, do not close the page while it loading to install the plugin')"
|
||||
{else} href="#" onclick="alert('PHP ZIP extension is not installed')"
|
||||
{/if}
|
||||
class="btn btn-success"><i class="fa fa-circle-arrow-down"></i> Install</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-primary card-hovered">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Payment Gateway')}</h3>
|
||||
</div>
|
||||
<div class="card-body row">
|
||||
foreach $pgs as $pg}
|
||||
<div class="col-md-6">
|
||||
<div class="card kanbanPreview-bx">
|
||||
<div class="card-body">
|
||||
<div class="sub-card">
|
||||
<span class="text-warning sub-title fs-14">{$pg['name']}</span>
|
||||
<p class="font-w300">{$pg['description']}</p>
|
||||
<div class="row justify-content-between align-items-center">
|
||||
<div>
|
||||
<span>
|
||||
<center>
|
||||
<small><i>@{$pg['author']} Last update: {$pg['last_update']}</i></small>
|
||||
</center>
|
||||
</span>
|
||||
</div>
|
||||
<div class="btn-group btn-group-justified" role="group" aria-label="...">
|
||||
<a href="{$pg['url']}" target="_blank" class="btn btn-primary"><i
|
||||
class="fa fa-globe"></i> Web</a>
|
||||
<a href="{$pg['github']}" target="_blank" class="btn btn-info"><i
|
||||
class="fa fa-align-left"></i> Source</a>
|
||||
<a {if $zipExt } href="{$_url}pluginmanager/install/payment/{$pg['id']}"
|
||||
onclick="return confirm('Installing plugin will take some time to complete, do not close the page while it loading to install the plugin')"
|
||||
{else} href="#" onclick="alert('PHP ZIP extension is not available')"
|
||||
{/if}
|
||||
class="btn btn-success"><i class="fa fa-circle-arrow-down"></i> Install</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
53
ui/ui/pool-add.tpl
Normal file
53
ui/ui/pool-add.tpl
Normal file
@ -0,0 +1,53 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Add Pool')}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}pool/add-post" >
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Name Pool')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name" name="name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Range IP')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="ip_address" name="ip_address" placeholder="ex: 192.168.88.2-192.168.88.254">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a href="{$_url}routers/add">{Lang::T('Routers')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="routers" name="routers" class="form-select" style="height: 52px; background-color: white;">
|
||||
{if $_c['radius_enable']}
|
||||
<option value="radius">Radius</option>
|
||||
{/if}
|
||||
{foreach $r as $rs}
|
||||
<option value="{$rs['name']}">{$rs['name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
{if $_c['radius_enable']}
|
||||
<p class="help-block col-md-4">For Radius, you need to add <b>Pool Name</b> in Mikrotik manually</p>
|
||||
{/if}
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary mb-3" type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a class="btn btn-outline-primary" href="{$_url}pool/list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
47
ui/ui/pool-edit.tpl
Normal file
47
ui/ui/pool-edit.tpl
Normal file
@ -0,0 +1,47 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Edit Pool')}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}pool/edit-post" >
|
||||
<input type="hidden" name="id" value="{$d['id']}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Name Pool')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name" name="name" value="{$d['pool_name']}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Range IP')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="ip_address" name="ip_address" value="{$d['range_ip']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Routers')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="routers" name="routers" value="{$d['routers']}" readonly>
|
||||
</div>
|
||||
{if $_c['radius_enable']}
|
||||
<p class="help-block col-md-4">For Radius, you need to add <b>Pool Name</b> in Mikrotik manually</p>
|
||||
{/if}
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success mb-3" type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a class="btn btn-outline-primary" href="{$_url}pool/list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
70
ui/ui/pool.tpl
Normal file
70
ui/ui/pool.tpl
Normal file
@ -0,0 +1,70 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('IP Pool')}</h3>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-xs" title="save" href="{$_url}pool/sync"
|
||||
onclick="return confirm('This will sync/send IP Pool to Mikrotik?')"><span
|
||||
class="fa fa-refresh" aria-hidden="true"></span> sync</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="md-whiteframe-z1 mb20 text-center row">
|
||||
<div class="col-md-8 mb-3">
|
||||
<form id="site-search" method="post" action="{$_url}pool/list/">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<input type="text" name="name" class="form-control"
|
||||
placeholder="{Lang::T('Search by Name')}...">
|
||||
<button class="btn btn-success input-group-btn" type="submit">{Lang::T('Search')}</button>
|
||||
<!-- <div class="input-group-btn">
|
||||
</div> -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="{$_url}pool/add" class="btn btn-primary btn-block"><i
|
||||
class="fa fa-add"> </i> {Lang::T('New Pool')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-condensed table-hover border-primary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Name Pool')}</th>
|
||||
<th>{Lang::T('Range IP')}</th>
|
||||
<th>{Lang::T('Routers')}</th>
|
||||
<th>{Lang::T('Manage')}</th>
|
||||
<th>ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['pool_name']}</td>
|
||||
<td>{$ds['range_ip']}</td>
|
||||
<td>{$ds['routers']}</td>
|
||||
<td align="center">
|
||||
<a href="{$_url}pool/edit/{$ds['id']}" class="btn btn-info btn-xs mb-1">{Lang::T('Edit')}</a>
|
||||
<a href="{$_url}pool/delete/{$ds['id']}" id="{$ds['id']}"
|
||||
onclick="return confirm('{Lang::T('Delete')}?')"
|
||||
class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
<td>{$ds['id']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
69
ui/ui/ppp_users.tpl
Normal file
69
ui/ui/ppp_users.tpl
Normal file
@ -0,0 +1,69 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card">
|
||||
<div class="card-header">{Lang::T('Hotspot Users')}</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive table_mobile">
|
||||
<table id="hotspot_users_table" class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Address')}</th>
|
||||
<th>{Lang::T('Uptime')}</th>
|
||||
<th>{Lang::T('Server')}</th>
|
||||
<th>{Lang::T('MAC Address')}</th>
|
||||
<th>{Lang::T('Session Time')}</th>
|
||||
<th style="color: red;">{Lang::T('Upload')}</th>
|
||||
<th style="color: green;">{Lang::T('Download')}</th>
|
||||
<th>{Lang::T('Total')}</th>
|
||||
<th>{Lang::T('Action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- DataTables will populate the table body dynamically -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
||||
|
||||
<!-- Include jQuery and DataTables JS CDN
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>-->
|
||||
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#hotspot_users_table').DataTable({
|
||||
"ajax": {
|
||||
"url": "{$_url}onlineusers/ppp_users",
|
||||
"dataSrc": ""
|
||||
},
|
||||
"columns": [
|
||||
{ "data": "username", "render": function(data, type, row) {
|
||||
return '<span style="color: blue;">' + data + '</span>';
|
||||
}
|
||||
},
|
||||
{ "data": "address" },
|
||||
{ "data": "uptime" },
|
||||
{ "data": "server" },
|
||||
{ "data": "mac" },
|
||||
{ "data": "session_time" },
|
||||
{ "data": "rx_bytes" },
|
||||
{ "data": "tx_bytes" },
|
||||
{ "data": "total" },
|
||||
{ "data": null, "render": function(data, type, row) {
|
||||
return '<form method="post" action="{$_url}onlineusers/disconnect/{$routerId}/' + row.username + '/hotspot">' +
|
||||
'<button type="submit" class="btn btn-danger btn-sm" title="Disconnect"><i class="fa fa-times"></i></button>' +
|
||||
'</form>';
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
181
ui/ui/pppoe-add.tpl
Normal file
181
ui/ui/pppoe-add.tpl
Normal file
@ -0,0 +1,181 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Add Service Plan')}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}services/pppoe-add-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Status')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" checked name="enabled" value="1"> Enable
|
||||
<input type="radio" name="enabled" value="0"> Disable
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="prepaid" onclick="prePaid()" value="yes" checked> Prepaid
|
||||
<input type="radio" name="prepaid" onclick="postPaid()" value="no"> Postpaid
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="plan_type" value="Personal" checked> Personal
|
||||
<input type="radio" name="plan_type" value="Business"> Business
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if $_c['radius_enable']}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Radius</label>
|
||||
<div class="col-md-6">
|
||||
<input type="checkbox" name="radius" onclick="isRadius(this)" value="1"> Radius Plan
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('Cannot be change after saved')}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name_plan" maxlength="40" name="name_plan">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}bandwidth/add">{Lang::T('Bandwidth Name')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="id_bw" name="id_bw" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="">{Lang::T('Select Bandwidth')}...</option>
|
||||
{foreach $d as $ds}
|
||||
<option value="{$ds['id']}">{$ds['name_bw']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Price')}</label>
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">{$_c['currency_code']}</span>
|
||||
<input type="number" class="form-control" name="price" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Validity')}</label>
|
||||
<div class="col-md-4 mb-2">
|
||||
<input type="text" class="form-control" id="validity" name="validity">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-control" id="validity_unit" name="validity_unit">
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">{Lang::T('1 Period = 1 Month, Expires the 20th of each month')}</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}routers/add">{Lang::T('Router Name')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="routers" name="routers" required class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value=''>{Lang::T('Select Routers')}</option>
|
||||
{foreach $r as $rs}
|
||||
<option value="{$rs['name']}">{$rs['name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
<p class="help-block">{Lang::T('Cannot be change after saved')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a href="{$_url}pool/add">{Lang::T('IP Pool')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="pool_name" name="pool_name" required class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value=''>{Lang::T('Select Pool')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<legend>{Lang::T('Expired Action')} <sub>{Lang::T('Optional')}</sub></legend>
|
||||
<div class="form-group row" id="ipPool">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}pool/add">{Lang::T('Expired IP Pool')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="pool_expired" name="pool_expired" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value=''>{Lang::T('Select Pool')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{* <div class="form-group row" id="AddressList">
|
||||
<label class="col-md-2 control-label">{Lang::T('Address List')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="list_expired" id="list_expired">
|
||||
</div>
|
||||
</div> *}
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button class="btn btn-primary mb-3"
|
||||
type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a class="btn btn-outline-primary" href="{$_url}services/pppoe">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var preOpt = `<option value="Mins">{Lang::T('Mins')}</option>
|
||||
<option value="Hrs">{Lang::T('Hrs')}</option>
|
||||
<option value="Days">{Lang::T('Days')}</option>
|
||||
<option value="Months">{Lang::T('Months')}</option>`;
|
||||
var postOpt = `<option value="Period">{Lang::T('Period')}</option>`;
|
||||
function prePaid() {
|
||||
$("#validity_unit").html(preOpt);
|
||||
}
|
||||
|
||||
function postPaid() {
|
||||
$("#validity_unit").html(postOpt);
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
prePaid()
|
||||
})
|
||||
</script>
|
||||
{if $_c['radius_enable']}
|
||||
{literal}
|
||||
<script>
|
||||
function isRadius(cek) {
|
||||
if (cek.checked) {
|
||||
document.getElementById("routers").required = false;
|
||||
document.getElementById("routers").disabled = true;
|
||||
$.ajax({
|
||||
url: "index.php?_route=autoload/pool",
|
||||
data: "routers=radius",
|
||||
cache: false,
|
||||
success: function(msg) {
|
||||
$("#pool_name").html(msg);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: "index.php?_route=autoload/pool",
|
||||
data: "routers=radius",
|
||||
cache: false,
|
||||
success: function(msg) {
|
||||
$("#pool_expired").html(msg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
document.getElementById("routers").required = true;
|
||||
document.getElementById("routers").disabled = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{/literal}
|
||||
{/if}
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
183
ui/ui/pppoe-edit.tpl
Normal file
183
ui/ui/pppoe-edit.tpl
Normal file
@ -0,0 +1,183 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Edit Service Plan')} || {$d['name_plan']}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}services/edit-pppoe-post">
|
||||
<input type="hidden" name="id" value="{$d['id']}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Status')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="enabled" value="1" {if $d['enabled'] == 1}checked{/if}> Enable
|
||||
<input type="radio" name="enabled" value="0" {if $d['enabled'] == 0}checked{/if}> Disable
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="prepaid" onclick="prePaid()" value="yes" {if $d['prepaid'] == yes}checked{/if}>
|
||||
Prepaid
|
||||
<input type="radio" name="prepaid" onclick="postPaid()" value="no" {if $d['prepaid'] == no}checked{/if}> Postpaid
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Type')}</label>
|
||||
<div class="col-md-10">
|
||||
<input type="radio" name="plan_type" value="Personal"
|
||||
{if $d['plan_type'] == 'Personal'}checked{/if}>
|
||||
Personal
|
||||
<input type="radio" name="plan_type" value="Business"
|
||||
{if $d['plan_type'] == 'Business'}checked{/if}> Business
|
||||
</div>
|
||||
</div>
|
||||
{if $_c['radius_enable'] and $d['is_radius']}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Radius</label>
|
||||
<div class="col-md-10">
|
||||
<label class="label label-primary">RADIUS</label>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name_plan" maxlength="40" name="name_plan"
|
||||
value="{$d['name_plan']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}bandwidth/add">{Lang::T('Bandwidth Name')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="id_bw" name="id_bw" class="form-select" style="height: 52px; background-color: white;">
|
||||
{foreach $b as $bs}
|
||||
<option value="{$bs['id']}" {if $d['id_bw'] eq $bs['id']} selected {/if}>
|
||||
{$bs['name_bw']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Price')}</label>
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">{$_c['currency_code']}</span>
|
||||
<input type="number" class="form-control" name="price" required value="{$d['price']}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Plan Validity')}</label>
|
||||
<div class="col-md-4 mb-3">
|
||||
<input type="text" class="form-control" id="validity" name="validity"
|
||||
value="{$d['validity']}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="form-control" id="validity_unit" name="validity_unit">
|
||||
{if $d['prepaid'] == yes}
|
||||
<option value="Mins" {if $d['validity_unit'] eq 'Mins'} selected {/if}>{Lang::T('Mins')}
|
||||
</option>
|
||||
<option value="Hrs" {if $d['validity_unit'] eq 'Hrs'} selected {/if}>{Lang::T('Hrs')}
|
||||
</option>
|
||||
<option value="Days" {if $d['validity_unit'] eq 'Days'} selected {/if}>{Lang::T('Days')}
|
||||
</option>
|
||||
<option value="Months" {if $d['validity_unit'] eq 'Months'} selected {/if}>
|
||||
{Lang::T('Months')}</option>
|
||||
{else}
|
||||
<option value="Period" {if $d['validity_unit'] eq 'Period'} selected {/if}>
|
||||
{Lang::T('Period')}</option>
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
{Lang::T('1 Period = 1 Month, Expires the 20th of each month')}</p>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a href="{$_url}pool/add">{Lang::T('IP Pool')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="pool_name" name="pool_name" required class="form-select" style="height: 52px; background-color: white;">
|
||||
{foreach $p as $ps}
|
||||
<option value="{$ps['pool_name']}" {if $d['pool'] eq $ps['pool_name']} selected {/if}>
|
||||
{$ps['pool_name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Router Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="routers" name="routers" value="{$d['routers']}"
|
||||
readonly>
|
||||
</div>
|
||||
</div>
|
||||
<legend>{Lang::T('Expired Action')} <sub>{Lang::T('Optional')}</sub></legend>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a
|
||||
href="{$_url}pool/add">{Lang::T('Expired IP Pool')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="pool_expired" name="pool_expired" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value=''>{Lang::T('Select Pool')}</option>
|
||||
{foreach $p as $ps}
|
||||
<option value="{$ps['pool_name']}" {if $d['pool_expired'] eq $ps['pool_name']} selected
|
||||
{/if}>{$ps['pool_name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{* <div class="form-group row" id="AddressList">
|
||||
<label class="col-md-2 control-label">{Lang::T('Address List')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" name="list_expired" id="list_expired" value="{$d['list_expired']}">
|
||||
</div>
|
||||
</div> *}
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success mb-3" type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a class="btn btn-outline-primary" href="{$_url}services/pppoe">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var preOpt = `<option value="Mins">{Lang::T('Mins')}</option>
|
||||
<option value="Hrs">{Lang::T('Hrs')}</option>
|
||||
<option value="Days">{Lang::T('Days')}</option>
|
||||
<option value="Months">{Lang::T('Months')}</option>`;
|
||||
var postOpt = `<option value="Period">{Lang::T('Period')}</option>`;
|
||||
function prePaid() {
|
||||
$("#validity_unit").html(preOpt);
|
||||
}
|
||||
|
||||
function postPaid() {
|
||||
$("#validity_unit").html(postOpt);
|
||||
}
|
||||
</script>
|
||||
{if $_c['radius_enable'] && $d['is_radius']}
|
||||
{literal}
|
||||
<script>
|
||||
document.getElementById("routers").required = false;
|
||||
document.getElementById("routers").disabled = true;
|
||||
setTimeout(() => {
|
||||
$.ajax({
|
||||
url: "index.php?_route=autoload/pool",
|
||||
data: "routers=radius",
|
||||
cache: false,
|
||||
success: function(msg) {
|
||||
$("#pool_expired").html(msg);
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
</script>
|
||||
{/literal}
|
||||
{/if}
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
92
ui/ui/pppoe.tpl
Normal file
92
ui/ui/pppoe.tpl
Normal file
@ -0,0 +1,92 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('PPPOE Plans')}</h3>
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary" title="save" href="{$_url}services/sync/pppoe"
|
||||
onclick="return confirm('This will sync/send PPPOE plan to Mikrotik?')"><span
|
||||
class="fa fa-refresh" aria-hidden="true"></span> sync</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="md-whiteframe-z1 mb20 text-center row">
|
||||
<div class="col-md-8 mb-3">
|
||||
<form id="site-search" method="post" action="{$_url}services/pppoe/">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<input type="text" name="name" class="form-control"
|
||||
placeholder="{Lang::T('Search by Name')}...">
|
||||
<button class="btn btn-success input-group-btn" type="submit">{Lang::T('Search')}</button>
|
||||
<!-- <div class="input-group-btn">
|
||||
</div> -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="{$_url}services/pppoe-add" class="btn btn-primary btn-block"><i
|
||||
class="fa fa-add"> </i> {Lang::T('New Service Plan')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Type')}</th>
|
||||
<th>{Lang::T('Bandwidth Plans')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
<th>{Lang::T('Plan Validity')}</th>
|
||||
<th>{Lang::T('IP Pool')}</th>
|
||||
<th>{Lang::T('Expired IP Pool')}</th>
|
||||
<th>{Lang::T('Routers')}</th>
|
||||
<th>{Lang::T('Manage')}</th>
|
||||
<th>ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr {if $ds['enabled'] != 1}class="danger" title="disabled"{/if}>
|
||||
<td>{$ds['name_plan']}</td>
|
||||
<td>{$ds['plan_type']} {if $ds['prepaid'] != 'yes'}<b>Postpaid</b>{else}Prepaid{/if}</td>
|
||||
<td>{$ds['name_bw']}</td>
|
||||
<td>{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>{$ds['validity']} {$ds['validity_unit']}</td>
|
||||
<td>{$ds['pool']}</td>
|
||||
<td>{$ds['pool_expired']}{if $ds['list_expired']}
|
||||
{if $ds['pool_expired']} |
|
||||
{/if}{$ds['list_expired']}
|
||||
{/if}</td>
|
||||
<td>
|
||||
{if $ds['is_radius']}
|
||||
<span class="label label-primary">RADIUS</span>
|
||||
{else}
|
||||
{if $ds['routers']!=''}
|
||||
<a href="{$_url}routers/edit/0&name={$ds['routers']}">{$ds['routers']}</a>
|
||||
{/if}
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{$_url}services/pppoe-edit/{$ds['id']}"
|
||||
class="btn btn-info btn-xs mb-1">{Lang::T('Edit')}</a>
|
||||
<a href="{$_url}services/pppoe-delete/{$ds['id']}"
|
||||
onclick="return confirm('{Lang::T('Delete')}?')" id="{$ds['id']}"
|
||||
class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
<td>{$ds['id']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
74
ui/ui/print-by-date.tpl
Normal file
74
ui/ui/print-by-date.tpl
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{$_title}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link href="ui/ui/styles/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="ui/ui/images/favicon.ico">
|
||||
|
||||
<style type="text/css">
|
||||
@media print
|
||||
{
|
||||
.no-print, .no-print *
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="printable">
|
||||
<h4>{Lang::T('All Transactions at Date')}: {date($_c['date_format'], strtotime($mdate))}</h4>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-condensed table-bordered" style="background: #ffffff">
|
||||
<th class="text-center">{Lang::T('Username')}</th>
|
||||
<th class="text-center">{Lang::T('Plan Name')}</th>
|
||||
<th class="text-center">{Lang::T('Type')}</th>
|
||||
<th class="text-center">{Lang::T('Plan Price')}</th>
|
||||
<th class="text-center">{Lang::T('Created On')}</th>
|
||||
<th class="text-center">{Lang::T('Expires On')}</th>
|
||||
<th class="text-center">{Lang::T('Method')}</th>
|
||||
<th class="text-center">{Lang::T('Routers')}</th>
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['username']}</td>
|
||||
<td class="text-center">{$ds['plan_name']}</td>
|
||||
<td class="text-center">{$ds['type']}</td>
|
||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['recharged_on'],$ds['recharged_time'])}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['expiration'],$ds['time'])}</td>
|
||||
<td class="text-center">{$ds['method']}</td>
|
||||
<td class="text-center">{$ds['routers']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</table>
|
||||
</div>
|
||||
<div class="clearfix text-right total-sum mb10">
|
||||
<h4 class="text-uppercase text-bold">{Lang::T('Total Income')}:</h4>
|
||||
<h3 class="sum">{$_c['currency_code']} {number_format($dr,2,$_c['dec_point'],$_c['thousands_sep'])}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" id="actprint" class="btn btn-default btn-sm no-print">{Lang::T('Click Here to Print')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<script src="ui/ui/scripts/jquery-1.10.2.js"></script>
|
||||
<script src="ui/ui/scripts/bootstrap.min.js"></script>
|
||||
{if isset($xfooter)}
|
||||
{$xfooter}
|
||||
{/if}
|
||||
<script>
|
||||
jQuery(document).ready(function() {
|
||||
// initiate layout and plugins
|
||||
$("#actprint").click(function() {
|
||||
window.print();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
74
ui/ui/print-by-period.tpl
Normal file
74
ui/ui/print-by-period.tpl
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{$_title}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link href="ui/ui/styles/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="ui/ui/images/favicon.ico">
|
||||
|
||||
<style type="text/css">
|
||||
@media print
|
||||
{
|
||||
.no-print, .no-print *
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="printable">
|
||||
<h4>{Lang::T('All Transactions at Date')}: {date( $_c['date_format'], strtotime($fdate))} - {date( $_c['date_format'], strtotime($tdate))}</h4>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-condensed table-striped " style="background: #ffffff">
|
||||
<th class="text-center">{Lang::T('Username')}</th>
|
||||
<th class="text-center">{Lang::T('Plan Name')}</th>
|
||||
<th class="text-center">{Lang::T('Type')}</th>
|
||||
<th class="text-center">{Lang::T('Plan Price')}</th>
|
||||
<th class="text-center">{Lang::T('Created On')}</th>
|
||||
<th class="text-center">{Lang::T('Expires On')}</th>
|
||||
<th class="text-center">{Lang::T('Method')}</th>
|
||||
<th class="text-center">{Lang::T('Routers')}</th>
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['username']}</td>
|
||||
<td class="text-center">{$ds['plan_name']}</td>
|
||||
<td class="text-center">{$ds['type']}</td>
|
||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['recharged_on'],$ds['recharged_time'])}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['expiration'],$ds['time'])}</td>
|
||||
<td class="text-center">{$ds['method']}</td>
|
||||
<td class="text-center">{$ds['routers']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</table>
|
||||
</div>
|
||||
<div class="clearfix text-right total-sum mb10">
|
||||
<h4 class="text-uppercase text-bold">{Lang::T('Total Income')}:</h4>
|
||||
<h3 class="sum">{Lang::moneyFormat($dr)}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" id="actprint" class="btn btn-default btn-sm no-print">{Lang::T('Click Here to Print')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<script src="ui/ui/scripts/jquery-1.10.2.js"></script>
|
||||
<script src="ui/ui/scripts/bootstrap.min.js"></script>
|
||||
{if isset($xfooter)}
|
||||
{$xfooter}
|
||||
{/if}
|
||||
<script>
|
||||
jQuery(document).ready(function() {
|
||||
// initiate layout and plugins
|
||||
$("#actprint").click(function() {
|
||||
window.print();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
136
ui/ui/print-voucher.tpl
Normal file
136
ui/ui/print-voucher.tpl
Normal file
@ -0,0 +1,136 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{$_title}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="ui/ui/images/favicon.ico">
|
||||
<style>
|
||||
.ukuran {
|
||||
size: A4;
|
||||
}
|
||||
|
||||
body,
|
||||
td,
|
||||
th {
|
||||
font-size: 12px;
|
||||
font-family: Segoe, "Segoe UI", "DejaVu Sans", "Trebuchet MS", Verdana, sans-serif;
|
||||
}
|
||||
|
||||
page[size="A4"] {
|
||||
background: white;
|
||||
width: 21cm;
|
||||
height: 29.7cm;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 0.5cm;
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
size: auto;
|
||||
margin: 0;
|
||||
box-shadow: 0;
|
||||
}
|
||||
|
||||
page[size="A4"] {
|
||||
margin: 0;
|
||||
size: auto;
|
||||
box-shadow: 0;
|
||||
}
|
||||
|
||||
.page-break {
|
||||
display: block;
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
.no-print,
|
||||
.no-print * {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<page size="A4">
|
||||
<form method="post" action="{$_url}plan/print-voucher/" class="no-print">
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="1" class="btn btn-default btn-sm">
|
||||
<tr>
|
||||
<td>From ID > <input type="text" name="from_id" style="width:40px" value="{$from_id}"> limit
|
||||
<input type="text" name="limit" style="width:40px" value="{$limit}"></td>
|
||||
<td>Voucher PerLine <input type="text" style="width:40px" name="vpl" value="{$vpl}">
|
||||
vouchers</td>
|
||||
<td>PageBreak after <input type="text" style="width:40px" name="pagebreak" value="{$pagebreak}">
|
||||
vouchers</td>
|
||||
<td>Plans <select id="plan_id" name="planid" style="width:50px">
|
||||
<option value="0">--all--</option>
|
||||
{foreach $plans as $plan}
|
||||
<option value="{$plan['id']}" {if $plan['id']==$planid}selected{/if}>{$plan['name_plan']}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select></td>
|
||||
<td><button type="submit">submit</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<center><button type="button" onclick="window.print()"
|
||||
class="btn btn-default btn-sm no-print">{Lang::T('Click Here to Print')}</button><br>
|
||||
{Lang::T('Print side by side, it will easy to cut')}<br>
|
||||
show {$v|@count} vouchers from {$vc} vouchers<br>
|
||||
from ID {$v[0]['id']} limit {$limit} vouchers
|
||||
</center>
|
||||
</form>
|
||||
<div id="printable" align="center">
|
||||
<hr>
|
||||
{$n = 1}
|
||||
{foreach $voucher as $vs}
|
||||
{$jml = $jml + 1}
|
||||
{if $n == 1}
|
||||
<table>
|
||||
<tr>
|
||||
{/if}
|
||||
<td>{$vs}</td>
|
||||
{if $n == $vpl}
|
||||
</table>
|
||||
{$n = 1}
|
||||
{else}
|
||||
{$n = $n + 1}
|
||||
{/if}
|
||||
|
||||
|
||||
{if $jml == $pagebreak}
|
||||
{$jml = 0}
|
||||
<!-- pageBreak -->
|
||||
<div class="page-break">
|
||||
<div class="no-print" style="background-color: #E91E63; color:#FFF;" align="center">-- pageBreak --
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/foreach}
|
||||
</div>
|
||||
</page>
|
||||
<script src="ui/ui/scripts/jquery-1.10.2.js"></script>
|
||||
{if isset($xfooter)}
|
||||
{$xfooter}
|
||||
{/if}
|
||||
<script>
|
||||
jQuery(document).ready(function() {
|
||||
// initiate layout and plugins
|
||||
$("#actprint").click(function() {
|
||||
window.print();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
88
ui/ui/radius-nas-add.tpl
Normal file
88
ui/ui/radius-nas-add.tpl
Normal file
@ -0,0 +1,88 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Radius - Add NAS </h3></div>
|
||||
<div class="card-body">
|
||||
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}radius/nas-add-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Router Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" required class="form-control" id="shortname" name="shortname" maxlength="32">
|
||||
<p class="help-block">{Lang::T('Name of Area that router operated')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('IP Address')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" placeholder="192.168.88.1" required class="form-control" id="nasname"
|
||||
name="nasname" maxlength="128">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Secret</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="secret" name="secret" required
|
||||
onmouseleave="this.type = 'password'" onmouseenter="this.type = 'text'" maxlength="60">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Ports</label>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" id="ports" name="ports" placeholder="null">
|
||||
</div>
|
||||
<label class="col-md-2 control-label">Type</label>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" id="type" name="type" value="other" required
|
||||
placeholder="other">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Server</label>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" id="server" name="server" placeholder="null">
|
||||
</div>
|
||||
<label class="col-md-2 control-label">Community</label>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" id="community" name="community" placeholder="null">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Description')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="description" name="description"></textarea>
|
||||
<p class="help-block">{Lang::T('Explain Coverage of router')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a href="{$_url}routers/add">{Lang::T('Routers')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="routers" name="routers" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="">No Router</option>
|
||||
{foreach $routers as $rs}
|
||||
<option value="{$rs['name']}">{$rs['name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">Assign NAS to Router</p>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary mb-3"
|
||||
type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a class="btn btn-outline-primary href="{$_url}radius/nas-list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
88
ui/ui/radius-nas-edit.tpl
Normal file
88
ui/ui/radius-nas-edit.tpl
Normal file
@ -0,0 +1,88 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Radius - Edit NAS</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}radius/nas-edit-post/{$d['id']}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Router Name')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" required class="form-control" id="shortname" name="shortname" value="{$d['shortname']}" maxlength="32">
|
||||
<p class="help-block">{Lang::T('Name of Area that router operated')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('IP Address')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" placeholder="192.168.88.1" value="{$d['nasname']}" required class="form-control" id="nasname"
|
||||
name="nasname" maxlength="128">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Secret</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="secret" name="secret" required value="{$d['secret']}"
|
||||
onmouseleave="this.type = 'password'" onmouseenter="this.type = 'text'" maxlength="60">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Ports</label>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" id="ports" name="ports" placeholder="null" value="{$d['ports']}">
|
||||
</div>
|
||||
<label class="col-md-2 control-label">Type</label>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" id="type" name="type" value="other" value="{$d['type']}" required
|
||||
placeholder="other">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">Server</label>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" id="server" name="server" value="{$d['server']}" placeholder="null">
|
||||
</div>
|
||||
<label class="col-md-2 control-label">Community</label>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" id="community" name="community" value="{$d['community']}" placeholder="null">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Description')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="description" name="description"> {htmlentities($d['description'])}</textarea>
|
||||
<p class="help-block">{Lang::T('Explain Coverage of router')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label"><a href="{$_url}routers/add">{Lang::T('Routers')}</a></label>
|
||||
<div class="col-md-6">
|
||||
<select id="routers" name="routers" class="form-select" style="height: 52px; background-color: white;">
|
||||
<option value="">No Router</option>
|
||||
{foreach $routers as $rs}
|
||||
<option {if $rs['name'] == $d['routers']}selected{/if} value="{$rs['name']}">{$rs['name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">Assign NAS to Router</p>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary mb-3"
|
||||
type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a class="btn btn-outline-primary" href="{$_url}radius/nas-list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
73
ui/ui/radius-nas.tpl
Normal file
73
ui/ui/radius-nas.tpl
Normal file
@ -0,0 +1,73 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Radius</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="md-whiteframe-z1 mb20 text-center row">
|
||||
<div class="col-md-8 mb-3">
|
||||
<form id="site-search" method="post" action="{$_url}radius/nas-list">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<input type="text" name="name" class="form-control" value="{$name}"
|
||||
placeholder="{Lang::T('Search by Name')}...">
|
||||
<button class="btn btn-success input-group-btn" type="submit">{Lang::T('Search')}</button>
|
||||
<!-- <div class="input-group-btn">
|
||||
</div> -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="{$_url}radius/nas-add" class="btn btn-primary btn-block"><i
|
||||
class="fa fa-add"> </i> New NAS</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-condensed table-hover border-primary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>IP</th>
|
||||
<th>Type</th>
|
||||
<th>Port</th>
|
||||
<th>Server</th>
|
||||
<th>Community</th>
|
||||
<th>Routers</th>
|
||||
<th>{Lang::T('Manage')}</th>
|
||||
<th>ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $nas as $ds}
|
||||
<tr>
|
||||
<td>{$ds['shortname']}</td>
|
||||
<td>{$ds['nasname']}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
<td>{$ds['ports']}</td>
|
||||
<td>{$ds['server']}</td>
|
||||
<td>{$ds['community']}</td>
|
||||
<td>{$ds['routers']}</td>
|
||||
<td align="center">
|
||||
<a href="{$_url}radius/nas-edit/{$ds['id']}" class="btn btn-info btn-xs mb-1">{Lang::T('Edit')}</a>
|
||||
<a href="{$_url}radius/nas-delete/{$ds['id']}" id="{$ds['id']}"
|
||||
onclick="return confirm('{Lang::T('Delete')}?')"
|
||||
class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
<td align="center">{$ds['id']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
96
ui/ui/recharge-confirm.tpl
Normal file
96
ui/ui/recharge-confirm.tpl
Normal file
@ -0,0 +1,96 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Confirm')}</h3></div>
|
||||
<div class="card-body">
|
||||
<center><b>{Lang::T('Customer')}</b></center>
|
||||
<ul class="list-group list-group-unbordered">
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Username')}</b> <span class="pull-right">{$cust['username']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Name')}</b> <span class="pull-right">{$cust['fullname']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Phone Number')}</b> <span class="pull-right">{$cust['phonenumber']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Email')}</b> <span class="pull-right">{$cust['email']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Address')}</b> <span class="pull-right">{$cust['address']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Balance')}</b> <span
|
||||
class="pull-right">{Lang::moneyFormat($cust['balance'])}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<center><b>{Lang::T('Plan')}</b></center>
|
||||
<ul class="list-group list-group-unbordered">
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Plan Name')}</b> <span class="pull-right">{$plan['name_plan']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Location')}</b> <span
|
||||
class="pull-right">{if $plan['is_radius']}Radius{else}{$plan['routers']}{/if}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Type')}</b> <span
|
||||
class="pull-right">{if $plan['prepaid'] eq 'yes'}Prepaid{else}Postpaid{/if}
|
||||
{$plan['type']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Plan Price')}</b> <span
|
||||
class="pull-right">{if $using eq 'zero'}{Lang::moneyFormat(0)}{else}{Lang::moneyFormat($plan['price'])}{/if}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Plan Validity')}</b> <span class="pull-right">{$plan['validity']}
|
||||
{$plan['validity_unit']}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Using')}</b> <span class="pull-right">{ucwords($using)}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<center><b>{Lang::T('Total')}</b></center>
|
||||
<ul class="list-group list-group-unbordered">
|
||||
{if $using neq 'zero' and $add_cost>0}
|
||||
{foreach $bills as $k => $v}
|
||||
<li class="list-group-item">
|
||||
<b>{$k}</b> <span class="pull-right">{Lang::moneyFormat($v)}</span>
|
||||
</li>
|
||||
{/foreach}
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Additional Cost')}</b> <span
|
||||
class="pull-right">{Lang::moneyFormat($add_cost)}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Total')}</b> <small>({Lang::T('Plan Price')} +{Lang::T('Additional Cost')})</small><span class="pull-right"
|
||||
style="font-size: large; font-weight:bolder; font-family: 'Courier New', Courier, monospace; ">{Lang::moneyFormat($plan['price']+$add_cost)}</span>
|
||||
</li>
|
||||
{else}
|
||||
<li class="list-group-item">
|
||||
<b>{Lang::T('Total')}</b> <span class="pull-right"
|
||||
style="font-size: large; font-weight:bolder; font-family: 'Courier New', Courier, monospace; ">{if $using eq 'zero'}{Lang::moneyFormat(0)}{else}{Lang::moneyFormat($plan['price'])}{/if}</span>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}plan/recharge-post">
|
||||
<input type="hidden" name="id_customer" value="{$cust['id']}">
|
||||
<input type="hidden" name="plan" value="{$plan['id']}">
|
||||
<input type="hidden" name="server" value="{$server}">
|
||||
<input type="hidden" name="using" value="{$using}">
|
||||
<input type="hidden" name="stoken" value="{App::getToken()}">
|
||||
<center>
|
||||
<button class="btn btn-success mb-3" type="submit">{Lang::T('Recharge')}</button><br>
|
||||
<a class="btn btn-outline-primary" href="{$_url}plan/recharge">{Lang::T('Cancel')}</a>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
73
ui/ui/recharge.tpl
Normal file
73
ui/ui/recharge.tpl
Normal file
@ -0,0 +1,73 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Recharge Account')}</h3></div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}plan/recharge-confirm">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Select Account')}</label>
|
||||
<div class="col-md-6">
|
||||
<select {if $cust}{else}id="personSelect"{/if} class="form-select select2" style="height: 52px; background-color: white;"
|
||||
name="id_customer" style="width: 100%" data-placeholder="{Lang::T('Select a customer')}...">
|
||||
{if $cust}
|
||||
<option value="{$cust['id']}">{$cust['username']} • {$cust['fullname']} • {$cust['email']}</option>
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Type')}</label>
|
||||
<div class="col-md-6">
|
||||
<label><input type="radio" id="Hot" name="type" value="Hotspot"> {Lang::T('Hotspot Plans')}</label>
|
||||
<label><input type="radio" id="POE" name="type" value="PPPOE"> {Lang::T('PPPOE Plans')}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Routers')}</label>
|
||||
<div class="col-md-6">
|
||||
<select id="server" data-type="server" name="server" class="form-control select2" style="height: 52px; background-color: white;">
|
||||
<option value=''>{Lang::T('Select Routers')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Service Plan')}</label>
|
||||
<div class="col-md-6">
|
||||
<select id="plan" name="plan" class="form-control select2" style="height: 52px; background-color: white;">
|
||||
<option value=''>{Lang::T('Select Plans')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Using')}</label>
|
||||
<div class="col-md-6">
|
||||
<select name="using" class="form-control select2" style="height: 52px; background-color: white;">
|
||||
<option value="cash">{Lang::T('Cash')}</option>
|
||||
{if $_c['enable_balance'] eq 'yes'}
|
||||
<option value="balance">{Lang::T('Customer Balance')}</option>
|
||||
{/if}
|
||||
<option value="zero">{$_c['currency_code']} 0</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help-block col-md-4">Postpaid Recharge for the first time use {$_c['currency_code']} 0</p>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success mb-3"
|
||||
type="submit">{Lang::T('Recharge')}</button>
|
||||
Or <a class="btn btn-outline-primary href="{$_url}customers/list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
40
ui/ui/refill.tpl
Normal file
40
ui/ui/refill.tpl
Normal file
@ -0,0 +1,40 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Refill Account')}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}plan/refill-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Select Account')}</label>
|
||||
<div class="col-md-6">
|
||||
<select id="personSelect" class="form-control select2" name="id_customer"
|
||||
style="width: 100%;" data-placeholder="{Lang::T('Select a customer')}...">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-2 control-label">{Lang::T('Code Voucher')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="code" name="code"
|
||||
placeholder="{Lang::T('Enter voucher code here')}">
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-success mb-3" type="submit">{Lang::T('Recharge')}</button>
|
||||
Or <a class="btn btn-outline-primary" href="{$_url}customers/list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
183
ui/ui/register-otp.tpl
Normal file
183
ui/ui/register-otp.tpl
Normal file
@ -0,0 +1,183 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="https://laravel.com/img/favicon/favicon-16x16.png" type='image/x-icon'>
|
||||
<title>{Lang::T('Login')} - {$_c['CompanyName']}</title>
|
||||
<link rel="stylesheet" href="assets/vendor/chartist/css/chartist.min.css">
|
||||
<link href="assets/vendor/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet">
|
||||
<link href="assets/css/style.css" rel="stylesheet">
|
||||
<script src="ui/ui/scripts/sweetalert2.all.min.js"></script>
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="app" class="app off-canvas body-full" style="background-color:#e9ecef;">
|
||||
<div class="container">
|
||||
<div class="hidden-xs" style="height:150px"></div>
|
||||
<div class="form-head mb20">
|
||||
<h1 class="site-logo h2 mb5 mt5 text-center text-uppercase text-bold"
|
||||
style="text-shadow: 2px 2px 4px #757575;">{$_c['CompanyName']}</h1>
|
||||
<hr>
|
||||
</div>
|
||||
{if isset($notify)}
|
||||
<script>
|
||||
// Display SweetAlert toast notification
|
||||
Swal.fire({
|
||||
icon: '{if $notify_t == "s"}success{else}warning{/if}',
|
||||
title: '{$notify}',
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 5000,
|
||||
timerProgressBar: true,
|
||||
didOpen: (toast) => {
|
||||
toast.addEventListener('mouseenter', Swal.stopTimer)
|
||||
toast.addEventListener('mouseleave', Swal.resumeTimer)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{/if}
|
||||
<div class="col-md-12">
|
||||
<div class="card card-info">
|
||||
<div class="card-header">{Lang::T('Registration Info')}</div>
|
||||
<div class="card-body">
|
||||
{include file="$_path/../pages/Registration_Info.html"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{$_url}register/post" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">1. {Lang::T('Register as Member')}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-container">
|
||||
<div class="form-group">
|
||||
<label>{if $_c['country_code_phone']!= ''}{Lang::T('Phone Number')}{else}{Lang::T('Username')}{/if}</label>
|
||||
<div class="input-group">
|
||||
{if $_c['country_code_phone']!= ''}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-phone-alt"></i></span>
|
||||
{else}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-user"></i></span>
|
||||
{/if}
|
||||
<input type="text" class="form-control" name="username" value="{$username}"
|
||||
placeholder="{if $_c['country_code_phone']!= ''}{$_c['country_code_phone']} {Lang::T('Phone Number')}{else}{Lang::T('Username')}{/if}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{Lang::T('SMS Verification Code')}</label>
|
||||
<input type="text" required class="form-control" id="otp_code" value=""
|
||||
placeholder="{Lang::T('Verification Code')}" name="otp_code">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{Lang::T('Full Name')}</label>
|
||||
<input type="text" required class="form-control" id="fullname" value="{$fullname}"
|
||||
name="fullname">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{Lang::T('Email')}</label>
|
||||
<input type="text" class="form-control" placeholder="xxxxxx@xxx.xx"
|
||||
id="email" value="{$email}" name="email">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{Lang::T('Address')}</label>
|
||||
<input type="text" name="address" id="address" value="{$address}"
|
||||
class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">2. {Lang::T('Password')}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-container">
|
||||
<div class="form-group">
|
||||
<label>{Lang::T('Password')}</label>
|
||||
<input type="password" required class="form-control" id="password" name="password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{Lang::T('Confirm Password')}</label>
|
||||
<input type="password" required class="form-control" id="cpassword"
|
||||
name="cpassword">
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-success btn-block" type="submit">{Lang::T('Register')}</button>
|
||||
</div>
|
||||
<div class="">
|
||||
<a href="{$_url}register" class="btn btn-primary btn-block">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<center>
|
||||
<a href="javascript:showPrivacy()">Privacy</a>
|
||||
•
|
||||
<a href="javascript:showTaC()">T & C</a>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="HTMLModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body" id="HTMLModal_konten"></div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if $_c['tawkto'] != ''}
|
||||
<!--Start of Tawk.to Script-->
|
||||
<script type="text/javascript">
|
||||
var Tawk_API = Tawk_API || {},
|
||||
Tawk_LoadStart = new Date();
|
||||
(function() {
|
||||
var s1 = document.createElement("script"),
|
||||
s0 = document.getElementsByTagName("script")[0];
|
||||
s1.async = true;
|
||||
s1.src='https://embed.tawk.to/{$_c['tawkto']}';
|
||||
s1.charset = 'UTF-8';
|
||||
s1.setAttribute('crossorigin', '*');
|
||||
s0.parentNode.insertBefore(s1, s0);
|
||||
})();
|
||||
</script>
|
||||
<!--End of Tawk.to Script-->
|
||||
{/if}
|
||||
<script src="ui/ui/scripts/vendors.js?v=1"></script>
|
||||
<script src="assets/vendor/global/global.min.js"></script>
|
||||
<script src="assets/vendor/bootstrap-select/dist/js/bootstrap-select.min.js"></script>
|
||||
<script src="assets/vendor/chart.js/Chart.bundle.min.js"></script>
|
||||
<!-- Chart piety plugin files -->
|
||||
<script src="assets/vendor/peity/jquery.peity.min.js"></script>
|
||||
|
||||
<!-- Apex Chart -->
|
||||
<script src="assets/vendor/apexchart/apexchart.js"></script>
|
||||
|
||||
<!-- Dashboard 1 -->
|
||||
<script src="assets/js/dashboard/dashboard-1.js"></script>
|
||||
|
||||
<script src="assets/js/custom.min.js"></script>
|
||||
<script src="assets/js/deznav-init.js"></script>
|
||||
<script src="assets/js/demo.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
145
ui/ui/register-rotp.tpl
Normal file
145
ui/ui/register-rotp.tpl
Normal file
@ -0,0 +1,145 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="https://laravel.com/img/favicon/favicon-16x16.png" type='image/x-icon'>
|
||||
<title>{Lang::T('Login')} - {$_c['CompanyName']}</title>
|
||||
<link rel="stylesheet" href="assets/vendor/chartist/css/chartist.min.css">
|
||||
<link href="assets/vendor/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet">
|
||||
<link href="assets/css/style.css" rel="stylesheet">
|
||||
<script src="ui/ui/scripts/sweetalert2.all.min.js"></script>
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="app" class="app off-canvas body-full" style="background-color:#e9ecef;">
|
||||
<div class="container">
|
||||
<div class="hidden-xs" style="height:150px"></div>
|
||||
<div class="form-head mb20">
|
||||
<h1 class="site-logo h2 mb5 mt5 text-center text-uppercase text-bold"
|
||||
style="text-shadow: 2px 2px 4px #757575;">{$_c['CompanyName']}</h1>
|
||||
<hr>
|
||||
</div>
|
||||
{if isset($notify)}
|
||||
<script>
|
||||
// Display SweetAlert toast notification
|
||||
Swal.fire({
|
||||
icon: '{if $notify_t == "s"}success{else}warning{/if}',
|
||||
title: '{$notify}',
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 5000,
|
||||
timerProgressBar: true,
|
||||
didOpen: (toast) => {
|
||||
toast.addEventListener('mouseenter', Swal.stopTimer)
|
||||
toast.addEventListener('mouseleave', Swal.resumeTimer)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{/if}
|
||||
<div class="col-md-2">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">{Lang::T('Registration Info')}</div>
|
||||
<div class="card-body">
|
||||
{include file="$_path/../pages/Registration_Info.html"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<form action="{$_url}register" method="post">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">1. {Lang::T('Register as Member')}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>{if $_c['country_code_phone']!= ''}{Lang::T('Phone Number')}{else}{Lang::T('Username')}{/if}</label>
|
||||
<div class="input-group">
|
||||
{if $_c['country_code_phone']!= ''}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-phone-alt"></i></span>
|
||||
{else}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-user"></i></span>
|
||||
{/if}
|
||||
<input type="text" class="form-control" name="username"
|
||||
placeholder="{if $_c['country_code_phone']!= ''}{$_c['country_code_phone']} {Lang::T('Phone Number')}{else}{Lang::T('Username')}{/if}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="mb-3">
|
||||
<a href="{$_url}login" class="btn btn-warning btn-block">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
<div class="">
|
||||
<button class="btn btn-success btn-block" type="submit">{Lang::T('Request OTP')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<center>
|
||||
<a href="javascript:showPrivacy()">Privacy</a>
|
||||
•
|
||||
<a href="javascript:showTaC()">T & C</a>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="HTMLModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body" id="HTMLModal_konten"></div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if $_c['tawkto'] != ''}
|
||||
<!--Start of Tawk.to Script-->
|
||||
<script type="text/javascript">
|
||||
var Tawk_API = Tawk_API || {},
|
||||
Tawk_LoadStart = new Date();
|
||||
(function() {
|
||||
var s1 = document.createElement("script"),
|
||||
s0 = document.getElementsByTagName("script")[0];
|
||||
s1.async = true;
|
||||
s1.src='https://embed.tawk.to/{$_c['tawkto']}';
|
||||
s1.charset = 'UTF-8';
|
||||
s1.setAttribute('crossorigin', '*');
|
||||
s0.parentNode.insertBefore(s1, s0);
|
||||
})();
|
||||
</script>
|
||||
<!--End of Tawk.to Script-->
|
||||
{/if}
|
||||
<script src="ui/ui/scripts/vendors.js?v=1"></script>
|
||||
<script src="assets/vendor/global/global.min.js"></script>
|
||||
<script src="assets/vendor/bootstrap-select/dist/js/bootstrap-select.min.js"></script>
|
||||
<script src="assets/vendor/chart.js/Chart.bundle.min.js"></script>
|
||||
<!-- Chart piety plugin files -->
|
||||
<script src="assets/vendor/peity/jquery.peity.min.js"></script>
|
||||
|
||||
<!-- Apex Chart -->
|
||||
<script src="assets/vendor/apexchart/apexchart.js"></script>
|
||||
|
||||
<!-- Dashboard 1 -->
|
||||
<script src="assets/js/dashboard/dashboard-1.js"></script>
|
||||
|
||||
<script src="assets/js/custom.min.js"></script>
|
||||
<script src="assets/js/deznav-init.js"></script>
|
||||
<script src="assets/js/demo.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
178
ui/ui/register.tpl
Normal file
178
ui/ui/register.tpl
Normal file
@ -0,0 +1,178 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="https://laravel.com/img/favicon/favicon-16x16.png" type='image/x-icon'>
|
||||
<title>{Lang::T('Login')} - {$_c['CompanyName']}</title>
|
||||
<link rel="stylesheet" href="assets/vendor/chartist/css/chartist.min.css">
|
||||
<link href="assets/vendor/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet">
|
||||
<link href="assets/css/style.css" rel="stylesheet">
|
||||
<script src="ui/ui/scripts/sweetalert2.all.min.js"></script>
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="app" class="app off-canvas body-full" style="background-color:#e9ecef;">
|
||||
<div class="container">
|
||||
<div class="hidden-xs" style="height:150px"></div>
|
||||
<div class="form-head mb20">
|
||||
<h1 class="site-logo h2 mb5 mt5 text-center text-uppercase text-bold"
|
||||
style="text-shadow: 2px 2px 4px #757575;">{$_c['CompanyName']}</h1>
|
||||
<hr>
|
||||
</div>
|
||||
{if isset($notify)}
|
||||
<script>
|
||||
// Display SweetAlert toast notification
|
||||
Swal.fire({
|
||||
icon: '{if $notify_t == "s"}success{else}warning{/if}',
|
||||
title: '{$notify}',
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 5000,
|
||||
timerProgressBar: true,
|
||||
didOpen: (toast) => {
|
||||
toast.addEventListener('mouseenter', Swal.stopTimer)
|
||||
toast.addEventListener('mouseleave', Swal.resumeTimer)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{/if}
|
||||
<div class="col-md-12">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">{Lang::T('Registration Info')}</div>
|
||||
<div class="card-body">
|
||||
{include file="$_path/../pages/Registration_Info.html"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form class="form-horizontal" action="{$_url}register/post" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">1. {Lang::T('Register as Member')}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-container">
|
||||
<div class="md-input-container">
|
||||
<label>{if $_c['country_code_phone']!= ''}{Lang::T('Phone Number')}{else}{Lang::T('Username')}{/if}</label>
|
||||
<div class="input-group">
|
||||
{if $_c['country_code_phone']!= ''}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-phone-alt"></i></span>
|
||||
{else}
|
||||
<span class="input-group-text" id="basic-addon1"><i
|
||||
class="fa fa-user"></i></span>
|
||||
{/if}
|
||||
<input type="text" class="form-control" name="username"
|
||||
placeholder="{if $_c['country_code_phone']!= ''}{$_c['country_code_phone']} {Lang::T('Phone Number')}{else}{Lang::T('Username')}{/if}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-input-container md-float-label">
|
||||
<label>{Lang::T('Full Name')}</label>
|
||||
<input type="text" required class="form-control" id="fullname" value="{$fullname}"
|
||||
name="fullname">
|
||||
</div>
|
||||
<div class="md-input-container md-float-label">
|
||||
<label>{Lang::T('Email')}</label>
|
||||
<input type="text" class="form-control" id="email" placeholder="xxxxxxx@xxxx.xx"
|
||||
value="{$email}" name="email">
|
||||
</div>
|
||||
<div class="md-input-container md-float-label">
|
||||
<label>{Lang::T('Address')}</label>
|
||||
<input type="text" name="address" id="address" value="{$address}"
|
||||
class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">2. {Lang::T('Password')}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-container">
|
||||
<div class="md-input-container md-float-label">
|
||||
<label>{Lang::T('Password')}</label>
|
||||
<input type="password" required class="form-control" id="password" name="password">
|
||||
</div>
|
||||
<div class="md-input-container md-float-label">
|
||||
<label>{Lang::T('Confirm Password')}</label>
|
||||
<input type="password" required class="form-control" id="cpassword"
|
||||
name="cpassword">
|
||||
</div>
|
||||
<br>
|
||||
<div class="">
|
||||
<div class="mb-3">
|
||||
<a href="{$_url}login" class="btn btn-warning btn-block">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
<div class="">
|
||||
<button class="btn btn-success btn-block" type="submit">{Lang::T('Register')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<center>
|
||||
<a href="javascript:showPrivacy()">Privacy</a>
|
||||
•
|
||||
<a href="javascript:showTaC()">T & C</a>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="HTMLModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body" id="HTMLModal_konten"></div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{if $_c['tawkto'] != ''}
|
||||
<!--Start of Tawk.to Script-->
|
||||
<script type="text/javascript">
|
||||
var Tawk_API = Tawk_API || {},
|
||||
Tawk_LoadStart = new Date();
|
||||
(function() {
|
||||
var s1 = document.createElement("script"),
|
||||
s0 = document.getElementsByTagName("script")[0];
|
||||
s1.async = true;
|
||||
s1.src='https://embed.tawk.to/{$_c['tawkto']}';
|
||||
s1.charset = 'UTF-8';
|
||||
s1.setAttribute('crossorigin', '*');
|
||||
s0.parentNode.insertBefore(s1, s0);
|
||||
})();
|
||||
</script>
|
||||
<!--End of Tawk.to Script-->
|
||||
{/if}
|
||||
<script src="ui/ui/scripts/vendors.js?v=1"></script>
|
||||
<script src="assets/vendor/global/global.min.js"></script>
|
||||
<script src="assets/vendor/bootstrap-select/dist/js/bootstrap-select.min.js"></script>
|
||||
<script src="assets/vendor/chart.js/Chart.bundle.min.js"></script>
|
||||
<!-- Chart piety plugin files -->
|
||||
<script src="assets/vendor/peity/jquery.peity.min.js"></script>
|
||||
|
||||
<!-- Apex Chart -->
|
||||
<script src="assets/vendor/apexchart/apexchart.js"></script>
|
||||
|
||||
<!-- Dashboard 1 -->
|
||||
<script src="assets/js/dashboard/dashboard-1.js"></script>
|
||||
|
||||
<script src="assets/js/custom.min.js"></script>
|
||||
<script src="assets/js/deznav-init.js"></script>
|
||||
<script src="assets/js/demo.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
69
ui/ui/reports-activation.tpl
Normal file
69
ui/ui/reports-activation.tpl
Normal file
@ -0,0 +1,69 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Activity Log</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="text-center">
|
||||
<div class="col-md-12">
|
||||
<form id="site-search" method="post" action="{$_url}reports/activation">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<input type="text" name="q" class="form-control" value="{$q}"
|
||||
placeholder="{Lang::T('Invoice')}...">
|
||||
<button class="btn btn-success input-group-btn" type="submit">{Lang::T('Search')}</button>
|
||||
<!-- <div class="input-group-btn">
|
||||
</div> -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="table-responsive">
|
||||
<table id="datatable" class="table table-bordered table-striped table-hover border-primary">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Invoice')}</th>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
<th>{Lang::T('Type')}</th>
|
||||
<th>{Lang::T('Created On')}</th>
|
||||
<th>{Lang::T('Expires On')}</th>
|
||||
<th>{Lang::T('Method')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $activation as $ds}
|
||||
<tr>
|
||||
<td onclick="window.location.href = '{$_url}plan/view/{$ds['id']}'"
|
||||
style="cursor:pointer;">{$ds['invoice']}</td>
|
||||
<td onclick="window.location.href = '{$_url}customers/viewu/{$ds['username']}'"
|
||||
style="cursor:pointer;">{$ds['username']}</td>
|
||||
<td>{$ds['plan_name']}</td>
|
||||
<td>{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
<td class="text-success">
|
||||
{Lang::dateAndTimeFormat($ds['recharged_on'],$ds['recharged_time'])}
|
||||
</td>
|
||||
<td class="text-danger">{Lang::dateAndTimeFormat($ds['expiration'],$ds['time'])}</td>
|
||||
<td>{$ds['method']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
68
ui/ui/reports-daily.tpl
Normal file
68
ui/ui/reports-daily.tpl
Normal file
@ -0,0 +1,68 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="card">
|
||||
<div class="col-md-12">
|
||||
<div class="invoice-wrap">
|
||||
<div class="card-header clearfix invoice-head">
|
||||
<h3 class="brand-logo text-uppercase text-bold left mt15">
|
||||
<span class="text">{Lang::T('Daily Reports')}</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="clearfix invoice-subhead mb20">
|
||||
<div class="group clearfix left">
|
||||
<p class="text-bold mb5">{Lang::T('All Transactions at Date')}:</p>
|
||||
<p class="small">{date($_c['date_format'], strtotime($mdate))} {$mtime}</p>
|
||||
</div>
|
||||
<div class="group clearfix right">
|
||||
<a href="{$_url}export/print-by-date" class="btn btn-default" target="_blank"><i
|
||||
class="ion ion-printer"></i>{Lang::T('Export for Print')}</a>
|
||||
<a href="{$_url}export/pdf-by-date" class="btn btn-default"><i
|
||||
class="fa fa-file-pdf-o"></i>{Lang::T('Export to PDF')}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Type')}</th>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
<th>{Lang::T('Created On')}</th>
|
||||
<th>{Lang::T('Expires On')}</th>
|
||||
<th>{Lang::T('Method')}</th>
|
||||
<th>{Lang::T('Routers')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['username']}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
<td>{$ds['plan_name']}</td>
|
||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['recharged_on'],$ds['recharged_time'])}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['expiration'],$ds['time'])}</td>
|
||||
<td>{$ds['method']}</td>
|
||||
<td>{$ds['routers']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
<div class="clearfix text-right total-sum mb10">
|
||||
<h4 class="text-uppercase text-bold">{Lang::T('Total Income')}:</h4>
|
||||
<h3 class="sum">{Lang::moneyFormat($dr)}</h3>
|
||||
</div>
|
||||
<p class="text-center small text-info">{Lang::T('All Transactions at Date')}:
|
||||
{date($_c['date_format'], strtotime($mdate))} {$mtime}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
78
ui/ui/reports-period-view.tpl
Normal file
78
ui/ui/reports-period-view.tpl
Normal file
@ -0,0 +1,78 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="invoice-wrap">
|
||||
<div class="card-header clearfix invoice-head">
|
||||
<h3 class="brand-logo text-uppercase text-bold left mt15">
|
||||
<span class="text">{Lang::T('Daily Reports')}</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="clearfix invoice-subhead mb20">
|
||||
<div class="group clearfix left">
|
||||
<p class="text-bold mb5">{Lang::T('All Transactions at Date')}:</p>
|
||||
<p class="small">{$stype} [{date( $_c['date_format'], strtotime($fdate))} -
|
||||
{date( $_c['date_format'], strtotime($tdate))}]</p>
|
||||
</div>
|
||||
<div class="group clearfix right">
|
||||
<form method="post" action="{$_url}export/print-by-period" target="_blank">
|
||||
<input type="hidden" name="fdate" value="{$fdate}">
|
||||
<input type="hidden" name="tdate" value="{$tdate}">
|
||||
<input type="hidden" name="stype" value="{$stype}">
|
||||
<button type="submit" class="btn btn-primary mb-3"><i class="fa fa-print"></i>
|
||||
{Lang::T('Export for Print')}</button>
|
||||
</form>
|
||||
<form method="post" action="{$_url}export/pdf-by-period" target="_blank">
|
||||
<input type="hidden" name="fdate" value="{$fdate}">
|
||||
<input type="hidden" name="tdate" value="{$tdate}">
|
||||
<input type="hidden" name="stype" value="{$stype}">
|
||||
<button type="submit" class="btn btn-info"><i class="fa fa-file-pdf"></i>
|
||||
{Lang::T('Export to PDF')}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>{Lang::T('Type')}</th>
|
||||
<th>{Lang::T('Plan Name')}</th>
|
||||
<th>{Lang::T('Plan Price')}</th>
|
||||
<th>{Lang::T('Created On')}</th>
|
||||
<th>{Lang::T('Expires On')}</th>
|
||||
<th>{Lang::T('Method')}</th>
|
||||
<th>{Lang::T('Routers')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr>
|
||||
<td>{$ds['username']}</td>
|
||||
<td>{$ds['type']}</td>
|
||||
<td>{$ds['plan_name']}</td>
|
||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['recharged_on'],$ds['recharged_time'])}</td>
|
||||
<td>{Lang::dateAndTimeFormat($ds['expiration'],$ds['time'])}</td>
|
||||
<td>{$ds['method']}</td>
|
||||
<td>{$ds['routers']}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="clearfix text-right total-sum mb10">
|
||||
<h4 class="text-uppercase text-bold">{Lang::T('Total Income')}:</h4>
|
||||
<h3 class="sum">{Lang::moneyFormat($dr)}</h3>
|
||||
</div>
|
||||
<p class="text-center small text-info">{$stype} [{date( $_c['date_format'], strtotime($fdate))} -
|
||||
{date( $_c['date_format'], strtotime($tdate))}]</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
44
ui/ui/reports-period.tpl
Normal file
44
ui/ui/reports-period.tpl
Normal file
@ -0,0 +1,44 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Period Reports')}</h3></div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}reports/period-view">
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{Lang::T('From Date')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="date" class="form-control" value="{$tdate}" name="fdate" id="fdate">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{Lang::T('To Date')}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="date" class="form-control" value="{$mdate}" name="tdate" id="tdate">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">{Lang::T('Type')}</label>
|
||||
<div class="col-md-9">
|
||||
<select class="form-select" style="height: 52px; background-color: white;" id="stype" name="stype">
|
||||
<option value="" selected="">{Lang::T('All Transactions')}</option>
|
||||
<option value="Hotspot">Hotspot</option>
|
||||
<option value="PPPOE">PPPOE</option>
|
||||
<option value="Balance">Balance</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<button type="submit" id="submit" class="btn btn-primary">{Lang::T('Period Reports')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
66
ui/ui/router-error.tpl
Normal file
66
ui/ui/router-error.tpl
Normal file
@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="https://laravel.com/img/favicon/favicon-16x16.png" type='image/x-icon'>
|
||||
<title>{Lang::T('Login')} - {$_c['CompanyName']}</title>
|
||||
<link rel="stylesheet" href="assets/vendor/chartist/css/chartist.min.css">
|
||||
<link href="assets/vendor/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet">
|
||||
<link href="assets/css/style.css" rel="stylesheet">
|
||||
<meta http-equiv="refresh" content="{$time}; url={$url}">
|
||||
<style>
|
||||
::-moz-selection {
|
||||
/* Code for Firefox */
|
||||
color: red;
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
::selection {
|
||||
color: red;
|
||||
background: yellow;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="hold-transition skin-blue" style="background-color:#e9ecef;">
|
||||
<div class="container-fluid">
|
||||
<center>
|
||||
<div class="col-md-8" style="margin-top: 80px;">
|
||||
<div class="card card-danger card-solid">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
{$error_title}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body" style="font-size: larger; text-align: start;">
|
||||
<h5 class="text-danger">{$error_message}</h5>
|
||||
<hr>
|
||||
<h5 class="text-info">Mikrotik troubleshooting: </h5>
|
||||
<ol>
|
||||
<li>1. Make sure you use API Port, Default 8728</li>
|
||||
<li>2. Make sure Username and Password are correct</li>
|
||||
<li>3. Make sure your hosting is not blocking port to external</li>
|
||||
<li>4. Make sure your is Mikrotik accessible from MikroPulse</li>
|
||||
</ol>
|
||||
<span class="text-info">Note: </span>If you just update MikroPulse from upload files, try
|
||||
contact admin
|
||||
</div>
|
||||
<!-- <div class="card-footer">
|
||||
<div class="" role="group" aria-label="...">
|
||||
<a href="./update.php?step=4"
|
||||
class="btn btn-info btn-sm btn-block mb-3">Update Database</a>
|
||||
<a href="#" " class="btn btn-success btn-sm btn-block mb-4">Update
|
||||
Mikropulse</a>
|
||||
</div>
|
||||
<a href="javascript::history.back()" onclick="history.back()"
|
||||
class="btn btn-warning btn-block">back</a>
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
70
ui/ui/routers-add.tpl
Normal file
70
ui/ui/routers-add.tpl
Normal file
@ -0,0 +1,70 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">{Lang::T('Add Router')}</h5></div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}routers/add-post">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Status')}</label>
|
||||
<div class="col-md-8">
|
||||
<label class="radio-inline warning">
|
||||
<input type="radio" checked name="enabled" value="1"> Enable
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="enabled" value="0"> Disable
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Router Name / Location')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name" name="name" maxlength="32">
|
||||
<p class="help-block">{Lang::T('Name of Area that router operated')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('IP Address')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" placeholder="192.168.88.1:8728" class="form-control" id="ip_address"
|
||||
name="ip_address">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Username')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="username" name="username">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Router Secret')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="password" name="password"
|
||||
onmouseleave="this.type = 'password'" onmouseenter="this.type = 'text'">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Description')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="description" name="description"></textarea>
|
||||
<p class="help-block">{Lang::T('Explain Coverage of router')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary mb-3"
|
||||
type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a class="btn btn-outline-primary" href="{$_url}routers/list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
70
ui/ui/routers-edit.tpl
Normal file
70
ui/ui/routers-edit.tpl
Normal file
@ -0,0 +1,70 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="card card-primary card-hovered card-stacked mb30">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Edit Router')}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" role="form" action="{$_url}routers/edit-post" >
|
||||
<input type="hidden" name="id" value="{$d['id']}">
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Status')}</label>
|
||||
<div class="col-md-8">
|
||||
<label class="radio-inline warning">
|
||||
<input type="radio" {if $d['enabled'] == 1}checked{/if} name="enabled" value="1"> Enable
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" {if $d['enabled'] == 0}checked{/if} name="enabled" value="0"> Disable
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Router Name / Location')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="name" name="name" maxlength="32" value="{$d['name']}">
|
||||
<p class="help-block">{Lang::T('Name of Area that router operated')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('IP Address')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" placeholder="192.168.88.1:8728" class="form-control" id="ip_address" name="ip_address" value="{$d['ip_address']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Username')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="username" name="username" value="{$d['username']}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Router Secret')}</label>
|
||||
<div class="col-md-6">
|
||||
<input type="password" class="form-control" id="password" name="password" value="{$d['password']}" onmouseleave="this.type = 'password'"
|
||||
onmouseenter="this.type = 'text'">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-4 control-label">{Lang::T('Description')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="description" name="description">{$d['description']}</textarea>
|
||||
<p class="help-block">{Lang::T('Explain Coverage of router')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
<div class="form-group row">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
<button class="btn btn-primary mb-3" type="submit">{Lang::T('Save Changes')}</button>
|
||||
Or <a class="btn btn-outline-primary" href="{$_url}routers/list">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
210
ui/ui/routers.tpl
Normal file
210
ui/ui/routers.tpl
Normal file
@ -0,0 +1,210 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="card card-hovered mb20 card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{Lang::T('Routers')}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="md-whiteframe-z1 mb20 text-center">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<form id="site-search" method="post" action="{$_url}routers/list/">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">
|
||||
<span class="fa fa-search"></span>
|
||||
</div>
|
||||
<input type="text" name="name" class="form-control"
|
||||
placeholder="{Lang::T('Search by Name')}...">
|
||||
<button class="btn btn-success input-group-btn" type="submit">{Lang::T('Search')}</button>
|
||||
<!-- <div class="input-group-btn">
|
||||
</div> -->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="{$_url}routers/add" class="btn btn-primary btn-block"><i
|
||||
class="fa fa-add"> </i> {Lang::T('New Router')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Router Name')}</th>
|
||||
<th>{Lang::T('IP Address')}</th>
|
||||
<th>{Lang::T('Status')}</th>
|
||||
<th>{Lang::T('Uptime')}</th>
|
||||
<th>{Lang::T('Free Memory')}</th>
|
||||
<th>{Lang::T('CPU Load')}</th>
|
||||
<th>{Lang::T('Manage')}</th>
|
||||
<th>ID</th>
|
||||
<th>{Lang::T('Download')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr {if $ds['enabled'] != 1}class="danger" title="disabled" {/if}>
|
||||
<td>{$ds['name']}</td>
|
||||
<td>{$ds['ip_address']}</td>
|
||||
<td class="router-status" data-router-id="{$ds['id']}"></td>
|
||||
<td class="router-uptime text-success" data-router-id="{$ds['id']}"></td>
|
||||
<td class="router-used-memory text-warning" data-router-id="{$ds['id']}"></td>
|
||||
<td class="router-cpu-load text-primary" data-router-id="{$ds['id']}"></td>
|
||||
<td>
|
||||
<a href="{$_url}routers/edit/{$ds['id']}" class="btn btn-info btn-xs">{Lang::T('Edit')}</a>
|
||||
<a href="{$_url}routers/delete/{$ds['id']}" id="{$ds['id']}"
|
||||
onclick="return confirm('{Lang::T('Delete')}?')"
|
||||
class="btn btn-danger btn-xs"><i class="fa fa-trash"></i></a>
|
||||
<button class="btn btn-warning btn-xs btn-reboot" data-router-id="{$ds['id']}"><i class="fa fa-refresh"></i> {Lang::T('Reboot')}</button>
|
||||
</td>
|
||||
<td>{$ds['id']}</td>
|
||||
<td>
|
||||
<form action="{$_url}routers/download" method="post" style="display:inline;">
|
||||
<input type="hidden" name="router_id" value="{$ds['id']}">
|
||||
<input type="hidden" name="router_name" value="{$ds['name']}">
|
||||
<button type="submit" class="btn btn-success btn-xs">
|
||||
<i class="fa fa-download"></i> {Lang::T('Download')}
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="sections/footer.tpl"}
|
||||
|
||||
<style>
|
||||
.loader {
|
||||
display: inline-block;
|
||||
animation: loading 1s infinite;
|
||||
margin: 0; /* Added this line */
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
content: ".";
|
||||
}
|
||||
33% {
|
||||
content: "..";
|
||||
}
|
||||
66% {
|
||||
content: "...";
|
||||
}
|
||||
}
|
||||
|
||||
.router-uptime {
|
||||
color: #28a745; /* Green for uptime */
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.router-used-memory {
|
||||
color: #fd7e14; /* Orange for memory */
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.router-cpu-load {
|
||||
color: #d63384; /* Pink for CPU load */
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.router-status {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.router-status .status {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.router-status .online {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.router-status .offline {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Fetch router resources asynchronously
|
||||
$('.router-uptime, .router-used-memory, .router-total-memory, .router-cpu-load, .router-status').each(function() {
|
||||
var routerId = $(this).data('router-id');
|
||||
var elementClass = $(this).attr('class').split(' ')[0];
|
||||
var row = $(this).closest('tr');
|
||||
|
||||
$.ajax({
|
||||
url: '{$_url}routers/get_resources',
|
||||
data: { router_id: routerId },
|
||||
dataType: 'json',
|
||||
success: function(resources) {
|
||||
if (resources) {
|
||||
if (elementClass === 'router-uptime') {
|
||||
row.find('.router-uptime').html(resources.uptime);
|
||||
} else if (elementClass === 'router-used-memory') {
|
||||
row.find('.router-used-memory').html(resources.freeMemory);
|
||||
} else if (elementClass === 'router-total-memory') {
|
||||
row.find('.router-total-memory').html(resources.totalMemory);
|
||||
} else if (elementClass === 'router-cpu-load') {
|
||||
row.find('.router-cpu-load').html(resources.cpuLoad);
|
||||
}
|
||||
|
||||
// Update router status
|
||||
var statusElement = row.find('.router-status');
|
||||
if (resources.status === 'Online') {
|
||||
statusElement.html('<span class="status online">Online</span>');
|
||||
} else {
|
||||
statusElement.html('<span class="status offline">Offline</span>');
|
||||
}
|
||||
} else {
|
||||
row.find('.' + elementClass).html('N/A');
|
||||
// Set router status to offline if resources are not available
|
||||
var statusElement = row.find('.router-status');
|
||||
statusElement.html('<span class="status offline">Offline</span>');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// Handle error cases
|
||||
console.error(xhr.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Reboot router
|
||||
$('.btn-reboot').on('click', function() {
|
||||
var routerId = $(this).data('router-id');
|
||||
if (confirm('Are you sure you want to reboot this router?')) {
|
||||
$.ajax({
|
||||
url: '{$_url}routers/reboot',
|
||||
data: { router_id: routerId },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.status === 'Rebooting') {
|
||||
alert(response.message);
|
||||
} else {
|
||||
alert('Error: ' + response.message);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error(xhr.responseText);
|
||||
alert('Failed to send reboot command. Please try again.');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
BIN
ui/ui/sections/.DS_Store
vendored
Normal file
BIN
ui/ui/sections/.DS_Store
vendored
Normal file
Binary file not shown.
121
ui/ui/sections/footer.tpl
Normal file
121
ui/ui/sections/footer.tpl
Normal file
@ -0,0 +1,121 @@
|
||||
</div>
|
||||
</div>
|
||||
<!--**********************************
|
||||
Content body end
|
||||
***********************************-->
|
||||
|
||||
<!--**********************************
|
||||
Footer start
|
||||
***********************************-->
|
||||
<div class="footer">
|
||||
<div class="copyright">
|
||||
<p>Designed & Developed with <span class="heart"></span> by <a href="https://www.nestict.africa/" target="_blank" rel="noopener noreferrer">NESTICT</a>
|
||||
© <span id="year"></span></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('year').textContent = new Date().getFullYear();
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<!--**********************************
|
||||
Footer end
|
||||
***********************************-->
|
||||
</div>
|
||||
<!--**********************************
|
||||
Scripts
|
||||
***********************************-->
|
||||
<script src="ui/ui/scripts/jquery.min.js"></script>
|
||||
<script src="ui/ui/assets/vendor/global/global.min.js"></script>
|
||||
<script src="ui/ui/scripts/bootstrap.min.js"></script>
|
||||
<script src="ui/ui/assets/vendor/bootstrap-select/dist/js/bootstrap-select.min.js"></script>
|
||||
<script src="ui/ui/assets/vendor/chart.js/Chart.bundle.min.js"></script>
|
||||
|
||||
<!-- Chart piety plugin files -->
|
||||
<script src="ui/ui/assets/vendor/peity/jquery.peity.min.js"></script>
|
||||
|
||||
<!-- Apex Chart -->
|
||||
<!--<script src="ui/ui/assets/vendor/apexchart/apexchart.js"></script>-->
|
||||
|
||||
<!-- Dashboard 1 -->
|
||||
<script src="ui/ui/assets/js/dashboard/dashboard-1.js"></script>
|
||||
|
||||
<script src="ui/ui/assets/js/custom.min.js"></script>
|
||||
<script src="ui/ui/assets/js/deznav-init.js"></script>
|
||||
<script src="ui/ui/assets/js/demo.js"></script>
|
||||
<!--<script src="ui/ui/assets/js/styleSwitcher.js"></script> -->
|
||||
|
||||
|
||||
<script src="ui/ui/scripts/plugins/select2.min.js"></script>
|
||||
<script src="ui/ui/scripts/pace.min.js"></script>
|
||||
<script src="ui/ui/scripts/custom.js"></script>
|
||||
|
||||
|
||||
|
||||
{if isset($xfooter)}
|
||||
{$xfooter}
|
||||
{/if}
|
||||
{literal}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Select2 initialization
|
||||
$('.select2').select2({
|
||||
width: '100%',
|
||||
height: '52px',
|
||||
containerCssClass: ':all:'
|
||||
});
|
||||
$('.select2tag').select2({
|
||||
width: '100%',
|
||||
height: '52px',
|
||||
tags: true,
|
||||
containerCssClass: ':all:'
|
||||
});
|
||||
|
||||
// Button click event listeners
|
||||
var listAtts = document.querySelectorAll(`button[type="submit"]`);
|
||||
listAtts.forEach(function(el) {
|
||||
if (el.addEventListener) {
|
||||
el.addEventListener("click", function() {
|
||||
$(this).html(`<span class="loading"></span>`);
|
||||
}, false);
|
||||
} else {
|
||||
if (el.attachEvent) {
|
||||
el.attachEvent("click", function() {
|
||||
$(this).html(`<span class="loading"></span>`);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var listAtts = document.querySelectorAll(`[api-get-text]`);
|
||||
listAtts.forEach(function(el) {
|
||||
$.get(el.getAttribute('api-get-text'), function(data) {
|
||||
el.innerHTML = data;
|
||||
});
|
||||
});
|
||||
|
||||
function setCookie(name, value, days) {
|
||||
var expires = "";
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + "=" + (value || "") + expires + "; path=/";
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
var nameEQ = name + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
</script>
|
||||
{/literal}
|
||||
</body>
|
||||
|
||||
</html>
|
510
ui/ui/sections/header.tpl
Normal file
510
ui/ui/sections/header.tpl
Normal file
@ -0,0 +1,510 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="https://laravel.com/img/favicon/favicon-16x16.png" type='image/x-icon'>
|
||||
<title>{$_title} - {$_c['CompanyName']}</title>
|
||||
<link rel="stylesheet" href="ui/ui/assets/vendor/chartist/css/chartist.min.css">
|
||||
<link href="ui/ui/assets/vendor/bootstrap-select/dist/css/bootstrap-select.min.css" rel="stylesheet">
|
||||
<link href="ui/ui/assets/css/style.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="ui/ui/fonts/ionicons/css/ionicons.min.css">
|
||||
<link rel="stylesheet" href="ui/ui/fonts/font-awesome/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="ui/ui/styles/select2.min.css" />
|
||||
<link rel="stylesheet" href="ui/ui/styles/select2-bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="ui/ui/styles/sweetalert2.min.css" />
|
||||
<link rel="stylesheet" href="ui/ui/styles/plugins/pace.css" />
|
||||
<script src="ui/ui/scripts/sweetalert2.all.min.js"></script>
|
||||
|
||||
<style>
|
||||
::-moz-selection {
|
||||
Code for Firefox
|
||||
color: red;
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
::selection {
|
||||
color: red;
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.outer {
|
||||
height: 200px
|
||||
/* Or whatever */
|
||||
}
|
||||
}
|
||||
|
||||
th:first-child,
|
||||
td:first-child {
|
||||
position: sticky;
|
||||
left: 0px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
|
||||
.text1line {
|
||||
display: block;
|
||||
/* or inline-block */
|
||||
text-overflow: ellipsis;
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
max-height: 1em;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
|
||||
.loading {
|
||||
pointer-events: none;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.loading::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
margin-left: 10px;
|
||||
border: 2px solid #fff;
|
||||
border-top-color: transparent;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* maintenance top-bar
|
||||
*/
|
||||
|
||||
.notification-top-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
width: 100%;
|
||||
background: #ec2106;
|
||||
text-align: center;
|
||||
color: #FFFFFF;
|
||||
font-family: serif;
|
||||
font-weight: bolder;
|
||||
font-size: 14px;
|
||||
z-index: 9999;
|
||||
box-sizing: border-box;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.notification-top-bar p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.notification-top-bar p a {
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
background: #FFF;
|
||||
color: #1ABC9C;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
display: inline;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.notification-top-bar {
|
||||
font-size: 12px;
|
||||
height: auto;
|
||||
line-height: normal;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.notification-top-bar p a {
|
||||
padding: 5px 10px;
|
||||
margin: 5px 0;
|
||||
font-size: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{if isset($xheader)}
|
||||
{$xheader}
|
||||
{/if}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--**********************************
|
||||
Main wrapper start
|
||||
***********************************-->
|
||||
<div id="main-wrapper">
|
||||
|
||||
<!--**********************************
|
||||
Nav header start
|
||||
***********************************-->
|
||||
<div class="nav-header">
|
||||
<a href="#" class="brand-logo">
|
||||
<i class="logo-abbr fa-brands fa-stumbleupon"></i>
|
||||
<p class="brand-title" width="124px" height="33px" style="font-size: 30px;">Admin</p>
|
||||
</a>
|
||||
<div class="nav-control">
|
||||
<div class="hamburger">
|
||||
<span class="line"></span><span class="line"></span><span class="line"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--**********************************
|
||||
Nav header end
|
||||
***********************************-->
|
||||
|
||||
|
||||
|
||||
<!--**********************************
|
||||
Header start
|
||||
***********************************-->
|
||||
<div class="header">
|
||||
<div class="header-content">
|
||||
<nav class="navbar navbar-expand">
|
||||
<div class="collapse navbar-collapse justify-content-between">
|
||||
<div class="header-left">
|
||||
<div class="dashboard_bar">
|
||||
{$_c['CompanyName']}
|
||||
</div>
|
||||
</div>
|
||||
<ul class="navbar-nav header-right">
|
||||
<li class="nav-item">
|
||||
<div class="input-group search-area d-lg-inline-flex d-none">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><a href="javascript:void(0)"><i
|
||||
class="flaticon-381-search-2"></i></a></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" placeholder="Search here...">
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown header-profile">
|
||||
<a class="nav-link" href="javascript:void(0);" role="button" data-bs-toggle="dropdown">
|
||||
<img src="https://robohash.org/{$_admin['id']}?set=set3&size=100x100&bgset=bg1"
|
||||
onerror="this.src='{$UPLOAD_PATH}/admin.default.png'" class="user-image"
|
||||
alt="Avatar">
|
||||
<div class="header-info">
|
||||
<p>
|
||||
{$_admin['fullname']}
|
||||
<small>{Lang::T($_admin['user_type'])}</small>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item ai-icon" href="{$_url}settings/change-password"><i class="fa fa-settings"></i>
|
||||
{Lang::T('Change Password')}</a>
|
||||
<a class="dropdown-item ai-icon" href="{$_url}settings/users-view/{$_admin['id']}">
|
||||
<i class="fa fa-person"></i> {Lang::T('My Account')}</a>
|
||||
<a href="{$_url}logout" class="dropdown-item ai-icon btn btn-primary btn-flat"><i
|
||||
class="fa fa-power"></i> {Lang::T('Logout')}</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<!--**********************************
|
||||
Header end ti-comment-alt
|
||||
***********************************-->
|
||||
|
||||
<!--**********************************
|
||||
Sidebar start
|
||||
***********************************-->
|
||||
<div class="deznav">
|
||||
<div class="deznav-scroll dz-demo-content">
|
||||
<ul class="metismenu" id="menu" data-widget="tree">
|
||||
<li {if $_system_menu eq 'dashboard' }class="active" {/if}>
|
||||
<a href="{$_url}dashboard">
|
||||
<i class="flaticon-layout"></i> <span class="nav-text">{Lang::T('Dashboard')}</span>
|
||||
</a>
|
||||
</li>
|
||||
{$_MENU_AFTER_DASHBOARD}
|
||||
{if !in_array($_admin['user_type'],['Report'])}
|
||||
<li class="{if in_array($_system_menu, ['customers', 'map'])}active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-user-2"></i> <span class="nav-text">{Lang::T('Clients')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li {if $_system_menu eq 'customers' }class="active" {/if}><a
|
||||
href="{$_url}customers">{Lang::T('All Clients')}</a></li>
|
||||
<li {if $_system_menu eq 'map' }class="active" {/if}><a
|
||||
href="{$_url}map/customer">{Lang::T('Clients Map')}</a></li>
|
||||
{$_MENU_CUSTOMERS}
|
||||
</ul>
|
||||
</li>
|
||||
{$_MENU_AFTER_CUSTOMERS}
|
||||
<li class="{if $_system_menu eq 'onlineusers'}active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="fa fa-signal"></i> <span class="nav-text">{Lang::T('Online Clients')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li {if $_routes[1] eq 'hotspot' }class="active" {/if}><a
|
||||
href="{$_url}onlineusers/hotspot">{Lang::T('Hotspot Client')}</a></li>
|
||||
<li {if $_routes[1] eq 'pppoe' }class="active" {/if}><a
|
||||
href="{$_url}plugin/pppoe">{Lang::T('Pppoe Clients')}</a></li>
|
||||
{$_MENU_ONLINEUSERS}
|
||||
</ul>
|
||||
</li>
|
||||
<li class="{if $_system_menu eq 'plan'}active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-layer"></i> <span class="nav-text">{Lang::T('Services')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
||||
href="{$_url}plan/list">{Lang::T('Active Clients')}</a></li>
|
||||
{if $_c['disable_voucher'] != 'yes'}
|
||||
<li {if $_routes[1] eq 'voucher' }class="active" {/if}><a
|
||||
href="{$_url}plan/voucher">{Lang::T('Vouchers')}</a></li>
|
||||
<li {if $_routes[1] eq 'refill' }class="active" {/if}><a
|
||||
href="{$_url}plan/refill">{Lang::T('Refill Client')}</a></li>
|
||||
{/if}
|
||||
<li {if $_routes[1] eq 'recharge' }class="active" {/if}><a
|
||||
href="{$_url}plan/recharge">{Lang::T('Recharge Client')}</a></li>
|
||||
{if $_c['enable_balance'] == 'yes'}
|
||||
<li {if $_routes[1] eq 'deposit' }class="active" {/if}><a
|
||||
href="{$_url}plan/deposit">{Lang::T('Refill Balance')}</a></li>
|
||||
{/if}
|
||||
{$_MENU_SERVICES}
|
||||
</ul>
|
||||
</li>
|
||||
{/if}
|
||||
{$_MENU_AFTER_SERVICES}
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<li class="{if $_system_menu eq 'services'}active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-network-3"></i> <span class="nav-text">{Lang::T('Internet Plan')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li {if $_routes[1] eq 'hotspot' }class="active" {/if}><a
|
||||
href="{$_url}services/hotspot">{Lang::T('Hotspot')}</a></li>
|
||||
<li {if $_routes[1] eq 'pppoe' }class="active" {/if}><a
|
||||
href="{$_url}services/pppoe">{Lang::T('PPPOE')}</a></li>
|
||||
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
||||
href="{$_url}bandwidth/list">{Lang::T('Bandwidth')}</a></li>
|
||||
{if $_c['enable_balance'] == 'yes'}
|
||||
<li {if $_routes[1] eq 'balance' }class="active" {/if}><a
|
||||
href="{$_url}services/balance">{Lang::T('Balance')}</a></li>
|
||||
{/if}
|
||||
{$_MENU_PLANS}
|
||||
</ul>
|
||||
</li>
|
||||
{/if}
|
||||
{$_MENU_AFTER_PLANS}
|
||||
<li class="{if $_system_menu eq 'reports'}active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-bookmark"></i> <span class="nav-text">{Lang::T('Reports')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li {if $_routes[1] eq 'daily-report' }class="active" {/if}><a
|
||||
href="{$_url}reports/daily-report">{Lang::T('Daily Reports')}</a></li>
|
||||
<li {if $_routes[1] eq 'by-period' }class="active" {/if}><a
|
||||
href="{$_url}reports/by-period">{Lang::T('Period Reports')}</a></li>
|
||||
<li {if $_routes[1] eq 'activation' }class="active" {/if}><a
|
||||
href="{$_url}reports/activation">{Lang::T('Activation History')}</a></li>
|
||||
{$_MENU_REPORTS}
|
||||
</ul>
|
||||
</li>
|
||||
{$_MENU_AFTER_REPORTS}
|
||||
<li class="{if $_system_menu eq 'message'}active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-send-1"></i> <span class="nav-text">{Lang::T('Send Message')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li {if $_routes[1] eq 'send' }class="active" {/if}><a
|
||||
href="{$_url}message/send">{Lang::T('Single Customer')}</a></li>
|
||||
<li {if $_routes[1] eq 'send_bulk' }class="active" {/if}><a
|
||||
href="{$_url}message/send_bulk">{Lang::T('Bulk Customers')}</a></li>
|
||||
<li {if $_routes[1] eq 'messages' }class="active" {/if}><a
|
||||
href="{$_url}messages">{Lang::T('Sent Messages')}</a></li>
|
||||
{$_MENU_MESSAGE}
|
||||
</ul>
|
||||
</li>
|
||||
{$_MENU_AFTER_MESSAGE}
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<li class="{if $_system_menu eq 'network'}active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-internet"></i> <span class="nav-text">{Lang::T('Network')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li {if $_routes[0] eq 'routers' and $_routes[1] eq 'list' }class="active" {/if}><a
|
||||
href="{$_url}routers/list">{Lang::T('Routers')}</a></li>
|
||||
<li {if $_routes[0] eq 'pool' and $_routes[1] eq 'list' }class="active" {/if}><a
|
||||
href="{$_url}pool/list">{Lang::T('IP Pool')}</a></li>
|
||||
{$_MENU_NETWORK}
|
||||
</ul>
|
||||
</li>
|
||||
{$_MENU_AFTER_NETWORKS}
|
||||
{if $_c['radius_enable']}
|
||||
<li class="{if $_system_menu eq 'radius'}active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-network"></i> <span class="nav-text">{Lang::T('Radius')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li {if $_routes[0] eq 'radius' and $_routes[1] eq 'nas-list' }class="active" {/if}><a
|
||||
href="{$_url}radius/nas-list">{Lang::T('Radius NAS')}</a></li>
|
||||
{$_MENU_RADIUS}
|
||||
</ul>
|
||||
</li>
|
||||
{/if}
|
||||
{$_MENU_AFTER_RADIUS}
|
||||
<li class="{if $_system_menu eq 'pages'}active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-folder"></i> <span class="nav-text">{Lang::T("Announcement")}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<!--<li {if $_routes[1] eq 'Order_Voucher' }class="active" {/if}><a
|
||||
href="{$_url}pages/Order_Voucher">{Lang::T('Order Voucher')}</a></li>
|
||||
<li {if $_routes[1] eq 'Voucher' }class="active" {/if}><a
|
||||
href="{$_url}pages/Voucher">{Lang::T('Voucher')} Template</a></li>-->
|
||||
<li {if $_routes[1] eq 'Announcement' }class="active" {/if}><a
|
||||
href="{$_url}pages/Announcement">{Lang::T('Announcement')}</a></li>
|
||||
<li {if $_routes[1] eq 'Announcement_Customer' }class="active" {/if}><a
|
||||
href="{$_url}pages/Announcement_Customer">{Lang::T('Customer Announcement')}</a>
|
||||
</li>
|
||||
<!--<li {if $_routes[1] eq 'Registratfa_Info' }class="active" {/if}><a
|
||||
href="{$_url}pages/Registration_Info">{Lang::T('Registration Info')}</a></li>
|
||||
<li {if $_routes[1] eq 'Privacy_Policy' }class="active" {/if}><a
|
||||
href="{$_url}pages/Privacy_Policy">{Lang::T('Privacy Policy')}</a></li>
|
||||
<li {if $_routes[1] eq 'Terms_and_Conditions' }class="active" {/if}><a
|
||||
href="{$_url}pages/Terms_and_Conditions">{Lang::T('Terms and Conditions')}</a></li>-->
|
||||
{$_MENU_PAGES}
|
||||
</ul>
|
||||
</li>
|
||||
{/if}
|
||||
{$_MENU_AFTER_PAGES}
|
||||
<li
|
||||
class="{if $_system_menu eq 'settings' || $_system_menu eq 'paymentgateway' }active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-settings-1"></i> <span class="nav-text">{Lang::T('Settings')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<li {if $_routes[1] eq 'app' }class="active" {/if}><a
|
||||
href="{$_url}settings/app">{Lang::T('General Settings')}</a></li>
|
||||
<li {if $_routes[1] eq 'localisation' }class="active" {/if}><a
|
||||
href="{$_url}settings/localisation">{Lang::T('Localisation')}</a></li>
|
||||
<li {if $_routes[1] eq 'maintenance' }class="active" {/if}><a
|
||||
href="{$_url}settings/maintenance">{Lang::T('Maintenance Mode')}</a></li>
|
||||
<li {if $_routes[1] eq 'notifications' }class="active" {/if}><a
|
||||
href="{$_url}settings/notifications">{Lang::T('User Notification')}</a></li>
|
||||
{/if}
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin','Agent'])}
|
||||
<li {if $_routes[1] eq 'users' }class="active" {/if}><a
|
||||
href="{$_url}settings/users">{Lang::T('Admin Users')}</a></li>
|
||||
{/if}
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<li {if $_routes[1] eq 'dbstatus' }class="active" {/if}><a
|
||||
href="{$_url}settings/dbstatus">{Lang::T('Backup/Restore')}</a></li>
|
||||
<li {if $_system_menu eq 'paymentgateway' }class="active" {/if}>
|
||||
<a href="{$_url}paymentgateway">
|
||||
<span class="text">{Lang::T('Payment Setup')}</span>
|
||||
</a>
|
||||
</li>
|
||||
{$_MENU_SETTINGS}
|
||||
{/if}
|
||||
</ul>
|
||||
</li>
|
||||
{$_MENU_AFTER_SETTINGS}
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<li class="{if $_system_menu eq 'logs' }active{/if} treeview">
|
||||
<a href="#">
|
||||
<i class="flaticon-381-clock"></i> <span class="nav-text">{Lang::T('Logs')}</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
||||
href="{$_url}logs/{$_c['CompanyName']}">{$_c['CompanyName']}</a></li>
|
||||
{if $_c['radius_enable']}
|
||||
<li {if $_routes[1] eq 'radius' }class="active" {/if}><a
|
||||
href="{$_url}logs/radius">Radius</a>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
{$_MENU_LOGS}
|
||||
</li>
|
||||
{/if}
|
||||
{$_MENU_AFTER_LOGS}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!--**********************************
|
||||
Sidebar end
|
||||
***********************************-->
|
||||
{if $_c['maintenance_mode'] == 1}
|
||||
<div class="notification-top-bar">
|
||||
<p>{Lang::T('The website is currently in maintenance mode, this means that some or all functionality may be
|
||||
unavailable to regular users during this time.')}<small> <a
|
||||
href="{$_url}settings/maintenance">{Lang::T('Turn Off')}</a></small></p>
|
||||
</div>
|
||||
{/if}
|
||||
<!--**********************************
|
||||
Content body start
|
||||
***********************************-->
|
||||
<div class="content-body">
|
||||
{if isset($notify)}
|
||||
<script>
|
||||
// Display SweetAlert toast notification
|
||||
Swal.fire({
|
||||
icon: '{if $notify_t == "s"}success{else}error{/if}',
|
||||
title: '{$notify}',
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 5000,
|
||||
timerProgressBar: true,
|
||||
didOpen: (toast) => {
|
||||
toast.addEventListener('mouseenter', Swal.stopTimer)
|
||||
toast.addEventListener('mouseleave', Swal.resumeTimer)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{/if}
|
8
ui/ui/sections/index.html
Normal file
8
ui/ui/sections/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
31
ui/ui/sections/user-footer.tpl
Normal file
31
ui/ui/sections/user-footer.tpl
Normal file
@ -0,0 +1,31 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- Content body end -->
|
||||
|
||||
<!-- Footer start -->
|
||||
<div class="footer">
|
||||
<div class="copyright">
|
||||
<p>© Designed & Developed & by <a href="#" target="_blank">Codevibe</a> 2024</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer end -->
|
||||
</div>
|
||||
<!-- Required vendors -->
|
||||
<script src="ui/ui/Ass/vendor/global/global.min.js"></script>
|
||||
<script src="ui/ui/Ass/vendor/chart.js/Chart.bundle.min.js"></script>
|
||||
<script src="ui/ui/Ass/vendor/jquery-nice-select/js/jquery.nice-select.min.js"></script>
|
||||
<!-- Apex Chart -->
|
||||
<script src="ui/ui/Ass/vendor/apexchart/apexchart.js"></script>
|
||||
<script src="ui/ui/Ass/vendor/nouislider/nouislider.min.js"></script>
|
||||
<script src="ui/ui/Ass/vendor/wnumb/wNumb.js"></script>
|
||||
<!-- Dashboard 1 -->
|
||||
<script src="ui/ui/Ass/js/dashboard/dashboard-1.js"></script>
|
||||
|
||||
<script src="ui/ui/Ass/js/custom.min.js"></script>
|
||||
<script src="ui/ui/Ass/js/dlabnav-init.js"></script>
|
||||
<script src="ui/ui/Ass/js/demo.js"></script>
|
||||
<script src="ui/ui/Ass/js/styleSwitcher.js"></script>
|
||||
<script src="ui/ui/scripts/pace.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user