Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
8f099bdefb | |||
9fae0f84fd | |||
d50a66845f | |||
915af6fc03 | |||
|
0a29ec9a86 | ||
|
27fd677a0a | ||
|
e2f24c0cc6 | ||
|
cf60c470b1 | ||
|
1cc7057dca | ||
|
1740c568f9 | ||
|
3347b39f3b | ||
|
f0b9b56bb0 | ||
|
24b713804a | ||
|
182add517c | ||
|
45cc2afab5 | ||
|
ba19b1c569 | ||
|
5caa9f905b | ||
|
28541f366c | ||
|
ad7998ebbf | ||
|
4c64cfabd2 | ||
|
9bae41dbe7 | ||
|
bad0545be5 | ||
|
43a92c5d3b | ||
|
11e5ebe103 | ||
|
c573c49fb9 | ||
|
7bfbdb1efb | ||
|
dc28298d53 | ||
|
655e0494d3 | ||
|
4a441c5763 | ||
|
5072ff8ba2 | ||
|
d506dd66ff | ||
|
e9b0cfd8f0 | ||
|
aa4dbc0cea | ||
|
4ef054466d | ||
|
41a3cbe700 | ||
|
4938840c5d | ||
|
127d43e45d | ||
|
cdfbab7119 | ||
|
1a2b85ae4f | ||
|
2e2d967a5b | ||
|
c45e19189a | ||
|
d372bf4711 | ||
|
8b8a0357f0 | ||
|
1cb0e30e6b | ||
|
20916b44f0 | ||
|
c63545d33a | ||
|
84500cdfc9 | ||
|
5b21ffcde5 | ||
|
009040cd3c | ||
|
781481e118 | ||
|
66d67cb61d | ||
|
3372da2ac4 | ||
|
803d04a91d | ||
|
17de653752 | ||
|
9301f1058c | ||
|
0868d61271 | ||
|
5f353392e3 | ||
|
fdd8dad509 | ||
|
3563fa531b | ||
|
525f2311fc | ||
|
3b6a6d2f55 | ||
|
3cebfa2171 | ||
|
c65b569f94 | ||
|
ca5a7d60cf | ||
|
5987ffafce | ||
|
d7bbb4d18f | ||
|
e3c173bea4 | ||
|
78e1e2f989 | ||
|
3c7e6c7a64 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -54,4 +54,9 @@ docs/**
|
|||||||
!docs/*.html
|
!docs/*.html
|
||||||
!docs/*.md
|
!docs/*.md
|
||||||
.htaccess
|
.htaccess
|
||||||
.idea
|
.idea
|
||||||
|
!docs/insomnia.rest.json
|
||||||
|
!system/uploads/paid.png
|
||||||
|
system/uploads/invoices/**
|
||||||
|
!system/uploads/invoices/
|
||||||
|
!system/uploads/invoices/index.html
|
26
README.md
26
README.md
@ -83,28 +83,4 @@ Contact me at [Telegram](https://t.me/ibnux)
|
|||||||
|
|
||||||
GNU General Public License version 2 or later
|
GNU General Public License version 2 or later
|
||||||
|
|
||||||
see [LICENSE](LICENSE) file
|
see [LICENSE](LICENSE) file
|
||||||
|
|
||||||
|
|
||||||
## Donate to ibnux
|
|
||||||
|
|
||||||
[](https://paypal.me/ibnux)
|
|
||||||
|
|
||||||
BCA: 5410454825
|
|
||||||
|
|
||||||
Mandiri: 163-000-1855-793
|
|
||||||
|
|
||||||
a.n Ibnu Maksum
|
|
||||||
|
|
||||||
## SPONSORS
|
|
||||||
|
|
||||||
- [mixradius.com](https://mixradius.com/) Paid Services Billing Radius
|
|
||||||
- [mlink.id](https://mlink.id)
|
|
||||||
- [https://github.com/sonyinside](https://github.com/sonyinside)
|
|
||||||
|
|
||||||
## Thanks
|
|
||||||
We appreciate all people who are participating in this project.
|
|
||||||
|
|
||||||
<a href="https://github.com/hotspotbilling/phpnuxbill/graphs/contributors">
|
|
||||||
<img src="https://contrib.rocks/image?repo=hotspotbilling/phpnuxbill" />
|
|
||||||
</a>
|
|
File diff suppressed because one or more lines are too long
1
docs/insomnia.rest.json
Normal file
1
docs/insomnia.rest.json
Normal file
File diff suppressed because one or more lines are too long
@ -251,6 +251,16 @@ CREATE TABLE `tbl_customers_inbox` (
|
|||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `tbl_port_pool`;
|
||||||
|
CREATE TABLE IF NOT EXISTS `tbl_port_pool` (
|
||||||
|
`id` int(10) NOT NULL AUTO_INCREMENT,
|
||||||
|
`public_ip` varchar(40) NOT NULL,
|
||||||
|
`port_name` varchar(40) NOT NULL,
|
||||||
|
`range_port` varchar(40) NOT NULL,
|
||||||
|
`routers` varchar(40) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `tbl_meta` (
|
CREATE TABLE IF NOT EXISTS `tbl_meta` (
|
||||||
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
|
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
`tbl` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Table name',
|
`tbl` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Table name',
|
||||||
|
643
login.html
Normal file
643
login.html
Normal file
@ -0,0 +1,643 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>yatmack</title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/3.8.3/core.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<link href='https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700&display=swap' rel='stylesheet'>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
navy: {
|
||||||
|
DEFAULT: '#023047',
|
||||||
|
50: '#e6f0f5',
|
||||||
|
100: '#cce1eb',
|
||||||
|
200: '#99c3d7',
|
||||||
|
300: '#66a5c3',
|
||||||
|
400: '#3387af',
|
||||||
|
500: '#023047',
|
||||||
|
600: '#022640',
|
||||||
|
700: '#011d39',
|
||||||
|
800: '#011332',
|
||||||
|
900: '#010a2b'
|
||||||
|
},
|
||||||
|
orange: {
|
||||||
|
DEFAULT: '#fb8500',
|
||||||
|
50: '#fff4e6',
|
||||||
|
100: '#ffe9cc',
|
||||||
|
200: '#ffd399',
|
||||||
|
300: '#ffbd66',
|
||||||
|
400: '#ffa733',
|
||||||
|
500: '#fb8500',
|
||||||
|
600: '#cc6d00',
|
||||||
|
700: '#995200',
|
||||||
|
800: '#663600',
|
||||||
|
900: '#331b00'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
'lexend': ['Lexend', 'sans-serif'],
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
'spin-fast': 'spin 0.5s linear infinite',
|
||||||
|
'fade-in': 'fadeIn 0.3s ease-in-out',
|
||||||
|
'slide-up': 'slideUp 0.3s ease-out'
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
fadeIn: {
|
||||||
|
'0%': { opacity: '0' },
|
||||||
|
'100%': { opacity: '1' }
|
||||||
|
},
|
||||||
|
slideUp: {
|
||||||
|
'0%': { transform: 'translateY(10px)', opacity: '0' },
|
||||||
|
'100%': { transform: 'translateY(0)', opacity: '1' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body class="font-lexend min-h-screen bg-gradient-to-b from-navy to-navy/95 text-gray-100">
|
||||||
|
<div class="container mx-auto px-2 py-4 max-w-3xl">
|
||||||
|
<div class="bg-white/10 backdrop-blur-lg rounded-2xl shadow-xl mb-6 overflow-hidden border border-white/10">
|
||||||
|
<div class="p-3 relative">
|
||||||
|
<h1 class="text-2xl font-bold text-center text-orange mb-3">YATMACK HOTSPOT</h1>
|
||||||
|
<p class="text-gray-100/90 text-center text-sm mb-4">
|
||||||
|
Select package ? Enter M-Pesa number ? Complete payment
|
||||||
|
</p>
|
||||||
|
<div class="flex items-center justify-center gap-2 py-2 px-4 bg-white/5 rounded-xl">
|
||||||
|
<i class="fas fa-headset text-orange"></i>
|
||||||
|
<p class="text-sm font-medium">Support: 254705042522</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="py-2 sm:py-1 lg:py-1">
|
||||||
|
<div class="mx-auto max-w-screen-2xl px-1 md:px-1">
|
||||||
|
<div class="mx-auto max-w-lg grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 gap-4 p-1" id="cards-container">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="redeemVoucher()" class="w-full bg-orange hover:bg-orange/90 text-white font-medium py-4 px-6 rounded-xl shadow-lg transition duration-200 flex items-center justify-center gap-3 mb-6">
|
||||||
|
<i class="fas fa-ticket-alt"></i>
|
||||||
|
<span>Redeem Voucher</span>
|
||||||
|
</button>
|
||||||
|
<div class="bg-white/10 backdrop-blur-lg rounded-2xl shadow-xl overflow-hidden border border-white/10 mb-6">
|
||||||
|
<div class="p-6 space-y-8">
|
||||||
|
<!-- Reconnect with M-Pesa -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h3 class="text-lg font-semibold text-orange">Reconnect with M-Pesa</h3>
|
||||||
|
<div class="flex flex-col sm:flex-row gap-3">
|
||||||
|
<input id="mpesaCodeInput" type="text" placeholder="Enter M-Pesa code (e.g., SCK15SKB4Z)" class="flex-grow px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:ring-2 focus:ring-orange/50 text-white placeholder:text-gray-400">
|
||||||
|
<button id="reconnectBtn" class="bg-orange hover:bg-orange/90 text-white font-medium py-3 px-6 rounded-xl transition duration-200">Reconnect</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h3 class="text-lg font-semibold text-orange">Active Package Login</h3>
|
||||||
|
<form id="loginForm" action="$(link-login-only)" method="post" $(if chap-id)onSubmit="return doLogin()" $(endif)>
|
||||||
|
<input type="hidden" name="dst" value="$(link-orig)">
|
||||||
|
<input type="hidden" name="popup" value="true">
|
||||||
|
<input type="hidden" name="mac" id="mac" value="$(mac)">
|
||||||
|
<div class="flex flex-col sm:flex-row gap-3">
|
||||||
|
<input id="usernameInput" name="username" type="text" placeholder="Enter Username (e.g., ACC123456)" class="flex-grow px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:ring-2 focus:ring-orange/50 text-white placeholder:text-gray-400">
|
||||||
|
<button id="submitBtn" type="button" onclick="submitLogin()" class="bg-orange hover:bg-orange/90 text-white font-medium py-3 px-6 rounded-xl transition duration-200">Connect</button>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="password" value="1234">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="text-sm text-gray-400">© 2025 yatmack. Created by Smartisp</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function fetchData() {
|
||||||
|
var domain = 'https://yatmack2.smartisp.co.ke/';
|
||||||
|
var siteUrl = domain + "/index.php?_route=plugin/hotspot_plan";
|
||||||
|
var routerName = encodeURIComponent("yatmack");
|
||||||
|
var dataparams = `routername=${routerName}`;
|
||||||
|
fetch(siteUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
body: dataparams
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Error ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
populateCards(data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Fetch error:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function populateCards(data) {
|
||||||
|
var cardsContainer = document.getElementById('cards-container');
|
||||||
|
cardsContainer.innerHTML = ''; // Clear existing content
|
||||||
|
// Sort the plans by price in ascending order
|
||||||
|
data.data.forEach(router => {
|
||||||
|
// Sort hotspot plans by price
|
||||||
|
router.plans_hotspot.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
|
||||||
|
router.plans_hotspot.forEach(item => {
|
||||||
|
var cardDiv = document.createElement('div');
|
||||||
|
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';
|
||||||
|
cardDiv.innerHTML = `
|
||||||
|
<div class="bg-blue-500 text-white w-full py-1">
|
||||||
|
<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;">
|
||||||
|
${item.planname}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="px-4 py-2 flex-grow">
|
||||||
|
<p class="text-2xl font-bold text-blue-600 mb-1">
|
||||||
|
<span class="text-lg font-medium text-black">${item.currency}</span>
|
||||||
|
${item.price}
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-black mb-2">
|
||||||
|
Valid for ${item.validity} ${item.timelimit}
|
||||||
|
</p>
|
||||||
|
<hr class="border-black mb-2">
|
||||||
|
</div>
|
||||||
|
<div class="px-4 py-2 flex-shrink-0">
|
||||||
|
<a href="#" class="inline-block bg-gray-900 text-white hover:bg-blue-600 font-semibold py-1 px-4 rounded-lg transition duration-300 text-md"
|
||||||
|
onclick="handlePhoneNumberSubmission('${item.planId}', '${item.routerId}'); return false;"
|
||||||
|
data-plan-id="${item.planId}"
|
||||||
|
data-router-id="${item.routerId}">
|
||||||
|
Buy
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
cardsContainer.appendChild(cardDiv);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
|
function getMacAddress() {
|
||||||
|
return "$(mac)"; // MikroTik replaces this with the user's MAC address
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOrCreateAccountId() {
|
||||||
|
var radiaxid = localStorage.getItem('radiaxid');
|
||||||
|
if (!radiaxid) {
|
||||||
|
radiaxid = getMacAddress();
|
||||||
|
localStorage.setItem('radiaxid', radiaxid);
|
||||||
|
setCookie('radiaxid', radiaxid, 365);
|
||||||
|
}
|
||||||
|
return radiaxid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAccountId() {
|
||||||
|
return localStorage.getItem('radiaxid') || getCookie('radiaxid') || getMacAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPhoneNumber(phoneNumber) {
|
||||||
|
if (phoneNumber.startsWith('+')) {
|
||||||
|
phoneNumber = phoneNumber.substring(1);
|
||||||
|
}
|
||||||
|
if (phoneNumber.startsWith('0')) {
|
||||||
|
phoneNumber = '254' + phoneNumber.substring(1);
|
||||||
|
}
|
||||||
|
if (phoneNumber.match(/^(7|1)/)) {
|
||||||
|
phoneNumber = '254' + phoneNumber;
|
||||||
|
}
|
||||||
|
return phoneNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginTimeout;
|
||||||
|
function handlePhoneNumberSubmission(planId, routerId, price) {
|
||||||
|
var accountId = getOrCreateAccountId();
|
||||||
|
var modalHtml = `
|
||||||
|
<div id="paymentModal" class='fixed inset-0 bg-black/30 backdrop-blur-sm z-50 animate-fade-in'>
|
||||||
|
<div class="fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-full max-w-md">
|
||||||
|
<div class="bg-white text-black rounded-lg shadow-xl">
|
||||||
|
<div class="flex items-center justify-between p-4 border-b border-gray-300">
|
||||||
|
<h5 class="text-xl font-semibold">
|
||||||
|
Enter Your Mpesa Number
|
||||||
|
</h5>
|
||||||
|
<button class="text-gray-500 hover:text-black" onclick="closeModal('paymentModal')">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="bg-navy-50 border-l-4 border-navy-400 p-4 rounded-md mb-4"> <div class="flex"> <div class="flex-shrink-0"> <svg class="h-5 w-5 text-navy-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1 4v.01m6.938-2.162A9 9 0 1111 3v0a9 9 0 018.938 10.838z" /> </svg> </div> <div class="ml-3"> <p class="text-sm text-navy-700"> You are about to initiate M-pesa payment. Enter phone number below and click Pay Now to initialize payment. </p> </div> </div></div> <input type="text" class="w-full px-4 py-3 border border-orange-300 rounded-lg focus:ring-2 focus:ring-navy-400 focus:border-blue-500 text-black" id="phoneNumberInput" required placeholder="e.g. 0712345678">
|
||||||
|
<div class="text-red-500 mt-1 hidden" id="invalidPhone">Please enter a valid phone number!</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end space-x-2 p-4 border-t border-gray-300">
|
||||||
|
<button onclick="closeModal('paymentModal')" class="px-4 py-2 bg-orange text-black rounded-lg hover:bg-gray-300">Close</button>
|
||||||
|
<button id="payNowBtn" class="px-4 py-2 bg-navy text-white font-semibold hover:from-blue-600 hover:to-blue-500 flex items-center rounded-lg">
|
||||||
|
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||||
|
</svg>
|
||||||
|
Pay Now
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||||
|
var modal = document.getElementById('paymentModal');
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
var payNowBtn = document.getElementById('payNowBtn');
|
||||||
|
var phoneInput = document.getElementById('phoneNumberInput');
|
||||||
|
phoneInput.focus();
|
||||||
|
payNowBtn.addEventListener('click', function() { handlePayment(); });
|
||||||
|
function handlePayment() {
|
||||||
|
if (!phoneInput.value) {
|
||||||
|
document.getElementById('invalidPhone').classList.remove('hidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payNowBtn.disabled = true;
|
||||||
|
payNowBtn.innerHTML = `
|
||||||
|
<svg class='animate-spin -ml-1 mr-3 h-5 w-5 text-white' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
|
||||||
|
<circle class='opacity-25' cx='12' cy='12' r='10' stroke='currentColor' stroke-width='4'></circle>
|
||||||
|
<path class='opacity-75' fill='currentColor' d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'></path>
|
||||||
|
</svg>
|
||||||
|
Processing...
|
||||||
|
`;
|
||||||
|
var formattedPhoneNumber = formatPhoneNumber(phoneInput.value);
|
||||||
|
document.getElementById('usernameInput').value = accountId;
|
||||||
|
|
||||||
|
fetch('https://yatmack2.smartisp.co.ke/index.php?_route=plugin/CreateHotspotuser&type=grant', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({
|
||||||
|
phone_number: formattedPhoneNumber,
|
||||||
|
plan_id: planId,
|
||||||
|
router_id: routerId,
|
||||||
|
account_id: accountId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === 'error') throw new Error(data.message);
|
||||||
|
closeModal('paymentModal');
|
||||||
|
showProcessingModal();
|
||||||
|
checkPaymentStatus(formattedPhoneNumber);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
closeModal('paymentModal');
|
||||||
|
showErrorModal(error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showProcessingModal() {
|
||||||
|
var processingModalHtml = `
|
||||||
|
<div id='processingModal' class='fixed inset-0 bg-black/30 backdrop-blur-sm z-50 animate-fade-in'>
|
||||||
|
<div class='fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-full max-w-md animate-slide-up'>
|
||||||
|
<div class='bg-gradient-to-br from-white to-gray-50 shadow-2xl rounded-2xl p-8 text-center border border-gray-100'>
|
||||||
|
<div class='flex justify-center'>
|
||||||
|
<svg class='animate-spin h-20 w-20 text-navy-500' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
|
||||||
|
<circle class='opacity-25' cx='12' cy='12' r='10' stroke='currentColor' stroke-width='4'></circle>
|
||||||
|
<path class='opacity-75' fill='currentColor' d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6 bg-navy-50/50 backdrop-blur-xs border border-navy-200 rounded-xl p-6 shadow-lg"> <div class="flex items-center space-x-4"> <svg class="h-8 w-8 text-navy-400 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1 4v.01m6.938-2.162A9 9 0 1111 3v0a9 9 0 018.938 10.838z" /> </svg> <div class="flex-1"> <h2 class="text-xl font-semibold text-navy-600 font-lexend">Initializing Payment</h2> <p class="text-navy-500 mt-2">A payment request has been sent to your phone. Please wait while we process your payment.</p> </div> </div> <div class="mt-4 h-2 w-full bg-navy-100 rounded-full overflow-hidden"> <div class="h-2 bg-navy-400 rounded-full animate-pulse"></div> </div> </div> </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.insertAdjacentHTML('beforeend', processingModalHtml);
|
||||||
|
}
|
||||||
|
function closeModal(modalId) {
|
||||||
|
var modal = document.getElementById(modalId);
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.add('hidden');
|
||||||
|
setTimeout(function() { modal.remove(); }, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSuccessModal() {
|
||||||
|
var successModalHtml = `
|
||||||
|
<div id='successModal' class='fixed inset-0 bg-black/60 backdrop-blur-sm z-50 transition-all duration-300'>
|
||||||
|
<div class='fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-full max-w-lg transition-all duration-300 ease-out'>
|
||||||
|
<div class='bg-white/95 backdrop-blur-md shadow-2xl rounded-3xl p-8 text-center border border-gray-200/50 ring-1 ring-gray-900/5'>
|
||||||
|
<div class='flex justify-center mb-6'>
|
||||||
|
<div class='relative'>
|
||||||
|
<div class='absolute inset-0 rounded-full bg-green-500/20 animate-pulse'></div>
|
||||||
|
<svg class='h-16 w-16 text-green-500' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
|
||||||
|
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='space-y-4'>
|
||||||
|
<h2 class='text-2xl font-semibold text-gray-900'>Payment Successful</h2>
|
||||||
|
<div class='bg-green-50 rounded-2xl p-6 border border-green-100'>
|
||||||
|
<div class='flex items-center gap-4'>
|
||||||
|
<svg class='h-6 w-6 text-green-500 flex-shrink-0' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
|
||||||
|
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 12l2 2 4-4'/>
|
||||||
|
</svg>
|
||||||
|
<p class='text-sm text-green-700'>Your transaction has been completed successfully. You will be redirected shortly.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.insertAdjacentHTML('beforeend', successModalHtml);
|
||||||
|
setTimeout(function() { closeModal('successModal'); }, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showErrorModal(errorMsg) {
|
||||||
|
var errorModalHtml = `
|
||||||
|
<div id='errorModal' class='fixed inset-0 bg-black/60 backdrop-blur-sm z-50 transition-all duration-300'>
|
||||||
|
<div class='fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-full max-w-lg transition-all duration-300 ease-out'>
|
||||||
|
<div class='bg-white/95 backdrop-blur-md shadow-2xl rounded-3xl p-8 text-center border border-gray-200/50 ring-1 ring-gray-900/5'>
|
||||||
|
<div class='flex justify-center mb-6'>
|
||||||
|
<div class='relative'>
|
||||||
|
<div class='absolute inset-0 rounded-full bg-red-500/20 animate-pulse'></div>
|
||||||
|
<svg class='h-16 w-16 text-red-500' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
|
||||||
|
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6 18L18 6M6 6l12 12'/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='space-y-4'>
|
||||||
|
<h2 class='text-2xl font-semibold text-gray-900'>Payment Failed</h2>
|
||||||
|
<div class='bg-red-50 rounded-2xl p-6 border border-red-100'>
|
||||||
|
<div class='flex items-center gap-4'>
|
||||||
|
<svg class='h-6 w-6 text-red-500 flex-shrink-0' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
|
||||||
|
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'/>
|
||||||
|
</svg>
|
||||||
|
<p class='text-sm text-red-700'>An error occurred while processing your payment. Please try again.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.insertAdjacentHTML('beforeend', errorModalHtml);
|
||||||
|
setTimeout(function() { closeModal('errorModal'); }, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPaymentStatus(phoneNumber) {
|
||||||
|
var accountId = getOrCreateAccountId();
|
||||||
|
var checkInterval = setInterval(function() {
|
||||||
|
fetch('https://yatmack2.smartisp.co.ke/index.php?_route=plugin/CreateHotspotuser&type=verify', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({account_id: accountId})
|
||||||
|
})
|
||||||
|
.then(function(response) { return response.json(); })
|
||||||
|
.then(function(data) {
|
||||||
|
console.log('Raw Response:', data);
|
||||||
|
if (data.Resultcode === '3') {
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
closeModal('processingModal');
|
||||||
|
showSuccessModal();
|
||||||
|
if (loginTimeout) clearTimeout(loginTimeout);
|
||||||
|
loginTimeout = setTimeout(function() { document.getElementById('loginForm').submit(); }, 2000);
|
||||||
|
} else if (data.Resultcode === '2') {
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
closeModal('processingModal');
|
||||||
|
showErrorModal(data.Message);
|
||||||
|
} else {
|
||||||
|
console.error('Unexpected result code:', data.Resultcode);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.error('Error during fetch request:', error);
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
closeModal('processingModal');
|
||||||
|
showErrorModal('An error occurred while checking payment status.');
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
closeModal('processingModal');
|
||||||
|
showErrorModal('Timeout while waiting for payment confirmation.');
|
||||||
|
}, 300000); // 5 minutes
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var accountId = getOrCreateAccountId();
|
||||||
|
var usernameInput = document.getElementById('usernameInput');
|
||||||
|
if (usernameInput && !usernameInput.value) {
|
||||||
|
usernameInput.value = accountId;
|
||||||
|
}
|
||||||
|
var submitBtn = document.getElementById('submitBtn');
|
||||||
|
if (submitBtn) {
|
||||||
|
submitBtn.addEventListener('click', function() {
|
||||||
|
document.getElementById('loginForm').submit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var loginTimeout; // Variable to store the timeout ID
|
||||||
|
function redeemVoucher() {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Redeem Voucher',
|
||||||
|
input: 'text',
|
||||||
|
inputPlaceholder: 'Enter voucher code',
|
||||||
|
inputValidator: function(value) {
|
||||||
|
if (!value) {
|
||||||
|
return 'You need to enter a voucher code!';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButtonColor: '#3085d6',
|
||||||
|
cancelButtonColor: '#d33',
|
||||||
|
confirmButtonText: 'Redeem',
|
||||||
|
showLoaderOnConfirm: true,
|
||||||
|
preConfirm: (voucherCode) => {
|
||||||
|
var accountId = voucherCode;
|
||||||
|
return fetch('https://yatmack2.smartisp.co.ke/index.php?_route=plugin/CreateHotspotuser&type=voucher', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({voucher_code: voucherCode, account_id: accountId}),
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) throw new Error('Network response was not ok');
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === 'error') throw new Error(data.message);
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
allowOutsideClick: () => !Swal.isLoading()
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Voucher Redeemed',
|
||||||
|
text: result.value.message,
|
||||||
|
showConfirmButton: false,
|
||||||
|
allowOutsideClick: false,
|
||||||
|
didOpen: () => {
|
||||||
|
Swal.showLoading();
|
||||||
|
var username = result.value.username;
|
||||||
|
console.log('Received username from server:', username);
|
||||||
|
var usernameInput = document.querySelector('input[name="username"]');
|
||||||
|
if (usernameInput) {
|
||||||
|
console.log('Found username input element.');
|
||||||
|
usernameInput.value = username;
|
||||||
|
loginTimeout = setTimeout(function() {
|
||||||
|
var loginForm = document.getElementById('loginForm');
|
||||||
|
if (loginForm) {
|
||||||
|
loginForm.submit();
|
||||||
|
} else {
|
||||||
|
console.error('Login form not found.');
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Error',
|
||||||
|
text: 'Login form not found. Please try again.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
console.error('Username input element not found.');
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Error',
|
||||||
|
text: 'Username input not found. Please try again.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Oops...',
|
||||||
|
text: error.message,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var reconnectBtn = document.getElementById('reconnectBtn');
|
||||||
|
var mpesaCodeInput = document.getElementById('mpesaCodeInput');
|
||||||
|
var macInput = document.getElementById('mac');
|
||||||
|
var loginForm = document.getElementById('loginForm');
|
||||||
|
if (reconnectBtn) {
|
||||||
|
reconnectBtn.addEventListener('click', function() {
|
||||||
|
// Validate inputs before processing
|
||||||
|
if (!mpesaCodeInput || !macInput || !loginForm) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Error',
|
||||||
|
text: 'Required form elements are missing'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var mpesaCode = mpesaCodeInput.value.trim().split(' ')[0];
|
||||||
|
var mac = macInput.value.trim();
|
||||||
|
fetch('https://yatmack2.smartisp.co.ke/index.php?_route=plugin/ReconnectUser', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({ mpesa_code: mpesaCode, mac: mac }),
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
console.log('Response data:', data);
|
||||||
|
var resultCode = data.Resultcode;
|
||||||
|
var message = data.Message;
|
||||||
|
var status = data.Status;
|
||||||
|
var username = data.username;
|
||||||
|
if (resultCode === '1') {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Invalid Code',
|
||||||
|
text: message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (resultCode === '3') {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Expired Package',
|
||||||
|
text: message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (resultCode === '2') {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: status,
|
||||||
|
text: message,
|
||||||
|
showConfirmButton: false,
|
||||||
|
allowOutsideClick: false,
|
||||||
|
didOpen: () => {
|
||||||
|
Swal.showLoading();
|
||||||
|
console.log('Received username from server:', username);
|
||||||
|
var usernameInput = document.querySelector('input[name="username"]');
|
||||||
|
if (usernameInput) {
|
||||||
|
console.log('Found username input element.');
|
||||||
|
usernameInput.value = username;
|
||||||
|
setTimeout(function() {
|
||||||
|
var loginForm = document.getElementById('loginForm');
|
||||||
|
if (loginForm) {
|
||||||
|
loginForm.submit();
|
||||||
|
} else {
|
||||||
|
console.error('Login form not found.');
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Error',
|
||||||
|
text: 'Login form not found. Please try again.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
console.error('Username input element not found.');
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Error',
|
||||||
|
text: 'Username input not found. Please try again.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Oops...',
|
||||||
|
text: error.message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('Reconnect button not found');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</html>
|
@ -48,7 +48,28 @@ $ui = new class($key)
|
|||||||
}
|
}
|
||||||
function getAll()
|
function getAll()
|
||||||
{
|
{
|
||||||
return $this->assign;
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($this->assign as $key => $value) {
|
||||||
|
if($value instanceof ORM){
|
||||||
|
$result[$key] = $value->as_array();
|
||||||
|
}else if($value instanceof IdiormResultSet){
|
||||||
|
$count = count($value);
|
||||||
|
for($n=0;$n<$count;$n++){
|
||||||
|
foreach ($value[$n] as $k=>$v) {
|
||||||
|
$result[$key][$n][$k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$result[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch()
|
||||||
|
{
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,8 +22,8 @@ class Csrf
|
|||||||
|
|
||||||
public static function check($token)
|
public static function check($token)
|
||||||
{
|
{
|
||||||
global $config;
|
global $config, $isApi;
|
||||||
if($config['csrf_enabled'] == 'yes') {
|
if($config['csrf_enabled'] == 'yes' && !$isApi) {
|
||||||
if (isset($_SESSION['csrf_token'], $_SESSION['csrf_token_time'], $token)) {
|
if (isset($_SESSION['csrf_token'], $_SESSION['csrf_token_time'], $token)) {
|
||||||
$storedToken = $_SESSION['csrf_token'];
|
$storedToken = $_SESSION['csrf_token'];
|
||||||
$tokenTime = $_SESSION['csrf_token_time'];
|
$tokenTime = $_SESSION['csrf_token_time'];
|
||||||
|
@ -87,7 +87,7 @@ class File
|
|||||||
$src_img = $image_create($source_file);
|
$src_img = $image_create($source_file);
|
||||||
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $nwidth, $nheight, $width, $height);
|
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $nwidth, $nheight, $width, $height);
|
||||||
|
|
||||||
$image($dst_img, $dst_dir, $quality);
|
imagepng($dst_img, $dst_dir);
|
||||||
|
|
||||||
if ($dst_img) imagedestroy($dst_img);
|
if ($dst_img) imagedestroy($dst_img);
|
||||||
if ($src_img) imagedestroy($src_img);
|
if ($src_img) imagedestroy($src_img);
|
||||||
|
347
system/autoload/Invoice.php
Normal file
347
system/autoload/Invoice.php
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Mpdf\Mpdf;
|
||||||
|
|
||||||
|
class Invoice
|
||||||
|
{
|
||||||
|
public static function generateInvoice($invoiceData)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (empty($invoiceData['invoice'])) {
|
||||||
|
throw new Exception(Lang::T("Invoice No is required"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$template = Lang::getNotifText('email_invoice');
|
||||||
|
if (!$template) {
|
||||||
|
throw new Exception(Lang::T("Invoice template not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($template, '<body') === false) {
|
||||||
|
$template = "<html><body>$template</body></html>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$processedHtml = self::renderTemplate($template, $invoiceData);
|
||||||
|
|
||||||
|
// Debugging: Save processed HTML to file for review
|
||||||
|
// file_put_contents('debug_invoice.html', $processedHtml);
|
||||||
|
|
||||||
|
// Generate PDF
|
||||||
|
$mpdf = new Mpdf([
|
||||||
|
'mode' => 'utf-8',
|
||||||
|
'format' => 'A4',
|
||||||
|
'margin_left' => 10,
|
||||||
|
'margin_right' => 10,
|
||||||
|
'margin_top' => 10,
|
||||||
|
'margin_bottom' => 10,
|
||||||
|
'default_font' => 'helvetica',
|
||||||
|
'orientation' => 'P',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$mpdf->SetDisplayMode('fullpage');
|
||||||
|
$mpdf->SetProtection(['print']);
|
||||||
|
$mpdf->shrink_tables_to_fit = 1;
|
||||||
|
$mpdf->SetWatermarkText(strtoupper($invoiceData['status'] ?? 'UNPAID'), 0.15);
|
||||||
|
$mpdf->showWatermarkText = true;
|
||||||
|
$mpdf->WriteHTML($processedHtml);
|
||||||
|
|
||||||
|
// Save PDF
|
||||||
|
$filename = "invoice_{$invoiceData['invoice']}.pdf";
|
||||||
|
$outputPath = "system/uploads/invoices/{$filename}";
|
||||||
|
$dir = dirname($outputPath);
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
mkdir($dir, 0755, true);
|
||||||
|
}
|
||||||
|
$mpdf->Output($outputPath, 'F');
|
||||||
|
|
||||||
|
if (!file_exists($outputPath)) {
|
||||||
|
throw new Exception(Lang::T("Failed to save PDF file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filename;
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
_log("Invoice generation failed: " . $e->getMessage());
|
||||||
|
sendTelegram("Invoice generation failed: " . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function renderTemplate($template, $invoiceData)
|
||||||
|
{
|
||||||
|
return preg_replace_callback('/\[\[(\w+)\]\]/', function ($matches) use ($invoiceData) {
|
||||||
|
$key = $matches[1];
|
||||||
|
if (!isset($invoiceData[$key])) {
|
||||||
|
_log(Lang::T("Missing invoice key: ") . $key);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($key, ['created_at', 'due_date'])) {
|
||||||
|
return date('F j, Y', strtotime($invoiceData[$key]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($key, ['amount', 'total', 'subtotal', 'tax'])) {
|
||||||
|
return $invoiceData['currency_code'] . number_format((float) $invoiceData[$key], 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key === 'bill_rows') {
|
||||||
|
return $invoiceData[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return htmlspecialchars($invoiceData[$key] ?? '');
|
||||||
|
}, $template);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send invoice to user
|
||||||
|
*
|
||||||
|
* @param int $userId
|
||||||
|
* @param array $invoice
|
||||||
|
* @param array $bills
|
||||||
|
* @param string $status
|
||||||
|
* @param string $invoiceNo
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function sendInvoice($userId, $invoice = null, $bills = [], $status = "Unpaid", $invoiceNo = null)
|
||||||
|
{
|
||||||
|
global $config, $root_path, $UPLOAD_PATH;
|
||||||
|
|
||||||
|
// Set default currency code
|
||||||
|
$config['currency_code'] ??= '$';
|
||||||
|
|
||||||
|
$account = ORM::for_table('tbl_customers')->find_one($userId);
|
||||||
|
self::validateAccount($account);
|
||||||
|
if (!$invoiceNo) {
|
||||||
|
$invoiceNo = "INV-" . Package::_raid();
|
||||||
|
}
|
||||||
|
// Fetch invoice if not provided
|
||||||
|
if ($status === "Unpaid" && !$invoice) {
|
||||||
|
$data = ORM::for_table('tbl_user_recharges')->where('customer_id', $userId)
|
||||||
|
->where('status', 'off')
|
||||||
|
->left_outer_join('tbl_plans', 'tbl_user_recharges.namebp = tbl_plans.name_plan')
|
||||||
|
->select('tbl_plans.price', 'price')
|
||||||
|
->select('tbl_plans.name_plan', 'namebp')
|
||||||
|
->find_one();
|
||||||
|
if (!$data) {
|
||||||
|
$data = ORM::for_table('tbl_user_recharges')->where('username', $account->username)
|
||||||
|
->left_outer_join('tbl_plans', 'tbl_user_recharges.namebp = tbl_plans.name_plan')
|
||||||
|
->select('tbl_plans.price', 'price')
|
||||||
|
->select('tbl_plans.name_plan', 'namebp')
|
||||||
|
->where('status', 'off')
|
||||||
|
->find_one();
|
||||||
|
}
|
||||||
|
if (!$data) {
|
||||||
|
throw new Exception(Lang::T("No unpaid invoice found for username:") . $account->username);
|
||||||
|
}
|
||||||
|
$invoice = [
|
||||||
|
'price' => $data->price,
|
||||||
|
'plan_name' => $data->namebp,
|
||||||
|
'routers' => $data->routers,
|
||||||
|
];
|
||||||
|
|
||||||
|
} else if ($status === "Paid" && !$invoice) {
|
||||||
|
$invoice = ORM::for_table("tbl_transactions")->where("username", $account->username)->find_one();
|
||||||
|
}
|
||||||
|
if (!$invoice) {
|
||||||
|
throw new Exception(Lang::T("Transaction not found for username: ") . $account->username);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get additional bills if not provided
|
||||||
|
if (empty($bills)) {
|
||||||
|
[$bills, $add_cost] = User::getBills($account->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$invoiceItems = self::generateInvoiceItems($invoice, $bills, $add_cost);
|
||||||
|
$subtotal = array_sum(array_column($invoiceItems, 'amount'));
|
||||||
|
$tax = $config['enable_tax'] ? Package::tax($subtotal) : 0;
|
||||||
|
$tax_rate = $config['tax_rate'] ?? 0;
|
||||||
|
$total = $subtotal + $tax;
|
||||||
|
|
||||||
|
$payLink = self::generatePaymentLink($account, $invoice, $status);
|
||||||
|
$logo = self::getCompanyLogo($UPLOAD_PATH, $root_path);
|
||||||
|
|
||||||
|
$invoiceData = [
|
||||||
|
'invoice' => $invoiceNo,
|
||||||
|
'fullname' => $account->fullname,
|
||||||
|
'email' => $account->email,
|
||||||
|
'address' => $account->address,
|
||||||
|
'phone' => $account->phonenumber,
|
||||||
|
'bill_rows' => self::generateBillRows($invoiceItems, $config['currency_code'], $subtotal, $tax_rate, $tax, $total),
|
||||||
|
'status' => $status,
|
||||||
|
'created_at' => date('Y-m-d H:i:s'),
|
||||||
|
'due_date' => date('Y-m-d H:i:s', strtotime('+7 days')),
|
||||||
|
'currency' => $config['currency_code'],
|
||||||
|
'company_address' => $config['address'],
|
||||||
|
'company_name' => $config['CompanyName'],
|
||||||
|
'company_phone' => $config['phone'],
|
||||||
|
'logo' => $logo,
|
||||||
|
'payment_link' => $payLink
|
||||||
|
];
|
||||||
|
|
||||||
|
if (empty($invoiceData['bill_rows'])) {
|
||||||
|
throw new Exception(Lang::T("Bill rows data is empty."));
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = self::generateInvoice($invoiceData);
|
||||||
|
if (!$filename) {
|
||||||
|
throw new Exception(Lang::T("Failed to generate invoice PDF"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdfPath = "system/uploads/invoices/{$filename}";
|
||||||
|
self::saveToDatabase($filename, $account->id, $invoiceData, $total);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Message::sendEmail(
|
||||||
|
$account->email,
|
||||||
|
Lang::T("Invoice for Account {$account->fullname}"),
|
||||||
|
Lang::T("Please find your invoice attached"),
|
||||||
|
$pdfPath
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new Exception(Lang::T("Failed to send email invoice to ") . $account->email . ". " . Lang::T("Reason: ") . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function validateAccount($account)
|
||||||
|
{
|
||||||
|
if (!$account) {
|
||||||
|
throw new Exception(Lang::T("User not found"));
|
||||||
|
}
|
||||||
|
if (!$account->email || !filter_var($account->email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
throw new Exception(Lang::T("Invalid user email"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function generateInvoiceItems($invoice, $bills, $add_cost)
|
||||||
|
{
|
||||||
|
$items = [
|
||||||
|
[
|
||||||
|
'description' => $invoice['plan_name'],
|
||||||
|
'details' => Lang::T('Subscription'),
|
||||||
|
'amount' => (float) $invoice['price']
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($invoice->routers != 'balance') {
|
||||||
|
foreach ($bills as $description => $amount) {
|
||||||
|
if (is_numeric($amount)) {
|
||||||
|
$items[] = [
|
||||||
|
'description' => $description,
|
||||||
|
'details' => Lang::T('Additional Bill'),
|
||||||
|
'amount' => (float) $amount
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
_log(Lang::T("Invalid bill amount for {$description}: {$amount}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function generatePaymentLink($account, $invoice, $status)
|
||||||
|
{
|
||||||
|
$token = User::generateToken($account->id, 1);
|
||||||
|
if (empty($token['token'])) {
|
||||||
|
return '?_route=home';
|
||||||
|
}
|
||||||
|
|
||||||
|
$tur = ORM::for_table('tbl_user_recharges')
|
||||||
|
->where('customer_id', $account->id)
|
||||||
|
->where('namebp', $invoice->plan_name);
|
||||||
|
|
||||||
|
$tur->where('status', $status === 'Paid' ? 'on' : 'off');
|
||||||
|
$turResult = $tur->find_one();
|
||||||
|
|
||||||
|
return $turResult ? '?_route=home&recharge=' . $turResult['id'] . '&uid=' . urlencode($token['token']) : '?_route=home';
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getCompanyLogo($UPLOAD_PATH, $root_path)
|
||||||
|
{
|
||||||
|
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||||
|
return file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png') ?
|
||||||
|
$UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png?' . time() :
|
||||||
|
$UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.default.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function generateBillRows($items, $currency, $subtotal, $tax_rate, $tax, $total)
|
||||||
|
{
|
||||||
|
$html = "<table style='width: 100%; border-collapse: collapse; margin: 20px 0;'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style='background: #3498db; color: white; padding: 12px; text-align: left;'>Description</th>
|
||||||
|
<th style='background: #3498db; color: white; padding: 12px; text-align: left;'>Details</th>
|
||||||
|
<th style='background: #3498db; color: white; padding: 12px; text-align: left;'>Amount</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>";
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$desc = htmlspecialchars($item['description'], ENT_QUOTES);
|
||||||
|
$details = htmlspecialchars($item['details'], ENT_QUOTES);
|
||||||
|
$html .= "<tr>
|
||||||
|
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$desc}</td>
|
||||||
|
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$details}</td>
|
||||||
|
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$currency}" . number_format((float) $item['amount'], 2) . "</td>
|
||||||
|
</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= "<tr>
|
||||||
|
<td colspan='2' style='text-align: right; padding: 10px; border-top: 2px solid #3498db;'>Subtotal:</td>
|
||||||
|
<td style='padding: 10px; border-top: 2px solid #3498db;'>{$currency}" . number_format($subtotal, 2) . "</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan='2' style='text-align: right; padding: 10px;'>TAX ({$tax_rate}%):</td>
|
||||||
|
<td style='padding: 10px;'>{$currency}" . number_format($tax, 2) . "</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan='2' style='text-align: right; padding: 10px; font-weight: bold;'>Total:</td>
|
||||||
|
<td style='padding: 10px; font-weight: bold;'>{$currency}" . number_format($total, 2) . "</td>
|
||||||
|
</tr>";
|
||||||
|
|
||||||
|
$html .= "</tbody></table>";
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function saveToDatabase($filename, $customer_id, $invoiceData, $total)
|
||||||
|
{
|
||||||
|
$invoice = ORM::for_table('tbl_invoices')->create();
|
||||||
|
$invoice->number = $invoiceData['invoice'];
|
||||||
|
$invoice->customer_id = $customer_id;
|
||||||
|
$invoice->fullname = $invoiceData['fullname'];
|
||||||
|
$invoice->email = $invoiceData['email'];
|
||||||
|
$invoice->address = $invoiceData['address'];
|
||||||
|
$invoice->status = $invoiceData['status'];
|
||||||
|
$invoice->due_date = $invoiceData['due_date'];
|
||||||
|
$invoice->filename = $filename;
|
||||||
|
$invoice->amount = $total;
|
||||||
|
$invoice->data = json_encode($invoiceData);
|
||||||
|
$invoice->created_at = date('Y-m-d H:i:s');
|
||||||
|
$invoice->save();
|
||||||
|
return $invoice->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAll()
|
||||||
|
{
|
||||||
|
return ORM::for_table('tbl_invoices')->order_by_desc('id')->find_many();
|
||||||
|
}
|
||||||
|
public static function getById($id)
|
||||||
|
{
|
||||||
|
return ORM::for_table('tbl_invoices')->find_one($id);
|
||||||
|
}
|
||||||
|
public static function getByNumber($number)
|
||||||
|
{
|
||||||
|
return ORM::for_table('tbl_invoices')->where('number', $number)->find_one();
|
||||||
|
}
|
||||||
|
public static function delete($id)
|
||||||
|
{
|
||||||
|
$invoice = ORM::for_table('tbl_invoices')->find_one($id);
|
||||||
|
if ($invoice) {
|
||||||
|
$invoice->delete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,7 +28,7 @@ class Message
|
|||||||
if (!empty($topik)) {
|
if (!empty($topik)) {
|
||||||
$topik = "message_thread_id=$topik&";
|
$topik = "message_thread_id=$topik&";
|
||||||
}
|
}
|
||||||
return Http::getData('https://api.telegram.org/bot' . $config['telegram_bot'] . '/sendMessage?'.$topik.'chat_id=' . $chat_id . '&text=' . urlencode($txt));
|
return Http::getData('https://api.telegram.org/bot' . $config['telegram_bot'] . '/sendMessage?' . $topik . 'chat_id=' . $chat_id . '&text=' . urlencode($txt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class Message
|
|||||||
$txts = str_split($txt, 160);
|
$txts = str_split($txt, 160);
|
||||||
try {
|
try {
|
||||||
foreach ($txts as $txt) {
|
foreach ($txts as $txt) {
|
||||||
self::sendSMS($config['sms_url'], $phone, $txt);
|
self::sendSMS($phone, $txt);
|
||||||
self::logMessage('SMS', $phone, $txt, 'Success');
|
self::logMessage('SMS', $phone, $txt, 'Success');
|
||||||
}
|
}
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
@ -120,7 +120,7 @@ class Message
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function sendEmail($to, $subject, $body)
|
public static function sendEmail($to, $subject, $body, $attachmentPath = null)
|
||||||
{
|
{
|
||||||
global $config, $PAGES_PATH, $debug_mail;
|
global $config, $PAGES_PATH, $debug_mail;
|
||||||
if (empty($body)) {
|
if (empty($body)) {
|
||||||
@ -130,7 +130,6 @@ class Message
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
run_hook('send_email', [$to, $subject, $body]); #HOOK
|
run_hook('send_email', [$to, $subject, $body]); #HOOK
|
||||||
self::logMessage('Email', $to, $body, 'Success');
|
|
||||||
if (empty($config['smtp_host'])) {
|
if (empty($config['smtp_host'])) {
|
||||||
$attr = "";
|
$attr = "";
|
||||||
if (!empty($config['mail_from'])) {
|
if (!empty($config['mail_from'])) {
|
||||||
@ -140,6 +139,8 @@ class Message
|
|||||||
$attr .= "Reply-To: " . $config['mail_reply_to'] . "\r\n";
|
$attr .= "Reply-To: " . $config['mail_reply_to'] . "\r\n";
|
||||||
}
|
}
|
||||||
mail($to, $subject, $body, $attr);
|
mail($to, $subject, $body, $attr);
|
||||||
|
self::logMessage('Email', $to, $body, 'Success');
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
$mail = new PHPMailer();
|
$mail = new PHPMailer();
|
||||||
$mail->isSMTP();
|
$mail->isSMTP();
|
||||||
@ -161,6 +162,10 @@ class Message
|
|||||||
|
|
||||||
$mail->addAddress($to);
|
$mail->addAddress($to);
|
||||||
$mail->Subject = $subject;
|
$mail->Subject = $subject;
|
||||||
|
// Attachments
|
||||||
|
if (!empty($attachmentPath)) {
|
||||||
|
$mail->addAttachment($attachmentPath);
|
||||||
|
}
|
||||||
|
|
||||||
if (!file_exists($PAGES_PATH . DIRECTORY_SEPARATOR . 'Email.html')) {
|
if (!file_exists($PAGES_PATH . DIRECTORY_SEPARATOR . 'Email.html')) {
|
||||||
if (!copy($PAGES_PATH . '_template' . DIRECTORY_SEPARATOR . 'Email.html', $PAGES_PATH . DIRECTORY_SEPARATOR . 'Email.html')) {
|
if (!copy($PAGES_PATH . '_template' . DIRECTORY_SEPARATOR . 'Email.html', $PAGES_PATH . DIRECTORY_SEPARATOR . 'Email.html')) {
|
||||||
@ -176,6 +181,7 @@ class Message
|
|||||||
$html = str_replace('[[Body]]', nl2br($body), $html);
|
$html = str_replace('[[Body]]', nl2br($body), $html);
|
||||||
$mail->isHTML(true);
|
$mail->isHTML(true);
|
||||||
$mail->Body = $html;
|
$mail->Body = $html;
|
||||||
|
$mail->Body = $html;
|
||||||
} else {
|
} else {
|
||||||
$mail->isHTML(false);
|
$mail->isHTML(false);
|
||||||
$mail->Body = $body;
|
$mail->Body = $body;
|
||||||
@ -183,8 +189,10 @@ class Message
|
|||||||
if (!$mail->send()) {
|
if (!$mail->send()) {
|
||||||
$errorMessage = Lang::T("Email not sent, Mailer Error: ") . $mail->ErrorInfo;
|
$errorMessage = Lang::T("Email not sent, Mailer Error: ") . $mail->ErrorInfo;
|
||||||
self::logMessage('Email', $to, $body, 'Error', $errorMessage);
|
self::logMessage('Email', $to, $body, 'Error', $errorMessage);
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
self::logMessage('Email', $to, $body, 'Success');
|
self::logMessage('Email', $to, $body, 'Success');
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">
|
//<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">
|
||||||
@ -303,7 +311,7 @@ class Message
|
|||||||
|
|
||||||
public static function sendInvoice($cust, $trx)
|
public static function sendInvoice($cust, $trx)
|
||||||
{
|
{
|
||||||
global $config;
|
global $config, $db_pass;
|
||||||
$textInvoice = Lang::getNotifText('invoice_paid');
|
$textInvoice = Lang::getNotifText('invoice_paid');
|
||||||
$textInvoice = str_replace('[[company_name]]', $config['CompanyName'], $textInvoice);
|
$textInvoice = str_replace('[[company_name]]', $config['CompanyName'], $textInvoice);
|
||||||
$textInvoice = str_replace('[[address]]', $config['address'], $textInvoice);
|
$textInvoice = str_replace('[[address]]', $config['address'], $textInvoice);
|
||||||
@ -328,7 +336,11 @@ class Message
|
|||||||
$textInvoice = str_replace('[[password]]', $cust['password'], $textInvoice);
|
$textInvoice = str_replace('[[password]]', $cust['password'], $textInvoice);
|
||||||
$textInvoice = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($trx['expiration'], $trx['time']), $textInvoice);
|
$textInvoice = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($trx['expiration'], $trx['time']), $textInvoice);
|
||||||
$textInvoice = str_replace('[[footer]]', $config['note'], $textInvoice);
|
$textInvoice = str_replace('[[footer]]', $config['note'], $textInvoice);
|
||||||
// Calculate bills and additional costs
|
|
||||||
|
$inv_url = "?_route=voucher/invoice/$trx[id]/" . md5($trx['id'] . $db_pass);
|
||||||
|
$textInvoice = str_replace('[[invoice_link]]', $inv_url, $textInvoice);
|
||||||
|
|
||||||
|
// Calculate bills and additional costs
|
||||||
list($bills, $add_cost) = User::getBills($cust['id']);
|
list($bills, $add_cost) = User::getBills($cust['id']);
|
||||||
|
|
||||||
// Initialize note and total variables
|
// Initialize note and total variables
|
||||||
@ -362,7 +374,7 @@ class Message
|
|||||||
// Add total to the note
|
// Add total to the note
|
||||||
$note .= "Total : " . Lang::moneyFormat($total) . "\n";
|
$note .= "Total : " . Lang::moneyFormat($total) . "\n";
|
||||||
|
|
||||||
// Replace placeholders in the message
|
// Replace placeholders in the message
|
||||||
$textInvoice = str_replace('[[bills]]', $note, $textInvoice);
|
$textInvoice = str_replace('[[bills]]', $note, $textInvoice);
|
||||||
|
|
||||||
if ($config['user_notification_payment'] == 'sms') {
|
if ($config['user_notification_payment'] == 'sms') {
|
||||||
@ -387,9 +399,24 @@ class Message
|
|||||||
$v->body = nl2br($body);
|
$v->body = nl2br($body);
|
||||||
$v->save();
|
$v->save();
|
||||||
self::logMessage("Inbox", $user->username, $body, "Success");
|
self::logMessage("Inbox", $user->username, $body, "Success");
|
||||||
|
return true;
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
$errorMessage = Lang::T("Error adding message to inbox: " . $e->getMessage());
|
$errorMessage = Lang::T("Error adding message to inbox: " . $e->getMessage());
|
||||||
self::logMessage('Inbox', $user->username, $body, 'Error', $errorMessage);
|
self::logMessage('Inbox', $user->username, $body, 'Error', $errorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getMessageType($type, $message)
|
||||||
|
{
|
||||||
|
if (strpos($message, "<divider>") === false) {
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
$msgs = explode("<divider>", $message);
|
||||||
|
if ($type == "PPPOE") {
|
||||||
|
return $msgs[1];
|
||||||
|
} else {
|
||||||
|
return $msgs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class Paginator
|
|||||||
{
|
{
|
||||||
public static function findMany($query, $search = [], $per_page = '10', $append_url = "", $toArray = false)
|
public static function findMany($query, $search = [], $per_page = '10', $append_url = "", $toArray = false)
|
||||||
{
|
{
|
||||||
global $routes, $ui;
|
global $routes, $ui, $isApi;
|
||||||
$adjacents = "2";
|
$adjacents = "2";
|
||||||
$page = _get('p', 1);
|
$page = _get('p', 1);
|
||||||
$page = (empty($page) ? 1 : $page);
|
$page = (empty($page) ? 1 : $page);
|
||||||
@ -72,7 +72,7 @@ class Paginator
|
|||||||
if ($ui) {
|
if ($ui) {
|
||||||
$ui->assign('paginator', $result);
|
$ui->assign('paginator', $result);
|
||||||
}
|
}
|
||||||
if($toArray){
|
if($toArray || $isApi){
|
||||||
return $query->offset($startpoint)->limit($per_page)->find_array();
|
return $query->offset($startpoint)->limit($per_page)->find_array();
|
||||||
}else{
|
}else{
|
||||||
return $query->offset($startpoint)->limit($per_page)->find_many();
|
return $query->offset($startpoint)->limit($per_page)->find_many();
|
||||||
|
@ -71,8 +71,8 @@ switch ($action) {
|
|||||||
if (count($plns) > 0) {
|
if (count($plns) > 0) {
|
||||||
$query->where_in('plan_name', $plns);
|
$query->where_in('plan_name', $plns);
|
||||||
}
|
}
|
||||||
$x = $query->find_array();
|
$x = $query->find_array();
|
||||||
$xy = $query->sum('price');
|
$xy = $query->sum('price');
|
||||||
|
|
||||||
$ui->assign('sd', $sd);
|
$ui->assign('sd', $sd);
|
||||||
$ui->assign('ed', $ed);
|
$ui->assign('ed', $ed);
|
||||||
@ -114,7 +114,10 @@ switch ($action) {
|
|||||||
$query = ORM::for_table('tbl_transactions')
|
$query = ORM::for_table('tbl_transactions')
|
||||||
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
|
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
|
||||||
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
|
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
|
||||||
->order_by_desc('id');
|
->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||||
|
->select('tbl_transactions.*')
|
||||||
|
->select('tbl_customers.fullname', 'fullname')
|
||||||
|
->order_by_desc('tbl_transactions.id');
|
||||||
if (count($tps) > 0) {
|
if (count($tps) > 0) {
|
||||||
$query->where_in('type', $tps);
|
$query->where_in('type', $tps);
|
||||||
}
|
}
|
||||||
@ -131,13 +134,13 @@ switch ($action) {
|
|||||||
if (count($plns) > 0) {
|
if (count($plns) > 0) {
|
||||||
$query->where_in('plan_name', $plns);
|
$query->where_in('plan_name', $plns);
|
||||||
}
|
}
|
||||||
$x = $query->find_array();
|
$x = $query->find_array();
|
||||||
$xy = $query->sum('price');
|
$xy = $query->sum('price');
|
||||||
|
|
||||||
$title = ' Reports [' . $mdate . ']';
|
$title = ' Reports [' . $mdate . ']';
|
||||||
$title = str_replace('-', ' ', $title);
|
$title = str_replace('-', ' ', $title);
|
||||||
|
|
||||||
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||||
if (file_exists($UPLOAD_PATH . '/logo.png')) {
|
if (file_exists($UPLOAD_PATH . '/logo.png')) {
|
||||||
$logo = $UPLOAD_URL_PATH . '/logo.png';
|
$logo = $UPLOAD_URL_PATH . '/logo.png';
|
||||||
} else {
|
} else {
|
||||||
@ -154,10 +157,11 @@ switch ($action) {
|
|||||||
</div>
|
</div>
|
||||||
<div id="logo"><img id="image" src="' . $logo . '" alt="logo" /></div>
|
<div id="logo"><img id="image" src="' . $logo . '" alt="logo" /></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . Lang::dateAndTimeFormat($sd, $ts) .' - '. Lang::dateAndTimeFormat($ed, $te) . '</div>
|
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . Lang::dateAndTimeFormat($sd, $ts) . ' - ' . Lang::dateAndTimeFormat($ed, $te) . '</div>
|
||||||
<table id="customers">
|
<table id="customers">
|
||||||
<tr>
|
<tr>
|
||||||
<th>' . Lang::T('Username') . '</th>
|
<th>' . Lang::T('Username') . '</th>
|
||||||
|
<th>' . Lang::T('Fullname') . '</th>
|
||||||
<th>' . Lang::T('Plan Name') . '</th>
|
<th>' . Lang::T('Plan Name') . '</th>
|
||||||
<th>' . Lang::T('Type') . '</th>
|
<th>' . Lang::T('Type') . '</th>
|
||||||
<th>' . Lang::T('Plan Price') . '</th>
|
<th>' . Lang::T('Plan Price') . '</th>
|
||||||
@ -170,6 +174,7 @@ switch ($action) {
|
|||||||
foreach ($x as $value) {
|
foreach ($x as $value) {
|
||||||
|
|
||||||
$username = $value['username'];
|
$username = $value['username'];
|
||||||
|
$fullname = $value['fullname'];
|
||||||
$plan_name = $value['plan_name'];
|
$plan_name = $value['plan_name'];
|
||||||
$type = $value['type'];
|
$type = $value['type'];
|
||||||
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
||||||
@ -181,6 +186,7 @@ switch ($action) {
|
|||||||
|
|
||||||
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
||||||
<td>$username</td>
|
<td>$username</td>
|
||||||
|
<td>$fullname</td>
|
||||||
<td>$plan_name</td>
|
<td>$plan_name</td>
|
||||||
<td>$type</td>
|
<td>$type</td>
|
||||||
<td align='right'>$price</td>
|
<td align='right'>$price</td>
|
||||||
@ -245,7 +251,7 @@ $style
|
|||||||
$html
|
$html
|
||||||
EOF;
|
EOF;
|
||||||
$mpdf->WriteHTML($nhtml);
|
$mpdf->WriteHTML($nhtml);
|
||||||
$mpdf->Output('phpnuxbill_reports_'.date('Ymd_His') . '.pdf', 'D');
|
$mpdf->Output('phpnuxbill_reports_' . date('Ymd_His') . '.pdf', 'D');
|
||||||
} else {
|
} else {
|
||||||
echo 'No Data';
|
echo 'No Data';
|
||||||
}
|
}
|
||||||
@ -258,13 +264,17 @@ EOF;
|
|||||||
$stype = _post('stype');
|
$stype = _post('stype');
|
||||||
|
|
||||||
$d = ORM::for_table('tbl_transactions');
|
$d = ORM::for_table('tbl_transactions');
|
||||||
|
$d->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||||
|
->select('tbl_transactions.*')
|
||||||
|
->select('tbl_customers.fullname', 'fullname')
|
||||||
|
->order_by_desc('tbl_transactions.id');
|
||||||
if ($stype != '') {
|
if ($stype != '') {
|
||||||
$d->where('type', $stype);
|
$d->where('type', $stype);
|
||||||
}
|
}
|
||||||
$d->where_gte('recharged_on', $fdate);
|
$d->where_gte('recharged_on', $fdate);
|
||||||
$d->where_lte('recharged_on', $tdate);
|
$d->where_lte('recharged_on', $tdate);
|
||||||
$d->order_by_desc('id');
|
$d->order_by_desc('id');
|
||||||
$x = $d->find_many();
|
$x = $d->find_many();
|
||||||
|
|
||||||
$dr = ORM::for_table('tbl_transactions');
|
$dr = ORM::for_table('tbl_transactions');
|
||||||
if ($stype != '') {
|
if ($stype != '') {
|
||||||
@ -290,6 +300,10 @@ EOF;
|
|||||||
$tdate = _post('tdate');
|
$tdate = _post('tdate');
|
||||||
$stype = _post('stype');
|
$stype = _post('stype');
|
||||||
$d = ORM::for_table('tbl_transactions');
|
$d = ORM::for_table('tbl_transactions');
|
||||||
|
$d->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||||
|
->select('tbl_transactions.*')
|
||||||
|
->select('tbl_customers.fullname', 'fullname')
|
||||||
|
->order_by_desc('tbl_transactions.id');
|
||||||
if ($stype != '') {
|
if ($stype != '') {
|
||||||
$d->where('type', $stype);
|
$d->where('type', $stype);
|
||||||
}
|
}
|
||||||
@ -297,7 +311,7 @@ EOF;
|
|||||||
$d->where_gte('recharged_on', $fdate);
|
$d->where_gte('recharged_on', $fdate);
|
||||||
$d->where_lte('recharged_on', $tdate);
|
$d->where_lte('recharged_on', $tdate);
|
||||||
$d->order_by_desc('id');
|
$d->order_by_desc('id');
|
||||||
$x = $d->find_many();
|
$x = $d->find_many();
|
||||||
|
|
||||||
$dr = ORM::for_table('tbl_transactions');
|
$dr = ORM::for_table('tbl_transactions');
|
||||||
if ($stype != '') {
|
if ($stype != '') {
|
||||||
@ -311,7 +325,7 @@ EOF;
|
|||||||
$title = ' Reports [' . $mdate . ']';
|
$title = ' Reports [' . $mdate . ']';
|
||||||
$title = str_replace('-', ' ', $title);
|
$title = str_replace('-', ' ', $title);
|
||||||
|
|
||||||
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||||
if (file_exists($UPLOAD_PATH . '/logo.png')) {
|
if (file_exists($UPLOAD_PATH . '/logo.png')) {
|
||||||
$logo = $UPLOAD_URL_PATH . '/logo.png';
|
$logo = $UPLOAD_URL_PATH . '/logo.png';
|
||||||
} else {
|
} else {
|
||||||
@ -332,6 +346,7 @@ EOF;
|
|||||||
<table id="customers">
|
<table id="customers">
|
||||||
<tr>
|
<tr>
|
||||||
<th>' . Lang::T('Username') . '</th>
|
<th>' . Lang::T('Username') . '</th>
|
||||||
|
<th>' . Lang::T('Fullname') . '</th>
|
||||||
<th>' . Lang::T('Plan Name') . '</th>
|
<th>' . Lang::T('Plan Name') . '</th>
|
||||||
<th>' . Lang::T('Type') . '</th>
|
<th>' . Lang::T('Type') . '</th>
|
||||||
<th>' . Lang::T('Plan Price') . '</th>
|
<th>' . Lang::T('Plan Price') . '</th>
|
||||||
@ -344,6 +359,7 @@ EOF;
|
|||||||
foreach ($x as $value) {
|
foreach ($x as $value) {
|
||||||
|
|
||||||
$username = $value['username'];
|
$username = $value['username'];
|
||||||
|
$fullname = $value['fullname'];
|
||||||
$plan_name = $value['plan_name'];
|
$plan_name = $value['plan_name'];
|
||||||
$type = $value['type'];
|
$type = $value['type'];
|
||||||
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
||||||
@ -355,7 +371,8 @@ EOF;
|
|||||||
|
|
||||||
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
||||||
<td>$username</td>
|
<td>$username</td>
|
||||||
<td>$plan_name</td>
|
<td>$fullname</td>
|
||||||
|
<td>$plan_name</td>
|
||||||
<td>$type</td>
|
<td>$type</td>
|
||||||
<td align='right'>$price</td>
|
<td align='right'>$price</td>
|
||||||
<td>$recharged_on </td>
|
<td>$recharged_on </td>
|
||||||
|
@ -196,8 +196,8 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
|
|||||||
$tur->save();
|
$tur->save();
|
||||||
App::setToken(_get('stoken'), $id);
|
App::setToken(_get('stoken'), $id);
|
||||||
file_put_contents($path, $m);
|
file_put_contents($path, $m);
|
||||||
_log("Customer $tur[customer_id] $tur[username] extend for $days days", "Customer", $user['id']);
|
_log("Customer $tur[customer_id] $user[fullname] ($tur[username]) extend for $days days", "Customer", $user['id']);
|
||||||
Message::sendTelegram("#u$user[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
|
Message::sendTelegram("#u$user[username] ($user[fullname]) #id$tur[customer_id] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
|
||||||
"\nLocation: " . $p['routers'] .
|
"\nLocation: " . $p['routers'] .
|
||||||
"\nCustomer: " . $user['fullname'] .
|
"\nCustomer: " . $user['fullname'] .
|
||||||
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
|
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
|
||||||
|
27
system/controllers/invoices.php
Normal file
27
system/controllers/invoices.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||||
|
* by https://t.me/ibnux
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
_admin();
|
||||||
|
$ui->assign('_title', Lang::T('Invoice Lists'));
|
||||||
|
$ui->assign('_system_menu', 'reports');
|
||||||
|
$action = $routes['1'];
|
||||||
|
$ui->assign('_admin', $admin);
|
||||||
|
|
||||||
|
if (empty($action)) {
|
||||||
|
$action = 'list';
|
||||||
|
}
|
||||||
|
switch ($action) {
|
||||||
|
case 'list':
|
||||||
|
$ui->assign('xheader', '<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">');
|
||||||
|
$ui->assign('invoices', Invoice::getAll());
|
||||||
|
$ui->display('admin/invoices/list.tpl');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$ui->display('admin/404.tpl');
|
||||||
|
}
|
@ -57,56 +57,79 @@ EOT;
|
|||||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get form data
|
$id_customer = $_POST['id_customer'] ?? '';
|
||||||
$id_customer = $_POST['id_customer'];
|
$message = $_POST['message'] ?? '';
|
||||||
$message = $_POST['message'];
|
$via = $_POST['via'] ?? '';
|
||||||
$via = $_POST['via'];
|
$subject = $_POST['subject'] ?? '';
|
||||||
|
|
||||||
// Check if fields are empty
|
// Validate subject based on the selected channel
|
||||||
if ($id_customer == '' or $message == '' or $via == '') {
|
if (($via === 'all' || $via === 'email' || $via === 'inbox') && empty($subject)) {
|
||||||
r2(getUrl('message/send'), 'e', Lang::T('All field is required'));
|
r2(getUrl('message/send'), 'e', LANG::T('Subject is required to send message using') . ' ' . $via . '.');
|
||||||
} else {
|
}
|
||||||
// Get customer details from the database
|
|
||||||
$c = ORM::for_table('tbl_customers')->find_one($id_customer);
|
|
||||||
|
|
||||||
// Replace placeholders in the message with actual values
|
if (empty($id_customer) || empty($message) || empty($via)) {
|
||||||
$message = str_replace('[[name]]', $c['fullname'], $message);
|
r2(getUrl('message/send'), 'e', Lang::T('Customer, Message, and Channel are required'));
|
||||||
$message = str_replace('[[user_name]]', $c['username'], $message);
|
}
|
||||||
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
|
|
||||||
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
|
$customer = ORM::for_table('tbl_customers')->find_one($id_customer);
|
||||||
if (strpos($message, '[[payment_link]]') !== false) {
|
if (!$customer) {
|
||||||
// token only valid for 1 day, for security reason
|
r2(getUrl('message/send'), 'e', Lang::T('Customer not found'));
|
||||||
$token = User::generateToken($c['id'], 1);
|
}
|
||||||
if (!empty($token['token'])) {
|
|
||||||
$tur = ORM::for_table('tbl_user_recharges')
|
// Replace placeholders in message and subject
|
||||||
->where('customer_id', $c['id'])
|
$currentMessage = str_replace(
|
||||||
//->where('namebp', $package)
|
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
|
||||||
->find_one();
|
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
|
||||||
if ($tur) {
|
$message
|
||||||
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
|
);
|
||||||
$message = str_replace('[[payment_link]]', $url, $message);
|
|
||||||
}
|
$currentSubject = str_replace(
|
||||||
} else {
|
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
|
||||||
$message = str_replace('[[payment_link]]', '', $message);
|
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
|
||||||
|
$subject
|
||||||
|
);
|
||||||
|
|
||||||
|
if (strpos($message, '[[payment_link]]') !== false) {
|
||||||
|
$token = User::generateToken($customer['id'], 1);
|
||||||
|
if (!empty($token['token'])) {
|
||||||
|
$tur = ORM::for_table('tbl_user_recharges')
|
||||||
|
->where('customer_id', $customer['id'])
|
||||||
|
->find_one();
|
||||||
|
if ($tur) {
|
||||||
|
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
|
||||||
|
$currentMessage = str_replace('[[payment_link]]', $url, $currentMessage);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Send the message
|
|
||||||
if ($via == 'sms' || $via == 'both') {
|
|
||||||
$smsSent = Message::sendSMS($c['phonenumber'], $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($via == 'wa' || $via == 'both') {
|
|
||||||
$waSent = Message::sendWhatsapp($c['phonenumber'], $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($smsSent) || isset($waSent)) {
|
|
||||||
r2(getUrl('message/send'), 's', Lang::T('Message Sent Successfully'));
|
|
||||||
} else {
|
} else {
|
||||||
r2(getUrl('message/send'), 'e', Lang::T('Failed to send message'));
|
$currentMessage = str_replace('[[payment_link]]', '', $currentMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send the message through the selected channels
|
||||||
|
$smsSent = $waSent = $emailSent = $inboxSent = false;
|
||||||
|
|
||||||
|
if ($via === 'sms' || $via === 'both' || $via === 'all') {
|
||||||
|
$smsSent = Message::sendSMS($customer['phonenumber'], $currentSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($via === 'wa' || $via === 'both' || $via === 'all') {
|
||||||
|
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($via === 'email' || $via === 'all') {
|
||||||
|
$emailSent = Message::sendEmail($customer['email'], $currentSubject, $currentMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($via === 'inbox' || $via === 'all') {
|
||||||
|
$inboxSent = Message::addToInbox($customer['id'], $currentSubject, $currentMessage, 'Admin');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any message was sent successfully
|
||||||
|
if ($smsSent || $waSent || $emailSent || $inboxSent) {
|
||||||
|
r2(getUrl('message/send'), 's', Lang::T('Message Sent Successfully'));
|
||||||
|
} else {
|
||||||
|
r2(getUrl('message/send'), 'e', Lang::T('Failed to send message'));
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'send_bulk':
|
case 'send_bulk':
|
||||||
@ -133,11 +156,16 @@ EOT;
|
|||||||
$batch = $_REQUEST['batch'] ?? 100;
|
$batch = $_REQUEST['batch'] ?? 100;
|
||||||
$page = $_REQUEST['page'] ?? 0;
|
$page = $_REQUEST['page'] ?? 0;
|
||||||
$router = $_REQUEST['router'] ?? null;
|
$router = $_REQUEST['router'] ?? null;
|
||||||
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on' ? true : false;
|
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on';
|
||||||
$service = $_REQUEST['service'] ?? '';
|
$service = $_REQUEST['service'] ?? '';
|
||||||
|
$subject = $_REQUEST['subject'] ?? '';
|
||||||
|
|
||||||
if (empty($group) || empty($message) || empty($via) || empty($service)) {
|
if (empty($group) || empty($message) || empty($via) || empty($service)) {
|
||||||
die(json_encode(['status' => 'error', 'message' => 'All fields are required']));
|
die(json_encode(['status' => 'error', 'message' => LANG::T('All fields are required')]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($via, ['all', 'email', 'inbox']) && empty($subject)) {
|
||||||
|
die(json_encode(['status' => 'error', 'message' => LANG::T('Subject is required to send message using') . ' ' . $via . '.']));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get batch of customers based on group
|
// Get batch of customers based on group
|
||||||
@ -153,7 +181,7 @@ EOT;
|
|||||||
default:
|
default:
|
||||||
$router = ORM::for_table('tbl_routers')->find_one($router);
|
$router = ORM::for_table('tbl_routers')->find_one($router);
|
||||||
if (!$router) {
|
if (!$router) {
|
||||||
die(json_encode(['status' => 'error', 'message' => 'Invalid router']));
|
die(json_encode(['status' => 'error', 'message' => LANG::T('Invalid router')]));
|
||||||
}
|
}
|
||||||
$routerName = $router->name;
|
$routerName = $router->name;
|
||||||
break;
|
break;
|
||||||
@ -200,6 +228,9 @@ EOT;
|
|||||||
['tbl_customers.phonenumber', 'phonenumber'],
|
['tbl_customers.phonenumber', 'phonenumber'],
|
||||||
['tbl_user_recharges.customer_id', 'customer_id'],
|
['tbl_user_recharges.customer_id', 'customer_id'],
|
||||||
['tbl_customers.fullname', 'fullname'],
|
['tbl_customers.fullname', 'fullname'],
|
||||||
|
['tbl_customers.username', 'username'],
|
||||||
|
['tbl_customers.email', 'email'],
|
||||||
|
['tbl_customers.service_type', 'service_type'],
|
||||||
]);
|
]);
|
||||||
$customers = $query->find_array();
|
$customers = $query->find_array();
|
||||||
} else {
|
} else {
|
||||||
@ -287,7 +318,13 @@ EOT;
|
|||||||
$totalSMSFailed = 0;
|
$totalSMSFailed = 0;
|
||||||
$totalWhatsappSent = 0;
|
$totalWhatsappSent = 0;
|
||||||
$totalWhatsappFailed = 0;
|
$totalWhatsappFailed = 0;
|
||||||
|
$totalEmailSent = 0;
|
||||||
|
$totalEmailFailed = 0;
|
||||||
|
$totalInboxSent = 0;
|
||||||
|
$totalInboxFailed = 0;
|
||||||
$batchStatus = [];
|
$batchStatus = [];
|
||||||
|
//$subject = $config['CompanyName'] . ' ' . Lang::T('Notification Message');
|
||||||
|
$form = 'Admin';
|
||||||
|
|
||||||
foreach ($customers as $customer) {
|
foreach ($customers as $customer) {
|
||||||
$currentMessage = str_replace(
|
$currentMessage = str_replace(
|
||||||
@ -296,6 +333,12 @@ EOT;
|
|||||||
$message
|
$message
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$currentSubject = str_replace(
|
||||||
|
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
|
||||||
|
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
|
||||||
|
$subject
|
||||||
|
);
|
||||||
|
|
||||||
$phoneNumber = preg_replace('/\D/', '', $customer['phonenumber']);
|
$phoneNumber = preg_replace('/\D/', '', $customer['phonenumber']);
|
||||||
|
|
||||||
if (empty($phoneNumber)) {
|
if (empty($phoneNumber)) {
|
||||||
@ -310,14 +353,14 @@ EOT;
|
|||||||
if ($test) {
|
if ($test) {
|
||||||
$batchStatus[] = [
|
$batchStatus[] = [
|
||||||
'name' => $customer['fullname'],
|
'name' => $customer['fullname'],
|
||||||
'phone' => $customer['phonenumber'],
|
'channel' => 'Test Channel',
|
||||||
'status' => 'Test Mode',
|
'status' => 'Test Mode',
|
||||||
'message' => $currentMessage,
|
'message' => $currentMessage,
|
||||||
'service' => $service,
|
'service' => $service,
|
||||||
'router' => $routerName,
|
'router' => $routerName,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
if ($via == 'sms' || $via == 'both') {
|
if ($via === 'sms' || $via === 'both' || $via === 'all') {
|
||||||
if (Message::sendSMS($customer['phonenumber'], $currentMessage)) {
|
if (Message::sendSMS($customer['phonenumber'], $currentMessage)) {
|
||||||
$totalSMSSent++;
|
$totalSMSSent++;
|
||||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Sent', 'message' => $currentMessage];
|
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Sent', 'message' => $currentMessage];
|
||||||
@ -327,13 +370,33 @@ EOT;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($via == 'wa' || $via == 'both') {
|
if ($via === 'wa' || $via == 'both' || $via === 'all') {
|
||||||
if (Message::sendWhatsapp($customer['phonenumber'], $currentMessage)) {
|
if (Message::sendWhatsapp($customer['phonenumber'], $currentMessage)) {
|
||||||
$totalWhatsappSent++;
|
$totalWhatsappSent++;
|
||||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
|
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
|
||||||
} else {
|
} else {
|
||||||
$totalWhatsappFailed++;
|
$totalWhatsappFailed++;
|
||||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
|
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($via === 'email' || $via === 'all') {
|
||||||
|
if (Message::sendEmail($customer['email'], $currentSubject, $currentMessage)) {
|
||||||
|
$totalEmailSent++;
|
||||||
|
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['email'], 'status' => 'Email Sent', 'message' => $currentMessage];
|
||||||
|
} else {
|
||||||
|
$totalEmailFailed++;
|
||||||
|
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['email'], 'status' => 'Email Failed', 'message' => $currentMessage];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($via === 'inbox' || $via === 'all') {
|
||||||
|
if (Message::addToInbox($customer['customer_id'], $currentSubject, $currentMessage, $form)) {
|
||||||
|
$totalInboxSent++;
|
||||||
|
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => 'Inbox', 'status' => 'Inbox Message Sent', 'message' => $currentMessage];
|
||||||
|
} else {
|
||||||
|
$totalInboxFailed++;
|
||||||
|
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => 'Inbox', 'status' => 'Inbox Message Failed', 'message' => $currentMessage];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -348,8 +411,8 @@ EOT;
|
|||||||
'page' => $page + 1,
|
'page' => $page + 1,
|
||||||
'batchStatus' => $batchStatus,
|
'batchStatus' => $batchStatus,
|
||||||
'message' => $currentMessage,
|
'message' => $currentMessage,
|
||||||
'totalSent' => $totalSMSSent + $totalWhatsappSent,
|
'totalSent' => $totalSMSSent + $totalWhatsappSent + $totalEmailSent + $totalInboxSent,
|
||||||
'totalFailed' => $totalSMSFailed + $totalWhatsappFailed,
|
'totalFailed' => $totalSMSFailed + $totalWhatsappFailed + $totalEmailFailed + $totalInboxFailed,
|
||||||
'hasMore' => $hasMore,
|
'hasMore' => $hasMore,
|
||||||
'service' => $service,
|
'service' => $service,
|
||||||
'router' => $routerName,
|
'router' => $routerName,
|
||||||
@ -365,16 +428,20 @@ EOT;
|
|||||||
// Get the posted data
|
// Get the posted data
|
||||||
$customerIds = $_POST['customer_ids'] ?? [];
|
$customerIds = $_POST['customer_ids'] ?? [];
|
||||||
$via = $_POST['message_type'] ?? '';
|
$via = $_POST['message_type'] ?? '';
|
||||||
|
$subject = $_POST['subject'] ?? '';
|
||||||
$message = isset($_POST['message']) ? trim($_POST['message']) : '';
|
$message = isset($_POST['message']) ? trim($_POST['message']) : '';
|
||||||
if (empty($customerIds) || empty($message) || empty($via)) {
|
if (empty($customerIds) || empty($message) || empty($via)) {
|
||||||
echo json_encode(['status' => 'error', 'message' => Lang::T('Invalid customer IDs, Message, or Message Type.')]);
|
echo json_encode(['status' => 'error', 'message' => Lang::T('Invalid customer IDs, Message, or Message Type.')]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($via === 'all' || $via === 'email' || $via === 'inbox' && empty($subject)) {
|
||||||
|
die(json_encode(['status' => 'error', 'message' => LANG::T('Subject is required to send message using') . ' ' . $via . '.']));
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare to send messages
|
// Prepare to send messages
|
||||||
$sentCount = 0;
|
$sentCount = 0;
|
||||||
$failedCount = 0;
|
$failedCount = 0;
|
||||||
$subject = Lang::T('Notification Message');
|
|
||||||
$form = 'Admin';
|
$form = 'Admin';
|
||||||
|
|
||||||
foreach ($customerIds as $customerId) {
|
foreach ($customerIds as $customerId) {
|
||||||
|
@ -371,7 +371,7 @@ switch ($action) {
|
|||||||
$d->trx_invoice = $result;
|
$d->trx_invoice = $result;
|
||||||
$d->status = 2;
|
$d->status = 2;
|
||||||
$d->save();
|
$d->save();
|
||||||
r2(getUrl('order/view/$trx_id'), 's', Lang::T("Success to send package"));
|
r2(getUrl("order/view/$trx_id"), 's', Lang::T("Success to send package"));
|
||||||
} else {
|
} else {
|
||||||
$errorMessage = "Send Package with Balance Failed\n\n#u$user[username] #send \n" . $plan['name_plan'] .
|
$errorMessage = "Send Package with Balance Failed\n\n#u$user[username] #send \n" . $plan['name_plan'] .
|
||||||
"\nRouter: " . $router_name .
|
"\nRouter: " . $router_name .
|
||||||
|
@ -269,6 +269,19 @@ switch ($action) {
|
|||||||
r2(getUrl('plan/view/') . $id, 'd', "Customer not found");
|
r2(getUrl('plan/view/') . $id, 'd', "Customer not found");
|
||||||
}
|
}
|
||||||
Package::createInvoice($in);
|
Package::createInvoice($in);
|
||||||
|
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||||
|
$logo = '';
|
||||||
|
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) {
|
||||||
|
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png';
|
||||||
|
$imgsize = getimagesize($logo);
|
||||||
|
$width = $imgsize[0];
|
||||||
|
$height = $imgsize[1];
|
||||||
|
$ui->assign('wlogo', $width);
|
||||||
|
$ui->assign('hlogo', $height);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ui->assign('public_url', getUrl("voucher/invoice/$id/".md5($id. $db_pass)));
|
||||||
|
$ui->assign('logo', $logo);
|
||||||
$ui->assign('_title', 'View Invoice');
|
$ui->assign('_title', 'View Invoice');
|
||||||
$ui->display('admin/plan/invoice.tpl');
|
$ui->display('admin/plan/invoice.tpl');
|
||||||
break;
|
break;
|
||||||
@ -1062,11 +1075,11 @@ switch ($action) {
|
|||||||
} else {
|
} else {
|
||||||
r2(getUrl('plan'), 's', "Customer not found");
|
r2(getUrl('plan'), 's', "Customer not found");
|
||||||
}
|
}
|
||||||
Message::sendTelegram("#u$tur[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
|
Message::sendTelegram("#u$tur[username] #id$tur[customer_id] #extend by $admin[fullname] #" . $p['type'] . " \n" . $p['name_plan'] .
|
||||||
"\nLocation: " . $p['routers'] .
|
"\nLocation: " . $p['routers'] .
|
||||||
"\nCustomer: " . $c['fullname'] .
|
"\nCustomer: " . $c['fullname'] .
|
||||||
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
|
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
|
||||||
_log("$admin[fullname] extend Customer $tur[customer_id] $tur[username] for $days days", $admin['user_type'], $admin['id']);
|
_log("$admin[fullname] extend Customer $tur[customer_id] $tur[username] #$tur[customer_id] for $days days", $admin['user_type'], $admin['id']);
|
||||||
r2(getUrl('plan'), 's', "Extend until $expiration");
|
r2(getUrl('plan'), 's', "Extend until $expiration");
|
||||||
} else {
|
} else {
|
||||||
r2(getUrl('plan'), 's', "Customer is not expired yet");
|
r2(getUrl('plan'), 's', "Customer is not expired yet");
|
||||||
|
@ -113,7 +113,8 @@ switch ($do) {
|
|||||||
$d->save();
|
$d->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
|
if (file_exists($_FILES['photo']['tmp_name']))
|
||||||
|
unlink($_FILES['photo']['tmp_name']);
|
||||||
User::setFormCustomField($user);
|
User::setFormCustomField($user);
|
||||||
run_hook('register_user'); #HOOK
|
run_hook('register_user'); #HOOK
|
||||||
$msg .= Lang::T('Registration successful') . '<br>';
|
$msg .= Lang::T('Registration successful') . '<br>';
|
||||||
@ -147,8 +148,45 @@ switch ($do) {
|
|||||||
// Display register-otp.tpl if OTP is enabled
|
// Display register-otp.tpl if OTP is enabled
|
||||||
$ui->display('customer/register-otp.tpl');
|
$ui->display('customer/register-otp.tpl');
|
||||||
} else {
|
} else {
|
||||||
// Display register.tpl if OTP is not enabled
|
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||||
$ui->display('customer/register.tpl');
|
if (!empty($config['login_page_logo']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'])) {
|
||||||
|
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'];
|
||||||
|
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png')) {
|
||||||
|
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png';
|
||||||
|
} else {
|
||||||
|
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.default.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($config['login_page_wallpaper']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'])) {
|
||||||
|
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'];
|
||||||
|
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png')) {
|
||||||
|
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png';
|
||||||
|
} else {
|
||||||
|
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.default.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($config['login_page_favicon']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'])) {
|
||||||
|
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'];
|
||||||
|
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png')) {
|
||||||
|
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png';
|
||||||
|
} else {
|
||||||
|
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.default.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
$ui->assign('login_logo', $login_logo);
|
||||||
|
$ui->assign('wallpaper', $wallpaper);
|
||||||
|
$ui->assign('favicon', $favicon);
|
||||||
|
$ui->assign('csrf_token', $csrf_token);
|
||||||
|
$ui->assign('_title', Lang::T('Login'));
|
||||||
|
$ui->assign('customFields', User::getFormCustomField($ui, true));
|
||||||
|
switch ($config['login_page_type']) {
|
||||||
|
case 'custom':
|
||||||
|
$ui->display('customer/reg-login-custom-' . $config['login_Page_template'] . '.tpl');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$ui->display('customer/register.tpl');
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -196,6 +234,36 @@ switch ($do) {
|
|||||||
$ui->display('customer/register-rotp.tpl');
|
$ui->display('customer/register-rotp.tpl');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||||
|
if (!empty($config['login_page_logo']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'])) {
|
||||||
|
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'];
|
||||||
|
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png')) {
|
||||||
|
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png';
|
||||||
|
} else {
|
||||||
|
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.default.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($config['login_page_wallpaper']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'])) {
|
||||||
|
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'];
|
||||||
|
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png')) {
|
||||||
|
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png';
|
||||||
|
} else {
|
||||||
|
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.default.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($config['login_page_favicon']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'])) {
|
||||||
|
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'];
|
||||||
|
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png')) {
|
||||||
|
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png';
|
||||||
|
} else {
|
||||||
|
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.default.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
$ui->assign('login_logo', $login_logo);
|
||||||
|
$ui->assign('wallpaper', $wallpaper);
|
||||||
|
$ui->assign('favicon', $favicon);
|
||||||
|
$ui->assign('csrf_token', $csrf_token);
|
||||||
|
$ui->assign('_title', Lang::T('Login'));
|
||||||
$ui->assign('customFields', User::getFormCustomField($ui, true));
|
$ui->assign('customFields', User::getFormCustomField($ui, true));
|
||||||
$ui->assign('username', "");
|
$ui->assign('username', "");
|
||||||
$ui->assign('fullname', "");
|
$ui->assign('fullname', "");
|
||||||
@ -204,7 +272,15 @@ switch ($do) {
|
|||||||
$ui->assign('otp', false);
|
$ui->assign('otp', false);
|
||||||
$ui->assign('_title', Lang::T('Register'));
|
$ui->assign('_title', Lang::T('Register'));
|
||||||
run_hook('view_register'); #HOOK
|
run_hook('view_register'); #HOOK
|
||||||
$ui->display('customer/register.tpl');
|
switch ($config['login_page_type']) {
|
||||||
|
case 'custom':
|
||||||
|
$ui->display('customer/reg-login-custom-' . $config['login_Page_template'] . '.tpl');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$ui->display('customer/register.tpl');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ switch ($action) {
|
|||||||
$w = [];
|
$w = [];
|
||||||
$v = [];
|
$v = [];
|
||||||
foreach ($mts as $mt) {
|
foreach ($mts as $mt) {
|
||||||
$w[] ='method';
|
$w[] = 'method';
|
||||||
$v[] = "$mt - %";
|
$v[] = "$mt - %";
|
||||||
}
|
}
|
||||||
$query->where_likes($w, $v);
|
$query->where_likes($w, $v);
|
||||||
@ -91,7 +91,7 @@ switch ($action) {
|
|||||||
$w = [];
|
$w = [];
|
||||||
$v = [];
|
$v = [];
|
||||||
foreach ($mts as $mt) {
|
foreach ($mts as $mt) {
|
||||||
$w[] ='method';
|
$w[] = 'method';
|
||||||
$v[] = "$mt - %";
|
$v[] = "$mt - %";
|
||||||
}
|
}
|
||||||
$query->where_likes($w, $v);
|
$query->where_likes($w, $v);
|
||||||
@ -161,7 +161,7 @@ switch ($action) {
|
|||||||
$w = [];
|
$w = [];
|
||||||
$v = [];
|
$v = [];
|
||||||
foreach ($mts as $mt) {
|
foreach ($mts as $mt) {
|
||||||
$w[] ='method';
|
$w[] = 'method';
|
||||||
$v[] = "$mt - %";
|
$v[] = "$mt - %";
|
||||||
}
|
}
|
||||||
$query->where_likes($w, $v);
|
$query->where_likes($w, $v);
|
||||||
@ -246,7 +246,7 @@ switch ($action) {
|
|||||||
$total += $v;
|
$total += $v;
|
||||||
$array['data'][] = $v;
|
$array['data'][] = $v;
|
||||||
}
|
}
|
||||||
if($total>0){
|
if ($total > 0) {
|
||||||
$result['datas'][] = $array;
|
$result['datas'][] = $array;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,18 +258,29 @@ switch ($action) {
|
|||||||
die();
|
die();
|
||||||
case 'by-date':
|
case 'by-date':
|
||||||
case 'activation':
|
case 'activation':
|
||||||
$q = (_post('q') ? _post('q') : _get('q'));
|
$q = trim(_post('q') ?: _get('q'));
|
||||||
$keep = _post('keep');
|
$keep = _post('keep');
|
||||||
|
|
||||||
if (!empty($keep)) {
|
if (!empty($keep)) {
|
||||||
ORM::raw_execute("DELETE FROM tbl_transactions WHERE date < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
|
ORM::raw_execute("DELETE FROM tbl_transactions WHERE date < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL ? DAY))", [$keep]);
|
||||||
r2(getUrl('logs/list/'), 's', "Delete logs older than $keep days");
|
r2(getUrl('reports/activation/'), 's', "Deleted logs older than $keep days");
|
||||||
}
|
}
|
||||||
if ($q != '') {
|
|
||||||
$query = ORM::for_table('tbl_transactions')->where_like('invoice', '%' . $q . '%')->order_by_desc('id');
|
$query = ORM::for_table('tbl_transactions')
|
||||||
|
->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||||
|
->select('tbl_transactions.*')
|
||||||
|
->select('tbl_customers.fullname', 'fullname')
|
||||||
|
->order_by_desc('tbl_transactions.id');
|
||||||
|
|
||||||
|
if ($q !== '') {
|
||||||
|
$query->where_like('invoice', "%$q%");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$d = Paginator::findMany($query, ['q' => $q]);
|
$d = Paginator::findMany($query, ['q' => $q]);
|
||||||
} else {
|
} catch (Exception $e) {
|
||||||
$query = ORM::for_table('tbl_transactions')->order_by_desc('id');
|
r2(getUrl('reports/activation/'), 'e', 'Database query failed: ' . $e->getMessage());
|
||||||
$d = Paginator::findMany($query);
|
$d = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$ui->assign('activation', $d);
|
$ui->assign('activation', $d);
|
||||||
@ -291,6 +302,10 @@ switch ($action) {
|
|||||||
$stype = _post('stype');
|
$stype = _post('stype');
|
||||||
|
|
||||||
$d = ORM::for_table('tbl_transactions');
|
$d = ORM::for_table('tbl_transactions');
|
||||||
|
$d->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||||
|
->select('tbl_transactions.*')
|
||||||
|
->select('tbl_customers.fullname', 'fullname')
|
||||||
|
->order_by_desc('tbl_transactions.id');
|
||||||
if ($stype != '') {
|
if ($stype != '') {
|
||||||
$d->where('type', $stype);
|
$d->where('type', $stype);
|
||||||
}
|
}
|
||||||
@ -298,7 +313,7 @@ switch ($action) {
|
|||||||
$d->where_gte('recharged_on', $fdate);
|
$d->where_gte('recharged_on', $fdate);
|
||||||
$d->where_lte('recharged_on', $tdate);
|
$d->where_lte('recharged_on', $tdate);
|
||||||
$d->order_by_desc('id');
|
$d->order_by_desc('id');
|
||||||
$x = $d->find_many();
|
$x = $d->find_many();
|
||||||
|
|
||||||
$dr = ORM::for_table('tbl_transactions');
|
$dr = ORM::for_table('tbl_transactions');
|
||||||
if ($stype != '') {
|
if ($stype != '') {
|
||||||
@ -348,7 +363,10 @@ switch ($action) {
|
|||||||
$query = ORM::for_table('tbl_transactions')
|
$query = ORM::for_table('tbl_transactions')
|
||||||
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
|
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
|
||||||
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
|
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
|
||||||
->order_by_desc('id');
|
->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
|
||||||
|
->select('tbl_transactions.*')
|
||||||
|
->select('tbl_customers.fullname', 'fullname')
|
||||||
|
->order_by_desc('tbl_transactions.id');
|
||||||
if (count($tps) > 0) {
|
if (count($tps) > 0) {
|
||||||
$query->where_in('type', $tps);
|
$query->where_in('type', $tps);
|
||||||
}
|
}
|
||||||
@ -356,7 +374,7 @@ switch ($action) {
|
|||||||
$w = [];
|
$w = [];
|
||||||
$v = [];
|
$v = [];
|
||||||
foreach ($mts as $mt) {
|
foreach ($mts as $mt) {
|
||||||
$w[] ='method';
|
$w[] = 'method';
|
||||||
$v[] = "$mt - %";
|
$v[] = "$mt - %";
|
||||||
}
|
}
|
||||||
$query->where_likes($w, $v);
|
$query->where_likes($w, $v);
|
||||||
|
@ -146,7 +146,8 @@ switch ($action) {
|
|||||||
$r = ORM::for_table('tbl_routers')->find_many();
|
$r = ORM::for_table('tbl_routers')->find_many();
|
||||||
$ui->assign('r', $r);
|
$ui->assign('r', $r);
|
||||||
if (function_exists("shell_exec")) {
|
if (function_exists("shell_exec")) {
|
||||||
$php = trim(shell_exec('which php'));
|
$which = stripos(php_uname('s'), "Win") === 0 ? 'where' : 'which';
|
||||||
|
$php = trim(shell_exec("$which php"));
|
||||||
if (empty($php)) {
|
if (empty($php)) {
|
||||||
$php = 'php';
|
$php = 'php';
|
||||||
}
|
}
|
||||||
@ -248,6 +249,75 @@ switch ($action) {
|
|||||||
$_POST['hide_pg'] = _post('hide_pg', 'no');
|
$_POST['hide_pg'] = _post('hide_pg', 'no');
|
||||||
$_POST['hide_aui'] = _post('hide_aui', 'no');
|
$_POST['hide_aui'] = _post('hide_aui', 'no');
|
||||||
|
|
||||||
|
// Login page post
|
||||||
|
$login_page_title = _post('login_page_head');
|
||||||
|
$login_page_description = _post('login_page_description');
|
||||||
|
$login_Page_template = _post('login_Page_template');
|
||||||
|
$login_page_type = _post('login_page_type');
|
||||||
|
$csrf_token = _post('csrf_token');
|
||||||
|
|
||||||
|
if (!Csrf::check($csrf_token)) {
|
||||||
|
r2(getUrl('settings/app'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($login_page_type == 'custom' && (empty($login_Page_template) || empty($login_page_title) || empty($login_page_description))) {
|
||||||
|
r2(getUrl('settings/app'), 'e', 'Please fill all required fields');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($login_page_title) > 25) {
|
||||||
|
r2(getUrl('settings/app'), 'e', 'Login page title must not exceed 25 characters');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strlen($login_page_description) > 100) {
|
||||||
|
r2(getUrl('settings/app'), 'e', 'Login page description must not exceed 50 characters');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$image_paths = [];
|
||||||
|
$allowed_types = ['image/jpeg', 'image/png'];
|
||||||
|
|
||||||
|
if ($_FILES['login_page_favicon']['name'] != '') {
|
||||||
|
$favicon_type = $_FILES['login_page_favicon']['type'];
|
||||||
|
if (in_array($favicon_type, $allowed_types) && preg_match('/\.(jpg|jpeg|png)$/i', $_FILES['login_page_favicon']['name'])) {
|
||||||
|
$extension = pathinfo($_FILES['login_page_favicon']['name'], PATHINFO_EXTENSION);
|
||||||
|
$favicon_path = $UPLOAD_PATH . DIRECTORY_SEPARATOR . uniqid('favicon_') . '.' . $extension;
|
||||||
|
File::resizeCropImage($_FILES['login_page_favicon']['tmp_name'], $favicon_path, 16, 16, 100);
|
||||||
|
$_POST['login_page_favicon'] = basename($favicon_path); // Save dynamic file name
|
||||||
|
if (file_exists($_FILES['login_page_favicon']['tmp_name']))
|
||||||
|
unlink($_FILES['login_page_favicon']['tmp_name']);
|
||||||
|
} else {
|
||||||
|
r2(getUrl('settings/app'), 'e', 'Favicon must be a JPG, JPEG, or PNG image.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_FILES['login_page_wallpaper']['name'] != '') {
|
||||||
|
$wallpaper_type = $_FILES['login_page_wallpaper']['type'];
|
||||||
|
if (in_array($wallpaper_type, $allowed_types) && preg_match('/\.(jpg|jpeg|png)$/i', $_FILES['login_page_wallpaper']['name'])) {
|
||||||
|
$extension = pathinfo($_FILES['login_page_wallpaper']['name'], PATHINFO_EXTENSION);
|
||||||
|
$wallpaper_path = $UPLOAD_PATH . DIRECTORY_SEPARATOR . uniqid('wallpaper_') . '.' . $extension;
|
||||||
|
File::resizeCropImage($_FILES['login_page_wallpaper']['tmp_name'], $wallpaper_path, 1920, 1080, 100);
|
||||||
|
$_POST['login_page_wallpaper'] = basename($wallpaper_path); // Save dynamic file name
|
||||||
|
if (file_exists($_FILES['login_page_wallpaper']['tmp_name']))
|
||||||
|
unlink($_FILES['login_page_wallpaper']['tmp_name']);
|
||||||
|
} else {
|
||||||
|
r2(getUrl('settings/app'), 'e', 'Wallpaper must be a JPG, JPEG, or PNG image.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_FILES['login_page_logo']['name'] != '') {
|
||||||
|
$logo_type = $_FILES['login_page_logo']['type'];
|
||||||
|
if (in_array($logo_type, $allowed_types) && preg_match('/\.(jpg|jpeg|png)$/i', $_FILES['login_page_logo']['name'])) {
|
||||||
|
$extension = pathinfo($_FILES['login_page_logo']['name'], PATHINFO_EXTENSION);
|
||||||
|
$logo_path = $UPLOAD_PATH . DIRECTORY_SEPARATOR . uniqid('logo_') . '.' . $extension;
|
||||||
|
File::resizeCropImage($_FILES['login_page_logo']['tmp_name'], $logo_path, 300, 60, 100);
|
||||||
|
$_POST['login_page_logo'] = basename($logo_path); // Save dynamic file name
|
||||||
|
if (file_exists($_FILES['login_page_logo']['tmp_name']))
|
||||||
|
unlink($_FILES['login_page_logo']['tmp_name']);
|
||||||
|
} else {
|
||||||
|
r2(getUrl('settings/app'), 'e', 'Logo must be a JPG, JPEG, or PNG image.');
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach ($_POST as $key => $value) {
|
foreach ($_POST as $key => $value) {
|
||||||
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
|
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
|
||||||
if ($d) {
|
if ($d) {
|
||||||
@ -266,105 +336,6 @@ switch ($action) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'login-page-post':
|
|
||||||
|
|
||||||
if ($_app_stage == 'Demo') {
|
|
||||||
r2(getUrl('settings/app'), 'e', 'You cannot perform this action in Demo mode');
|
|
||||||
}
|
|
||||||
// Login page post
|
|
||||||
$login_page_title = _post('login_page_head');
|
|
||||||
$login_page_description = _post('login_page_description');
|
|
||||||
$login_Page_template = _post('login_Page_template');
|
|
||||||
$login_page_type = _post('login_page_type');
|
|
||||||
$csrf_token = _post('csrf_token');
|
|
||||||
|
|
||||||
if (!Csrf::check($csrf_token)) {
|
|
||||||
r2(getUrl('settings/app'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($login_page_type == 'custom' && (empty($login_Page_template) || empty($login_page_title) || empty($login_page_description))) {
|
|
||||||
r2(getUrl('settings/app'), 'e', 'Please fill all required fields');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen($login_page_title) > 25) {
|
|
||||||
r2(getUrl('settings/app'), 'e', 'Login page title must not exceed 25 characters');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (strlen($login_page_description) > 100) {
|
|
||||||
r2(getUrl('settings/app'), 'e', 'Login page description must not exceed 50 characters');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings = [
|
|
||||||
'login_page_head' => $login_page_title,
|
|
||||||
'login_page_description' => $login_page_description,
|
|
||||||
'login_Page_template' => $login_Page_template,
|
|
||||||
'login_page_type' => $login_page_type,
|
|
||||||
];
|
|
||||||
|
|
||||||
$image_paths = [];
|
|
||||||
$allowed_types = ['image/jpeg', 'image/png'];
|
|
||||||
|
|
||||||
if ($_FILES['login_page_favicon']['name'] != '') {
|
|
||||||
$favicon_type = $_FILES['login_page_favicon']['type'];
|
|
||||||
if (in_array($favicon_type, $allowed_types) && preg_match('/\.(jpg|jpeg|png)$/i', $_FILES['login_page_favicon']['name'])) {
|
|
||||||
$extension = pathinfo($_FILES['login_page_favicon']['name'], PATHINFO_EXTENSION);
|
|
||||||
$favicon_path = $UPLOAD_PATH . DIRECTORY_SEPARATOR . uniqid('favicon_') . '.' . $extension;
|
|
||||||
File::resizeCropImage($_FILES['login_page_favicon']['tmp_name'], $favicon_path, 16, 16, 100);
|
|
||||||
$settings['login_page_favicon'] = basename($favicon_path); // Save dynamic file name
|
|
||||||
if (file_exists($_FILES['login_page_favicon']['tmp_name']))
|
|
||||||
unlink($_FILES['login_page_favicon']['tmp_name']);
|
|
||||||
} else {
|
|
||||||
r2(getUrl('settings/app'), 'e', 'Favicon must be a JPG, JPEG, or PNG image.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_FILES['login_page_wallpaper']['name'] != '') {
|
|
||||||
$wallpaper_type = $_FILES['login_page_wallpaper']['type'];
|
|
||||||
if (in_array($wallpaper_type, $allowed_types) && preg_match('/\.(jpg|jpeg|png)$/i', $_FILES['login_page_wallpaper']['name'])) {
|
|
||||||
$extension = pathinfo($_FILES['login_page_wallpaper']['name'], PATHINFO_EXTENSION);
|
|
||||||
$wallpaper_path = $UPLOAD_PATH . DIRECTORY_SEPARATOR . uniqid('wallpaper_') . '.' . $extension;
|
|
||||||
File::resizeCropImage($_FILES['login_page_wallpaper']['tmp_name'], $wallpaper_path, 1920, 1080, 100);
|
|
||||||
$settings['login_page_wallpaper'] = basename($wallpaper_path); // Save dynamic file name
|
|
||||||
if (file_exists($_FILES['login_page_wallpaper']['tmp_name']))
|
|
||||||
unlink($_FILES['login_page_wallpaper']['tmp_name']);
|
|
||||||
} else {
|
|
||||||
r2(getUrl('settings/app'), 'e', 'Wallpaper must be a JPG, JPEG, or PNG image.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_FILES['login_page_logo']['name'] != '') {
|
|
||||||
$logo_type = $_FILES['login_page_logo']['type'];
|
|
||||||
if (in_array($logo_type, $allowed_types) && preg_match('/\.(jpg|jpeg|png)$/i', $_FILES['login_page_logo']['name'])) {
|
|
||||||
$extension = pathinfo($_FILES['login_page_logo']['name'], PATHINFO_EXTENSION);
|
|
||||||
$logo_path = $UPLOAD_PATH . DIRECTORY_SEPARATOR . uniqid('logo_') . '.' . $extension;
|
|
||||||
File::resizeCropImage($_FILES['login_page_logo']['tmp_name'], $logo_path, 300, 60, 100);
|
|
||||||
$settings['login_page_logo'] = basename($logo_path); // Save dynamic file name
|
|
||||||
if (file_exists($_FILES['login_page_logo']['tmp_name']))
|
|
||||||
unlink($_FILES['login_page_logo']['tmp_name']);
|
|
||||||
} else {
|
|
||||||
r2(getUrl('settings/app'), 'e', 'Logo must be a JPG, JPEG, or PNG image.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_log('[' . $admin['username'] . ']: ' . Lang::T('Login Page Settings Saved Successfully'), $admin['user_type'], $admin['id']);
|
|
||||||
r2(getUrl('settings/app'), 's', Lang::T('Login Page Settings Saved Successfully'));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'localisation':
|
case 'localisation':
|
||||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||||
@ -590,7 +561,7 @@ switch ($action) {
|
|||||||
}
|
}
|
||||||
//allow see himself
|
//allow see himself
|
||||||
if ($admin['id'] == $id) {
|
if ($admin['id'] == $id) {
|
||||||
$d = ORM::for_table('tbl_users')->where('id', $id)->find_array($id)[0];
|
$d = ORM::for_table('tbl_users')->where('id', $id)->find_array()[0];
|
||||||
} else {
|
} else {
|
||||||
if (in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
if (in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||||
// Super Admin can see anyone
|
// Super Admin can see anyone
|
||||||
|
@ -4,11 +4,47 @@
|
|||||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||||
* by https://t.me/ibnux
|
* by https://t.me/ibnux
|
||||||
**/
|
**/
|
||||||
_auth();
|
|
||||||
$ui->assign('_title', Lang::T('Voucher'));
|
$ui->assign('_title', Lang::T('Voucher'));
|
||||||
$ui->assign('_system_menu', 'voucher');
|
$ui->assign('_system_menu', 'voucher');
|
||||||
|
|
||||||
$action = $routes['1'];
|
$action = $routes['1'];
|
||||||
|
if(!_auth(false)){
|
||||||
|
if($action== 'invoice'){
|
||||||
|
$id = $routes[2];
|
||||||
|
$sign = $routes[3];
|
||||||
|
if($sign != md5($id. $db_pass)) {
|
||||||
|
die("beda");
|
||||||
|
}
|
||||||
|
if (empty($id)) {
|
||||||
|
$in = ORM::for_table('tbl_transactions')->order_by_desc('id')->find_one();
|
||||||
|
} else {
|
||||||
|
$in = ORM::for_table('tbl_transactions')->where('id', $id)->find_one();
|
||||||
|
}
|
||||||
|
if ($in) {
|
||||||
|
Package::createInvoice($in);
|
||||||
|
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||||
|
$logo = '';
|
||||||
|
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) {
|
||||||
|
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png';
|
||||||
|
$imgsize = getimagesize($logo);
|
||||||
|
$width = $imgsize[0];
|
||||||
|
$height = $imgsize[1];
|
||||||
|
$ui->assign('wlogo', $width);
|
||||||
|
$ui->assign('hlogo', $height);
|
||||||
|
}
|
||||||
|
$ui->assign('public_url', getUrl("voucher/invoice/$id/".md5($id. $db_pass)));
|
||||||
|
$ui->assign('logo', $logo);
|
||||||
|
$ui->display('customer/invoice-customer.tpl');
|
||||||
|
die();
|
||||||
|
} else {
|
||||||
|
r2(getUrl('voucher/list-activated'), 'e', Lang::T('Not Found'));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
r2(getUrl('login'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$user = User::_info();
|
$user = User::_info();
|
||||||
$ui->assign('_user', $user);
|
$ui->assign('_user', $user);
|
||||||
|
|
||||||
@ -64,6 +100,18 @@ switch ($action) {
|
|||||||
}
|
}
|
||||||
if ($in) {
|
if ($in) {
|
||||||
Package::createInvoice($in);
|
Package::createInvoice($in);
|
||||||
|
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||||
|
$logo = '';
|
||||||
|
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) {
|
||||||
|
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png';
|
||||||
|
$imgsize = getimagesize($logo);
|
||||||
|
$width = $imgsize[0];
|
||||||
|
$height = $imgsize[1];
|
||||||
|
$ui->assign('wlogo', $width);
|
||||||
|
$ui->assign('hlogo', $height);
|
||||||
|
}
|
||||||
|
$ui->assign('public_url', getUrl("voucher/invoice/$id/".md5($id. $db_pass)));
|
||||||
|
$ui->assign('logo', $logo);
|
||||||
$ui->display('customer/invoice-customer.tpl');
|
$ui->display('customer/invoice-customer.tpl');
|
||||||
} else {
|
} else {
|
||||||
r2(getUrl('voucher/list-activated'), 'e', Lang::T('Not Found'));
|
r2(getUrl('voucher/list-activated'), 'e', Lang::T('Not Found'));
|
||||||
|
@ -75,13 +75,7 @@ foreach ($d as $ds) {
|
|||||||
if ($_app_stage != 'demo') {
|
if ($_app_stage != 'demo') {
|
||||||
if (file_exists($dvc)) {
|
if (file_exists($dvc)) {
|
||||||
require_once $dvc;
|
require_once $dvc;
|
||||||
try {
|
(new $p['device'])->remove_customer($c, $p);
|
||||||
(new $p['device'])->remove_customer($c, $p);
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
_log($e->getMessage());
|
|
||||||
sendTelegram($e->getMessage());
|
|
||||||
echo "Error: " . $e->getMessage() . "\n";
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new Exception("Cron error: Devices " . $p['device'] . "not found, cannot disconnect ".$c['username']."\n");
|
throw new Exception("Cron error: Devices " . $p['device'] . "not found, cannot disconnect ".$c['username']."\n");
|
||||||
}
|
}
|
||||||
@ -89,7 +83,13 @@ foreach ($d as $ds) {
|
|||||||
|
|
||||||
// Send notification and update user status
|
// Send notification and update user status
|
||||||
try {
|
try {
|
||||||
echo Message::sendPackageNotification($c, $u['namebp'], $p['price'], $textExpired, $config['user_notification_expired']) . "\n";
|
echo Message::sendPackageNotification(
|
||||||
|
$c,
|
||||||
|
$u['namebp'],
|
||||||
|
$p['price'],
|
||||||
|
Message::getMessageType($p['type'], $textExpired),
|
||||||
|
$config['user_notification_expired']
|
||||||
|
) . "\n";
|
||||||
$u->status = 'off';
|
$u->status = 'off';
|
||||||
$u->save();
|
$u->save();
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
|
@ -49,25 +49,42 @@ foreach ($d as $ds) {
|
|||||||
} else {
|
} else {
|
||||||
$price = $p['price'];
|
$price = $p['price'];
|
||||||
}
|
}
|
||||||
if ($ds['expiration'] == $day7 && $config['notification_reminder_7day'] !== 'no') {
|
if ($ds['expiration'] == $day7 && $config['notification_reminder_7days'] !== 'no') {
|
||||||
try {
|
try {
|
||||||
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_7_day'), $config['user_notification_reminder']) . "\n";
|
echo Message::sendPackageNotification(
|
||||||
|
$c,
|
||||||
|
$p['name_plan'],
|
||||||
|
$price,
|
||||||
|
Message::getMessageType($p['type'], Lang::getNotifText('reminder_7_day')),
|
||||||
|
$config['user_notification_reminder']
|
||||||
|
) . "\n";
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
sendTelegram("Cron Reminder failed to send 7-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
|
sendTelegram("Cron Reminder failed to send 7-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
} else if ($ds['expiration'] == $day3 && $config['notification_reminder_3day'] !== 'no') {
|
} else if ($ds['expiration'] == $day3 && $config['notification_reminder_3days'] !== 'no') {
|
||||||
try {
|
try {
|
||||||
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_3_day'), $config['user_notification_reminder']) . "\n";
|
echo Message::sendPackageNotification(
|
||||||
|
$c,
|
||||||
|
$p['name_plan'],
|
||||||
|
$price,
|
||||||
|
Message::getMessageType($p['type'], Lang::getNotifText('reminder_3_day')),
|
||||||
|
$config['user_notification_reminder']
|
||||||
|
) . "\n";
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
sendTelegram("Cron Reminder failed to send 3-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
|
sendTelegram("Cron Reminder failed to send 3-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
} else if ($ds['expiration'] == $day1 && $config['notification_reminder_1day'] !== 'no') {
|
} else if ($ds['expiration'] == $day1 && $config['notification_reminder_1day'] !== 'no') {
|
||||||
try {
|
try {
|
||||||
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_1_day'), $config['user_notification_reminder']) . "\n";
|
echo Message::sendPackageNotification(
|
||||||
|
$c,
|
||||||
|
$p['name_plan'],
|
||||||
|
$price,
|
||||||
|
Message::getMessageType($p['type'], Lang::getNotifText('reminder_1_day')),
|
||||||
|
$config['user_notification_reminder']
|
||||||
|
) . "\n";
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
sendTelegram("Cron Reminder failed to send 1-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
|
sendTelegram("Cron Reminder failed to send 1-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ class MikrotikPppoe
|
|||||||
|
|
||||||
function add_pool($pool){
|
function add_pool($pool){
|
||||||
global $_app_stage;
|
global $_app_stage;
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$mikrotik = $this->info($pool['routers']);
|
$mikrotik = $this->info($pool['routers']);
|
||||||
@ -259,7 +259,7 @@ class MikrotikPppoe
|
|||||||
|
|
||||||
function update_pool($old_pool, $new_pool){
|
function update_pool($old_pool, $new_pool){
|
||||||
global $_app_stage;
|
global $_app_stage;
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$mikrotik = $this->info($new_pool['routers']);
|
$mikrotik = $this->info($new_pool['routers']);
|
||||||
@ -284,7 +284,7 @@ class MikrotikPppoe
|
|||||||
|
|
||||||
function remove_pool($pool){
|
function remove_pool($pool){
|
||||||
global $_app_stage;
|
global $_app_stage;
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$mikrotik = $this->info($pool['routers']);
|
$mikrotik = $this->info($pool['routers']);
|
||||||
@ -329,7 +329,7 @@ class MikrotikPppoe
|
|||||||
function getClient($ip, $user, $pass)
|
function getClient($ip, $user, $pass)
|
||||||
{
|
{
|
||||||
global $_app_stage;
|
global $_app_stage;
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$iport = explode(":", $ip);
|
$iport = explode(":", $ip);
|
||||||
@ -339,7 +339,7 @@ class MikrotikPppoe
|
|||||||
function removePpoeUser($client, $username)
|
function removePpoeUser($client, $username)
|
||||||
{
|
{
|
||||||
global $_app_stage;
|
global $_app_stage;
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$printRequest = new RouterOS\Request('/ppp/secret/print');
|
$printRequest = new RouterOS\Request('/ppp/secret/print');
|
||||||
@ -376,7 +376,7 @@ class MikrotikPppoe
|
|||||||
function removePpoeActive($client, $username)
|
function removePpoeActive($client, $username)
|
||||||
{
|
{
|
||||||
global $_app_stage;
|
global $_app_stage;
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$onlineRequest = new RouterOS\Request('/ppp/active/print');
|
$onlineRequest = new RouterOS\Request('/ppp/active/print');
|
||||||
@ -392,7 +392,7 @@ class MikrotikPppoe
|
|||||||
function getIpHotspotUser($client, $username)
|
function getIpHotspotUser($client, $username)
|
||||||
{
|
{
|
||||||
global $_app_stage;
|
global $_app_stage;
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$printRequest = new RouterOS\Request(
|
$printRequest = new RouterOS\Request(
|
||||||
@ -405,7 +405,7 @@ class MikrotikPppoe
|
|||||||
function addIpToAddressList($client, $ip, $listName, $comment = '')
|
function addIpToAddressList($client, $ip, $listName, $comment = '')
|
||||||
{
|
{
|
||||||
global $_app_stage;
|
global $_app_stage;
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$addRequest = new RouterOS\Request('/ip/firewall/address-list/add');
|
$addRequest = new RouterOS\Request('/ip/firewall/address-list/add');
|
||||||
@ -420,7 +420,7 @@ class MikrotikPppoe
|
|||||||
function removeIpFromAddressList($client, $ip)
|
function removeIpFromAddressList($client, $ip)
|
||||||
{
|
{
|
||||||
global $_app_stage;
|
global $_app_stage;
|
||||||
if ($_app_stage == 'demo') {
|
if ($_app_stage == 'Demo') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$printRequest = new RouterOS\Request(
|
$printRequest = new RouterOS\Request(
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -384,6 +384,7 @@
|
|||||||
"Vouchers": "Voucher",
|
"Vouchers": "Voucher",
|
||||||
"Refill_Customer": "Isi Ulang Voucher",
|
"Refill_Customer": "Isi Ulang Voucher",
|
||||||
"Recharge_Customer": "Isi Ulang Paket",
|
"Recharge_Customer": "Isi Ulang Paket",
|
||||||
|
"Plan": "Paket",
|
||||||
"Plans": "Paket",
|
"Plans": "Paket",
|
||||||
"PPPOE": "PPPOE",
|
"PPPOE": "PPPOE",
|
||||||
"Bandwidth": "Bandwidth",
|
"Bandwidth": "Bandwidth",
|
||||||
@ -911,5 +912,7 @@
|
|||||||
"Mandatory_Fields": "Bidang yang wajib diisi",
|
"Mandatory_Fields": "Bidang yang wajib diisi",
|
||||||
"Single_Admin_Session": "Sesi Admin Tunggal",
|
"Single_Admin_Session": "Sesi Admin Tunggal",
|
||||||
"Mikrotik_SMS_Command": "Perintah SMS Mikrotik",
|
"Mikrotik_SMS_Command": "Perintah SMS Mikrotik",
|
||||||
"Expired_Cronjob_Every_5_Minutes__Recommended_": "Cronjob Kedaluwarsa Setiap 5 Menit [Direkomendasikan]"
|
"Expired_Cronjob_Every_5_Minutes__Recommended_": "Cronjob Kedaluwarsa Setiap 5 Menit [Direkomendasikan]",
|
||||||
|
"Visit": "Kunjungi",
|
||||||
|
"sync": "Sinkron"
|
||||||
}
|
}
|
||||||
|
@ -1,210 +1,211 @@
|
|||||||
{
|
{
|
||||||
"2023.8.9": [
|
"2023.8.9": [
|
||||||
"ALTER TABLE `tbl_customers` ADD `balance` decimal(15,2) NOT NULL DEFAULT 0.00 COMMENT 'For Money Deposit' AFTER `email`;",
|
"ALTER TABLE `tbl_customers` ADD `balance` decimal(15,2) NOT NULL DEFAULT 0.00 COMMENT 'For Money Deposit' AFTER `email`;",
|
||||||
"CREATE TABLE `tbl_customers_meta` (`id` int(11) NOT NULL, `customer_id` int(11) NOT NULL,`meta_key` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `meta_value` longtext COLLATE utf8mb4_general_ci) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
|
"CREATE TABLE `tbl_customers_meta` (`id` int(11) NOT NULL, `customer_id` int(11) NOT NULL,`meta_key` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `meta_value` longtext COLLATE utf8mb4_general_ci) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
|
||||||
"ALTER TABLE `tbl_customers_meta` ADD PRIMARY KEY (`id`);",
|
"ALTER TABLE `tbl_customers_meta` ADD PRIMARY KEY (`id`);",
|
||||||
"ALTER TABLE `tbl_customers_meta` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;"
|
"ALTER TABLE `tbl_customers_meta` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;"
|
||||||
],
|
],
|
||||||
"2023.8.14": [
|
"2023.8.14": [
|
||||||
"ALTER TABLE `tbl_customers` ADD `pppoe_password` varchar(45) NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;",
|
"ALTER TABLE `tbl_customers` ADD `pppoe_password` varchar(45) NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;",
|
||||||
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
||||||
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
||||||
"ALTER TABLE `tbl_customers` ADD `auto_renewal` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auto renewall using balance' AFTER `balance`;"
|
"ALTER TABLE `tbl_customers` ADD `auto_renewal` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auto renewall using balance' AFTER `balance`;"
|
||||||
],
|
],
|
||||||
"2023.8.23": [
|
"2023.8.23": [
|
||||||
"ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';"
|
"ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';"
|
||||||
],
|
],
|
||||||
"2023.8.28": [
|
"2023.8.28": [
|
||||||
"ALTER TABLE `tbl_user_recharges` ADD `recharged_time` time NOT NULL DEFAULT '00:00:00' AFTER `recharged_on`;",
|
"ALTER TABLE `tbl_user_recharges` ADD `recharged_time` time NOT NULL DEFAULT '00:00:00' AFTER `recharged_on`;",
|
||||||
"ALTER TABLE `tbl_transactions` ADD `recharged_time` time NOT NULL DEFAULT '00:00:00' AFTER `recharged_on`;"
|
"ALTER TABLE `tbl_transactions` ADD `recharged_time` time NOT NULL DEFAULT '00:00:00' AFTER `recharged_on`;"
|
||||||
],
|
],
|
||||||
"2023.9.5": [
|
"2023.9.5": [
|
||||||
"DROP TABLE `tbl_language`;",
|
"DROP TABLE `tbl_language`;",
|
||||||
"ALTER TABLE `tbl_plans` ADD `pool_expired` varchar(40) NOT NULL DEFAULT '' AFTER `pool`;"
|
"ALTER TABLE `tbl_plans` ADD `pool_expired` varchar(40) NOT NULL DEFAULT '' AFTER `pool`;"
|
||||||
],
|
],
|
||||||
"2023.9.27": [
|
"2023.9.27": [
|
||||||
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance','Radius') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance','Radius') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
||||||
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance','Radius') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
|
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance','Radius') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
|
||||||
],
|
],
|
||||||
"2023.9.28": [
|
"2023.9.28": [
|
||||||
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
||||||
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
|
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
|
||||||
],
|
],
|
||||||
"2023.10.1": [
|
"2023.10.1": [
|
||||||
"ALTER TABLE `tbl_plans` ADD `is_radius` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '1 is radius' AFTER `routers`; "
|
"ALTER TABLE `tbl_plans` ADD `is_radius` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '1 is radius' AFTER `routers`; "
|
||||||
],
|
],
|
||||||
"2023.10.24": [
|
"2023.10.24": [
|
||||||
"ALTER TABLE `nas` ADD `routers` VARCHAR(32) NOT NULL DEFAULT '' AFTER `description`;"
|
"ALTER TABLE `nas` ADD `routers` VARCHAR(32) NOT NULL DEFAULT '' AFTER `description`;"
|
||||||
],
|
],
|
||||||
"2023.12.15": [
|
"2023.12.15": [
|
||||||
"ALTER TABLE `tbl_customers` ADD `service_type` ENUM('Hotspot','PPPoE','Others') DEFAULT 'Others' COMMENT 'For selecting user type' AFTER `balance`;"
|
"ALTER TABLE `tbl_customers` ADD `service_type` ENUM('Hotspot','PPPoE','Others') DEFAULT 'Others' COMMENT 'For selecting user type' AFTER `balance`;"
|
||||||
],
|
],
|
||||||
"2024.1.11": [
|
"2024.1.11": [
|
||||||
"ALTER TABLE `tbl_plans` ADD `allow_purchase` ENUM('yes','no') DEFAULT 'yes' COMMENT 'allow to show package in buy package page' AFTER `enabled`;"
|
"ALTER TABLE `tbl_plans` ADD `allow_purchase` ENUM('yes','no') DEFAULT 'yes' COMMENT 'allow to show package in buy package page' AFTER `enabled`;"
|
||||||
],
|
],
|
||||||
"2024.2.7": [
|
"2024.2.7": [
|
||||||
"ALTER TABLE `tbl_voucher` ADD `generated_by` INT NOT NULL DEFAULT '0' COMMENT 'id admin' AFTER `status`;",
|
"ALTER TABLE `tbl_voucher` ADD `generated_by` INT NOT NULL DEFAULT '0' COMMENT 'id admin' AFTER `status`;",
|
||||||
"ALTER TABLE `tbl_users` ADD `root` INT NOT NULL DEFAULT '0' COMMENT 'for sub account' AFTER `id`;"
|
"ALTER TABLE `tbl_users` ADD `root` INT NOT NULL DEFAULT '0' COMMENT 'for sub account' AFTER `id`;"
|
||||||
],
|
],
|
||||||
"2024.2.12": [
|
"2024.2.12": [
|
||||||
"ALTER TABLE `tbl_users` CHANGE `user_type` `user_type` ENUM('SuperAdmin','Admin','Report','Agent','Sales') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
|
"ALTER TABLE `tbl_users` CHANGE `user_type` `user_type` ENUM('SuperAdmin','Admin','Report','Agent','Sales') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
|
||||||
],
|
],
|
||||||
"2024.2.15": [
|
"2024.2.15": [
|
||||||
"ALTER TABLE `tbl_users` CHANGE `password` `password` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
"ALTER TABLE `tbl_users` CHANGE `password` `password` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
||||||
"ALTER TABLE `tbl_users` ADD `phone` VARCHAR(32) NOT NULL DEFAULT '' AFTER `password`, ADD `email` VARCHAR(128) NOT NULL DEFAULT '' AFTER `phone`, ADD `city` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kota' AFTER `email`, ADD `subdistrict` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kecamatan' AFTER `city`, ADD `ward` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kelurahan' AFTER `subdistrict`;"
|
"ALTER TABLE `tbl_users` ADD `phone` VARCHAR(32) NOT NULL DEFAULT '' AFTER `password`, ADD `email` VARCHAR(128) NOT NULL DEFAULT '' AFTER `phone`, ADD `city` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kota' AFTER `email`, ADD `subdistrict` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kecamatan' AFTER `city`, ADD `ward` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kelurahan' AFTER `subdistrict`;"
|
||||||
],
|
],
|
||||||
"2024.2.16": [
|
"2024.2.16": [
|
||||||
"ALTER TABLE `tbl_customers` ADD `created_by` INT NOT NULL DEFAULT '0' AFTER `auto_renewal`;"
|
"ALTER TABLE `tbl_customers` ADD `created_by` INT NOT NULL DEFAULT '0' AFTER `auto_renewal`;"
|
||||||
],
|
],
|
||||||
"2024.2.19": [
|
"2024.2.19": [
|
||||||
"CREATE TABLE `tbl_customers_fields` (`id` INT PRIMARY KEY AUTO_INCREMENT, `customer_id` INT NOT NULL, `field_name` VARCHAR(255) NOT NULL, `field_value` VARCHAR(255) NOT NULL, FOREIGN KEY (customer_id) REFERENCES tbl_customers(id));"
|
"CREATE TABLE `tbl_customers_fields` (`id` INT PRIMARY KEY AUTO_INCREMENT, `customer_id` INT NOT NULL, `field_name` VARCHAR(255) NOT NULL, `field_value` VARCHAR(255) NOT NULL, FOREIGN KEY (customer_id) REFERENCES tbl_customers(id));"
|
||||||
],
|
],
|
||||||
"2024.2.20": [
|
"2024.2.20": [
|
||||||
"ALTER TABLE `tbl_plans` ADD `list_expired` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'address list' AFTER `pool_expired`;",
|
"ALTER TABLE `tbl_plans` ADD `list_expired` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'address list' AFTER `pool_expired`;",
|
||||||
"ALTER TABLE `tbl_bandwidth` ADD `burst` VARCHAR(128) NOT NULL DEFAULT '' AFTER `rate_up_unit`;"
|
"ALTER TABLE `tbl_bandwidth` ADD `burst` VARCHAR(128) NOT NULL DEFAULT '' AFTER `rate_up_unit`;"
|
||||||
],
|
],
|
||||||
"2024.2.20.1": [
|
"2024.2.20.1": ["DROP TABLE IF EXISTS `tbl_customers_meta`;"],
|
||||||
"DROP TABLE IF EXISTS `tbl_customers_meta`;"
|
"2024.2.23": [
|
||||||
],
|
"ALTER TABLE `tbl_transactions` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;",
|
||||||
"2024.2.23": [
|
"ALTER TABLE `tbl_user_recharges` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;"
|
||||||
"ALTER TABLE `tbl_transactions` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;",
|
],
|
||||||
"ALTER TABLE `tbl_user_recharges` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;"
|
"2024.3.3": [
|
||||||
],
|
"ALTER TABLE `tbl_plans` CHANGE `validity_unit` `validity_unit` ENUM('Mins','Hrs','Days','Months','Period') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
|
||||||
"2024.3.3": [
|
],
|
||||||
"ALTER TABLE `tbl_plans` CHANGE `validity_unit` `validity_unit` ENUM('Mins','Hrs','Days','Months','Period') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
|
"2024.3.12": [
|
||||||
],
|
"ALTER TABLE `tbl_plans` CHANGE `allow_purchase` `prepaid` ENUM('yes','no') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'yes' COMMENT 'is prepaid';"
|
||||||
"2024.3.12": [
|
],
|
||||||
"ALTER TABLE `tbl_plans` CHANGE `allow_purchase` `prepaid` ENUM('yes','no') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'yes' COMMENT 'is prepaid';"
|
"2024.3.14": [
|
||||||
],
|
"ALTER TABLE `tbl_transactions` ADD `note` VARCHAR(256) NOT NULL DEFAULT '' COMMENT 'for note' AFTER `type`;"
|
||||||
"2024.3.14": [
|
],
|
||||||
"ALTER TABLE `tbl_transactions` ADD `note` VARCHAR(256) NOT NULL DEFAULT '' COMMENT 'for note' AFTER `type`;"
|
"2024.3.19": [
|
||||||
],
|
"ALTER TABLE `tbl_customers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates' AFTER `email`;"
|
||||||
"2024.3.19": [
|
],
|
||||||
"ALTER TABLE `tbl_customers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates' AFTER `email`;"
|
"2024.3.19.1": [
|
||||||
],
|
"ALTER TABLE `tbl_customers` ADD `account_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' AFTER `coordinates`;"
|
||||||
"2024.3.19.1": [
|
],
|
||||||
"ALTER TABLE `tbl_customers` ADD `account_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' AFTER `coordinates`;"
|
"2024.3.19.2": [
|
||||||
],
|
"ALTER TABLE `tbl_plans` ADD `plan_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' ;"
|
||||||
"2024.3.19.2": [
|
],
|
||||||
"ALTER TABLE `tbl_plans` ADD `plan_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' ;"
|
"2023.3.20": [
|
||||||
],
|
"ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';"
|
||||||
"2023.3.20": [
|
],
|
||||||
"ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';"
|
"2024.4.5": [
|
||||||
],
|
"ALTER TABLE `tbl_payment_gateway` ADD `trx_invoice` VARCHAR(25) NOT NULL DEFAULT '' COMMENT 'from tbl_transactions' AFTER `paid_date`;"
|
||||||
"2024.4.5": [
|
],
|
||||||
"ALTER TABLE `tbl_payment_gateway` ADD `trx_invoice` VARCHAR(25) NOT NULL DEFAULT '' COMMENT 'from tbl_transactions' AFTER `paid_date`;"
|
"2024.5.17": [
|
||||||
],
|
"ALTER TABLE `tbl_customers` ADD `status` ENUM('Active','Banned','Disabled') NOT NULL DEFAULT 'Active' AFTER `auto_renewal`;"
|
||||||
"2024.5.17": [
|
],
|
||||||
"ALTER TABLE `tbl_customers` ADD `status` ENUM('Active','Banned','Disabled') NOT NULL DEFAULT 'Active' AFTER `auto_renewal`;"
|
"2024.5.18": [
|
||||||
],
|
"ALTER TABLE `tbl_customers` CHANGE `status` `status` ENUM('Active','Banned','Disabled','Inactive','Limited','Suspended') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'Active';"
|
||||||
"2024.5.18": [
|
],
|
||||||
"ALTER TABLE `tbl_customers` CHANGE `status` `status` ENUM('Active','Banned','Disabled','Inactive','Limited','Suspended') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'Active';"
|
"2024.5.20": [
|
||||||
],
|
"ALTER TABLE `tbl_customers` ADD `city` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `address`, ADD `district` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `city`, ADD `state` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `district`, ADD `zip` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `state`;"
|
||||||
"2024.5.20": [
|
],
|
||||||
"ALTER TABLE `tbl_customers` ADD `city` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `address`, ADD `district` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `city`, ADD `state` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `district`, ADD `zip` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `state`;"
|
"2024.6.5": [
|
||||||
],
|
"ALTER TABLE `tbl_plans` ADD `price_old` VARCHAR(40) NOT NULL DEFAULT '' AFTER `price`;",
|
||||||
"2024.6.5": [
|
"ALTER TABLE `tbl_plans` ADD `device` VARCHAR(32) NOT NULL DEFAULT '' AFTER `plan_type`;"
|
||||||
"ALTER TABLE `tbl_plans` ADD `price_old` VARCHAR(40) NOT NULL DEFAULT '' AFTER `price`;",
|
],
|
||||||
"ALTER TABLE `tbl_plans` ADD `device` VARCHAR(32) NOT NULL DEFAULT '' AFTER `plan_type`;"
|
"2024.6.10": [
|
||||||
],
|
"ALTER TABLE `tbl_pool` ADD `local_ip` VARCHAR(40) NOT NULL DEFAULT '' AFTER `pool_name`;"
|
||||||
"2024.6.10": [
|
],
|
||||||
"ALTER TABLE `tbl_pool` ADD `local_ip` VARCHAR(40) NOT NULL DEFAULT '' AFTER `pool_name`;"
|
"2024.6.11": [
|
||||||
],
|
"ALTER TABLE `tbl_plans` ADD `plan_expired` INT NOT NULL DEFAULT '0' AFTER `pool`;",
|
||||||
"2024.6.11": [
|
"ALTER TABLE `tbl_plans` DROP `pool_expired`, DROP `list_expired`;"
|
||||||
"ALTER TABLE `tbl_plans` ADD `plan_expired` INT NOT NULL DEFAULT '0' AFTER `pool`;",
|
],
|
||||||
"ALTER TABLE `tbl_plans` DROP `pool_expired`, DROP `list_expired`;"
|
"2024.6.19": [
|
||||||
],
|
"ALTER TABLE `tbl_plans` ADD `expired_date` TINYINT(1) NOT NULL DEFAULT '20' AFTER `plan_expired`;"
|
||||||
"2024.6.19": [
|
],
|
||||||
"ALTER TABLE `tbl_plans` ADD `expired_date` TINYINT(1) NOT NULL DEFAULT '20' AFTER `plan_expired`;"
|
"2024.6.21": [
|
||||||
],
|
"ALTER TABLE `tbl_plans` ADD `on_login` TEXT NULL DEFAULT NULL AFTER `device`;",
|
||||||
"2024.6.21": [
|
"ALTER TABLE `tbl_plans` ADD `on_logout` TEXT NULL DEFAULT NULL AFTER `on_login`;"
|
||||||
"ALTER TABLE `tbl_plans` ADD `on_login` TEXT NULL DEFAULT NULL AFTER `device`;",
|
],
|
||||||
"ALTER TABLE `tbl_plans` ADD `on_logout` TEXT NULL DEFAULT NULL AFTER `on_login`;"
|
"2024.7.6": [
|
||||||
],
|
"CREATE TABLE IF NOT EXISTS `rad_acct` ( `id` bigint NOT NULL, `acctsessionid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `realm` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasid` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
|
||||||
"2024.7.6": [
|
"ALTER TABLE `rad_acct` ADD PRIMARY KEY (`id`), ADD KEY `username` (`username`), ADD KEY `framedipaddress` (`framedipaddress`), ADD KEY `acctsessionid` (`acctsessionid`), ADD KEY `nasipaddress` (`nasipaddress`);",
|
||||||
"CREATE TABLE IF NOT EXISTS `rad_acct` ( `id` bigint NOT NULL, `acctsessionid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `realm` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasid` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
|
"ALTER TABLE `rad_acct` MODIFY `id` bigint NOT NULL AUTO_INCREMENT;"
|
||||||
"ALTER TABLE `rad_acct` ADD PRIMARY KEY (`id`), ADD KEY `username` (`username`), ADD KEY `framedipaddress` (`framedipaddress`), ADD KEY `acctsessionid` (`acctsessionid`), ADD KEY `nasipaddress` (`nasipaddress`);",
|
],
|
||||||
"ALTER TABLE `rad_acct` MODIFY `id` bigint NOT NULL AUTO_INCREMENT;"
|
"2024.7.24": [
|
||||||
],
|
"ALTER TABLE `tbl_voucher` ADD `used_date` DATETIME NULL DEFAULT NULL AFTER `status`;",
|
||||||
"2024.7.24": [
|
"UPDATE `tbl_voucher` SET `used_date`=now() WHERE `status`=1;"
|
||||||
"ALTER TABLE `tbl_voucher` ADD `used_date` DATETIME NULL DEFAULT NULL AFTER `status`;",
|
],
|
||||||
"UPDATE `tbl_voucher` SET `used_date`=now() WHERE `status`=1;"
|
"2024.8.1": [
|
||||||
],
|
"ALTER TABLE `tbl_payment_gateway` CHANGE `gateway_trx_id` `gateway_trx_id` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';",
|
||||||
"2024.8.1": [
|
"ALTER TABLE `tbl_payment_gateway` CHANGE `pg_url_payment` `pg_url_payment` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';"
|
||||||
"ALTER TABLE `tbl_payment_gateway` CHANGE `gateway_trx_id` `gateway_trx_id` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';",
|
],
|
||||||
"ALTER TABLE `tbl_payment_gateway` CHANGE `pg_url_payment` `pg_url_payment` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';"
|
"2024.8.2": [
|
||||||
],
|
"CREATE TABLE IF NOT EXISTS `tbl_customers_inbox` (`id` int UNSIGNED NOT NULL AUTO_INCREMENT, `customer_id` int NOT NULL, `date_created` datetime NOT NULL, `date_read` datetime DEFAULT NULL, `subject` varchar(64) COLLATE utf8mb4_general_ci NOT NULL, `body` TEXT NULL DEFAULT NULL, `from` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'System' COMMENT 'System or Admin or Else',`admin_id` int NOT NULL DEFAULT '0' COMMENT 'other than admin is 0', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
||||||
"2024.8.2": [
|
],
|
||||||
"CREATE TABLE IF NOT EXISTS `tbl_customers_inbox` (`id` int UNSIGNED NOT NULL AUTO_INCREMENT, `customer_id` int NOT NULL, `date_created` datetime NOT NULL, `date_read` datetime DEFAULT NULL, `subject` varchar(64) COLLATE utf8mb4_general_ci NOT NULL, `body` TEXT NULL DEFAULT NULL, `from` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'System' COMMENT 'System or Admin or Else',`admin_id` int NOT NULL DEFAULT '0' COMMENT 'other than admin is 0', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
"2024.8.5": [
|
||||||
],
|
"ALTER TABLE `tbl_customers` ADD `pppoe_username` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;",
|
||||||
"2024.8.5": [
|
"ALTER TABLE `tbl_customers` ADD `pppoe_ip` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `pppoe_password`;"
|
||||||
"ALTER TABLE `tbl_customers` ADD `pppoe_username` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;",
|
],
|
||||||
"ALTER TABLE `tbl_customers` ADD `pppoe_ip` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `pppoe_password`;"
|
"2024.8.5.1": [
|
||||||
],
|
"ALTER TABLE `tbl_routers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' AFTER `description`;",
|
||||||
"2024.8.5.1": [
|
"ALTER TABLE `tbl_routers` ADD `coverage` VARCHAR(8) NOT NULL DEFAULT '0' AFTER `coordinates`;"
|
||||||
"ALTER TABLE `tbl_routers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' AFTER `description`;",
|
],
|
||||||
"ALTER TABLE `tbl_routers` ADD `coverage` VARCHAR(8) NOT NULL DEFAULT '0' AFTER `coordinates`;"
|
"2024.8.6": [
|
||||||
],
|
"ALTER TABLE `rad_acct` ADD `acctinputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `framedipaddress`;",
|
||||||
"2024.8.6": [
|
"ALTER TABLE `rad_acct` ADD `acctoutputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `acctinputoctets`;"
|
||||||
"ALTER TABLE `rad_acct` ADD `acctinputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `framedipaddress`;",
|
],
|
||||||
"ALTER TABLE `rad_acct` ADD `acctoutputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `acctinputoctets`;"
|
"2024.8.7": [
|
||||||
],
|
"ALTER TABLE `tbl_customers` CHANGE `coordinates` `coordinates` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates';"
|
||||||
"2024.8.7": [
|
],
|
||||||
"ALTER TABLE `tbl_customers` CHANGE `coordinates` `coordinates` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates';"
|
"2024.8.28": [
|
||||||
],
|
"ALTER TABLE `tbl_routers` ADD `status` ENUM('Online', 'Offline') DEFAULT 'Online' AFTER `coordinates`;",
|
||||||
"2024.8.28": [
|
"ALTER TABLE `tbl_routers` ADD `last_seen` DATETIME AFTER `status`;"
|
||||||
"ALTER TABLE `tbl_routers` ADD `status` ENUM('Online', 'Offline') DEFAULT 'Online' AFTER `coordinates`;",
|
],
|
||||||
"ALTER TABLE `tbl_routers` ADD `last_seen` DATETIME AFTER `status`;"
|
"2024.9.13": [
|
||||||
],
|
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
||||||
"2024.9.13": [
|
"ALTER TABLE `tbl_customers` CHANGE `service_type` `service_type` ENUM('Hotspot','PPPoE','VPN','Others') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'Others' COMMENT 'For selecting user type';",
|
||||||
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
||||||
"ALTER TABLE `tbl_customers` CHANGE `service_type` `service_type` ENUM('Hotspot','PPPoE','VPN','Others') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'Others' COMMENT 'For selecting user type';",
|
"CREATE TABLE IF NOT EXISTS `tbl_port_pool` ( `id` int(10) NOT NULL AUTO_INCREMENT , `public_ip` varchar(40) NOT NULL, `port_name` varchar(40) NOT NULL, `range_port` varchar(40) NOT NULL, `routers` varchar(40) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
||||||
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
|
],
|
||||||
"CREATE TABLE IF NOT EXISTS `tbl_port_pool` ( `id` int(10) NOT NULL AUTO_INCREMENT , `public_ip` varchar(40) NOT NULL, `port_name` varchar(40) NOT NULL, `range_port` varchar(40) NOT NULL, `routers` varchar(40) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
"2024.10.10": [
|
||||||
],
|
"ALTER TABLE `tbl_users` ADD `login_token` VARCHAR(40) AFTER `last_login`;"
|
||||||
"2024.10.10": [
|
],
|
||||||
"ALTER TABLE `tbl_users` ADD `login_token` VARCHAR(40) AFTER `last_login`;"
|
"2024.10.17": [
|
||||||
],
|
"CREATE TABLE IF NOT EXISTS `tbl_meta` ( `id` int UNSIGNED NOT NULL AUTO_INCREMENT, `tbl` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Table name', `tbl_id` int NOT NULL COMMENT 'table value id', `name` varchar(32) COLLATE utf8mb4_general_ci NOT NULL, `value` mediumtext COLLATE utf8mb4_general_ci, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='This Table to add additional data for any table';"
|
||||||
"2024.10.17": [
|
],
|
||||||
"CREATE TABLE IF NOT EXISTS `tbl_meta` ( `id` int UNSIGNED NOT NULL AUTO_INCREMENT, `tbl` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Table name', `tbl_id` int NOT NULL COMMENT 'table value id', `name` varchar(32) COLLATE utf8mb4_general_ci NOT NULL, `value` mediumtext COLLATE utf8mb4_general_ci, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='This Table to add additional data for any table';"
|
"2024.10.30": [
|
||||||
],
|
"ALTER TABLE `tbl_users` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/admin.default.png' AFTER `root`;",
|
||||||
"2024.10.30": [
|
"ALTER TABLE `tbl_users` ADD `data` TEXT NULL DEFAULT NULL COMMENT 'to put additional data' AFTER `status`;"
|
||||||
"ALTER TABLE `tbl_users` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/admin.default.png' AFTER `root`;",
|
],
|
||||||
"ALTER TABLE `tbl_users` ADD `data` TEXT NULL DEFAULT NULL COMMENT 'to put additional data' AFTER `status`;"
|
"2024.10.31": [
|
||||||
],
|
"ALTER TABLE `tbl_customers` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/user.default.jpg' AFTER `password`;"
|
||||||
"2024.10.31": [
|
],
|
||||||
"ALTER TABLE `tbl_customers` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/user.default.jpg' AFTER `password`;"
|
"2024.12.5.1": [
|
||||||
],
|
"ALTER TABLE `tbl_transactions` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;",
|
||||||
"2024.12.5.1": [
|
"ALTER TABLE `tbl_payment_gateway` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;"
|
||||||
"ALTER TABLE `tbl_transactions` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;",
|
],
|
||||||
"ALTER TABLE `tbl_payment_gateway` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;"
|
"2024.12.16": [
|
||||||
],
|
"CREATE TABLE `tbl_coupons` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `code` VARCHAR(50) NOT NULL UNIQUE, `type` ENUM('fixed', 'percent') NOT NULL, `value` DECIMAL(10,2) NOT NULL, `description` TEXT NOT NULL, `max_usage` INT NOT NULL DEFAULT 1,`usage_count` INT NOT NULL DEFAULT 0,`status` ENUM('active', 'inactive') NOT NULL, `min_order_amount` DECIMAL(10,2) NOT NULL, `max_discount_amount` DECIMAL(10,2) NOT NULL, `start_date` DATE NOT NULL,`end_date` DATE NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);"
|
||||||
"2024.12.16": [
|
],
|
||||||
"CREATE TABLE `tbl_coupons` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `code` VARCHAR(50) NOT NULL UNIQUE, `type` ENUM('fixed', 'percent') NOT NULL, `value` DECIMAL(10,2) NOT NULL, `description` TEXT NOT NULL, `max_usage` INT NOT NULL DEFAULT 1,`usage_count` INT NOT NULL DEFAULT 0,`status` ENUM('active', 'inactive') NOT NULL, `min_order_amount` DECIMAL(10,2) NOT NULL, `max_discount_amount` DECIMAL(10,2) NOT NULL, `start_date` DATE NOT NULL,`end_date` DATE NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);"
|
"2024.12.20": [
|
||||||
],
|
"ALTER TABLE `tbl_voucher` ADD `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;"
|
||||||
"2024.12.20": [
|
],
|
||||||
"ALTER TABLE `tbl_voucher` ADD `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;"
|
"2025.1.23": [
|
||||||
],
|
"ALTER TABLE `rad_acct` ADD `acctsessiontime` BIGINT(12) NOT NULL DEFAULT '0' AFTER `framedipaddress`;"
|
||||||
"2025.1.23": [
|
],
|
||||||
"ALTER TABLE `rad_acct` ADD `acctsessiontime` BIGINT(12) NOT NULL DEFAULT '0' AFTER `framedipaddress`;"
|
"2025.2.14": [
|
||||||
],
|
"CREATE TABLE IF NOT EXISTS `tbl_widgets` ( `id` int NOT NULL AUTO_INCREMENT, `orders` int NOT NULL DEFAULT '99', `position` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1. top 2. left 3. right 4. bottom',`enabled` tinyint(1) NOT NULL DEFAULT '1', `title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `widget` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `content` text COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
||||||
"2025.2.14": [
|
],
|
||||||
"CREATE TABLE IF NOT EXISTS `tbl_widgets` ( `id` int NOT NULL AUTO_INCREMENT, `orders` int NOT NULL DEFAULT '99', `position` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1. top 2. left 3. right 4. bottom',`enabled` tinyint(1) NOT NULL DEFAULT '1', `title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `widget` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `content` text COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
"2025.2.17": [
|
||||||
],
|
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `enabled`, `title`, `widget`, `content`) VALUES (1, 1, 1, 1, 'Top Widget', 'top_widget', ''),(2, 2, 1, 1, 'Default Info', 'default_info_row', ''),(3, 1, 2, 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''),(4, 2, 2, 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''),(5, 3, 2, 1, 'Voucher Stocks', 'voucher_stocks', ''),(6, 4, 2, 1, 'Customer Expired', 'customer_expired', ''),(7, 1, 3, 1, 'Cron Monitor', 'cron_monitor', ''),(8, 2, 3, 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''),(9, 3, 3, 1, 'Info Payment Gateway', 'info_payment_gateway', ''),(10, 4, 3, 1, 'Graph Customers Insight', 'graph_customers_insight', ''),(11, 5, 3, 1, 'Activity Log', 'activity_log', '');"
|
||||||
"2025.2.17" : [
|
],
|
||||||
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `enabled`, `title`, `widget`, `content`) VALUES (1, 1, 1, 1, 'Top Widget', 'top_widget', ''),(2, 2, 1, 1, 'Default Info', 'default_info_row', ''),(3, 1, 2, 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''),(4, 2, 2, 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''),(5, 3, 2, 1, 'Voucher Stocks', 'voucher_stocks', ''),(6, 4, 2, 1, 'Customer Expired', 'customer_expired', ''),(7, 1, 3, 1, 'Cron Monitor', 'cron_monitor', ''),(8, 2, 3, 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''),(9, 3, 3, 1, 'Info Payment Gateway', 'info_payment_gateway', ''),(10, 4, 3, 1, 'Graph Customers Insight', 'graph_customers_insight', ''),(11, 5, 3, 1, 'Activity Log', 'activity_log', '');"
|
"2025.2.19": [
|
||||||
],
|
"ALTER TABLE `tbl_widgets` ADD `user` ENUM('Admin','Agent','Sales','Customer') NOT NULL DEFAULT 'Admin' AFTER `position`;"
|
||||||
"2025.2.19" : [
|
],
|
||||||
"ALTER TABLE `tbl_widgets` ADD `user` ENUM('Admin','Agent','Sales','Customer') NOT NULL DEFAULT 'Admin' AFTER `position`;"
|
"2025.2.21": [
|
||||||
],
|
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (60, 1, 2, 'Customer', 1, 'Account Info', 'account_info', ''),(61, 3, 1, 'Customer', 1, 'Active Internet Plan', 'active_internet_plan', ''),(62, 4, 1, 'Customer', 1, 'Balance Transfer', 'balance_transfer', ''),(63, 1, 1, 'Customer', 1, 'Unpaid Order', 'unpaid_order', ''),(64, 2, 1, 'Customer', 1, 'Announcement', 'announcement', ''),(65, 5, 1, 'Customer', 1, 'Recharge A Friend', 'recharge_a_friend', ''),(66, 2, 2, 'Customer', 1, 'Voucher Activation', 'voucher_activation', '');"
|
||||||
"2025.2.21" : [
|
],
|
||||||
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (60, 1, 2, 'Customer', 1, 'Account Info', 'account_info', ''),(61, 3, 1, 'Customer', 1, 'Active Internet Plan', 'active_internet_plan', ''),(62, 4, 1, 'Customer', 1, 'Balance Transfer', 'balance_transfer', ''),(63, 1, 1, 'Customer', 1, 'Unpaid Order', 'unpaid_order', ''),(64, 2, 1, 'Customer', 1, 'Announcement', 'announcement', ''),(65, 5, 1, 'Customer', 1, 'Recharge A Friend', 'recharge_a_friend', ''),(66, 2, 2, 'Customer', 1, 'Voucher Activation', 'voucher_activation', '');"
|
"2025.2.25": [
|
||||||
],
|
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (30, 1, 1, 'Agent', 1, 'Top Widget', 'top_widget', ''), (31, 2, 1, 'Agent', 1, 'Default Info', 'default_info_row', ''), (32, 1, 2, 'Agent', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''), (33, 2, 2, 'Agent', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''), (34, 3, 2, 'Agent', 1, 'Voucher Stocks', 'voucher_stocks', ''), (35, 4, 2, 'Agent', 1, 'Customer Expired', 'customer_expired', ''), (36, 1, 3, 'Agent', 1, 'Cron Monitor', 'cron_monitor', ''), (37, 2, 3, 'Agent', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''), (38, 3, 3, 'Agent', 1, 'Info Payment Gateway', 'info_payment_gateway', ''), (39, 4, 3, 'Agent', 1, 'Graph Customers Insight', 'graph_customers_insight', ''),(40, 5, 3, 'Agent', 1, 'Activity Log', 'activity_log', '');",
|
||||||
"2025.2.25" : [
|
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (41, 1, 1, 'Sales', 1, 'Top Widget', 'top_widget', ''), (42, 2, 1, 'Sales', 1, 'Default Info', 'default_info_row', ''), (43, 1, 2, 'Sales', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''), (44, 2, 2, 'Sales', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''), (45, 3, 2, 'Sales', 1, 'Voucher Stocks', 'voucher_stocks', ''), (46, 4, 2, 'Sales', 1, 'Customer Expired', 'customer_expired', ''), (47, 1, 3, 'Sales', 1, 'Cron Monitor', 'cron_monitor', ''), (48, 2, 3, 'Sales', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''), (49, 3, 3, 'Sales', 1, 'Info Payment Gateway', 'info_payment_gateway', ''), (50, 4, 3, 'Sales', 1, 'Graph Customers Insight', 'graph_customers_insight', ''), (51, 5, 3, 'Sales', 1, 'Activity Log', 'activity_log', '');"
|
||||||
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (30, 1, 1, 'Agent', 1, 'Top Widget', 'top_widget', ''), (31, 2, 1, 'Agent', 1, 'Default Info', 'default_info_row', ''), (32, 1, 2, 'Agent', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''), (33, 2, 2, 'Agent', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''), (34, 3, 2, 'Agent', 1, 'Voucher Stocks', 'voucher_stocks', ''), (35, 4, 2, 'Agent', 1, 'Customer Expired', 'customer_expired', ''), (36, 1, 3, 'Agent', 1, 'Cron Monitor', 'cron_monitor', ''), (37, 2, 3, 'Agent', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''), (38, 3, 3, 'Agent', 1, 'Info Payment Gateway', 'info_payment_gateway', ''), (39, 4, 3, 'Agent', 1, 'Graph Customers Insight', 'graph_customers_insight', ''),(40, 5, 3, 'Agent', 1, 'Activity Log', 'activity_log', '');",
|
],
|
||||||
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (41, 1, 1, 'Sales', 1, 'Top Widget', 'top_widget', ''), (42, 2, 1, 'Sales', 1, 'Default Info', 'default_info_row', ''), (43, 1, 2, 'Sales', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''), (44, 2, 2, 'Sales', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''), (45, 3, 2, 'Sales', 1, 'Voucher Stocks', 'voucher_stocks', ''), (46, 4, 2, 'Sales', 1, 'Customer Expired', 'customer_expired', ''), (47, 1, 3, 'Sales', 1, 'Cron Monitor', 'cron_monitor', ''), (48, 2, 3, 'Sales', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''), (49, 3, 3, 'Sales', 1, 'Info Payment Gateway', 'info_payment_gateway', ''), (50, 4, 3, 'Sales', 1, 'Graph Customers Insight', 'graph_customers_insight', ''), (51, 5, 3, 'Sales', 1, 'Activity Log', 'activity_log', '');"
|
"2025.3.5": [
|
||||||
],
|
"CREATE TABLE IF NOT EXISTS `tbl_message_logs` ( `id` SERIAL PRIMARY KEY, `message_type` VARCHAR(50), `recipient` VARCHAR(255), `message_content` TEXT, `status` VARCHAR(50), `error_message` TEXT, `sent_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
||||||
"2025.3.5" : [
|
],
|
||||||
"CREATE TABLE IF NOT EXISTS `tbl_message_logs` ( `id` SERIAL PRIMARY KEY, `message_type` VARCHAR(50), `recipient` VARCHAR(255), `message_content` TEXT, `status` VARCHAR(50), `error_message` TEXT, `sent_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
|
"2025.3.10": [
|
||||||
]
|
"CREATE TABLE IF NOT EXISTS `tbl_invoices` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `number` VARCHAR(50) NOT NULL, `customer_id` INT NOT NULL, `fullname` VARCHAR(100) NOT NULL, `email` VARCHAR(100) NOT NULL, `address` TEXT, `status` ENUM('Unpaid', 'Paid', 'Cancelled') NOT NULL DEFAULT 'Unpaid', `due_date` DATETIME NOT NULL, `filename` VARCHAR(255), `amount` DECIMAL(10, 2) NOT NULL, `data` JSON NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP);"
|
||||||
}
|
]
|
||||||
|
}
|
||||||
|
0
system/uploads/invoices/index.html
Normal file
0
system/uploads/invoices/index.html
Normal file
File diff suppressed because one or more lines are too long
BIN
system/uploads/paid.png
Normal file
BIN
system/uploads/paid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
system/vendor/mpdf/mpdf/ttfonts/DejaVuSansCondensed-Oblique.ttf
vendored
Normal file
BIN
system/vendor/mpdf/mpdf/ttfonts/DejaVuSansCondensed-Oblique.ttf
vendored
Normal file
Binary file not shown.
@ -7,10 +7,25 @@ class customer_expired
|
|||||||
|
|
||||||
public function getWidget()
|
public function getWidget()
|
||||||
{
|
{
|
||||||
global $ui, $current_date;
|
global $ui, $current_date, $config;
|
||||||
|
|
||||||
//user expire
|
//user expire
|
||||||
$query = ORM::for_table('tbl_user_recharges')
|
$query = ORM::for_table('tbl_user_recharges')
|
||||||
|
->table_alias('tur')
|
||||||
|
->selects([
|
||||||
|
'c.id',
|
||||||
|
'tur.username',
|
||||||
|
'c.fullname',
|
||||||
|
'c.phonenumber',
|
||||||
|
'c.email',
|
||||||
|
'tur.expiration',
|
||||||
|
'tur.time',
|
||||||
|
'tur.recharged_on',
|
||||||
|
'tur.recharged_time',
|
||||||
|
'tur.namebp',
|
||||||
|
'tur.routers'
|
||||||
|
])
|
||||||
|
->innerJoin('tbl_customers', ['tur.customer_id', '=', 'c.id'], 'c')
|
||||||
->where_lte('expiration', $current_date)
|
->where_lte('expiration', $current_date)
|
||||||
->order_by_desc('expiration');
|
->order_by_desc('expiration');
|
||||||
$expire = Paginator::findMany($query);
|
$expire = Paginator::findMany($query);
|
||||||
@ -23,8 +38,26 @@ class customer_expired
|
|||||||
// Pass the total count and current page to the paginator
|
// Pass the total count and current page to the paginator
|
||||||
$paginator['total_count'] = $totalCount;
|
$paginator['total_count'] = $totalCount;
|
||||||
|
|
||||||
|
if(!empty($_COOKIE['expdef']) && $_COOKIE['expdef'] != $config['customer_expired_expdef']) {
|
||||||
|
$d = ORM::for_table('tbl_appconfig')->where('setting', 'customer_expired_expdef')->find_one();
|
||||||
|
if ($d) {
|
||||||
|
$d->value = $_COOKIE['expdef'];
|
||||||
|
$d->save();
|
||||||
|
} else {
|
||||||
|
$d = ORM::for_table('tbl_appconfig')->create();
|
||||||
|
$d->setting = 'customer_expired_expdef';
|
||||||
|
$d->value = $_COOKIE['expdef'];
|
||||||
|
$d->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!empty($config['customer_expired_expdef']) && empty($_COOKIE['expdef'])){
|
||||||
|
$_COOKIE['expdef'] = $config['customer_expired_expdef'];
|
||||||
|
setcookie('expdef', $config['customer_expired_expdef'], time() + (86400 * 30), "/");
|
||||||
|
}
|
||||||
|
|
||||||
// Assign the pagination HTML to the template variable
|
// Assign the pagination HTML to the template variable
|
||||||
$ui->assign('expire', $expire);
|
$ui->assign('expire', $expire);
|
||||||
|
$ui->assign('cookie', $_COOKIE);
|
||||||
return $ui->fetch('widget/customer_expired.tpl');
|
return $ui->fetch('widget/customer_expired.tpl');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,11 @@ class graph_monthly_registered_customers
|
|||||||
{
|
{
|
||||||
global $CACHE_PATH,$ui;
|
global $CACHE_PATH,$ui;
|
||||||
|
|
||||||
$cacheMRfile = File::pathFixer('/monthlyRegistered.temp');
|
$cacheMRfile = $CACHE_PATH . File::pathFixer('/monthlyRegistered.temp');
|
||||||
|
//Compatibility for old path
|
||||||
|
if (file_exists($oldCacheMRfile = str_replace($CACHE_PATH, '', $cacheMRfile))) {
|
||||||
|
rename($oldCacheMRfile, $cacheMRfile);
|
||||||
|
}
|
||||||
//Cache for 1 hour
|
//Cache for 1 hour
|
||||||
if (file_exists($cacheMRfile) && time() - filemtime($cacheMRfile) < 3600) {
|
if (file_exists($cacheMRfile) && time() - filemtime($cacheMRfile) < 3600) {
|
||||||
$monthlyRegistered = json_decode(file_get_contents($cacheMRfile), true);
|
$monthlyRegistered = json_decode(file_get_contents($cacheMRfile), true);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<div class="box box-hovered mb20 box-primary">
|
<div class="box box-hovered mb20 box-primary">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<h3 class="box-title">{Lang::T('Contributors')}</h3>
|
<h3 class="box-title">{Lang::T('Contribution')} PHPNuxBill</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
<a href="https://github.com/hotspotbilling/phpnuxbill/graphs/contributors" target="_blank">
|
<a href="https://github.com/hotspotbilling/phpnuxbill/graphs/contributors" target="_blank">
|
||||||
@ -22,9 +22,9 @@
|
|||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="box box-hovered mb20 box-primary">
|
<div class="box box-hovered mb20 box-primary">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<h3 class="box-title">{Lang::T('Discussions')}</h3>
|
<h3 class="box-title">{Lang::T('Discussion – Get Help from the Community')}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">{Lang::T('Get help from community')}</div>
|
<div class="box-body">{Lang::T('Join the discussion to find solutions and support from a community ready to help.')}</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<div class="btn-group btn-group-justified" role="group" aria-label="...">
|
<div class="btn-group btn-group-justified" role="group" aria-label="...">
|
||||||
<a href="https://github.com/hotspotbilling/phpnuxbill/discussions" target="_blank"
|
<a href="https://github.com/hotspotbilling/phpnuxbill/discussions" target="_blank"
|
||||||
@ -35,18 +35,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box box-hovered mb20 box-primary">
|
|
||||||
<div class="box-header">
|
|
||||||
<h3 class="box-title">{Lang::T('')}Feedback</h3>
|
|
||||||
</div>
|
|
||||||
<div class="box-body">
|
|
||||||
{Lang::T('Feedback and Bug Report')}
|
|
||||||
</div>
|
|
||||||
<div class="box-footer">
|
|
||||||
<a href="https://github.com/hotspotbilling/phpnuxbill/issues" target="_blank"
|
|
||||||
class="btn btn-primary btn-sm btn-block"><i class="ion ion-chatboxes"></i> {Lang::T('Give Feedback')}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -54,10 +42,9 @@
|
|||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="box box-hovered mb20 box-primary">
|
<div class="box box-hovered mb20 box-primary">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<h3 class="box-title">Donasi</h3>
|
<h3 class="box-title">{Lang::T('Donations')} 🇮🇩</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">Untuk pengembangan lebih baik, donasi ke iBNuX, donasi akan membantu terus
|
<div class="box-body">{Lang::T('To support further development, please donate to iBNuX. Your donation will help ensure the continued development of this application.')}</div>
|
||||||
pengembangan aplikasi</div>
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -89,10 +76,10 @@
|
|||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="box box-hovered mb20 box-primary">
|
<div class="box box-hovered mb20 box-primary">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<h3 class="box-title">Donations</h3>
|
<h3 class="box-title">{Lang::T('Donations other than')} 🇮🇩</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
Donations will help to continue phpnuxbill development
|
{Lang::T('Your donation will help support and continue the development of PHPNuxBill.')}
|
||||||
</div>
|
</div>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
@ -129,19 +116,21 @@
|
|||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="box box-hovered mb20 box-primary">
|
<div class="box box-hovered mb20 box-primary">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<h3 class="box-title">{Lang::T('Chat with me')}</h3>
|
<h3 class="box-title">{Lang::T('Chat with Me — Paid Support $50')}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
{Lang::T('Confirm your donation to continue this paid support. Or, ask about alternative donations available to suit your needs.')}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">{Lang::T('$50 Paid Support')}<br>{Lang::T('donation confirmation?')}<br>{Lang::T('Or ask any Donation Alternative')}</div>
|
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<a href="https://t.me/ibnux" target="_blank" class="btn btn-primary btn-sm btn-block">Telegram</a>
|
<a href="https://t.me/ibnux" target="_blank" class="btn btn-primary btn-sm btn-block">{Lang::T('Telegram')}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box box-primary box-hovered mb20 activities">
|
<div class="box box-primary box-hovered mb20 activities">
|
||||||
<div class="box-header">
|
<div class="box-header">
|
||||||
<h3 class="box-title">{Lang::T('Free WhatsApp Gateway and Telegram Bot creater')}</h3>
|
<h3 class="box-title">{Lang::T('WhatsApp Gateway and Free Telegram Bot')}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
{Lang::T('There is a Telegram bot wizard in here')}
|
{Lang::T('Connect your PHPNuxBill to WhatsApp efficiently using WhatsApp Gateway. Also, create Telegram bots easily and practically.')}
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<a href="https://wa.nux.my.id/login" target="_blank"
|
<a href="https://wa.nux.my.id/login" target="_blank"
|
||||||
@ -155,35 +144,128 @@
|
|||||||
<h3 class="box-title">PHPNUXBILL</h3>
|
<h3 class="box-title">PHPNUXBILL</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
<b>PHPNuxBill</b> {Lang::T('is a billing Hotspot and PPPOE for Mikrotik using PHP and Mikrotik API to comunicate
|
<b>PHPNuxBill</b>
|
||||||
with router. If you get more profit with this application, please donate us.')}<br>{Lang::T('Watch project')} <a
|
{Lang::T('is a Hotspot and PPPoE billing platform for Mikrotik developed using PHP. The application uses Mikrotik API to communicate with the router, ensuring efficient and easy integration. If you feel you get more benefits from this application, we would greatly appreciate your contribution through donation.')}<br>{Lang::T('Watch project –')} <a
|
||||||
href="https://github.com/hotspotbilling/phpnuxbill" target="_blank">{Lang::T('in here')}</a>
|
href="https://github.com/hotspotbilling/phpnuxbill" target="_blank">{Lang::T('IN HERE')}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer" id="currentVersion">ver</div>
|
<div class="box-footer" id="currentVersion">ver</div>
|
||||||
<div class="box-footer" id="latestVersion">ver</div>
|
<div class="box-footer" id="latestVersion">ver</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<div class="btn-group btn-group-justified" role="group" aria-label="...">
|
<div class="btn-group btn-group-justified" role="group" aria-label="...">
|
||||||
<a href="./update.php" class="btn btn-success btn-sm btn-block">{Lang::T('Install Latest Version')}</a>
|
<a href="./update.php"
|
||||||
|
class="btn btn-success btn-sm btn-block">{Lang::T('Install Latest Version')}</a>
|
||||||
<a href="https://github.com/hotspotbilling/phpnuxbill/archive/refs/heads/master.zip" target="_blank"
|
<a href="https://github.com/hotspotbilling/phpnuxbill/archive/refs/heads/master.zip" target="_blank"
|
||||||
class="btn btn-warning btn-sm btn-block text-black">{Lang::T('Download Latest Version')}</a>
|
class="btn btn-warning btn-sm btn-block text-black">{Lang::T('Download Latest Version')}</a>
|
||||||
</div>
|
</div>
|
||||||
<center><a href="{Text::url('community/rollback')}" class="btn btn-link btn-sm btn-block">{Lang::T('Select Old Version')}</a>
|
<center><a href="{Text::url('community/rollback')}"
|
||||||
|
class="btn btn-link btn-sm btn-block">{Lang::T('Select Old Version')}</a>
|
||||||
</center>
|
</center>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<div class="btn-group btn-group-justified" role="group" aria-label="...">
|
<div class="btn-group btn-group-justified" role="group" aria-label="...">
|
||||||
<a href="./CHANGELOG.md" target="_blank" class="btn btn-default btn-sm btn-block">{Lang::T('Current Changelog')}</a>
|
<a href="./CHANGELOG.md" target="_blank"
|
||||||
|
class="btn btn-default btn-sm btn-block">{Lang::T('Current Changelog')}</a>
|
||||||
<a href="https://github.com/hotspotbilling/phpnuxbill/blob/master/CHANGELOG.md" target="_blank"
|
<a href="https://github.com/hotspotbilling/phpnuxbill/blob/master/CHANGELOG.md" target="_blank"
|
||||||
class="btn btn-default btn-sm btn-block">{Lang::T('Repo Changelog')}</a>
|
class="btn btn-default btn-sm btn-block">{Lang::T('Repo Changelog')}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
{Lang::T('If you Download manual the update file, sometime update change database, after uploading, click this
|
{Lang::T('If you download the update file manually, sometimes the update may change the database structure. After the file is successfully uploaded, click this button to update the database structure.')}
|
||||||
button to update database structure.')}
|
|
||||||
<a href="./update.php?step=4" class="btn btn-default btn-sm btn-block">{Lang::T('Update Database')}</a>
|
<a href="./update.php?step=4" class="btn btn-default btn-sm btn-block">{Lang::T('Update Database')}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="box box-hovered mb20 box-primary">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title">{Lang::T('Credits')}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{Lang::T('Souce')}</th>
|
||||||
|
<th>{Lang::T('Details')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Bootstrap V3</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://getbootstrap.com/docs/3.4/" target="_blank">
|
||||||
|
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Admin LTE V3</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://adminlte.io/themes/v3/" target="_blank">
|
||||||
|
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Smarty Template V4</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://www.smarty.net/" target="_blank">
|
||||||
|
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PHP IdiORM</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://idiorm.readthedocs.io/" target="_blank">
|
||||||
|
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PHP mPDF</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://mpdf.github.io/" target="_blank">
|
||||||
|
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PHP QRCode</td>
|
||||||
|
<td>
|
||||||
|
<a href="http://phpqrcode.sourceforge.net/" target="_blank">
|
||||||
|
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PHP Net_RouterOS</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/pear2/Net_RouterOS" target="_blank">
|
||||||
|
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Summernote</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://summernote.org/" target="_blank">
|
||||||
|
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PHP Mailer</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://github.com/PHPMailer/PHPMailer/" target="_blank">
|
||||||
|
<i class="glyphicon glyphicon-globe"></i> {Lang::T('Visit')}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
window.addEventListener('DOMContentLoaded', function() {
|
window.addEventListener('DOMContentLoaded', function() {
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
<label class="col-md-3 control-label">{Lang::T('Coordinates')}</label>
|
<label class="col-md-3 control-label">{Lang::T('Coordinates')}</label>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<input name="coordinates" id="coordinates" class="form-control" value=""
|
<input name="coordinates" id="coordinates" class="form-control" value=""
|
||||||
placeholder="6.465422, 3.406448">
|
placeholder="-6.465422, 3.406448">
|
||||||
<div id="map" style="width: '100%'; height: 200px; min-height: 150px;"></div>
|
<div id="map" style="width: '100%'; height: 200px; min-height: 150px;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -98,9 +98,11 @@
|
|||||||
<div class="panel-heading">PPPoE</div>
|
<div class="panel-heading">PPPoE</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-3 control-label">{Lang::T('Usernames')} <span class="label label-danger" id="warning_username"></span></label>
|
<label class="col-md-3 control-label">{Lang::T('Usernames')} <span class="label label-danger"
|
||||||
|
id="warning_username"></span></label>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<input type="username" class="form-control" id="pppoe_username" name="pppoe_username" onkeyup="checkUsername(this, '0')">
|
<input type="username" class="form-control" id="pppoe_username" name="pppoe_username"
|
||||||
|
onkeyup="checkUsername(this, '0')">
|
||||||
<span class="help-block">{Lang::T('Not Working for freeradius')}</span>
|
<span class="help-block">{Lang::T('Not Working for freeradius')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -112,9 +114,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-3 control-label">Remote IP <span class="label label-danger" id="warning_ip"></span></label>
|
<label class="col-md-3 control-label">Remote IP <span class="label label-danger"
|
||||||
|
id="warning_ip"></span></label>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<input type="text" class="form-control" id="pppoe_ip" name="pppoe_ip" onkeyup="checkIP(this, '0')">
|
<input type="text" class="form-control" id="pppoe_ip" name="pppoe_ip"
|
||||||
|
onkeyup="checkIP(this, '0')">
|
||||||
<span class="help-block">{Lang::T('Also Working for freeradius')}</span>
|
<span class="help-block">{Lang::T('Also Working for freeradius')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -204,7 +208,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<center>
|
<center>
|
||||||
<button class="btn btn-primary" onclick="return ask(this, '{Lang::T("Continue the process of adding Customer Data?")}')" type="submit">
|
<button class="btn btn-primary"
|
||||||
|
onclick="return ask(this, '{Lang::T("Continue the process of adding Customer Data?")}')" type="submit">
|
||||||
{Lang::T('Save Changes')}
|
{Lang::T('Save Changes')}
|
||||||
</button>
|
</button>
|
||||||
<br><a href="{Text::url('customers/list')}" class="btn btn-link">{Lang::T('Cancel')}</a>
|
<br><a href="{Text::url('customers/list')}" class="btn btn-link">{Lang::T('Cancel')}</a>
|
||||||
@ -311,4 +316,4 @@
|
|||||||
{/literal}
|
{/literal}
|
||||||
|
|
||||||
|
|
||||||
{include file="sections/footer.tpl"}
|
{include file="sections/footer.tpl"}
|
@ -18,9 +18,9 @@
|
|||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
<a class="btn btn-primary btn-xs" title="save"
|
<a class="btn btn-primary btn-xs" title="save"
|
||||||
href="{Text::url('customers/csv&token=', $csrf_token)}"
|
href="{Text::url('customers/csv&token=', $csrf_token)}" onclick="return ask(this, '{Lang::T("
|
||||||
onclick="return ask(this, '{Lang::T("This will export to CSV")}?')"><span
|
This will export to CSV")}?')"><span class="glyphicon glyphicon-download"
|
||||||
class="glyphicon glyphicon-download" aria-hidden="true"></span> CSV</a>
|
aria-hidden="true"></span> CSV</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{Lang::T('Manage Contact')}
|
{Lang::T('Manage Contact')}
|
||||||
@ -205,14 +205,15 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<select id="messageType" class="form-control">
|
<select style="margin-bottom: 10px;" id="messageType" class="form-control">
|
||||||
<option value="all">{Lang::T('All')}</option>
|
<option value="all">{Lang::T('All')}</option>
|
||||||
<option value="email">{Lang::T('Email')}</option>
|
<option value="email">{Lang::T('Email')}</option>
|
||||||
<option value="inbox">{Lang::T('Inbox')}</option>
|
<option value="inbox">{Lang::T('Inbox')}</option>
|
||||||
<option value="sms">{Lang::T('SMS')}</option>
|
<option value="sms">{Lang::T('SMS')}</option>
|
||||||
<option value="wa">{Lang::T('WhatsApp')}</option>
|
<option value="wa">{Lang::T('WhatsApp')}</option>
|
||||||
</select>
|
</select>
|
||||||
<br>
|
<input type="text" style="margin-bottom: 10px;" class="form-control" id="subject-content" value=""
|
||||||
|
placeholder="{Lang::T('Enter message subject here')}">
|
||||||
<textarea id="messageContent" class="form-control" rows="4"
|
<textarea id="messageContent" class="form-control" rows="4"
|
||||||
placeholder="{Lang::T('Enter your message here...')}"></textarea>
|
placeholder="{Lang::T('Enter your message here...')}"></textarea>
|
||||||
</div>
|
</div>
|
||||||
@ -260,6 +261,8 @@
|
|||||||
$('#sendMessageButton').on('click', function () {
|
$('#sendMessageButton').on('click', function () {
|
||||||
const message = $('#messageContent').val().trim();
|
const message = $('#messageContent').val().trim();
|
||||||
const messageType = $('#messageType').val();
|
const messageType = $('#messageType').val();
|
||||||
|
const subject = $('#subject-content').val().trim();
|
||||||
|
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
@ -271,6 +274,16 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (messageType == 'all' || messageType == 'inbox' || messageType == 'email' && !subject) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Error!',
|
||||||
|
text: "{Lang::T('Please enter a subject for the message.')}",
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Disable the button and show loading text
|
// Disable the button and show loading text
|
||||||
$(this).prop('disabled', true).text('{Lang::T('Sending...')}');
|
$(this).prop('disabled', true).text('{Lang::T('Sending...')}');
|
||||||
|
|
||||||
@ -332,4 +345,31 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
document.getElementById('messageType').addEventListener('change', function () {
|
||||||
|
const messageType = this.value;
|
||||||
|
const subjectField = document.getElementById('subject-content');
|
||||||
|
|
||||||
|
subjectField.style.display = (messageType === 'all' || messageType === 'email' || messageType === 'inbox') ? 'block' : 'none';
|
||||||
|
|
||||||
|
switch (messageType) {
|
||||||
|
case 'all':
|
||||||
|
subjectField.placeholder = 'Enter a subject for all channels';
|
||||||
|
subjectField.required = true;
|
||||||
|
break;
|
||||||
|
case 'email':
|
||||||
|
subjectField.placeholder = 'Enter a subject for email';
|
||||||
|
subjectField.required = true;
|
||||||
|
break;
|
||||||
|
case 'inbox':
|
||||||
|
subjectField.placeholder = 'Enter a subject for inbox';
|
||||||
|
subjectField.required = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
subjectField.placeholder = 'Enter message subject here';
|
||||||
|
subjectField.required = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{include file = "sections/footer.tpl" }
|
{include file = "sections/footer.tpl" }
|
@ -85,22 +85,32 @@
|
|||||||
listAtts.forEach(function(el) {
|
listAtts.forEach(function(el) {
|
||||||
if (el.addEventListener) { // all browsers except IE before version 9
|
if (el.addEventListener) { // all browsers except IE before version 9
|
||||||
el.addEventListener("click", function() {
|
el.addEventListener("click", function() {
|
||||||
|
var txt = $(this).html();
|
||||||
$(this).html(
|
$(this).html(
|
||||||
`<span class="loading"></span>`
|
`<span class="loading"></span>`
|
||||||
);
|
);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
$(this).prop("disabled", true);
|
$(this).prop("disabled", true);
|
||||||
}, 100);
|
}, 100);
|
||||||
|
setTimeout(() => {
|
||||||
|
$(this).html(txt);
|
||||||
|
$(this).prop("disabled", false);
|
||||||
|
}, 5000);
|
||||||
}, false);
|
}, false);
|
||||||
} else {
|
} else {
|
||||||
if (el.attachEvent) { // IE before version 9
|
if (el.attachEvent) { // IE before version 9
|
||||||
el.attachEvent("click", function() {
|
el.attachEvent("click", function() {
|
||||||
|
var txt = $(this).html();
|
||||||
$(this).html(
|
$(this).html(
|
||||||
`<span class="loading"></span>`
|
`<span class="loading"></span>`
|
||||||
);
|
);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
$(this).prop("disabled", true);
|
$(this).prop("disabled", true);
|
||||||
}, 100);
|
}, 100);
|
||||||
|
setTimeout(() => {
|
||||||
|
$(this).html(txt);
|
||||||
|
$(this).prop("disabled", false);
|
||||||
|
}, 5000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/bootstrap.min.css">
|
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="{$app_url}/ui/ui/fonts/ionicons/css/ionicons.min.css">
|
<link rel="stylesheet" href="{$app_url}/ui/ui/fonts/ionicons/css/ionicons.min.css">
|
||||||
<link rel="stylesheet" href="{$app_url}/ui/ui/fonts/font-awesome/css/font-awesome.min.css">
|
<link rel="stylesheet" href="{$app_url}/ui/ui/fonts/font-awesome/css/font-awesome.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/modern-AdminLTE.min.css">
|
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/modern-AdminLTE.min.css">
|
||||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/select2.min.css" />
|
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/select2.min.css" />
|
||||||
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/select2-bootstrap.min.css" />
|
<link rel="stylesheet" href="{$app_url}/ui/ui/styles/select2-bootstrap.min.css" />
|
||||||
@ -29,7 +30,7 @@
|
|||||||
|
|
||||||
</style>
|
</style>
|
||||||
{if isset($xheader)}
|
{if isset($xheader)}
|
||||||
{$xheader}
|
{$xheader}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
@ -77,8 +78,8 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li class="user-header">
|
<li class="user-header">
|
||||||
<img src="{$app_url}/{$UPLOAD_PATH}{$_admin['photo']}.thumb.jpg"
|
<img src="{$app_url}/{$UPLOAD_PATH}{$_admin['photo']}.thumb.jpg"
|
||||||
onerror="this.src='{$app_url}/{$UPLOAD_PATH}/admin.default.png'" class="img-circle"
|
onerror="this.src='{$app_url}/{$UPLOAD_PATH}/admin.default.png'"
|
||||||
alt="Avatar">
|
class="img-circle" alt="Avatar">
|
||||||
<p>
|
<p>
|
||||||
{$_admin['fullname']}
|
{$_admin['fullname']}
|
||||||
<small>{Lang::T($_admin['user_type'])}</small>
|
<small>{Lang::T($_admin['user_type'])}</small>
|
||||||
@ -127,63 +128,64 @@
|
|||||||
</li>
|
</li>
|
||||||
{$_MENU_AFTER_CUSTOMERS}
|
{$_MENU_AFTER_CUSTOMERS}
|
||||||
{if !in_array($_admin['user_type'],['Report'])}
|
{if !in_array($_admin['user_type'],['Report'])}
|
||||||
<li class="{if $_routes[0] eq 'plan' || $_routes[0] eq 'coupons'}active{/if} treeview">
|
<li class="{if $_routes[0] eq 'plan' || $_routes[0] eq 'coupons'}active{/if} treeview">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<i class="fa fa-ticket"></i> <span>{Lang::T('Services')}</span>
|
<i class="fa fa-ticket"></i> <span>{Lang::T('Services')}</span>
|
||||||
<span class="pull-right-container">
|
<span class="pull-right-container">
|
||||||
<i class="fa fa-angle-left pull-right"></i>
|
<i class="fa fa-angle-left pull-right"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="treeview-menu">
|
<ul class="treeview-menu">
|
||||||
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
||||||
href="{Text::url('plan/list')}">{Lang::T('Active Customers')}</a></li>
|
href="{Text::url('plan/list')}">{Lang::T('Active Customers')}</a></li>
|
||||||
{if $_c['disable_voucher'] != 'yes'}
|
{if $_c['disable_voucher'] != 'yes'}
|
||||||
<li {if $_routes[1] eq 'refill' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'refill' }class="active" {/if}><a
|
||||||
href="{Text::url('plan/refill')}">{Lang::T('Refill Customer')}</a></li>
|
href="{Text::url('plan/refill')}">{Lang::T('Refill Customer')}</a></li>
|
||||||
{/if}
|
{/if}
|
||||||
{if $_c['disable_voucher'] != 'yes'}
|
{if $_c['disable_voucher'] != 'yes'}
|
||||||
<li {if $_routes[1] eq 'voucher' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'voucher' }class="active" {/if}><a
|
||||||
href="{Text::url('plan/voucher')}">{Lang::T('Vouchers')}</a></li>
|
href="{Text::url('plan/voucher')}">{Lang::T('Vouchers')}</a></li>
|
||||||
{/if}
|
{/if}
|
||||||
{if $_c['enable_coupons'] == 'yes'}
|
{if $_c['enable_coupons'] == 'yes'}
|
||||||
<li {if $_routes[0] eq 'coupons' }class="active" {/if}><a
|
<li {if $_routes[0] eq 'coupons' }class="active" {/if}><a
|
||||||
href="{Text::url('coupons')}">{Lang::T('Coupons')}</a></li>
|
href="{Text::url('coupons')}">{Lang::T('Coupons')}</a></li>
|
||||||
{/if}
|
{/if}
|
||||||
<li {if $_routes[1] eq 'recharge' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'recharge' }class="active" {/if}><a
|
||||||
href="{Text::url('plan/recharge')}">{Lang::T('Recharge Customer')}</a></li>
|
href="{Text::url('plan/recharge')}">{Lang::T('Recharge Customer')}</a></li>
|
||||||
{if $_c['enable_balance'] == 'yes'}
|
{if $_c['enable_balance'] == 'yes'}
|
||||||
<li {if $_routes[1] eq 'deposit' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'deposit' }class="active" {/if}><a
|
||||||
href="{Text::url('plan/deposit')}">{Lang::T('Refill Balance')}</a></li>
|
href="{Text::url('plan/deposit')}">{Lang::T('Refill Balance')}</a></li>
|
||||||
{/if}
|
{/if}
|
||||||
{$_MENU_SERVICES}
|
{$_MENU_SERVICES}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
{$_MENU_AFTER_SERVICES}
|
{$_MENU_AFTER_SERVICES}
|
||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||||
<li class="{if $_system_menu eq 'services'}active{/if} treeview">
|
<li class="{if $_system_menu eq 'services'}active{/if} treeview">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<i class="ion ion-cube"></i> <span>{Lang::T('Internet Plan')}</span>
|
<i class="ion ion-cube"></i> <span>{Lang::T('Internet Plan')}</span>
|
||||||
<span class="pull-right-container">
|
<span class="pull-right-container">
|
||||||
<i class="fa fa-angle-left pull-right"></i>
|
<i class="fa fa-angle-left pull-right"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="treeview-menu">
|
<ul class="treeview-menu">
|
||||||
<li {if $_routes[1] eq 'hotspot' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'hotspot' }class="active" {/if}><a
|
||||||
href="{Text::url('services/hotspot')}">Hotspot</a></li>
|
href="{Text::url('services/hotspot')}">Hotspot</a></li>
|
||||||
<li {if $_routes[1] eq 'pppoe' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'pppoe' }class="active" {/if}><a
|
||||||
href="{Text::url('services/pppoe')}">PPPOE</a></li>
|
href="{Text::url('services/pppoe')}">PPPOE</a></li>
|
||||||
<li {if $_routes[1] eq 'vpn' }class="active" {/if}><a href="{Text::url('services/vpn')}">VPN</a>
|
<li {if $_routes[1] eq 'vpn' }class="active" {/if}><a
|
||||||
</li>
|
href="{Text::url('services/vpn')}">VPN</a>
|
||||||
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
</li>
|
||||||
href="{Text::url('bandwidth/list')}">Bandwidth</a></li>
|
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
||||||
{if $_c['enable_balance'] == 'yes'}
|
href="{Text::url('bandwidth/list')}">Bandwidth</a></li>
|
||||||
<li {if $_routes[1] eq 'balance' }class="active" {/if}><a
|
{if $_c['enable_balance'] == 'yes'}
|
||||||
href="{Text::url('services/balance')}">{Lang::T('Customer Balance')}</a></li>
|
<li {if $_routes[1] eq 'balance' }class="active" {/if}><a
|
||||||
{/if}
|
href="{Text::url('services/balance')}">{Lang::T('Customer Balance')}</a></li>
|
||||||
{$_MENU_PLANS}
|
{/if}
|
||||||
</ul>
|
{$_MENU_PLANS}
|
||||||
</li>
|
</ul>
|
||||||
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
{$_MENU_AFTER_PLANS}
|
{$_MENU_AFTER_PLANS}
|
||||||
<li class="{if in_array($_routes[0], ['maps'])}active{/if} treeview">
|
<li class="{if in_array($_routes[0], ['maps'])}active{/if} treeview">
|
||||||
@ -203,18 +205,20 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="{if $_system_menu eq 'reports'}active{/if} treeview">
|
<li class="{if $_system_menu eq 'reports'}active{/if} treeview">
|
||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin', 'Report'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin', 'Report'])}
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<i class="ion ion-clipboard"></i> <span>{Lang::T('Reports')}</span>
|
<i class="ion ion-clipboard"></i> <span>{Lang::T('Reports')}</span>
|
||||||
<span class="pull-right-container">
|
<span class="pull-right-container">
|
||||||
<i class="fa fa-angle-left pull-right"></i>
|
<i class="fa fa-angle-left pull-right"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
<ul class="treeview-menu">
|
<ul class="treeview-menu">
|
||||||
<li {if $_routes[1] eq 'reports' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'reports' }class="active" {/if}><a
|
||||||
href="{Text::url('reports')}">{Lang::T('Daily Reports')}</a></li>
|
href="{Text::url('reports')}">{Lang::T('Daily Reports')}</a></li>
|
||||||
<li {if $_routes[1] eq 'activation' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'activation' }class="active" {/if}><a
|
||||||
href="{Text::url('reports/activation')}">{Lang::T('Activation History')}</a></li>
|
href="{Text::url('reports/activation')}">{Lang::T('Activation History')}</a></li>
|
||||||
|
{* <li {if $_routes[0] eq 'invoices' }class="active" {/if}><a
|
||||||
|
href="{Text::url('invoices')}">{Lang::T('Invoices')}</a></li> *}
|
||||||
{$_MENU_REPORTS}
|
{$_MENU_REPORTS}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@ -236,68 +240,71 @@
|
|||||||
</li>
|
</li>
|
||||||
{$_MENU_AFTER_MESSAGE}
|
{$_MENU_AFTER_MESSAGE}
|
||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||||
<li class="{if $_system_menu eq 'network'}active{/if} treeview">
|
<li class="{if $_system_menu eq 'network'}active{/if} treeview">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<i class="ion ion-network"></i> <span>{Lang::T('Network')}</span>
|
<i class="ion ion-network"></i> <span>{Lang::T('Network')}</span>
|
||||||
<span class="pull-right-container">
|
<span class="pull-right-container">
|
||||||
<i class="fa fa-angle-left pull-right"></i>
|
<i class="fa fa-angle-left pull-right"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="treeview-menu">
|
<ul class="treeview-menu">
|
||||||
<li {if $_routes[0] eq 'routers' and $_routes[1] eq '' }class="active" {/if}><a
|
<li {if $_routes[0] eq 'routers' and $_routes[1] eq '' }class="active" {/if}><a
|
||||||
href="{Text::url('routers')}">Routers</a></li>
|
href="{Text::url('routers')}">Routers</a></li>
|
||||||
<li {if $_routes[0] eq 'pool' and $_routes[1] eq 'list' }class="active" {/if}><a
|
<li {if $_routes[0] eq 'pool' and $_routes[1] eq 'list' }class="active" {/if}><a
|
||||||
href="{Text::url('pool/list')}">IP Pool</a></li>
|
href="{Text::url('pool/list')}">IP Pool</a></li>
|
||||||
<li {if $_routes[0] eq 'pool' and $_routes[1] eq 'port' }class="active" {/if}><a
|
<li {if $_routes[0] eq 'pool' and $_routes[1] eq 'port' }class="active" {/if}><a
|
||||||
href="{Text::url('pool/port')}">Port Pool</a></li>
|
href="{Text::url('pool/port')}">Port Pool</a></li>
|
||||||
{$_MENU_NETWORK}
|
{$_MENU_NETWORK}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{$_MENU_AFTER_NETWORKS}
|
{$_MENU_AFTER_NETWORKS}
|
||||||
{if $_c['radius_enable']}
|
{if $_c['radius_enable']}
|
||||||
<li class="{if $_system_menu eq 'radius'}active{/if} treeview">
|
<li class="{if $_system_menu eq 'radius'}active{/if} treeview">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<i class="fa fa-database"></i> <span>{Lang::T('Radius')}</span>
|
<i class="fa fa-database"></i> <span>{Lang::T('Radius')}</span>
|
||||||
<span class="pull-right-container">
|
<span class="pull-right-container">
|
||||||
<i class="fa fa-angle-left pull-right"></i>
|
<i class="fa fa-angle-left pull-right"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="treeview-menu">
|
<ul class="treeview-menu">
|
||||||
<li {if $_routes[0] eq 'radius' and $_routes[1] eq 'nas-list' }class="active" {/if}><a
|
<li {if $_routes[0] eq 'radius' and $_routes[1] eq 'nas-list' }class="active" {/if}><a
|
||||||
href="{Text::url('radius/nas-list')}">{Lang::T('Radius NAS')}</a></li>
|
href="{Text::url('radius/nas-list')}">{Lang::T('Radius NAS')}</a></li>
|
||||||
{$_MENU_RADIUS}
|
{$_MENU_RADIUS}
|
||||||
</ul>
|
</ul>
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
|
{$_MENU_AFTER_RADIUS}
|
||||||
|
<li class="{if $_system_menu eq 'pages'}active{/if} treeview">
|
||||||
|
<a href="#">
|
||||||
|
<i class="ion ion-document"></i> <span>{Lang::T("Static Pages")}</span>
|
||||||
|
<span class="pull-right-container">
|
||||||
|
<i class="fa fa-angle-left pull-right"></i>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<ul class="treeview-menu">
|
||||||
|
<li {if $_routes[1] eq 'Order_Voucher' }class="active" {/if}><a
|
||||||
|
href="{Text::url('pages/Order_Voucher')}">{Lang::T('Order Voucher')}</a></li>
|
||||||
|
<li {if $_routes[1] eq 'Voucher' }class="active" {/if}><a
|
||||||
|
href="{Text::url('pages/Voucher')}">{Lang::T('Theme Voucher')}</a></li>
|
||||||
|
<li {if $_routes[1] eq 'Announcement' }class="active" {/if}><a
|
||||||
|
href="{Text::url('pages/Announcement')}">{Lang::T('Announcement')}</a></li>
|
||||||
|
<li {if $_routes[1] eq 'Announcement_Customer' }class="active" {/if}><a
|
||||||
|
href="{Text::url('pages/Announcement_Customer')}">{Lang::T('Customer
|
||||||
|
Announcement')}</a>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
<li {if $_routes[1] eq 'Registration_Info' }class="active" {/if}><a
|
||||||
{$_MENU_AFTER_RADIUS}
|
href="{Text::url('pages/Registration_Info')}">{Lang::T('Registration Info')}</a>
|
||||||
<li class="{if $_system_menu eq 'pages'}active{/if} treeview">
|
</li>
|
||||||
<a href="#">
|
<li {if $_routes[1] eq 'Payment_Info' }class="active" {/if}><a
|
||||||
<i class="ion ion-document"></i> <span>{Lang::T("Static Pages")}</span>
|
href="{Text::url('pages/Payment_Info')}">{Lang::T('Payment Info')}</a></li>
|
||||||
<span class="pull-right-container">
|
<li {if $_routes[1] eq 'Privacy_Policy' }class="active" {/if}><a
|
||||||
<i class="fa fa-angle-left pull-right"></i>
|
href="{Text::url('pages/Privacy_Policy')}">{Lang::T('Privacy Policy')}</a></li>
|
||||||
</span>
|
<li {if $_routes[1] eq 'Terms_and_Conditions' }class="active" {/if}><a
|
||||||
</a>
|
href="{Text::url('pages/Terms_and_Conditions')}">{Lang::T('Terms and
|
||||||
<ul class="treeview-menu">
|
Conditions')}</a></li>
|
||||||
<li {if $_routes[1] eq 'Order_Voucher' }class="active" {/if}><a
|
{$_MENU_PAGES}
|
||||||
href="{Text::url('pages/Order_Voucher')}">{Lang::T('Order Voucher')}</a></li>
|
</ul>
|
||||||
<li {if $_routes[1] eq 'Voucher' }class="active" {/if}><a
|
</li>
|
||||||
href="{Text::url('pages/Voucher')}">{Lang::T('Theme Voucher')}</a></li>
|
|
||||||
<li {if $_routes[1] eq 'Announcement' }class="active" {/if}><a
|
|
||||||
href="{Text::url('pages/Announcement')}">{Lang::T('Announcement')}</a></li>
|
|
||||||
<li {if $_routes[1] eq 'Announcement_Customer' }class="active" {/if}><a
|
|
||||||
href="{Text::url('pages/Announcement_Customer')}">{Lang::T('Customer Announcement')}</a>
|
|
||||||
</li>
|
|
||||||
<li {if $_routes[1] eq 'Registration_Info' }class="active" {/if}><a
|
|
||||||
href="{Text::url('pages/Registration_Info')}">{Lang::T('Registration Info')}</a></li>
|
|
||||||
<li {if $_routes[1] eq 'Payment_Info' }class="active" {/if}><a
|
|
||||||
href="{Text::url('pages/Payment_Info')}">{Lang::T('Payment Info')}</a></li>
|
|
||||||
<li {if $_routes[1] eq 'Privacy_Policy' }class="active" {/if}><a
|
|
||||||
href="{Text::url('pages/Privacy_Policy')}">{Lang::T('Privacy Policy')}</a></li>
|
|
||||||
<li {if $_routes[1] eq 'Terms_and_Conditions' }class="active" {/if}><a
|
|
||||||
href="{Text::url('pages/Terms_and_Conditions')}">{Lang::T('Terms and Conditions')}</a></li>
|
|
||||||
{$_MENU_PAGES}
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
{/if}
|
{/if}
|
||||||
{$_MENU_AFTER_PAGES}
|
{$_MENU_AFTER_PAGES}
|
||||||
<li
|
<li
|
||||||
@ -310,84 +317,85 @@
|
|||||||
</a>
|
</a>
|
||||||
<ul class="treeview-menu">
|
<ul class="treeview-menu">
|
||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||||
<li {if $_routes[1] eq 'app' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'app' }class="active" {/if}><a
|
||||||
href="{Text::url('settings/app')}">{Lang::T('General Settings')}</a></li>
|
href="{Text::url('settings/app')}">{Lang::T('General Settings')}</a></li>
|
||||||
<li {if $_routes[1] eq 'localisation' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'localisation' }class="active" {/if}><a
|
||||||
href="{Text::url('settings/localisation')}">{Lang::T('Localisation')}</a></li>
|
href="{Text::url('settings/localisation')}">{Lang::T('Localisation')}</a></li>
|
||||||
<li {if $_routes[0] eq 'customfield' }class="active" {/if}><a
|
<li {if $_routes[0] eq 'customfield' }class="active" {/if}><a
|
||||||
href="{Text::url('customfield')}">{Lang::T('Custom Fields')}</a></li>
|
href="{Text::url('customfield')}">{Lang::T('Custom Fields')}</a></li>
|
||||||
<li {if $_routes[1] eq 'miscellaneous' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'miscellaneous' }class="active" {/if}><a
|
||||||
href="{Text::url('settings/miscellaneous')}">{Lang::T('Miscellaneous')}</a></li>
|
href="{Text::url('settings/miscellaneous')}">{Lang::T('Miscellaneous')}</a></li>
|
||||||
<li {if $_routes[1] eq 'maintenance' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'maintenance' }class="active" {/if}><a
|
||||||
href="{Text::url('settings/maintenance')}">{Lang::T('Maintenance Mode')}</a></li>
|
href="{Text::url('settings/maintenance')}">{Lang::T('Maintenance Mode')}</a></li>
|
||||||
<li {if $_routes[0] eq 'widgets' }class="active" {/if}><a
|
<li {if $_routes[0] eq 'widgets' }class="active" {/if}><a
|
||||||
href="{Text::url('widgets')}">{Lang::T('Widgets')}</a></li>
|
href="{Text::url('widgets')}">{Lang::T('Widgets')}</a></li>
|
||||||
<li {if $_routes[1] eq 'notifications' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'notifications' }class="active" {/if}><a
|
||||||
href="{Text::url('settings/notifications')}">{Lang::T('User Notification')}</a></li>
|
href="{Text::url('settings/notifications')}">{Lang::T('User Notification')}</a></li>
|
||||||
<li {if $_routes[1] eq 'devices' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'devices' }class="active" {/if}><a
|
||||||
href="{Text::url('settings/devices')}">{Lang::T('Devices')}</a></li>
|
href="{Text::url('settings/devices')}">{Lang::T('Devices')}</a></li>
|
||||||
{/if}
|
{/if}
|
||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin','Agent'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin','Agent'])}
|
||||||
<li {if $_routes[1] eq 'users' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'users' }class="active" {/if}><a
|
||||||
href="{Text::url('settings/users')}">{Lang::T('Administrator Users')}</a></li>
|
href="{Text::url('settings/users')}">{Lang::T('Administrator Users')}</a></li>
|
||||||
{/if}
|
{/if}
|
||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||||
<li {if $_routes[1] eq 'dbstatus' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'dbstatus' }class="active" {/if}><a
|
||||||
href="{Text::url('settings/dbstatus')}">{Lang::T('Backup/Restore')}</a></li>
|
href="{Text::url('settings/dbstatus')}">{Lang::T('Backup/Restore')}</a></li>
|
||||||
<li {if $_system_menu eq 'paymentgateway' }class="active" {/if}>
|
<li {if $_system_menu eq 'paymentgateway' }class="active" {/if}>
|
||||||
<a href="{Text::url('paymentgateway')}">
|
<a href="{Text::url('paymentgateway')}">
|
||||||
<span class="text">{Lang::T('Payment Gateway')}</span>
|
<span class="text">{Lang::T('Payment Gateway')}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{$_MENU_SETTINGS}
|
{$_MENU_SETTINGS}
|
||||||
<li {if $_routes[0] eq 'pluginmanager' }class="active" {/if}>
|
<li {if $_routes[0] eq 'pluginmanager' }class="active" {/if}>
|
||||||
<a href="{Text::url('pluginmanager')}"><i class="glyphicon glyphicon-tasks"></i>
|
<a href="{Text::url('pluginmanager')}"><i class="glyphicon glyphicon-tasks"></i>
|
||||||
{Lang::T('Plugin Manager')}</a>
|
{Lang::T('Plugin Manager')}</a>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{$_MENU_AFTER_SETTINGS}
|
{$_MENU_AFTER_SETTINGS}
|
||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||||
<li class="{if $_system_menu eq 'logs' }active{/if} treeview">
|
<li class="{if $_system_menu eq 'logs' }active{/if} treeview">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<i class="ion ion-clock"></i> <span>{Lang::T('Logs')}</span>
|
<i class="ion ion-clock"></i> <span>{Lang::T('Logs')}</span>
|
||||||
<span class="pull-right-container">
|
<span class="pull-right-container">
|
||||||
<i class="fa fa-angle-left pull-right"></i>
|
<i class="fa fa-angle-left pull-right"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="treeview-menu">
|
<ul class="treeview-menu">
|
||||||
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
|
||||||
href="{Text::url('logs/phpnuxbill')}">PhpNuxBill</a></li>
|
href="{Text::url('logs/phpnuxbill')}">PhpNuxBill</a></li>
|
||||||
{if $_c['radius_enable']}
|
{if $_c['radius_enable']}
|
||||||
<li {if $_routes[1] eq 'radius' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'radius' }class="active" {/if}><a
|
||||||
href="{Text::url('logs/radius')}">Radius</a>
|
href="{Text::url('logs/radius')}">Radius</a>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
<li {if $_routes[1] eq 'message' }class="active" {/if}><a
|
<li {if $_routes[1] eq 'message' }class="active" {/if}><a
|
||||||
href="{Text::url('logs/message')}">Message</a></li>
|
href="{Text::url('logs/message')}">Message</a></li>
|
||||||
{$_MENU_LOGS}
|
{$_MENU_LOGS}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
{$_MENU_AFTER_LOGS}
|
{$_MENU_AFTER_LOGS}
|
||||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||||
<li {if $_routes[1] eq 'docs' }class="active" {/if}>
|
<li {if $_routes[1] eq 'docs' }class="active" {/if}>
|
||||||
<a href="{if $_c['docs_clicked'] != 'yes'}{Text::url('settings/docs')}{else}{$app_url}/docs{/if}">
|
<a
|
||||||
<i class="ion ion-ios-bookmarks"></i>
|
href="{if $_c['docs_clicked'] != 'yes'}{Text::url('settings/docs')}{else}{$app_url}/docs{/if}">
|
||||||
<span class="text">{Lang::T('Documentation')}</span>
|
<i class="ion ion-ios-bookmarks"></i>
|
||||||
{if $_c['docs_clicked'] != 'yes'}
|
<span class="text">{Lang::T('Documentation')}</span>
|
||||||
<span class="pull-right-container"><small
|
{if $_c['docs_clicked'] != 'yes'}
|
||||||
class="label pull-right bg-green">New</small></span>
|
<span class="pull-right-container"><small
|
||||||
{/if}
|
class="label pull-right bg-green">New</small></span>
|
||||||
</a>
|
{/if}
|
||||||
</li>
|
</a>
|
||||||
<li {if $_system_menu eq 'community' }class="active" {/if}>
|
</li>
|
||||||
<a href="{Text::url('community')}">
|
<li {if $_system_menu eq 'community' }class="active" {/if}>
|
||||||
<i class="ion ion-chatboxes"></i>
|
<a href="{Text::url('community')}">
|
||||||
<span class="text">Community</span>
|
<i class="ion ion-chatboxes"></i>
|
||||||
</a>
|
<span class="text">Community</span>
|
||||||
</li>
|
</a>
|
||||||
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
{$_MENU_AFTER_COMMUNITY}
|
{$_MENU_AFTER_COMMUNITY}
|
||||||
</ul>
|
</ul>
|
||||||
@ -395,11 +403,11 @@
|
|||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{if $_c['maintenance_mode'] == 1}
|
{if $_c['maintenance_mode'] == 1}
|
||||||
<div class="notification-top-bar">
|
<div class="notification-top-bar">
|
||||||
<p>{Lang::T('The website is currently in maintenance mode, this means that some or all functionality may be
|
<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
|
unavailable to regular users during this time.')}<small> <a
|
||||||
href="{Text::url('settings/maintenance')}">{Lang::T('Turn Off')}</a></small></p>
|
href="{Text::url('settings/maintenance')}">{Lang::T('Turn Off')}</a></small></p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
@ -411,19 +419,19 @@
|
|||||||
|
|
||||||
<section class="content">
|
<section class="content">
|
||||||
{if isset($notify)}
|
{if isset($notify)}
|
||||||
<script>
|
<script>
|
||||||
// Display SweetAlert toast notification
|
// Display SweetAlert toast notification
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: '{if $notify_t == "s"}success{else}error{/if}',
|
icon: '{if $notify_t == "s"}success{else}error{/if}',
|
||||||
title: '{$notify}',
|
title: '{$notify}',
|
||||||
position: 'top-end',
|
position: 'top-end',
|
||||||
showConfirmButton: false,
|
showConfirmButton: false,
|
||||||
timer: 5000,
|
timer: 5000,
|
||||||
timerProgressBar: true,
|
timerProgressBar: true,
|
||||||
didOpen: (toast) => {
|
didOpen: (toast) => {
|
||||||
toast.addEventListener('mouseenter', Swal.stopTimer)
|
toast.addEventListener('mouseenter', Swal.stopTimer)
|
||||||
toast.addEventListener('mouseleave', Swal.resumeTimer)
|
toast.addEventListener('mouseleave', Swal.resumeTimer)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{/if}
|
{/if}
|
57
ui/ui/admin/invoices/list.tpl
Normal file
57
ui/ui/admin/invoices/list.tpl
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{include file="sections/header.tpl"}
|
||||||
|
|
||||||
|
<!-- Add a Table for Sent History -->
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">{Lang::T('Invoices')}</div>
|
||||||
|
<div class="panel-body" style="overflow: auto;">
|
||||||
|
<table class="table table-bordered" id="invoiceTable" style="width:100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{Lang::T('Invoice No')}</th>
|
||||||
|
<th>{Lang::T('Customer Name')}</th>
|
||||||
|
<th>{Lang::T('Email')}</th>
|
||||||
|
<th>{Lang::T('Address')}</th>
|
||||||
|
<th>{Lang::T('Amount')}</th>
|
||||||
|
<th>{Lang::T('Status')}</th>
|
||||||
|
<th>{Lang::T('Created Date')}</th>
|
||||||
|
<th>{Lang::T('Due Date')}</th>
|
||||||
|
<th>{Lang::T('Actions')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{foreach $invoices as $invoice}
|
||||||
|
<tr>
|
||||||
|
<td>{$invoice->number}</td>
|
||||||
|
<td>{$invoice->fullname}</td>
|
||||||
|
<td>{$invoice->email}</td>
|
||||||
|
<td>{$invoice->address}</td>
|
||||||
|
<td>{$invoice->amount}</td>
|
||||||
|
<td>
|
||||||
|
{if $invoice->status == 'paid'}
|
||||||
|
<span class="label label-success">{Lang::T('Paid')}</span>
|
||||||
|
{elseif $invoice->status == 'unpaid'}
|
||||||
|
<span class="label label-danger">{Lang::T('Unpaid')}</span>
|
||||||
|
{else}
|
||||||
|
<span class="label label-warning">{Lang::T('Pending')}</span>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
<td>{$invoice->created_at}</td>
|
||||||
|
<td>{$invoice->due_date}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{$app_url}/system/uploads/invoices/{$invoice->filename}" class="btn btn-primary btn-xs">{Lang::T('View')}</a>
|
||||||
|
<!-- <a href="javascript:void(0);" class="btn btn-danger btn-xs" onclick="deleteInvoice({$invoice->id});">{Lang::T('Delete')}</a>
|
||||||
|
<a href="javascript:void(0);" class="btn btn-success btn-xs" onclick="sendInvoice({$invoice->id});">{Lang::T('Send')}</a> -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/foreach}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</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>
|
||||||
|
new DataTable('#invoiceTable');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{include file="sections/footer.tpl"}
|
@ -30,7 +30,8 @@
|
|||||||
<select class="form-control" name="service" id="service">
|
<select class="form-control" name="service" id="service">
|
||||||
<option value="all" {if $group=='all' }selected{/if}>{Lang::T('All')}</option>
|
<option value="all" {if $group=='all' }selected{/if}>{Lang::T('All')}</option>
|
||||||
<option value="PPPoE" {if $service=='PPPoE' }selected{/if}>{Lang::T('PPPoE')}</option>
|
<option value="PPPoE" {if $service=='PPPoE' }selected{/if}>{Lang::T('PPPoE')}</option>
|
||||||
<option value="Hotspot" {if $service=='Hotspot' }selected{/if}>{Lang::T('Hotspot')}</option>
|
<option value="Hotspot" {if $service=='Hotspot' }selected{/if}>{Lang::T('Hotspot')}
|
||||||
|
</option>
|
||||||
<option value="VPN" {if $service=='VPN' }selected{/if}>{Lang::T('VPN')}</option>
|
<option value="VPN" {if $service=='VPN' }selected{/if}>{Lang::T('VPN')}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -41,8 +42,10 @@
|
|||||||
<select class="form-control" name="group" id="group">
|
<select class="form-control" name="group" id="group">
|
||||||
<option value="all" {if $group=='all' }selected{/if}>{Lang::T('All Customers')}</option>
|
<option value="all" {if $group=='all' }selected{/if}>{Lang::T('All Customers')}</option>
|
||||||
<option value="new" {if $group=='new' }selected{/if}>{Lang::T('New Customers')}</option>
|
<option value="new" {if $group=='new' }selected{/if}>{Lang::T('New Customers')}</option>
|
||||||
<option value="expired" {if $group=='expired' }selected{/if}>{Lang::T('Expired Customers')}</option>
|
<option value="expired" {if $group=='expired' }selected{/if}>{Lang::T('Expired
|
||||||
<option value="active" {if $group=='active' }selected{/if}>{Lang::T('Active Customers')}</option>
|
Customers')}</option>
|
||||||
|
<option value="active" {if $group=='active' }selected{/if}>{Lang::T('Active Customers')}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,9 +53,13 @@
|
|||||||
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<select class="form-control" name="via" id="via">
|
<select class="form-control" name="via" id="via">
|
||||||
|
<option value="all" {if $via=='all' }selected{/if}>{Lang::T('All Channels')}</option>
|
||||||
|
<option value="inbox" {if $via=='inbox' }selected{/if}>{Lang::T('Inbox')}</option>
|
||||||
|
<option value="email" {if $via=='email' }selected{/if}>{Lang::T('Email')}</option>
|
||||||
<option value="sms" {if $via=='sms' }selected{/if}>{Lang::T('SMS')}</option>
|
<option value="sms" {if $via=='sms' }selected{/if}>{Lang::T('SMS')}</option>
|
||||||
<option value="wa" {if $via=='wa' }selected{/if}>{Lang::T('WhatsApp')}</option>
|
<option value="wa" {if $via=='wa' }selected{/if}>{Lang::T('WhatsApp')}</option>
|
||||||
<option value="both" {if $via=='both' }selected{/if}>{Lang::T('SMS and WhatsApp')}</option>
|
<option value="both" {if $via=='both' }selected{/if}>{Lang::T('SMS and WhatsApp')}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -72,10 +79,21 @@
|
|||||||
{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
|
{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" id="subject-content">
|
||||||
|
<label class="col-md-2 control-label">{Lang::T('Subject')}</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="text" class="form-control" name="subject" id="subject" value=""
|
||||||
|
placeholder="{Lang::T('Enter message subject here')}">
|
||||||
|
</div>
|
||||||
|
<p class="help-block col-md-4">
|
||||||
|
{Lang::T('You can also use the below placeholders here too')}.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<textarea class="form-control" id="message" name="message" required placeholder="{Lang::T('Compose your message...')}" rows="5">{$message}</textarea>
|
<textarea class="form-control" id="message" name="message" required
|
||||||
|
placeholder="{Lang::T('Compose your message...')}" rows="5">{$message}</textarea>
|
||||||
<input name="test" id="test" type="checkbox">
|
<input name="test" id="test" type="checkbox">
|
||||||
{Lang::T('Testing [if checked no real message is sent]')}
|
{Lang::T('Testing [if checked no real message is sent]')}
|
||||||
</div>
|
</div>
|
||||||
@ -93,7 +111,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-lg-offset-2 col-lg-10">
|
<div class="col-lg-offset-2 col-lg-10">
|
||||||
<button type="button" id="startBulk" class="btn btn-primary">{Lang::T('Start Bulk Messaging')}</button>
|
<button type="button" id="startBulk" class="btn btn-primary">{Lang::T('Start Bulk
|
||||||
|
Messaging')}</button>
|
||||||
<a href="{Text::url('dashboard')}" class="btn btn-default">{Lang::T('Cancel')}</a>
|
<a href="{Text::url('dashboard')}" class="btn btn-default">{Lang::T('Cancel')}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -112,7 +131,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{Lang::T('Customer')}</th>
|
<th>{Lang::T('Customer')}</th>
|
||||||
<th>{Lang::T('Phone')}</th>
|
<th>{Lang::T('Channel')}</th>
|
||||||
<th>{Lang::T('Status')}</th>
|
<th>{Lang::T('Status')}</th>
|
||||||
<th>{Lang::T('Message')}</th>
|
<th>{Lang::T('Message')}</th>
|
||||||
<th>{Lang::T('Router')}</th>
|
<th>{Lang::T('Router')}</th>
|
||||||
@ -126,6 +145,34 @@
|
|||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
||||||
|
<script>
|
||||||
|
document.getElementById('via').addEventListener('change', function () {
|
||||||
|
const via = this.value;
|
||||||
|
const subject = document.getElementById('subject-content');
|
||||||
|
const subjectField = document.getElementById('subject');
|
||||||
|
|
||||||
|
subject.style.display = (via === 'all' || via === 'email' || via === 'inbox') ? 'block' : 'none';
|
||||||
|
|
||||||
|
switch (via) {
|
||||||
|
case 'all':
|
||||||
|
subjectField.placeholder = 'Enter a subject for all channels';
|
||||||
|
subjectField.required = true;
|
||||||
|
break;
|
||||||
|
case 'email':
|
||||||
|
subjectField.placeholder = 'Enter a subject for email';
|
||||||
|
subjectField.required = true;
|
||||||
|
break;
|
||||||
|
case 'inbox':
|
||||||
|
subjectField.placeholder = 'Enter a subject for inbox';
|
||||||
|
subjectField.required = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
subjectField.placeholder = 'Enter message subject here';
|
||||||
|
subjectField.required = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{literal}
|
{literal}
|
||||||
<script>
|
<script>
|
||||||
let page = 0;
|
let page = 0;
|
||||||
@ -158,6 +205,7 @@
|
|||||||
page: page,
|
page: page,
|
||||||
test: $('#test').is(':checked') ? 'on' : 'off',
|
test: $('#test').is(':checked') ? 'on' : 'off',
|
||||||
service: $('#service').val(),
|
service: $('#service').val(),
|
||||||
|
subject: $('#subject').val(),
|
||||||
},
|
},
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
beforeSend: function () {
|
beforeSend: function () {
|
||||||
@ -186,10 +234,10 @@
|
|||||||
let statusClass = msg.status.includes('Failed') ? 'danger' : 'success';
|
let statusClass = msg.status.includes('Failed') ? 'danger' : 'success';
|
||||||
historyTable.row.add([
|
historyTable.row.add([
|
||||||
msg.name,
|
msg.name,
|
||||||
msg.phone,
|
msg.channel,
|
||||||
`<span class="text-${statusClass}">${msg.status}</span>`,
|
`<span class="text-${statusClass}">${msg.status}</span>`,
|
||||||
msg.message || 'No message',
|
msg.message || 'No message',
|
||||||
msg.router ? msg.router : 'All Router',
|
msg.router ? msg.router : 'All Router',
|
||||||
msg.service == 'all' ? 'All Service' : (msg.service || 'No Service')
|
msg.service == 'all' ? 'All Service' : (msg.service || 'No Service')
|
||||||
]).draw(false); // Add row without redrawing the table
|
]).draw(false); // Add row without redrawing the table
|
||||||
});
|
});
|
||||||
@ -207,7 +255,7 @@
|
|||||||
console.error("Unexpected response format:", response);
|
console.error("Unexpected response format:", response);
|
||||||
$('#status').html(`
|
$('#status').html(`
|
||||||
<div class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
<i class="fas fa-exclamation-circle"></i> Error: Unexpected response format.
|
<i class="fas fa-exclamation-circle"></i> Error: ${response.message}
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,26 @@
|
|||||||
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<select class="form-control" name="via" id="via">
|
<select class="form-control" name="via" id="via">
|
||||||
<option value="sms" selected> {Lang::T('via SMS')}</option>
|
<option value="all" {if $via=='all' }selected{/if}>{Lang::T('All Channels')}</option>
|
||||||
<option value="wa"> {Lang::T('Via WhatsApp')}</option>
|
<option value="inbox" {if $via=='inbox' }selected{/if}>{Lang::T('Inbox')}</option>
|
||||||
<option value="both"> {Lang::T('Via WhatsApp and SMS')}</option>
|
<option value="email" {if $via=='email' }selected{/if}>{Lang::T('Email')}</option>
|
||||||
</select>
|
<option value="sms" {if $via=='sms' }selected{/if}>{Lang::T('SMS')}</option>
|
||||||
|
<option value="wa" {if $via=='wa' }selected{/if}>{Lang::T('WhatsApp')}</option>
|
||||||
|
<option value="both" {if $via=='both' }selected{/if}>{Lang::T('SMS and WhatsApp')}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" id="subject">
|
||||||
|
<label class="col-md-2 control-label">{Lang::T('Subject')}</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="text" class="form-control" name="subject" id="subject-content" value=""
|
||||||
|
placeholder="{Lang::T('Enter message subject here')}">
|
||||||
|
</div>
|
||||||
|
<p class="help-block col-md-4">
|
||||||
|
{Lang::T('You can also use the below placeholders here too')}.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@ -64,6 +78,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
document.getElementById('via').addEventListener('change', function () {
|
||||||
|
const via = this.value;
|
||||||
|
const subject = document.getElementById('subject');
|
||||||
|
const subjectField = document.getElementById('subject-content');
|
||||||
|
|
||||||
|
subject.style.display = (via === 'all' || via === 'email' || via === 'inbox') ? 'block' : 'none';
|
||||||
|
|
||||||
|
switch (via) {
|
||||||
|
case 'all':
|
||||||
|
subjectField.placeholder = 'Enter a subject for all channels';
|
||||||
|
subjectField.required = true;
|
||||||
|
break;
|
||||||
|
case 'email':
|
||||||
|
subjectField.placeholder = 'Enter a subject for email';
|
||||||
|
subjectField.required = true;
|
||||||
|
break;
|
||||||
|
case 'inbox':
|
||||||
|
subjectField.placeholder = 'Enter a subject for inbox';
|
||||||
|
subjectField.required = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
subjectField.placeholder = 'Enter message subject here';
|
||||||
|
subjectField.required = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
{include file="sections/footer.tpl"}
|
{include file="sections/footer.tpl"}
|
@ -1,16 +1,22 @@
|
|||||||
{include file="sections/header.tpl"}
|
{include file="sections/header.tpl"}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.4/jspdf.min.js"></script>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-sm-12 col-md-offset-3">
|
<div class="col-md-6 col-sm-12 col-md-offset-3">
|
||||||
<div class="panel panel-hovered panel-primary panel-stacked mb30">
|
<div class="panel panel-hovered panel-primary panel-stacked mb30">
|
||||||
<div class="panel-heading">{$in['invoice']}</div>
|
<div class="panel-heading">{$in['invoice']}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
{if !empty($logo)}
|
||||||
|
<center><img src="{$app_url}/{$logo}?"></center>
|
||||||
|
{/if}
|
||||||
<form class="form-horizontal" method="post" action="{Text::url('')}plan/print" target="_blank">
|
<form class="form-horizontal" method="post" action="{Text::url('')}plan/print" target="_blank">
|
||||||
<pre id="content" style="text-align: center;"></pre>
|
<pre id="content"
|
||||||
|
style="border: 0px; ;text-align: center; background-color: transparent; background-image: url('{$app_url}/system/uploads/paid.png');background-repeat:no-repeat;background-position: center"></pre>
|
||||||
<textarea class="hidden" id="formcontent" name="content">{$invoice}</textarea>
|
<textarea class="hidden" id="formcontent" name="content">{$invoice}</textarea>
|
||||||
<input type="hidden" name="id" value="{$in['id']}">
|
<input type="hidden" name="id" value="{$in['id']}">
|
||||||
<a href="{Text::url('')}plan/list" class="btn btn-default btn-sm"><i
|
<a href="{Text::url('plan/list')}" class="btn btn-default btn-sm"><i
|
||||||
class="ion-reply-all"></i>{Lang::T('Finish')}</a>
|
class="ion-reply-all"></i>{Lang::T('Finish')}</a>
|
||||||
|
<a href="javascript:download()" class="btn btn-success btn-sm text-black">
|
||||||
|
<i class="glyphicon glyphicon-share"></i> Download</a>
|
||||||
<a href="https://api.whatsapp.com/send/?text={$whatsapp}" target="_blank"
|
<a href="https://api.whatsapp.com/send/?text={$whatsapp}" target="_blank"
|
||||||
class="btn btn-primary btn-sm">
|
class="btn btn-primary btn-sm">
|
||||||
<i class="glyphicon glyphicon-share"></i> WhatsApp</a>
|
<i class="glyphicon glyphicon-share"></i> WhatsApp</a>
|
||||||
@ -32,13 +38,49 @@
|
|||||||
class="btn btn-success text-black btn-sm hidden-xs hidden-sm" target="_blank">
|
class="btn btn-success text-black btn-sm hidden-xs hidden-sm" target="_blank">
|
||||||
<i class="glyphicon glyphicon-phone"></i>
|
<i class="glyphicon glyphicon-phone"></i>
|
||||||
NuxPrint
|
NuxPrint
|
||||||
</a>
|
</a><br><br>
|
||||||
|
<input type="text" class="form-control form-sm" style="border: 0px; padding: 1px; background-color: white;" readonly onclick="this.select()" value="{$public_url}">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">
|
<script>
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.font = '16px Courier';
|
||||||
|
var text = document.getElementById("formcontent").innerHTML;
|
||||||
|
var lines = text.split(/\r\n|\r|\n/).length;
|
||||||
|
var meas = ctx.measureText("A");
|
||||||
|
let width = Math.round({$_c['printer_cols']} * 9.6);
|
||||||
|
var height = Math.round((14 * lines));
|
||||||
|
console.log(width, height, lines);
|
||||||
|
var paid = new Image();
|
||||||
|
paid.src = '{$app_url}/system/uploads/paid.png';
|
||||||
|
{if !empty($logo)}
|
||||||
|
var img = new Image();
|
||||||
|
img.src = '{$app_url}/{$logo}?{time()}';
|
||||||
|
var new_width = (width / 4) * 2;
|
||||||
|
var new_height = Math.ceil({$hlogo} * (new_width/{$wlogo}));
|
||||||
|
height = height + new_height;
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
function download() {
|
||||||
|
var doc = new jsPDF('p', 'px', [width, height]);
|
||||||
|
{if !empty($logo)}
|
||||||
|
try {
|
||||||
|
doc.addImage(img, 'PNG', (width - new_width) / 2, 10, new_width, new_height);
|
||||||
|
} catch (err) {}
|
||||||
|
{/if}
|
||||||
|
try {
|
||||||
|
doc.addImage(paid, 'PNG', (width - 200) / 2, (height - 145) / 2, 200, 145);
|
||||||
|
} catch (err) {}
|
||||||
|
doc.setFont("Courier");
|
||||||
|
doc.setFontSize(16);
|
||||||
|
doc.text($('#formcontent').html(), width / 2, new_height + 30, 'center');
|
||||||
|
doc.save('{$in['invoice']}.pdf');
|
||||||
|
}
|
||||||
|
|
||||||
var s5_taf_parent = window.location;
|
var s5_taf_parent = window.location;
|
||||||
document.getElementById('content').innerHTML = document.getElementById('formcontent').innerHTML;
|
document.getElementById('content').innerHTML = document.getElementById('formcontent').innerHTML;
|
||||||
</script>
|
</script>
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
{$plan['validity_unit']}</span>
|
{$plan['validity_unit']}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<b>{Lang::T('Using')}</b> <span class="pull-right">
|
<b>{Lang::T('Payment via')}</b> <span class="pull-right">
|
||||||
<select name="using"
|
<select name="using"
|
||||||
style="background-color: white;outline: 1px;border: 1px solid #b7b7b7;">
|
style="background-color: white;outline: 1px;border: 1px solid #b7b7b7;">
|
||||||
{foreach $usings as $us}
|
{foreach $usings as $us}
|
||||||
@ -182,4 +182,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{include file="sections/footer.tpl"}
|
{include file="sections/footer.tpl"}
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-bordered table-condensed table-striped " style="background: #ffffff">
|
<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('Username')}</th>
|
||||||
|
<th class="text-center">{Lang::T('Fullname')}</th>
|
||||||
<th class="text-center">{Lang::T('Plan Name')}</th>
|
<th class="text-center">{Lang::T('Plan Name')}</th>
|
||||||
<th class="text-center">{Lang::T('Type')}</th>
|
<th class="text-center">{Lang::T('Type')}</th>
|
||||||
<th class="text-center">{Lang::T('Plan Price')}</th>
|
<th class="text-center">{Lang::T('Plan Price')}</th>
|
||||||
@ -36,6 +37,7 @@
|
|||||||
{foreach $d as $ds}
|
{foreach $d as $ds}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{$ds['username']}</td>
|
<td>{$ds['username']}</td>
|
||||||
|
<td>{$ds['fullname']}</td>
|
||||||
<td class="text-center">{$ds['plan_name']}</td>
|
<td class="text-center">{$ds['plan_name']}</td>
|
||||||
<td class="text-center">{$ds['type']}</td>
|
<td class="text-center">{$ds['type']}</td>
|
||||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{Lang::T('Invoice')}</th>
|
<th>{Lang::T('Invoice')}</th>
|
||||||
<th>{Lang::T('Username')}</th>
|
<th>{Lang::T('Username')}</th>
|
||||||
|
<th>{Lang::T('Fullname')}</th>
|
||||||
<th>{Lang::T('Plan Name')}</th>
|
<th>{Lang::T('Plan Name')}</th>
|
||||||
<th>{Lang::T('Plan Price')}</th>
|
<th>{Lang::T('Plan Price')}</th>
|
||||||
<th>{Lang::T('Type')}</th>
|
<th>{Lang::T('Type')}</th>
|
||||||
@ -48,6 +49,7 @@
|
|||||||
style="cursor:pointer;">{$ds['invoice']}</td>
|
style="cursor:pointer;">{$ds['invoice']}</td>
|
||||||
<td onclick="window.location.href = '{Text::url('')}customers/viewu/{$ds['username']}'"
|
<td onclick="window.location.href = '{Text::url('')}customers/viewu/{$ds['username']}'"
|
||||||
style="cursor:pointer;">{$ds['username']}</td>
|
style="cursor:pointer;">{$ds['username']}</td>
|
||||||
|
<td>{$ds['fullname']}</td>
|
||||||
<td>{$ds['plan_name']}</td>
|
<td>{$ds['plan_name']}</td>
|
||||||
<td>{Lang::moneyFormat($ds['price'])}</td>
|
<td>{Lang::moneyFormat($ds['price'])}</td>
|
||||||
<td>{$ds['type']}</td>
|
<td>{$ds['type']}</td>
|
||||||
|
@ -94,10 +94,11 @@
|
|||||||
<a href="{Text::url('export/pdf-by-date&')}{$filter}" class="btn btn-default"><i
|
<a href="{Text::url('export/pdf-by-date&')}{$filter}" class="btn btn-default"><i
|
||||||
class="fa fa-file-pdf-o"></i></a>
|
class="fa fa-file-pdf-o"></i></a>
|
||||||
</th>
|
</th>
|
||||||
<th colspan="7"></th>
|
<th colspan="8"></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{Lang::T('Username')}</th>
|
<th>{Lang::T('Username')}</th>
|
||||||
|
<th>{Lang::T('Fullname')}</th>
|
||||||
<th>{Lang::T('Type')}</th>
|
<th>{Lang::T('Type')}</th>
|
||||||
<th>{Lang::T('Plan Name')}</th>
|
<th>{Lang::T('Plan Name')}</th>
|
||||||
<th>{Lang::T('Plan Price')}</th>
|
<th>{Lang::T('Plan Price')}</th>
|
||||||
@ -111,6 +112,7 @@
|
|||||||
{foreach $d as $ds}
|
{foreach $d as $ds}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{$ds['username']}</td>
|
<td>{$ds['username']}</td>
|
||||||
|
<td>{$ds['fullname']}</td>
|
||||||
<td>{$ds['type']}</td>
|
<td>{$ds['type']}</td>
|
||||||
<td>{$ds['plan_name']}</td>
|
<td>{$ds['plan_name']}</td>
|
||||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||||
@ -122,9 +124,8 @@
|
|||||||
{/foreach}
|
{/foreach}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{Lang::T('Total')}</th>
|
<th>{Lang::T('Total')}</th>
|
||||||
<td colspan="2"></td>
|
|
||||||
<th class="text-right">{Lang::moneyFormat($dr)}</th>
|
<th class="text-right">{Lang::moneyFormat($dr)}</th>
|
||||||
<td colspan="4"></td>
|
<td colspan="7"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{Lang::T('Username')}</th>
|
<th>{Lang::T('Username')}</th>
|
||||||
|
<th>{Lang::T('Fullname')}</th>
|
||||||
<th>{Lang::T('Type')}</th>
|
<th>{Lang::T('Type')}</th>
|
||||||
<th>{Lang::T('Plan Name')}</th>
|
<th>{Lang::T('Plan Name')}</th>
|
||||||
<th>{Lang::T('Plan Price')}</th>
|
<th>{Lang::T('Plan Price')}</th>
|
||||||
@ -50,6 +51,7 @@
|
|||||||
{foreach $d as $ds}
|
{foreach $d as $ds}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{$ds['username']}</td>
|
<td>{$ds['username']}</td>
|
||||||
|
<td>{$ds['fullname']}</td>
|
||||||
<td>{$ds['type']}</td>
|
<td>{$ds['type']}</td>
|
||||||
<td>{$ds['plan_name']}</td>
|
<td>{$ds['plan_name']}</td>
|
||||||
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
<td class="text-right">{Lang::moneyFormat($ds['price'])}</td>
|
||||||
|
@ -155,7 +155,7 @@
|
|||||||
<h3 class="panel-title">
|
<h3 class="panel-title">
|
||||||
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseLoginPage"
|
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseLoginPage"
|
||||||
aria-expanded="true" aria-controls="collapseLoginPage">
|
aria-expanded="true" aria-controls="collapseLoginPage">
|
||||||
{Lang::T('Customer Login Page Settings')}
|
{Lang::T('Customer Login Page')}
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@ -254,6 +254,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-heading" role="tab" id="Coupon">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion"
|
||||||
|
href="#collapseCoupon" aria-expanded="false" aria-controls="collapseCoupon">
|
||||||
|
{Lang::T('Coupons')}
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="collapseCoupon" class="panel-collapse collapse" role="tabpanel">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label">{Lang::T('Enable Coupon')}</label>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<select name="enable_coupons" id="enable_coupons" class="form-control text-muted">
|
||||||
|
<option value="no">{Lang::T('No')}</option>
|
||||||
|
<option value="yes" {if $_c['enable_coupons'] == 'yes'}selected="selected" {/if}>{Lang::T('Yes')}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p class="help-block col-md-4">
|
||||||
|
<small>{Lang::T('Enable or disable coupons')}</small>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-success btn-block" type="submit">
|
||||||
|
{Lang::T('Save Changes')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="panel-heading" role="tab" id="Registration">
|
<div class="panel-heading" role="tab" id="Registration">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
@ -649,6 +680,24 @@
|
|||||||
value="{$_c['minimum_transfer']}">
|
value="{$_c['minimum_transfer']}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label">{Lang::T('Allow Balance Custom
|
||||||
|
Amount')}</label>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<select name="allow_balance_custom" id="allow_balance_custom" class="form-control">
|
||||||
|
<option value="no">
|
||||||
|
{Lang::T('No')}
|
||||||
|
</option>
|
||||||
|
<option value="yes" {if $_c['allow_balance_custom']=='yes' }selected="selected" {/if}>
|
||||||
|
{Lang::T('Yes')}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p class="help-block col-md-4"><small>
|
||||||
|
{Lang::T('Allow Customer buy balance with any amount')}
|
||||||
|
</small>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<button class="btn btn-success btn-block" type="submit">
|
<button class="btn btn-success btn-block" type="submit">
|
||||||
{Lang::T('Save Changes')}
|
{Lang::T('Save Changes')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -175,24 +175,6 @@
|
|||||||
extend')}</small>
|
extend')}</small>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-error">
|
|
||||||
<label class="col-md-3 control-label">{Lang::T('Allow Balance Custom
|
|
||||||
Amount')}</label>
|
|
||||||
<div class="col-md-5">
|
|
||||||
<select name="allow_balance_custom" id="allow_balance_custom" class="form-control">
|
|
||||||
<option value="no">
|
|
||||||
{Lang::T('No')}
|
|
||||||
</option>
|
|
||||||
<option value="yes" {if $_c['allow_balance_custom']=='yes' }selected="selected" {/if}>
|
|
||||||
{Lang::T('Yes')}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<p class="help-block col-md-4"><small>
|
|
||||||
{Lang::T('Allow Customer buy balance with any amount')}
|
|
||||||
<br>*Please report any issue or bugs</small>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -113,6 +113,8 @@
|
|||||||
<b>[[expired_date]]</b> - {Lang::T('Expired datetime')}.<br>
|
<b>[[expired_date]]</b> - {Lang::T('Expired datetime')}.<br>
|
||||||
<b>[[footer]]</b> - {Lang::T('Invoice Footer')}.<br>
|
<b>[[footer]]</b> - {Lang::T('Invoice Footer')}.<br>
|
||||||
<b>[[note]]</b> - {Lang::T('For Notes by admin')}.<br>
|
<b>[[note]]</b> - {Lang::T('For Notes by admin')}.<br>
|
||||||
|
<b>[[invoice_link]]</b> - <a href="{$app_url}/docs/#Reminder%20with%20payment%20link"
|
||||||
|
target="_blank">{Lang::T("read documentation")}</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -162,43 +164,79 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{if $_c['enable_balance'] == 'yes'}
|
{if $_c['enable_balance'] == 'yes'}
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label">{Lang::T('Send Balance')}</label>
|
<label class="col-md-2 control-label">{Lang::T('Send Balance')}</label>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<textarea class="form-control" id="balance_send" name="balance_send"
|
<textarea class="form-control" id="balance_send" name="balance_send"
|
||||||
rows="4">{if $_json['balance_send']}{Lang::htmlspecialchars($_json['balance_send'])}{else}{Lang::htmlspecialchars($_default['balance_send'])}{/if}</textarea>
|
rows="4">{if $_json['balance_send']}{Lang::htmlspecialchars($_json['balance_send'])}{else}{Lang::htmlspecialchars($_default['balance_send'])}{/if}</textarea>
|
||||||
</div>
|
|
||||||
<p class="col-md-4 help-block">
|
|
||||||
<b>[[name]]</b> - {Lang::T('Receiver name')}.<br>
|
|
||||||
<b>[[balance]]</b> - {Lang::T('how much balance have been send')}.<br>
|
|
||||||
<b>[[current_balance]]</b> - {Lang::T('Current Balance')}.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="col-md-4 help-block">
|
||||||
|
<b>[[name]]</b> - {Lang::T('Receiver name')}.<br>
|
||||||
|
<b>[[balance]]</b> - {Lang::T('how much balance have been send')}.<br>
|
||||||
|
<b>[[current_balance]]</b> - {Lang::T('Current Balance')}.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
</div>
|
||||||
<div class="form-group">
|
<div class="panel-body">
|
||||||
<label class="col-md-2 control-label">{Lang::T('Received Balance')}</label>
|
<div class="form-group">
|
||||||
<div class="col-md-6">
|
<label class="col-md-2 control-label">{Lang::T('Received Balance')}</label>
|
||||||
<textarea class="form-control" id="balance_received" name="balance_received"
|
<div class="col-md-6">
|
||||||
rows="4">{if $_json['balance_received']}{Lang::htmlspecialchars($_json['balance_received'])}{else}{Lang::htmlspecialchars($_default['balance_received'])}{/if}</textarea>
|
<textarea class="form-control" id="balance_received" name="balance_received"
|
||||||
</div>
|
rows="4">{if $_json['balance_received']}{Lang::htmlspecialchars($_json['balance_received'])}{else}{Lang::htmlspecialchars($_default['balance_received'])}{/if}</textarea>
|
||||||
<p class="col-md-4 help-block">
|
|
||||||
<b>[[name]]</b> - {Lang::T('Sender name')}.<br>
|
|
||||||
<b>[[balance]]</b> - {Lang::T('how much balance have been received')}.<br>
|
|
||||||
<b>[[current_balance]]</b> - {Lang::T('Current Balance')}.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="col-md-4 help-block">
|
||||||
|
<b>[[name]]</b> - {Lang::T('Sender name')}.<br>
|
||||||
|
<b>[[balance]]</b> - {Lang::T('how much balance have been received')}.<br>
|
||||||
|
<b>[[current_balance]]</b> - {Lang::T('Current Balance')}.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
{* <div class="panel-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label">{Lang::T('PDF Invoice Template')}</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<textarea class="form-control" id="email_invoice" name="email_invoice"
|
||||||
|
placeholder="{Lang::T('Template for sending pdf invoice')}" rows="20">
|
||||||
|
{if !empty($_json['email_invoice'])}
|
||||||
|
{Lang::htmlspecialchars($_json['email_invoice'])}
|
||||||
|
{else}
|
||||||
|
{Lang::htmlspecialchars($_default['email_invoice'])}
|
||||||
|
{/if}
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
<p class="col-md-4 help-block">
|
||||||
|
<b>[[company_name]]</b> {Lang::T('Your Company Name at Settings')}.<br>
|
||||||
|
<b>[[company_address]]</b> {Lang::T('Your Company Address at Settings')}.<br>
|
||||||
|
<b>[[company_phone]]</b> - {Lang::T('Your Company Phone at Settings')}.<br>
|
||||||
|
<b>[[invoice]]</b> - {Lang::T('Invoice number')}.<br>
|
||||||
|
<b>[[created_at]]</b> - {Lang::T('Date invoice created')}.<br>
|
||||||
|
<b>[[payment_gateway]]</b> - {Lang::T('Payment gateway user paid from')}.<br>
|
||||||
|
<b>[[payment_channel]]</b> - {Lang::T('Payment channel user paid from')}.<br>
|
||||||
|
<b>[[bill_rows]]</b> - {Lang::T('Bills table, where bills are listed')}.<br>
|
||||||
|
<b>[[currency]]</b> - {Lang::T('Your currency code at localisation Settings')}.<br>
|
||||||
|
<b>[[status]]</b> - {Lang::T('Invoice status')}.<br>
|
||||||
|
<b>[[fullname]]</b> - {Lang::T('Receiver name')}.<br>
|
||||||
|
<b>[[user_name]]</b> - {Lang::T('Username internet')}.<br>
|
||||||
|
<b>[[email]]</b> - {Lang::T('Customer email')} .<br>
|
||||||
|
<b>[[phone]]</b> - {Lang::T('Customer phone')}. <br>
|
||||||
|
<b>[[address]]</b> - {Lang::T('Customer phone')}. <br>
|
||||||
|
<b>[[expired_date]]</b> - {Lang::T('Expired datetime')}.<br>
|
||||||
|
<b>[[logo]]</b> - {Lang::T('Your company logo at Settings')}.<br>
|
||||||
|
<b>[[due_date]]</b> - {Lang::T('Invoice Due date, 7 Days after invoice created')}.<br>
|
||||||
|
<b>[[payment_link]]</b> - <a href="{$app_url}/docs/#Reminder%20with%20payment%20link"
|
||||||
|
target="_blank">{Lang::T("read documentation")}</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div> *}
|
||||||
|
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-success btn-block" type="submit">{Lang::T('Save Changes')}</button>
|
<button class="btn btn-success btn-block" type="submit">{Lang::T('Save Changes')}</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
{include file="sections/footer.tpl"}
|
{include file="sections/footer.tpl"}
|
@ -1,25 +1,25 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
{if isset($_c['CompanyFooter'])}
|
{if isset($_c['CompanyFooter'])}
|
||||||
<footer class="main-footer">
|
<footer class="main-footer">
|
||||||
{$_c['CompanyFooter']}
|
{$_c['CompanyFooter']}
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="javascript:showPrivacy()">Privacy</a>
|
<a href="javascript:showPrivacy()">Privacy</a>
|
||||||
•
|
•
|
||||||
<a href="javascript:showTaC()">T & C</a>
|
<a href="javascript:showTaC()">T & C</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
{else}
|
{else}
|
||||||
<footer class="main-footer">
|
<footer class="main-footer">
|
||||||
PHPNuxBill by <a href="https://github.com/hotspotbilling/phpnuxbill" rel="nofollow noreferrer noopener"
|
PHPNuxBill by <a href="https://github.com/hotspotbilling/phpnuxbill" rel="nofollow noreferrer noopener"
|
||||||
target="_blank">iBNuX</a>, Theme by <a href="https://adminlte.io/" rel="nofollow noreferrer noopener"
|
target="_blank">iBNuX</a>, Theme by <a href="https://adminlte.io/" rel="nofollow noreferrer noopener"
|
||||||
target="_blank">AdminLTE</a>
|
target="_blank">AdminLTE</a>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="javascript:showPrivacy()">Privacy</a>
|
<a href="javascript:showPrivacy()">Privacy</a>
|
||||||
•
|
•
|
||||||
<a href="javascript:showTaC()">T & C</a>
|
<a href="javascript:showTaC()">T & C</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -50,156 +50,175 @@
|
|||||||
<script src="{$app_url}/ui/ui/scripts/custom.js?2025.2.5"></script>
|
<script src="{$app_url}/ui/ui/scripts/custom.js?2025.2.5"></script>
|
||||||
|
|
||||||
{if isset($xfooter)}
|
{if isset($xfooter)}
|
||||||
{$xfooter}
|
{$xfooter}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{if $_c['tawkto'] != ''}
|
{if $_c['tawkto'] != ''}
|
||||||
<!--Start of Tawk.to Script-->
|
<!--Start of Tawk.to Script-->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var isLoggedIn = false;
|
var isLoggedIn = false;
|
||||||
var Tawk_API = {
|
var Tawk_API = {
|
||||||
onLoad: function() {
|
onLoad: function () {
|
||||||
Tawk_API.setAttributes({
|
Tawk_API.setAttributes({
|
||||||
'username' : '{$_user['username']}',
|
'username': '{$_user['username']}',
|
||||||
'service' : '{$_user['service_type']}',
|
'service': '{$_user['service_type']}',
|
||||||
'balance' : '{$_user['balance']}',
|
'balance': '{$_user['balance']}',
|
||||||
'account' : '{$_user['account_type']}',
|
'account': '{$_user['account_type']}',
|
||||||
'phone' : '{$_user['phonenumber']}'
|
'phone': '{$_user['phonenumber']}'
|
||||||
}, function(error) {
|
}, function (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
var Tawk_LoadStart = new Date();
|
||||||
|
Tawk_API.visitor = {
|
||||||
|
name: '{$_user['fullname']}',
|
||||||
|
email: '{$_user['email']}',
|
||||||
|
phone: '{$_user['phonenumber']}'
|
||||||
};
|
};
|
||||||
var Tawk_LoadStart = new Date();
|
(function () {
|
||||||
Tawk_API.visitor = {
|
var s1 = document.createElement("script"),
|
||||||
name: '{$_user['fullname']}',
|
s0 = document.getElementsByTagName("script")[0];
|
||||||
email: '{$_user['email']}',
|
s1.async = true;
|
||||||
phone: '{$_user['phonenumber']}'
|
s1.src = 'https://embed.tawk.to/{$_c['tawkto']}';
|
||||||
};
|
s1.charset = 'UTF-8';
|
||||||
(function() {
|
s1.setAttribute('crossorigin', '*');
|
||||||
var s1 = document.createElement("script"),
|
s0.parentNode.insertBefore(s1, s0);
|
||||||
s0 = document.getElementsByTagName("script")[0];
|
})();
|
||||||
s1.async = true;
|
</script>
|
||||||
s1.src = 'https://embed.tawk.to/{$_c['tawkto']}';
|
<!--End of Tawk.to Script-->
|
||||||
s1.charset = 'UTF-8';
|
{/if}
|
||||||
s1.setAttribute('crossorigin', '*');
|
|
||||||
s0.parentNode.insertBefore(s1, s0);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<!--End of Tawk.to Script-->
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const toggleIcon = document.getElementById('toggleIcon');
|
const toggleIcon = document.getElementById('toggleIcon');
|
||||||
const body = document.body;
|
const body = document.body;
|
||||||
const savedMode = localStorage.getItem('mode');
|
const savedMode = localStorage.getItem('mode');
|
||||||
if (savedMode === 'dark') {
|
if (savedMode === 'dark') {
|
||||||
|
body.classList.add('dark-mode');
|
||||||
|
toggleIcon.textContent = '🌞';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMode(mode) {
|
||||||
|
if (mode === 'dark') {
|
||||||
body.classList.add('dark-mode');
|
body.classList.add('dark-mode');
|
||||||
|
toggleIcon.textContent = '🌞';
|
||||||
|
} else {
|
||||||
|
body.classList.remove('dark-mode');
|
||||||
toggleIcon.textContent = '🌜';
|
toggleIcon.textContent = '🌜';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setMode(mode) {
|
toggleIcon.addEventListener('click', () => {
|
||||||
if (mode === 'dark') {
|
if (body.classList.contains('dark-mode')) {
|
||||||
body.classList.add('dark-mode');
|
setMode('light');
|
||||||
toggleIcon.textContent = '🌜';
|
localStorage.setItem('mode', 'light');
|
||||||
} else {
|
} else {
|
||||||
body.classList.remove('dark-mode');
|
setMode('dark');
|
||||||
toggleIcon.textContent = '🌞';
|
localStorage.setItem('mode', 'dark');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
toggleIcon.addEventListener('click', () => {
|
</script>
|
||||||
if (body.classList.contains('dark-mode')) {
|
|
||||||
setMode('light');
|
|
||||||
localStorage.setItem('mode', 'light');
|
|
||||||
} else {
|
|
||||||
setMode('dark');
|
|
||||||
localStorage.setItem('mode', 'dark');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
{literal}
|
{literal}
|
||||||
<script>
|
<script>
|
||||||
var listAtts = document.querySelectorAll(`[api-get-text]`);
|
var listAtts = document.querySelectorAll(`[api-get-text]`);
|
||||||
listAtts.forEach(function(el) {
|
listAtts.forEach(function (el) {
|
||||||
$.get(el.getAttribute('api-get-text'), function(data) {
|
$.get(el.getAttribute('api-get-text'), function (data) {
|
||||||
el.innerHTML = data;
|
el.innerHTML = data;
|
||||||
});
|
|
||||||
});
|
});
|
||||||
$(document).ready(function() {
|
});
|
||||||
var listAtts = document.querySelectorAll(`button[type="submit"]`);
|
$(document).ready(function () {
|
||||||
listAtts.forEach(function(el) {
|
var listAtts = document.querySelectorAll(`button[type="submit"]`);
|
||||||
if (el.addEventListener) { // all browsers except IE before version 9
|
listAtts.forEach(function (el) {
|
||||||
el.addEventListener("click", function() {
|
if (el.addEventListener) { // all browsers except IE before version 9
|
||||||
|
el.addEventListener("click", function () {
|
||||||
|
$(this).html(
|
||||||
|
`<span class="loading"></span>`
|
||||||
|
);
|
||||||
|
setTimeout(() => {
|
||||||
|
$(this).prop("disabled", true);
|
||||||
|
}, 100);
|
||||||
|
}, false);
|
||||||
|
} else {
|
||||||
|
if (el.attachEvent) { // IE before version 9
|
||||||
|
el.attachEvent("click", function () {
|
||||||
$(this).html(
|
$(this).html(
|
||||||
`<span class="loading"></span>`
|
`<span class="loading"></span>`
|
||||||
);
|
);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
$(this).prop("disabled", true);
|
$(this).prop("disabled", true);
|
||||||
}, 100);
|
}, 100);
|
||||||
}, false);
|
});
|
||||||
} else {
|
}
|
||||||
if (el.attachEvent) { // IE before version 9
|
}
|
||||||
el.attachEvent("click", function() {
|
$(function () {
|
||||||
$(this).html(
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
`<span class="loading"></span>`
|
})
|
||||||
);
|
});
|
||||||
setTimeout(() => {
|
});
|
||||||
$(this).prop("disabled", true);
|
|
||||||
}, 100);
|
function ask(field, text) {
|
||||||
});
|
const txt = field.innerHTML;
|
||||||
|
|
||||||
|
field.innerHTML = `<span class="loading"></span>`;
|
||||||
|
field.setAttribute("disabled", true);
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Are you sure?',
|
||||||
|
text: text,
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Yes, proceed',
|
||||||
|
cancelButtonText: 'Cancel',
|
||||||
|
}).then((result) => {
|
||||||
|
let delay = result.isConfirmed ? 400 : 500;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
field.innerHTML = txt;
|
||||||
|
field.removeAttribute("disabled");
|
||||||
|
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
const form = field.closest('form');
|
||||||
|
if (form) {
|
||||||
|
form.submit(); // manually submit the form
|
||||||
|
} else {
|
||||||
|
//fallback if not in a form
|
||||||
|
const href = field.getAttribute("href") || field.dataset.href;
|
||||||
|
if (href) window.location.href = href;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$(function() {
|
}, delay);
|
||||||
$('[data-toggle="tooltip"]').tooltip()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function ask(field, text){
|
return false;
|
||||||
var txt = field.innerHTML;
|
}
|
||||||
if (confirm(text)) {
|
|
||||||
setTimeout(() => {
|
|
||||||
field.innerHTML = field.innerHTML.replace(`<span class="loading"></span>`, txt);
|
|
||||||
field.removeAttribute("disabled");
|
|
||||||
}, 5000);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
field.innerHTML = field.innerHTML.replace(`<span class="loading"></span>`, txt);
|
|
||||||
field.removeAttribute("disabled");
|
|
||||||
}, 500);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCookie(name, value, days) {
|
function setCookie(name, value, days) {
|
||||||
var expires = "";
|
var expires = "";
|
||||||
if (days) {
|
if (days) {
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
expires = "; expires=" + date.toUTCString();
|
expires = "; expires=" + date.toUTCString();
|
||||||
}
|
|
||||||
document.cookie = name + "=" + (value || "") + expires + "; path=/";
|
|
||||||
}
|
}
|
||||||
|
document.cookie = name + "=" + (value || "") + expires + "; path=/";
|
||||||
|
}
|
||||||
|
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
var nameEQ = name + "=";
|
var nameEQ = name + "=";
|
||||||
var ca = document.cookie.split(';');
|
var ca = document.cookie.split(';');
|
||||||
for (var i = 0; i < ca.length; i++) {
|
for (var i = 0; i < ca.length; i++) {
|
||||||
var c = ca[i];
|
var c = ca[i];
|
||||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
</script>
|
return null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
{/literal}
|
{/literal}
|
||||||
<script>
|
<script>
|
||||||
setCookie('user_language', '{$user_language}', 365);
|
setCookie('user_language', '{$user_language}', 365);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li>
|
<li>
|
||||||
<a class="toggle-container" href="#">
|
<a class="toggle-container" href="#">
|
||||||
<i class="toggle-icon" id="toggleIcon">🌞</i>
|
<i class="toggle-icon" id="toggleIcon">🌜</i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="dropdown tasks-menu">
|
<li class="dropdown tasks-menu">
|
||||||
@ -164,7 +164,7 @@
|
|||||||
<li {if $_system_menu eq 'history'}class="active" {/if}>
|
<li {if $_system_menu eq 'history'}class="active" {/if}>
|
||||||
<a href="{Text::url('order/history')}">
|
<a href="{Text::url('order/history')}">
|
||||||
<i class="fa fa-file-text"></i>
|
<i class="fa fa-file-text"></i>
|
||||||
<span>{Lang::T('Order History')}</span>
|
<span>{Lang::T('Payment History')}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,21 +1,70 @@
|
|||||||
{include file="customer/header.tpl"}
|
{if empty($_user)}
|
||||||
|
{include file="customer/header-public.tpl"}
|
||||||
|
{else}
|
||||||
|
{include file="customer/header.tpl"}
|
||||||
|
{/if}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.4/jspdf.min.js"></script>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<div class="col-md-6 col-md-offset-3">
|
||||||
<div class="panel panel-hovered panel-primary panel-stacked mb30">
|
<div class="panel panel-hovered panel-primary panel-stacked mb30">
|
||||||
<div class="panel-heading">{$in['invoice']}</div>
|
<div class="panel-heading">{$in['invoice']}</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
{if !empty($logo)}
|
||||||
|
<center><img src="{$app_url}/{$logo}"></center>
|
||||||
|
{/if}
|
||||||
<form class="form-horizontal" method="post" action="{Text::url('plan/print')}" target="_blank">
|
<form class="form-horizontal" method="post" action="{Text::url('plan/print')}" target="_blank">
|
||||||
<pre id="content" style="text-align: center;">{$invoice}</pre>
|
<pre id="content"
|
||||||
|
style="border: 0px; ;text-align: center; background-color: transparent; background-image: url('{$app_url}/system/uploads/paid.png');background-repeat:no-repeat;background-position: center">{$invoice}</pre>
|
||||||
<input type="hidden" name="id" value="{$in['id']}">
|
<input type="hidden" name="id" value="{$in['id']}">
|
||||||
<a href="{Text::url('voucher/list-activated')}" class="btn btn-default btn-sm"><i
|
{if !empty($_user)}
|
||||||
|
<a href="{Text::url('voucher/list-activated')}" class="btn btn-default btn-sm"><i
|
||||||
class="ion-reply-all"></i>{Lang::T('Finish')}</a>
|
class="ion-reply-all"></i>{Lang::T('Finish')}</a>
|
||||||
<a href="https://api.whatsapp.com/send/?text={$whatsapp}" target="_blank"
|
{/if}
|
||||||
class="btn btn-primary btn-sm">
|
<a href="javascript:download()" class="btn btn-success btn-sm text-black">
|
||||||
|
<i class="glyphicon glyphicon-share"></i> Download</a>
|
||||||
|
<a href="https://api.whatsapp.com/send/?text={$whatsapp}" class="btn btn-primary btn-sm">
|
||||||
<i class="glyphicon glyphicon-share"></i> WhatsApp</a>
|
<i class="glyphicon glyphicon-share"></i> WhatsApp</a>
|
||||||
|
<br><br>
|
||||||
|
<input type="text" class="form-control form-sm" style="border: 0px; padding: 1px; background-color: white;" readonly onclick="this.select()" value="{$public_url}">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.font = '16px Courier';
|
||||||
|
var text = document.getElementById("content").innerHTML;
|
||||||
|
var lines = text.split(/\r\n|\r|\n/).length;
|
||||||
|
var meas = ctx.measureText("A");
|
||||||
|
let width = Math.round({$_c['printer_cols']} * 9.6);
|
||||||
|
var height = Math.round((14 * lines));
|
||||||
|
console.log(width, height, lines);
|
||||||
|
var paid = new Image();
|
||||||
|
paid.src = '{$app_url}/system/uploads/paid.png';
|
||||||
|
{if !empty($logo)}
|
||||||
|
var img = new Image();
|
||||||
|
img.src = '{$app_url}/{$logo}?{time()}';
|
||||||
|
var new_width = (width / 4) * 2;
|
||||||
|
var new_height = Math.ceil({$hlogo} * (new_width/{$wlogo}));
|
||||||
|
height = height + new_height;
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
function download() {
|
||||||
|
var doc = new jsPDF('p', 'px', [width, height]);
|
||||||
|
{if !empty($logo)}
|
||||||
|
try {
|
||||||
|
doc.addImage(img, 'PNG', (width - new_width) / 2, 10, new_width, new_height);
|
||||||
|
} catch (err) {}
|
||||||
|
{/if}
|
||||||
|
try {
|
||||||
|
doc.addImage(paid, 'PNG', (width - 200) / 2, (height - 145) / 2, 200, 145);
|
||||||
|
} catch (err) {}
|
||||||
|
doc.setFont("Courier");
|
||||||
|
doc.setFontSize(16);
|
||||||
|
doc.text($('#content').html(), width / 2, new_height + 30, 'center');
|
||||||
|
doc.save('{$in['invoice']}.pdf');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
{include file="customer/footer.tpl"}
|
{include file="customer/footer.tpl"}
|
@ -3,13 +3,12 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<title>{$_title} - {$_c['CompanyName']}</title>
|
<title>{$_title} - {$_c['CompanyName']}</title>
|
||||||
<link rel="shortcut icon" href="./{$favicon}" type="image/x-icon" />
|
<link rel="shortcut icon" href="./{$favicon}" type="image/x-icon" />
|
||||||
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css' />
|
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css' />
|
||||||
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css' />
|
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css' />
|
||||||
<!-- SweetAlert CSS -->
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" />
|
||||||
<!-- SweetAlert JS -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
.login-fg .form-container {
|
.login-fg .form-container {
|
||||||
@ -364,28 +363,6 @@
|
|||||||
color: #db4437;
|
color: #db4437;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
|
||||||
.login-fg .info h1 {
|
|
||||||
font-size: 45px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 992px) {
|
|
||||||
.login-fg .bg {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.login-fg .login-section .social li a {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-fg .logo a {
|
|
||||||
font-size: 26px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -429,6 +406,47 @@
|
|||||||
background-color: #00f2fe;
|
background-color: #00f2fe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.submit-btn {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-btn:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
input[type="password"] {
|
||||||
|
inputmode: "verbatim";
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.login-fg .info h1 {
|
||||||
|
font-size: 45px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.login-fg .bg {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
|
||||||
input,
|
input,
|
||||||
@ -436,14 +454,76 @@
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section {
|
||||||
|
padding: 15px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .logo img {
|
||||||
|
max-width: 200px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .info h1 {
|
||||||
|
font-size: 32px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .info p {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
position: fixed;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section .social li a {
|
||||||
|
width: 100%;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox a {
|
||||||
|
display: block;
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section .social li a {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .logo a {
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
|
.login-fg .login {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section h4 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,18 +540,18 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
{if isset($notify)}
|
{if isset($notify)}
|
||||||
<script>
|
<script>
|
||||||
// Display SweetAlert toast notification
|
// Display SweetAlert toast notification
|
||||||
document.body.style.overflow = 'hidden';
|
document.body.style.overflow = 'hidden';
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: '{if $notify_t == "s"}success{else}warning{/if}',
|
icon: '{if $notify_t == "s"}success{else}warning{/if}',
|
||||||
title: '{if $notify_t == "s"}Success{else}Error{/if}',
|
title: '{if $notify_t == "s"}Success{else}Error{/if}',
|
||||||
text: '{$notify}',
|
text: '{$notify}',
|
||||||
backdrop: 'rgba(0, 0, 0, 0.5)',
|
backdrop: 'rgba(0, 0, 0, 0.5)',
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{/if}
|
{/if}
|
||||||
<!-- partial:index.partial.html -->
|
<!-- partial:index.partial.html -->
|
||||||
<div class="login-fg">
|
<div class="login-fg">
|
||||||
@ -530,8 +610,8 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{if $_c['disable_registration'] != 'noreg'}
|
{if $_c['disable_registration'] != 'noreg'}
|
||||||
<p>{Lang::T('Don\'t have an account?')} <a href="{Text::url('register')}"
|
<p>{Lang::T('Don\'t have an account?')} <a href="{Text::url('register')}"
|
||||||
class="linkButton">{Lang::T('Register')}</a></p>
|
class="linkButton">{Lang::T('Register')}</a></p>
|
||||||
{/if}
|
{/if}
|
||||||
<footer>
|
<footer>
|
||||||
© {$smarty.now|date_format:"%Y"} {$_c['CompanyName']}. All rights reserved. <br> <a
|
© {$smarty.now|date_format:"%Y"} {$_c['CompanyName']}. All rights reserved. <br> <a
|
||||||
@ -551,25 +631,25 @@
|
|||||||
const loginBtn = document.getElementById('login-btn');
|
const loginBtn = document.getElementById('login-btn');
|
||||||
const loginText = document.getElementById('login-text');
|
const loginText = document.getElementById('login-text');
|
||||||
|
|
||||||
loginForm.addEventListener('submit', function(event) {
|
loginForm.addEventListener('submit', function (event) {
|
||||||
loginBtn.classList.add('loading');
|
loginBtn.classList.add('loading');
|
||||||
loginText.textContent = 'Please Wait...';
|
loginText.textContent = 'Please Wait...';
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{if $_c['tawkto'] != ''}
|
{if $_c['tawkto'] != ''}
|
||||||
<!--Start of Tawk.to Script-->
|
<!--Start of Tawk.to Script-->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var isLoggedIn = false;
|
var isLoggedIn = false;
|
||||||
var Tawk_API = {
|
var Tawk_API = {
|
||||||
onLoad: function() {
|
onLoad: function () {
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
isLoggedIn = true;
|
isLoggedIn = true;
|
||||||
window.Tawk_API.login({
|
window.Tawk_API.login({
|
||||||
name: '{$_user['fullname']}',
|
name: '{$_user['fullname']}',
|
||||||
email: '{$_user['email']}',
|
email: '{$_user['email']}',
|
||||||
userId: '{$_user['id']}'
|
userId: '{$_user['id']}'
|
||||||
}, function(error) {
|
}, function (error) {
|
||||||
//do something if there's an error
|
//do something if there's an error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Tawk_API.setAttributes({
|
Tawk_API.setAttributes({
|
||||||
@ -579,7 +659,7 @@
|
|||||||
'balance': '{$_user['balance']}',
|
'balance': '{$_user['balance']}',
|
||||||
'account_type': '{$_user['account_type']}',
|
'account_type': '{$_user['account_type']}',
|
||||||
'phone': '{$_user['phonenumber']}'
|
'phone': '{$_user['phonenumber']}'
|
||||||
}, function(error) {});
|
}, function (error) { });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var Tawk_LoadStart = new Date();
|
var Tawk_LoadStart = new Date();
|
||||||
@ -588,18 +668,44 @@
|
|||||||
email: '{$_user['email']}',
|
email: '{$_user['email']}',
|
||||||
userId: '{$_user['id']}'
|
userId: '{$_user['id']}'
|
||||||
};
|
};
|
||||||
(function() {
|
(function () {
|
||||||
var s1 = document.createElement("script"),
|
var s1 = document.createElement("script"),
|
||||||
s0 = document.getElementsByTagName("script")[0];
|
s0 = document.getElementsByTagName("script")[0];
|
||||||
s1.async = true;
|
s1.async = true;
|
||||||
s1.src = 'https://embed.tawk.to/{$_c['tawkto']}';
|
s1.src = 'https://embed.tawk.to/{$_c['tawkto']}';
|
||||||
s1.charset = 'UTF-8';
|
s1.charset = 'UTF-8';
|
||||||
s1.setAttribute('crossorigin', '*');
|
s1.setAttribute('crossorigin', '*');
|
||||||
s0.parentNode.insertBefore(s1, s0);
|
s0.parentNode.insertBefore(s1, s0);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<!--End of Tawk.to Script-->
|
<!--End of Tawk.to Script-->
|
||||||
{/if}
|
{/if}
|
||||||
|
<script>
|
||||||
|
document.body.classList.add('touch-device');
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
let metaViewport = document.querySelector('meta[name="viewport"]');
|
||||||
|
const originalContent = metaViewport.content;
|
||||||
|
|
||||||
|
document.querySelectorAll('input').forEach(input => {
|
||||||
|
input.addEventListener('focus', () => {
|
||||||
|
metaViewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0';
|
||||||
|
});
|
||||||
|
|
||||||
|
input.addEventListener('blur', () => {
|
||||||
|
metaViewport.content = originalContent;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Touch detection
|
||||||
|
if ('ontouchstart' in window) {
|
||||||
|
document.body.classList.add('touch-device');
|
||||||
|
} else {
|
||||||
|
document.body.classList.add('no-touch');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
448
ui/ui/customer/reg-login-custom-moon.tpl
Normal file
448
ui/ui/customer/reg-login-custom-moon.tpl
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<title>{$_title} - {$_c['CompanyName']}</title>
|
||||||
|
<link rel="shortcut icon" href="./{$favicon}" type="image/x-icon" />
|
||||||
|
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css' />
|
||||||
|
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css' />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js"></script>
|
||||||
|
<style>
|
||||||
|
.login-fg .form-container {
|
||||||
|
color: #ccc;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login {
|
||||||
|
min-height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 30px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section {
|
||||||
|
max-width: 370px;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-fg {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-container .form-group {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-container .input-text {
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
color: #616161;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-container img {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-container .form-fg input {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
padding: 11px 45px 11px 20px;
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-container .form-fg i {
|
||||||
|
position: absolute;
|
||||||
|
top: 13px;
|
||||||
|
right: 20px;
|
||||||
|
font-size: 19px;
|
||||||
|
color: #616161;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-container label {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-container .btn-md {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 10px 30px 9px;
|
||||||
|
height: 45px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: "Open Sans", sans-serif;
|
||||||
|
border-radius: 50px;
|
||||||
|
color: #d6d6d6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-container .btn-fg {
|
||||||
|
background: #0f96f9;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .form-container .btn-fg:hover {
|
||||||
|
background: #108ae4;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: inherit;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 1px 0;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
border-color: #4facfe;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-btn {
|
||||||
|
background-color: #4facfe;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-btn:hover {
|
||||||
|
background-color: #00f2fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-btn {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-btn:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-section {
|
||||||
|
max-height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #888 #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-section::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-section::-webkit-scrollbar-track {
|
||||||
|
background: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-section::-webkit-scrollbar-thumb {
|
||||||
|
background: #888;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-section::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .info h1 {
|
||||||
|
font-size: 60px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-shadow: 2px 0px #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .info p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 28px;
|
||||||
|
text-shadow: 1px 1px #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .info {
|
||||||
|
text-align: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 33%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
|
||||||
|
input,
|
||||||
|
.submit-btn {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.login-fg .info h1 {
|
||||||
|
font-size: 45px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.login-fg .bg {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
|
||||||
|
input,
|
||||||
|
.submit-btn {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section {
|
||||||
|
padding: 15px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .logo img {
|
||||||
|
max-width: 200px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .info h1 {
|
||||||
|
font-size: 32px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .info p {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
position: fixed;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section .social li a {
|
||||||
|
width: 100%;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox a {
|
||||||
|
display: block;
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section .social li a {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .logo a {
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.login-fg .login {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
.submit-btn {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-fg .login-section h4 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 320px) {
|
||||||
|
|
||||||
|
input,
|
||||||
|
.submit-btn {
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!-- SweetAlert Notification -->
|
||||||
|
{if isset($notify)}
|
||||||
|
<script>
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
Swal.fire({
|
||||||
|
icon: '{if $notify_t == "s"}success{else}warning{/if}',
|
||||||
|
title: '{if $notify_t == "s"}Success{else}Error{/if}',
|
||||||
|
html: '{$notify}',
|
||||||
|
backdrop: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
}).then(() => {
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="login-fg">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-8 col-lg-7 col-md-12 bg"
|
||||||
|
style="background-image:url('./{$wallpaper}'); background-attachment: fixed;">
|
||||||
|
<div class="info">
|
||||||
|
<h1>{$_c['login_page_head']}</h1>
|
||||||
|
<p>{$_c['login_page_description']}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xl-4 col-lg-5 col-md-12 login">
|
||||||
|
<div class="login-section">
|
||||||
|
<div class="logo clearfix">
|
||||||
|
<a href="./{$login_logo}" target="_blank"><img src="./{$login_logo}" height="60"
|
||||||
|
alt="Logo"></a>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<h4>{Lang::T('Create your account')}</h4>
|
||||||
|
<div class="form-container">
|
||||||
|
<form id="register-form" method="POST" action="{Text::url('register/post')}">
|
||||||
|
<input type="hidden" name="csrf_token" value="{$csrf_token}">
|
||||||
|
|
||||||
|
<!-- Basic Information (Initially Visible) -->
|
||||||
|
<div id="basicFields">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name="username"
|
||||||
|
placeholder="{if $_c['country_code_phone']!= '' || $_c['registration_username'] == 'phone'}{$_c['country_code_phone']} {Lang::T('Phone Number')}{elseif $_c['registration_username'] == 'email'}{Lang::T('Email')}{else}{Lang::T('Usernames')}{/if}">
|
||||||
|
</div>
|
||||||
|
{if $_c['photo_register'] == 'yes'}
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="file" required id="photo" name="photo"
|
||||||
|
accept="image/*">
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name="fullname" placeholder="{Lang::T('Full Name')}"
|
||||||
|
{if $_c['man_fields_fname'] neq 'no'}required{/if} >
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="email" name="email" placeholder="{Lang::T('Email Address')}"
|
||||||
|
{if $_c['man_fields_email'] neq 'no'}required{/if}>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="button" onclick="toggleFields()" class="submit-btn">
|
||||||
|
{Lang::T('Next Step')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Password Fields (Initially Hidden) -->
|
||||||
|
<div id="passwordFields" style="display: none;">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name="address" placeholder="{Lang::T('Home Address')}"
|
||||||
|
{if $_c['man_fields_address'] neq 'no'}required{/if}>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" name="password" placeholder="{Lang::T('Password')}"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" name="cpassword"
|
||||||
|
placeholder="{Lang::T('Confirm Password')}" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button id="register-btn" type="submit" class="submit-btn">
|
||||||
|
<span id="register-text">{Lang::T('Register')}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<p>{Lang::T('Already have an account?')} <a href="{Text::url('login')}"
|
||||||
|
class="linkButton">{Lang::T('Login')}</a></p>
|
||||||
|
<footer>
|
||||||
|
© {$smarty.now|date_format:"%Y"} {$_c['CompanyName']}. All rights reserved. <br> <a
|
||||||
|
href="pages/Privacy_Policy.html">Privacy</a> | <a
|
||||||
|
href="pages/Terms_and_Conditions.html">Terms & Conditions</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const registerForm = document.getElementById('register-form');
|
||||||
|
const registerBtn = document.getElementById('register-btn');
|
||||||
|
const registerText = document.getElementById('register-text');
|
||||||
|
registerForm.addEventListener('submit', function (event) {
|
||||||
|
registerBtn.classList.add('loading');
|
||||||
|
registerText.textContent = 'Please Wait...';
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleFields() {
|
||||||
|
|
||||||
|
document.getElementById('basicFields').style.display = 'none';
|
||||||
|
document.getElementById('passwordFields').style.display = 'block';
|
||||||
|
|
||||||
|
const backButton = document.createElement('button');
|
||||||
|
backButton.className = 'submit-btn';
|
||||||
|
backButton.textContent = '← Back';
|
||||||
|
backButton.style.marginBottom = '15px';
|
||||||
|
backButton.onclick = () => {
|
||||||
|
document.getElementById('basicFields').style.display = 'block';
|
||||||
|
document.getElementById('passwordFields').style.display = 'none';
|
||||||
|
backButton.remove();
|
||||||
|
};
|
||||||
|
document.getElementById('passwordFields').prepend(backButton);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -4,7 +4,20 @@
|
|||||||
<table class="table table-condensed">
|
<table class="table table-condensed">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{Lang::T('Username')}</th>
|
<th>
|
||||||
|
<select style="border: 0px; width: 100%; background-color: #f9f9f9;"
|
||||||
|
onchange="changeExpiredDefault(this)">
|
||||||
|
<option value="username" {if $cookie['expdef'] == 'username'}selected{/if}>
|
||||||
|
{Lang::T('Username')}
|
||||||
|
</option>
|
||||||
|
<option value="fullname" {if $cookie['expdef'] == 'fullname'}selected{/if}>
|
||||||
|
{Lang::T('Full Name')}</option>
|
||||||
|
<option value="phone" {if $cookie['expdef'] == 'phone'}selected{/if}>{Lang::T('Phone')}
|
||||||
|
</option>
|
||||||
|
<option value="email" {if $cookie['expdef'] == 'email'}selected{/if}>{Lang::T('Email')}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</th>
|
||||||
<th>{Lang::T('Created / Expired')}</th>
|
<th>{Lang::T('Created / Expired')}</th>
|
||||||
<th>{Lang::T('Internet Package')}</th>
|
<th>{Lang::T('Internet Package')}</th>
|
||||||
<th>{Lang::T('Location')}</th>
|
<th>{Lang::T('Location')}</th>
|
||||||
@ -15,7 +28,17 @@
|
|||||||
{assign var="rem_exp" value="{$expired['expiration']} {$expired['time']}"}
|
{assign var="rem_exp" value="{$expired['expiration']} {$expired['time']}"}
|
||||||
{assign var="rem_started" value="{$expired['recharged_on']} {$expired['recharged_time']}"}
|
{assign var="rem_started" value="{$expired['recharged_on']} {$expired['recharged_time']}"}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{Text::url('customers/viewu/',$expired['username'])}">{$expired['username']}</a></td>
|
<td><a href="{Text::url('customers/view/',$expired['id'])}">
|
||||||
|
{if $cookie['expdef'] == 'fullname'}
|
||||||
|
{$expired['fullname']}
|
||||||
|
{elseif $cookie['expdef'] == 'phone'}
|
||||||
|
{$expired['phonenumber']}
|
||||||
|
{elseif $cookie['expdef'] == 'email'}
|
||||||
|
{$expired['email']}
|
||||||
|
{else}
|
||||||
|
{$expired['username']}
|
||||||
|
{/if}
|
||||||
|
</a></td>
|
||||||
<td><small data-toggle="tooltip" data-placement="top"
|
<td><small data-toggle="tooltip" data-placement="top"
|
||||||
title="{Lang::dateAndTimeFormat($expired['recharged_on'],$expired['recharged_time'])}">{Lang::timeElapsed($rem_started)}</small>
|
title="{Lang::dateAndTimeFormat($expired['recharged_on'],$expired['recharged_time'])}">{Lang::timeElapsed($rem_started)}</small>
|
||||||
/
|
/
|
||||||
@ -30,4 +53,12 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{include file="pagination.tpl"}
|
{include file="pagination.tpl"}
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
function changeExpiredDefault(fl) {
|
||||||
|
setCookie('expdef', fl.value, 365);
|
||||||
|
setTimeout(() => {
|
||||||
|
location.reload();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"version": "2025.3.10"
|
"version": "2025.3.20"
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user