Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b8a8ef0c8e | |||
| def320f488 | |||
| bdad2adfff | |||
| 68c1a4b782 | |||
| b06fa81593 | |||
| 4e0bf2408f | |||
| c0a2ce105c |
24
system/.htaccess
Normal file
24
system/.htaccess
Normal file
@@ -0,0 +1,24 @@
|
||||
<Files *.php>
|
||||
Order Deny,Allow
|
||||
Deny from all
|
||||
</Files>
|
||||
|
||||
<Files cron.php>
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</Files>
|
||||
|
||||
<Files api.php>
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</Files>
|
||||
|
||||
<Files cron_reminder.php>
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</Files>
|
||||
|
||||
<Files download.php>
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</Files>
|
||||
127
system/api.php
Normal file
127
system/api.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
*
|
||||
* This File is for API Access
|
||||
**/
|
||||
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === "OPTIONS" || $_SERVER['REQUEST_METHOD'] === "HEAD") {
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization");
|
||||
header("HTTP/1.1 200 OK");
|
||||
die();
|
||||
}
|
||||
|
||||
$isApi = true;
|
||||
|
||||
include "../init.php";
|
||||
|
||||
// Dummy Class
|
||||
$ui = new class($key)
|
||||
{
|
||||
var $assign = [];
|
||||
function display($key)
|
||||
{
|
||||
global $req;
|
||||
showResult(true, $req, $this->getAll());
|
||||
}
|
||||
function assign($key, $value)
|
||||
{
|
||||
$this->assign[$key] = $value;
|
||||
}
|
||||
function get($key)
|
||||
{
|
||||
if (isset($this->assign[$key])) {
|
||||
return $this->assign[$key];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
function getTemplateVars($key)
|
||||
{
|
||||
if (isset($this->assign[$key])) {
|
||||
return $this->assign[$key];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
function getAll()
|
||||
{
|
||||
return $this->assign;
|
||||
}
|
||||
};
|
||||
|
||||
$req = _get('r');
|
||||
# a/c.id.time.md5
|
||||
# md5(a/c.id.time.$api_secret)
|
||||
$token = _req('token');
|
||||
$routes = explode('/', $req);
|
||||
$handler = $routes[0];
|
||||
|
||||
if (!empty($token)) {
|
||||
if ($token == $config['api_key']) {
|
||||
$admin = ORM::for_table('tbl_users')->where('user_type', 'SuperAdmin')->find_one($id);
|
||||
if (empty($admin)) {
|
||||
$admin = ORM::for_table('tbl_users')->where('user_type', 'Admin')->find_one($id);
|
||||
if (empty($admin)) {
|
||||
showResult(false, Lang::T("Token is invalid"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# validate token
|
||||
list($tipe, $uid, $time, $sha1) = explode('.', $token);
|
||||
if (trim($sha1) != sha1($uid . '.' . $time . '.' . $api_secret)) {
|
||||
showResult(false, Lang::T("Token is invalid"));
|
||||
}
|
||||
|
||||
#cek token expiration
|
||||
// 3 bulan
|
||||
if ($time != 0 && time() - $time > 7776000) {
|
||||
die("$time != " . (time() - $time));
|
||||
showResult(false, Lang::T("Token Expired"), [], ['login' => true]);
|
||||
}
|
||||
|
||||
if ($tipe == 'a') {
|
||||
$_SESSION['aid'] = $uid;
|
||||
$admin = Admin::_info();
|
||||
} else if ($tipe == 'c') {
|
||||
$_SESSION['uid'] = $uid;
|
||||
} else {
|
||||
showResult(false, Lang::T("Unknown Token"), [], ['login' => true]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($handler) || empty($handler)) {
|
||||
showResult(true, Lang::T("Token is valid"));
|
||||
}
|
||||
|
||||
|
||||
if ($handler == 'isValid') {
|
||||
showResult(true, Lang::T("Token is valid"));
|
||||
}
|
||||
|
||||
if ($handler == 'me') {
|
||||
$admin = Admin::_info();
|
||||
if (!empty($admin['id'])) {
|
||||
showResult(true, "", $admin);
|
||||
} else {
|
||||
showResult(false, Lang::T("Token is invalid"));
|
||||
}
|
||||
}
|
||||
}else{
|
||||
unset($_COOKIE);
|
||||
unset($_SESSION);
|
||||
}
|
||||
|
||||
try {
|
||||
$sys_render = File::pathFixer($root_path . 'system/controllers/' . $handler . '.php');
|
||||
if (file_exists($sys_render)) {
|
||||
include($sys_render);
|
||||
showResult(true, $req, $ui->getAll());
|
||||
} else {
|
||||
showResult(false, Lang::T('Command not found'));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
showResult(false, $e->getMessage());
|
||||
}
|
||||
61
system/autoload/Admin.php
Normal file
61
system/autoload/Admin.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
class Admin
|
||||
{
|
||||
|
||||
public static function getID()
|
||||
{
|
||||
global $db_password;
|
||||
if (isset($_SESSION['aid'])) {
|
||||
return $_SESSION['aid'];
|
||||
} else if (isset($_COOKIE['aid'])) {
|
||||
// id.time.sha1
|
||||
$tmp = explode('.', $_COOKIE['aid']);
|
||||
if (sha1($tmp[0] . '.' . $tmp[1] . '.' . $db_password) == $tmp[2]) {
|
||||
if (time() - $tmp[1] < 86400 * 7) {
|
||||
$_SESSION['aid'] = $tmp[0];
|
||||
return $tmp[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function setCookie($aid)
|
||||
{
|
||||
global $db_password;
|
||||
if (isset($aid)) {
|
||||
$time = time();
|
||||
$token = $aid . '.' . $time . '.' . sha1($aid . '.' . $time . '.' . $db_password);
|
||||
setcookie('aid', $token, time() + 86400 * 7);
|
||||
return $token;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
public static function removeCookie()
|
||||
{
|
||||
if (isset($_COOKIE['aid'])) {
|
||||
setcookie('aid', '', time() - 86400);
|
||||
}
|
||||
}
|
||||
|
||||
public static function _info($id = 0)
|
||||
{
|
||||
if (empty($id) && $id == 0) {
|
||||
$id = Admin::getID();
|
||||
}
|
||||
if ($id) {
|
||||
return ORM::for_table('tbl_users')->find_one($id);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
system/autoload/App.php
Normal file
29
system/autoload/App.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
class App{
|
||||
public static function _run(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getToken(){
|
||||
return md5(microtime());
|
||||
}
|
||||
|
||||
public static function setToken($token, $value){
|
||||
$_SESSION[$token] = $value;
|
||||
}
|
||||
|
||||
public static function getTokenValue($key){
|
||||
if(isset($_SESSION[$key])){
|
||||
return $_SESSION[$key];
|
||||
}else{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
64
system/autoload/Balance.php
Normal file
64
system/autoload/Balance.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
/**
|
||||
* This script is for managing user balance
|
||||
**/
|
||||
|
||||
class Balance
|
||||
{
|
||||
|
||||
public static function plus($id_customer, $amount)
|
||||
{
|
||||
$c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one();
|
||||
$c->balance = $amount + $c['balance'];
|
||||
$c->save();
|
||||
}
|
||||
|
||||
public static function transfer($id_customer, $phoneTarget, $amount)
|
||||
{
|
||||
global $config;
|
||||
if (Balance::min($id_customer, $amount)) {
|
||||
return Balance::plusByPhone($phoneTarget, $amount);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function min($id_customer, $amount)
|
||||
{
|
||||
$c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one();
|
||||
if ($c && $c['balance'] >= $amount) {
|
||||
$c->balance = $c['balance'] - $amount;
|
||||
$c->save();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function plusByPhone($phone_customer, $amount)
|
||||
{
|
||||
$c = ORM::for_table('tbl_customers')->where('username', $phone_customer)->find_one();
|
||||
if ($c) {
|
||||
$c->balance = $amount + $c['balance'];
|
||||
$c->save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function minByPhone($phone_customer, $amount)
|
||||
{
|
||||
$c = ORM::for_table('tbl_customers')->where('username', $phone_customer)->find_one();
|
||||
if ($c && $c['balance'] >= $amount) {
|
||||
$c->balance = $c['balance'] - $amount;
|
||||
$c->save();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
283
system/autoload/CronLog.php
Normal file
283
system/autoload/CronLog.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Cron Logging System
|
||||
* Tracks cron job execution and performance
|
||||
*/
|
||||
|
||||
class CronLog
|
||||
{
|
||||
private static $logId = null;
|
||||
private static $startTime = null;
|
||||
private static $startMemory = null;
|
||||
|
||||
/**
|
||||
* Start logging a cron job
|
||||
* @param string $cronType Type of cron job (main, reminder, etc.)
|
||||
* @return int Log ID
|
||||
*/
|
||||
public static function start($cronType = 'main')
|
||||
{
|
||||
self::$startTime = microtime(true);
|
||||
self::$startMemory = memory_get_usage();
|
||||
|
||||
$log = ORM::for_table('tbl_cron_logs')->create();
|
||||
$log->cron_type = $cronType;
|
||||
$log->started_at = date('Y-m-d H:i:s');
|
||||
$log->status = 'running';
|
||||
$log->save();
|
||||
|
||||
self::$logId = $log->id();
|
||||
return self::$logId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update log with current statistics
|
||||
* @param array $stats Statistics array
|
||||
*/
|
||||
public static function updateStats($stats = [])
|
||||
{
|
||||
if (!self::$logId) return;
|
||||
|
||||
$log = ORM::for_table('tbl_cron_logs')->find_one(self::$logId);
|
||||
if (!$log) return;
|
||||
|
||||
foreach ($stats as $key => $value) {
|
||||
if (property_exists($log, $key)) {
|
||||
$log->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$log->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment a counter
|
||||
* @param string $field Field to increment
|
||||
* @param int $amount Amount to increment by
|
||||
*/
|
||||
public static function increment($field, $amount = 1)
|
||||
{
|
||||
if (!self::$logId) return;
|
||||
|
||||
$log = ORM::for_table('tbl_cron_logs')->find_one(self::$logId);
|
||||
if (!$log) return;
|
||||
|
||||
if (property_exists($log, $field)) {
|
||||
$log->$field = $log->$field + $amount;
|
||||
$log->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the cron job successfully
|
||||
* @param array $finalStats Final statistics
|
||||
*/
|
||||
public static function complete($finalStats = [])
|
||||
{
|
||||
if (!self::$logId) return;
|
||||
|
||||
$log = ORM::for_table('tbl_cron_logs')->find_one(self::$logId);
|
||||
if (!$log) return;
|
||||
|
||||
$log->finished_at = date('Y-m-d H:i:s');
|
||||
$log->status = 'completed';
|
||||
|
||||
// Calculate execution time
|
||||
if (self::$startTime) {
|
||||
$log->execution_time = round(microtime(true) - self::$startTime, 3);
|
||||
}
|
||||
|
||||
// Calculate memory usage
|
||||
if (self::$startMemory) {
|
||||
$currentMemory = memory_get_usage();
|
||||
$peakMemory = memory_get_peak_usage();
|
||||
$log->memory_usage = self::formatBytes($peakMemory - self::$startMemory);
|
||||
}
|
||||
|
||||
// Update final stats
|
||||
foreach ($finalStats as $key => $value) {
|
||||
if (property_exists($log, $key)) {
|
||||
$log->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$log->save();
|
||||
|
||||
// Clean up old logs (keep last 1000 records)
|
||||
self::cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark cron job as failed
|
||||
* @param string $errorMessage Error message
|
||||
* @param array $finalStats Final statistics
|
||||
*/
|
||||
public static function fail($errorMessage, $finalStats = [])
|
||||
{
|
||||
if (!self::$logId) return;
|
||||
|
||||
$log = ORM::for_table('tbl_cron_logs')->find_one(self::$logId);
|
||||
if (!$log) return;
|
||||
|
||||
$log->finished_at = date('Y-m-d H:i:s');
|
||||
$log->status = 'failed';
|
||||
$log->error_message = $errorMessage;
|
||||
|
||||
// Calculate execution time
|
||||
if (self::$startTime) {
|
||||
$log->execution_time = round(microtime(true) - self::$startTime, 3);
|
||||
}
|
||||
|
||||
// Update final stats
|
||||
foreach ($finalStats as $key => $value) {
|
||||
if (property_exists($log, $key)) {
|
||||
$log->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$log->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent cron logs
|
||||
* @param int $limit Number of records to return
|
||||
* @param string $cronType Filter by cron type
|
||||
* @return array
|
||||
*/
|
||||
public static function getRecent($limit = 50, $cronType = null)
|
||||
{
|
||||
$query = ORM::for_table('tbl_cron_logs');
|
||||
|
||||
if ($cronType) {
|
||||
$query->where('cron_type', $cronType);
|
||||
}
|
||||
|
||||
return $query->order_by_desc('started_at')
|
||||
->limit($limit)
|
||||
->find_many();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cron job statistics
|
||||
* @param int $days Number of days to look back
|
||||
* @return array
|
||||
*/
|
||||
public static function getStats($days = 7)
|
||||
{
|
||||
$since = date('Y-m-d H:i:s', strtotime("-$days days"));
|
||||
|
||||
$stats = ORM::for_table('tbl_cron_logs')
|
||||
->where_gte('started_at', $since)
|
||||
->find_many();
|
||||
|
||||
$result = [
|
||||
'total_runs' => count($stats),
|
||||
'successful_runs' => 0,
|
||||
'failed_runs' => 0,
|
||||
'total_expired_users' => 0,
|
||||
'total_notifications_sent' => 0,
|
||||
'total_auto_renewals' => 0,
|
||||
'avg_execution_time' => 0,
|
||||
'last_run' => null,
|
||||
'last_success' => null,
|
||||
'last_failure' => null
|
||||
];
|
||||
|
||||
$totalExecutionTime = 0;
|
||||
$executionCount = 0;
|
||||
|
||||
foreach ($stats as $log) {
|
||||
if ($log->status == 'completed') {
|
||||
$result['successful_runs']++;
|
||||
$result['last_success'] = $log->finished_at;
|
||||
} elseif ($log->status == 'failed') {
|
||||
$result['failed_runs']++;
|
||||
$result['last_failure'] = $log->finished_at;
|
||||
}
|
||||
|
||||
$result['total_expired_users'] += $log->expired_users_processed;
|
||||
$result['total_notifications_sent'] += $log->notifications_sent;
|
||||
$result['total_auto_renewals'] += $log->auto_renewals_successful;
|
||||
|
||||
if ($log->execution_time) {
|
||||
$totalExecutionTime += $log->execution_time;
|
||||
$executionCount++;
|
||||
}
|
||||
|
||||
if (!$result['last_run'] || $log->started_at > $result['last_run']) {
|
||||
$result['last_run'] = $log->started_at;
|
||||
}
|
||||
}
|
||||
|
||||
if ($executionCount > 0) {
|
||||
$result['avg_execution_time'] = round($totalExecutionTime / $executionCount, 3);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up old logs (keep last 1000 records)
|
||||
*/
|
||||
private static function cleanup()
|
||||
{
|
||||
$count = ORM::for_table('tbl_cron_logs')->count();
|
||||
|
||||
if ($count > 1000) {
|
||||
$logsToDelete = ORM::for_table('tbl_cron_logs')
|
||||
->order_by_asc('started_at')
|
||||
->limit($count - 1000)
|
||||
->find_many();
|
||||
|
||||
foreach ($logsToDelete as $log) {
|
||||
$log->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format bytes to human readable format
|
||||
* @param int $bytes
|
||||
* @return string
|
||||
*/
|
||||
private static function formatBytes($bytes)
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB'];
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= pow(1024, $pow);
|
||||
|
||||
return round($bytes, 2) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if cron is running (has a running log in last 5 minutes)
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRunning()
|
||||
{
|
||||
$fiveMinutesAgo = date('Y-m-d H:i:s', strtotime('-5 minutes'));
|
||||
|
||||
$runningLog = ORM::for_table('tbl_cron_logs')
|
||||
->where('status', 'running')
|
||||
->where_gte('started_at', $fiveMinutesAgo)
|
||||
->find_one();
|
||||
|
||||
return $runningLog ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last successful cron run
|
||||
* @return object|null
|
||||
*/
|
||||
public static function getLastSuccess()
|
||||
{
|
||||
return ORM::for_table('tbl_cron_logs')
|
||||
->where('status', 'completed')
|
||||
->order_by_desc('finished_at')
|
||||
->find_one();
|
||||
}
|
||||
}
|
||||
55
system/autoload/Csrf.php
Normal file
55
system/autoload/Csrf.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
class Csrf
|
||||
{
|
||||
private static $tokenExpiration = 1800; // 30 minutes
|
||||
|
||||
public static function generateToken($length = 16)
|
||||
{
|
||||
return bin2hex(random_bytes($length));
|
||||
}
|
||||
|
||||
public static function validateToken($token, $storedToken)
|
||||
{
|
||||
return hash_equals($token, $storedToken);
|
||||
}
|
||||
|
||||
public static function check($token)
|
||||
{
|
||||
global $config;
|
||||
if($config['csrf_enabled'] == 'yes') {
|
||||
if (isset($_SESSION['csrf_token'], $_SESSION['csrf_token_time'], $token)) {
|
||||
$storedToken = $_SESSION['csrf_token'];
|
||||
$tokenTime = $_SESSION['csrf_token_time'];
|
||||
|
||||
if (time() - $tokenTime > self::$tokenExpiration) {
|
||||
self::clearToken();
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::validateToken($token, $storedToken);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function generateAndStoreToken()
|
||||
{
|
||||
$token = self::generateToken();
|
||||
$_SESSION['csrf_token'] = $token;
|
||||
$_SESSION['csrf_token_time'] = time();
|
||||
return $token;
|
||||
}
|
||||
|
||||
public static function clearToken()
|
||||
{
|
||||
unset($_SESSION['csrf_token'], $_SESSION['csrf_token_time']);
|
||||
}
|
||||
}
|
||||
108
system/autoload/File.php
Normal file
108
system/autoload/File.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
class File
|
||||
{
|
||||
|
||||
public static function copyFolder($from, $to, $exclude = [])
|
||||
{
|
||||
$files = scandir($from);
|
||||
foreach ($files as $file) {
|
||||
if (is_file($from . $file) && !in_array($file, $exclude)) {
|
||||
if (file_exists($to . $file)) unlink($to . $file);
|
||||
rename($from . $file, $to . $file);
|
||||
} else if (is_dir($from . $file) && !in_array($file, ['.', '..'])) {
|
||||
if (!file_exists($to . $file)) {
|
||||
mkdir($to . $file);
|
||||
}
|
||||
File::copyFolder($from . $file . DIRECTORY_SEPARATOR, $to . $file . DIRECTORY_SEPARATOR, $exclude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function deleteFolder($path)
|
||||
{
|
||||
$files = scandir($path);
|
||||
foreach ($files as $file) {
|
||||
if (is_file($path . $file)) {
|
||||
unlink($path . $file);
|
||||
} else if (is_dir($path . $file) && !in_array($file, ['.', '..'])) {
|
||||
File::deleteFolder($path . $file . DIRECTORY_SEPARATOR);
|
||||
rmdir($path . $file);
|
||||
}
|
||||
}
|
||||
rmdir($path);
|
||||
}
|
||||
|
||||
public static function resizeCropImage($source_file, $dst_dir, $max_width, $max_height, $quality = 80)
|
||||
{
|
||||
$imgsize = getimagesize($source_file);
|
||||
$width = $imgsize[0];
|
||||
$height = $imgsize[1];
|
||||
$mime = $imgsize['mime'];
|
||||
|
||||
switch ($mime) {
|
||||
case 'image/gif':
|
||||
$image_create = "imagecreatefromgif";
|
||||
$image = "imagegif";
|
||||
break;
|
||||
|
||||
case 'image/png':
|
||||
$image_create = "imagecreatefrompng";
|
||||
$image = "imagepng";
|
||||
$quality = 7;
|
||||
break;
|
||||
|
||||
case 'image/jpeg':
|
||||
$image_create = "imagecreatefromjpeg";
|
||||
$image = "imagejpeg";
|
||||
$quality = 80;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($max_width == 0) {
|
||||
$max_width = $width;
|
||||
}
|
||||
|
||||
if ($max_height == 0) {
|
||||
$max_height = $height;
|
||||
}
|
||||
|
||||
$widthRatio = $max_width / $width;
|
||||
$heightRatio = $max_height / $height;
|
||||
$ratio = min($widthRatio, $heightRatio);
|
||||
$nwidth = (int)$width * $ratio;
|
||||
$nheight = (int)$height * $ratio;
|
||||
|
||||
$dst_img = imagecreatetruecolor($nwidth, $nheight);
|
||||
$white = imagecolorallocate($dst_img, 255, 255, 255);
|
||||
imagefill($dst_img, 0, 0, $white);
|
||||
$src_img = $image_create($source_file);
|
||||
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $nwidth, $nheight, $width, $height);
|
||||
|
||||
$image($dst_img, $dst_dir, $quality);
|
||||
|
||||
if ($dst_img) imagedestroy($dst_img);
|
||||
if ($src_img) imagedestroy($src_img);
|
||||
return file_exists($dst_dir);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* file path fixer
|
||||
*
|
||||
* @access public
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public static function pathFixer($path)
|
||||
{
|
||||
return str_replace("/", DIRECTORY_SEPARATOR, $path);
|
||||
}
|
||||
}
|
||||
57
system/autoload/Hookers.php
Normal file
57
system/autoload/Hookers.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
$menu_registered = array();
|
||||
|
||||
/**
|
||||
* Register for global menu
|
||||
* @param string name Name of the menu
|
||||
* @param bool admin true if for admin and false for customer
|
||||
* @param string function function to run after menu clicks
|
||||
* @param string position position of menu, use AFTER_ for root menu |
|
||||
* Admin/Sales menu: AFTER_DASHBOARD, CUSTOMERS, PREPAID, SERVICES, REPORTS, VOUCHER, AFTER_ORDER, NETWORK, SETTINGS, AFTER_PAYMENTGATEWAY
|
||||
* | Customer menu: AFTER_DASHBOARD, ORDER, HISTORY, ACCOUNTS
|
||||
* @param string icon from ion icon, ion-person, only for AFTER_
|
||||
* @param string label for showing label or number of notification or update
|
||||
* @param string color Label color
|
||||
* @param string auth authorization ['SuperAdmin', 'Admin', 'Report', 'Agent', 'Sales'] will only show in this user, empty array for all users
|
||||
*/
|
||||
function register_menu($name, $admin, $function, $position, $icon = '', $label = '', $color = 'success', $auth = [])
|
||||
{
|
||||
global $menu_registered;
|
||||
$menu_registered[] = [
|
||||
"name" => $name,
|
||||
"admin" => $admin,
|
||||
"position" => $position,
|
||||
"icon" => $icon,
|
||||
"function" => $function,
|
||||
"label" => $label,
|
||||
"color" => $color,
|
||||
"auth" => $auth
|
||||
];
|
||||
}
|
||||
|
||||
$hook_registered = array();
|
||||
|
||||
function register_hook($action, $function){
|
||||
global $hook_registered;
|
||||
$hook_registered[] = [
|
||||
'action' => $action,
|
||||
'function' => $function
|
||||
];
|
||||
}
|
||||
|
||||
function run_hook($action){
|
||||
global $hook_registered;
|
||||
foreach($hook_registered as $hook){
|
||||
if($hook['action'] == $action){
|
||||
if(function_exists($hook['function'])){
|
||||
call_user_func($hook['function']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
system/autoload/Http.php
Normal file
117
system/autoload/Http.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
/**
|
||||
* using proxy, add this variable in config.php
|
||||
* $http_proxy = '127.0.0.1:3128';
|
||||
* if proxy using authentication, use this parameter
|
||||
* $http_proxyauth = 'user:password';
|
||||
**/
|
||||
|
||||
class Http
|
||||
{
|
||||
public static function getData($url, $headers = [])
|
||||
{
|
||||
global $http_proxy, $http_proxyauth, $admin;
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
if(is_array($headers) && count($headers)>0){
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
if (!empty($http_proxy)) {
|
||||
curl_setopt($ch, CURLOPT_PROXY, $http_proxy);
|
||||
if (!empty($http_proxyauth)) {
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $http_proxyauth);
|
||||
}
|
||||
}
|
||||
$server_output = curl_exec($ch);
|
||||
if (curl_errno($ch)) {
|
||||
$error_msg = curl_error($ch);
|
||||
}
|
||||
curl_close($ch);
|
||||
if($admin && $error_msg){
|
||||
r2(U . 'dashboard', 'd', $error_msg);
|
||||
}
|
||||
return ($server_output) ? $server_output : $error_msg;
|
||||
}
|
||||
|
||||
public static function postJsonData($url, $array_post, $headers = [], $basic = null)
|
||||
{
|
||||
global $http_proxy, $http_proxyauth, $admin;
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, false);
|
||||
curl_setopt($ch, CURLINFO_HEADER_OUT, false);
|
||||
if (!empty($http_proxy)) {
|
||||
curl_setopt($ch, CURLOPT_PROXY, $http_proxy);
|
||||
if (!empty($http_proxyauth)) {
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $http_proxyauth);
|
||||
}
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($array_post));
|
||||
if(is_array($headers) && count($headers)>0){
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
}
|
||||
if (!empty($basic)) {
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $basic);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$server_output = curl_exec($ch);
|
||||
if (curl_errno($ch)) {
|
||||
$error_msg = curl_error($ch);
|
||||
}
|
||||
curl_close($ch);
|
||||
if($admin && $error_msg){
|
||||
r2(U . 'dashboard', 'd', $error_msg);
|
||||
}
|
||||
return ($server_output) ? $server_output : $error_msg;
|
||||
}
|
||||
|
||||
|
||||
public static function postData($url, $array_post, $headers = [], $basic = null)
|
||||
{
|
||||
global $http_proxy, $http_proxyauth, $admin;
|
||||
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, false);
|
||||
curl_setopt($ch, CURLINFO_HEADER_OUT, false);
|
||||
if (!empty($http_proxy)) {
|
||||
curl_setopt($ch, CURLOPT_PROXY, $http_proxy);
|
||||
if (!empty($http_proxyauth)) {
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $http_proxyauth);
|
||||
}
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($array_post));
|
||||
if(is_array($headers) && count($headers)>0){
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
}
|
||||
if (!empty($basic)) {
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $basic);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$server_output = curl_exec($ch);
|
||||
if (curl_errno($ch)) {
|
||||
$error_msg = curl_error($ch);
|
||||
}
|
||||
curl_close($ch);
|
||||
if($admin && $error_msg){
|
||||
r2(U . 'dashboard', 'd', $error_msg);
|
||||
}
|
||||
return ($server_output) ? $server_output : $error_msg;
|
||||
}
|
||||
}
|
||||
262
system/autoload/Lang.php
Normal file
262
system/autoload/Lang.php
Normal file
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
class Lang
|
||||
{
|
||||
public static function T($key)
|
||||
{
|
||||
global $_L, $lan_file, $config;
|
||||
if(is_array($_SESSION['Lang'])){
|
||||
$_L = array_merge($_L, $_SESSION['Lang']);
|
||||
}
|
||||
$key = preg_replace('/\s+/', ' ', $key);
|
||||
if (!empty($_L[$key])) {
|
||||
return $_L[$key];
|
||||
}
|
||||
$val = $key;
|
||||
$key = Lang::sanitize($key);
|
||||
if (isset($_L[$key])) {
|
||||
return $_L[$key];
|
||||
} else if (isset($_L[$key])) {
|
||||
return $_L[$key];
|
||||
} else {
|
||||
$iso = Lang::getIsoLang()[$config['language']];
|
||||
if (empty($iso)) {
|
||||
return $val;
|
||||
}
|
||||
if (!empty($iso) && !empty($val)) {
|
||||
$temp = Lang::translate($val, $iso);
|
||||
if (!empty($temp)) {
|
||||
$val = $temp;
|
||||
}
|
||||
}
|
||||
$_L[$key] = $val;
|
||||
$_SESSION['Lang'][$key] = $val;
|
||||
file_put_contents($lan_file, json_encode($_SESSION['Lang'], JSON_PRETTY_PRINT));
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
|
||||
public static function sanitize($str)
|
||||
{
|
||||
return preg_replace("/[^A-Za-z0-9]/", '_', $str);;
|
||||
}
|
||||
|
||||
public static function getIsoLang()
|
||||
{
|
||||
global $isolang;
|
||||
if (empty($isolang) || count($isolang) == 0) {
|
||||
$isolang = json_decode(file_get_contents(File::pathFixer("system/lan/country.json")), true);
|
||||
}
|
||||
return $isolang;
|
||||
}
|
||||
|
||||
public static function htmlspecialchars($var)
|
||||
{
|
||||
return htmlspecialchars($var);
|
||||
}
|
||||
|
||||
public static function moneyFormat($var)
|
||||
{
|
||||
global $config;
|
||||
return $config['currency_code'] . ' ' . number_format($var, 0, $config['dec_point'], $config['thousands_sep']);
|
||||
}
|
||||
|
||||
public static function phoneFormat($phone)
|
||||
{
|
||||
global $config;
|
||||
if (Validator::UnsignedNumber($phone) && !empty($config['country_code_phone'])) {
|
||||
return preg_replace('/^0/', $config['country_code_phone'], $phone);
|
||||
} else {
|
||||
return $phone;
|
||||
}
|
||||
}
|
||||
|
||||
public static function dateFormat($date)
|
||||
{
|
||||
global $config;
|
||||
return date($config['date_format'], strtotime($date));
|
||||
}
|
||||
|
||||
public static function dateTimeFormat($date)
|
||||
{
|
||||
global $config;
|
||||
if (strtotime($date) < strtotime("2000-01-01 00:00:00")) {
|
||||
return "";
|
||||
} else {
|
||||
return date($config['date_format'] . ' H:i', strtotime($date));
|
||||
}
|
||||
}
|
||||
|
||||
public static function dateAndTimeFormat($date, $time)
|
||||
{
|
||||
global $config;
|
||||
return date($config['date_format'] . ' H:i', strtotime("$date $time"));
|
||||
}
|
||||
|
||||
public static function timeElapsed($datetime, $full = false)
|
||||
{
|
||||
$now = new DateTime;
|
||||
$ago = new DateTime($datetime);
|
||||
$diff = $now->diff($ago);
|
||||
|
||||
$diff->w = floor($diff->d / 7);
|
||||
$diff->d -= $diff->w * 7;
|
||||
|
||||
$string = array(
|
||||
'y' => Lang::T('year'),
|
||||
'm' => Lang::T('month'),
|
||||
'w' => Lang::T('week'),
|
||||
'd' => Lang::T('day'),
|
||||
'h' => Lang::T('hour'),
|
||||
'i' => Lang::T('minute'),
|
||||
's' => Lang::T('second'),
|
||||
);
|
||||
foreach ($string as $k => &$v) {
|
||||
if ($diff->$k) {
|
||||
$v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
|
||||
} else {
|
||||
unset($string[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$full)
|
||||
$string = array_slice($string, 0, 1);
|
||||
return $string ? implode(', ', $string) .' '. Lang::T('ago') : Lang::T('just now');
|
||||
}
|
||||
|
||||
public static function nl2br($text)
|
||||
{
|
||||
return nl2br($text);
|
||||
}
|
||||
|
||||
public static function arrayCount($arr)
|
||||
{
|
||||
if (is_array($arr)) {
|
||||
return count($arr);
|
||||
} else if (is_object($arr)) {
|
||||
// Handle IdiormResultSet and other countable objects
|
||||
if (method_exists($arr, 'count')) {
|
||||
return $arr->count();
|
||||
} elseif (method_exists($arr, 'toArray')) {
|
||||
return count($arr->toArray());
|
||||
} elseif (is_countable($arr)) {
|
||||
return count($arr);
|
||||
} else {
|
||||
// Try to iterate and count
|
||||
$count = 0;
|
||||
foreach ($arr as $item) {
|
||||
$count++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getNotifText($key)
|
||||
{
|
||||
global $_notifmsg, $_notifmsg_default;
|
||||
if (isset($_notifmsg[$key])) {
|
||||
return $_notifmsg[$key];
|
||||
} else {
|
||||
return $_notifmsg_default[$key];
|
||||
}
|
||||
}
|
||||
|
||||
public static function ucWords($text)
|
||||
{
|
||||
return ucwords(str_replace('_', ' ', $text));
|
||||
}
|
||||
|
||||
public static function randomUpLowCase($text)
|
||||
{
|
||||
$jml = strlen($text);
|
||||
$result = '';
|
||||
for ($i = 0; $i < $jml; $i++) {
|
||||
if (rand(0, 99) % 2) {
|
||||
$result .= strtolower(substr($text, $i, 1));
|
||||
} else {
|
||||
$result .= substr($text, $i, 1);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* $pad_type
|
||||
* 0 Left
|
||||
* 1 right
|
||||
* 2 center
|
||||
* */
|
||||
public static function pad($text, $pad_string = ' ', $pad_type = 0)
|
||||
{
|
||||
global $config;
|
||||
$cols = 37;
|
||||
if ($config['printer_cols']) {
|
||||
$cols = $config['printer_cols'];
|
||||
}
|
||||
$text = trim($text);
|
||||
$texts = explode("\n", $text);
|
||||
if (count($texts) > 1) {
|
||||
$text = '';
|
||||
foreach ($texts as $t) {
|
||||
$text .= self::pad(trim($t), $pad_string, $pad_type) . "\n";
|
||||
}
|
||||
return $text;
|
||||
} else {
|
||||
return str_pad(trim($text), $cols, $pad_string, $pad_type);
|
||||
}
|
||||
}
|
||||
|
||||
public static function pads($textLeft, $textRight, $pad_string = ' ')
|
||||
{
|
||||
global $config;
|
||||
$cols = 37;
|
||||
if ($config['printer_cols']) {
|
||||
$cols = $config['printer_cols'];
|
||||
}
|
||||
return $textLeft . str_pad($textRight, $cols - strlen($textLeft), $pad_string, 0);
|
||||
}
|
||||
|
||||
public static function translate($txt, $to = 'id')
|
||||
{
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "https://translate.google.com/m?hl=en&sl=en&tl=$to&ie=UTF-8&prev=_m&q=" . urlencode($txt));
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (iPhone; CPU OS 13_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/28.1 Mobile/15E148 Safari/605.1.15");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||||
curl_setopt($ch, CURLOPT_HEADER, 0);
|
||||
$hasil = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
$temp = explode('<div class="result-container">', $hasil);
|
||||
if (count($temp) > 0) {
|
||||
$temp = explode("</div", $temp[1]);
|
||||
if (!empty($temp[0])) {
|
||||
return $temp[0];
|
||||
}
|
||||
}
|
||||
return $txt;
|
||||
}
|
||||
|
||||
public static function maskText($text){
|
||||
$len = strlen($text);
|
||||
if($len < 3){
|
||||
return "***";
|
||||
}else if($len<5){
|
||||
return substr($text,0,1)."***".substr($text,-1,1);
|
||||
}else if($len<8){
|
||||
return substr($text,0,2)."***".substr($text,-2,2);
|
||||
}else{
|
||||
return substr($text,0,4)."******".substr($text,-3,3);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
system/autoload/Log.php
Normal file
32
system/autoload/Log.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
class Log{
|
||||
public static function put($type, $description, $userid = '', $username = '')
|
||||
{
|
||||
$d = ORM::for_table('tbl_logs')->create();
|
||||
$d->date = date('Y-m-d H:i:s');
|
||||
$d->type = $type;
|
||||
$d->description = $description;
|
||||
$d->userid = $userid;
|
||||
$d->ip = (empty($username)) ? $_SERVER["REMOTE_ADDR"] : $username;
|
||||
$d->save();
|
||||
}
|
||||
|
||||
public static function arrayToText($array, $start = '', $result = '')
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
$result = Log::arrayToText($v, "$start$k.", $result);
|
||||
} else {
|
||||
$result .= $start.$k ." : ". strval($v) ."\n";
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
333
system/autoload/Message.php
Normal file
333
system/autoload/Message.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
use PHPMailer\PHPMailer\SMTP;
|
||||
require $root_path . 'system/autoload/mail/Exception.php';
|
||||
require $root_path . 'system/autoload/mail/PHPMailer.php';
|
||||
require $root_path . 'system/autoload/mail/SMTP.php';
|
||||
|
||||
class Message
|
||||
{
|
||||
|
||||
public static function sendTelegram($txt)
|
||||
{
|
||||
global $config;
|
||||
run_hook('send_telegram'); #HOOK
|
||||
if (!empty($config['telegram_bot']) && !empty($config['telegram_target_id'])) {
|
||||
return Http::getData('https://api.telegram.org/bot' . $config['telegram_bot'] . '/sendMessage?chat_id=' . $config['telegram_target_id'] . '&text=' . urlencode($txt));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function sendSMS($phone, $txt)
|
||||
{
|
||||
global $config;
|
||||
if(empty($txt)){
|
||||
return "";
|
||||
}
|
||||
run_hook('send_sms'); #HOOK
|
||||
if (!empty($config['sms_url'])) {
|
||||
if (strlen($config['sms_url']) > 4 && substr($config['sms_url'], 0, 4) != "http") {
|
||||
if (strlen($txt) > 160) {
|
||||
$txts = str_split($txt, 160);
|
||||
try {
|
||||
$mikrotik = Mikrotik::info($config['sms_url']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
foreach ($txts as $txt) {
|
||||
Mikrotik::sendSMS($client, $phone, $txt);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// ignore, add to logs
|
||||
_log("Failed to send SMS using Mikrotik.\n" . $e->getMessage(), 'SMS', 0);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$mikrotik = Mikrotik::info($config['sms_url']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::sendSMS($client, $phone, $txt);
|
||||
} catch (Exception $e) {
|
||||
// ignore, add to logs
|
||||
_log("Failed to send SMS using Mikrotik.\n" . $e->getMessage(), 'SMS', 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$smsurl = str_replace('[number]', urlencode($phone), $config['sms_url']);
|
||||
$smsurl = str_replace('[text]', urlencode($txt), $smsurl);
|
||||
return Http::getData($smsurl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function sendWhatsapp($phone, $txt)
|
||||
{
|
||||
global $config;
|
||||
if(empty($txt)){
|
||||
return "";
|
||||
}
|
||||
run_hook('send_whatsapp'); #HOOK
|
||||
if (!empty($config['wa_url'])) {
|
||||
$waurl = str_replace('[number]', urlencode(Lang::phoneFormat($phone)), $config['wa_url']);
|
||||
$waurl = str_replace('[text]', urlencode($txt), $waurl);
|
||||
return Http::getData($waurl);
|
||||
}
|
||||
}
|
||||
|
||||
public static function sendEmail($to, $subject, $body)
|
||||
{
|
||||
global $config;
|
||||
if(empty($body)){
|
||||
return "";
|
||||
}
|
||||
run_hook('send_email'); #HOOK
|
||||
if (empty($config['smtp_host'])) {
|
||||
$attr = "";
|
||||
if (!empty($config['mail_from'])) {
|
||||
$attr .= "From: " . $config['mail_from'] . "\r\n";
|
||||
}
|
||||
if (!empty($config['mail_reply_to'])) {
|
||||
$attr .= "Reply-To: " . $config['mail_reply_to'] . "\r\n";
|
||||
}
|
||||
mail($to, $subject, $body, $attr);
|
||||
} else {
|
||||
$mail = new PHPMailer();
|
||||
$mail->isSMTP();
|
||||
$mail->SMTPDebug = SMTP::DEBUG_SERVER;
|
||||
$mail->Host = $config['smtp_host'];
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = $config['smtp_user'];
|
||||
$mail->Password = $config['smtp_pass'];
|
||||
$mail->SMTPSecure = $config['smtp_ssltls'];
|
||||
$mail->Port = $config['smtp_port'];
|
||||
if (!empty($config['mail_from'])) {
|
||||
$mail->setFrom($config['mail_from']);
|
||||
}
|
||||
if (!empty($config['mail_reply_to'])) {
|
||||
$mail->addReplyTo($config['mail_reply_to']);
|
||||
}
|
||||
$mail->isHTML(false);
|
||||
$mail->addAddress($to);
|
||||
$mail->Subject = $subject;
|
||||
$mail->Body = $body;
|
||||
$mail->send();
|
||||
}
|
||||
}
|
||||
|
||||
public static function sendPackageNotification($customer, $package, $price, $message, $via, $plan_type = null)
|
||||
{
|
||||
global $ds;
|
||||
if(empty($message)){
|
||||
return "";
|
||||
}
|
||||
|
||||
// Get plan-specific template if plan_type is provided
|
||||
$template = self::getPlanSpecificTemplate($message, $plan_type);
|
||||
|
||||
$msg = str_replace('[[name]]', $customer['fullname'], $template);
|
||||
$msg = str_replace('[[username]]', $customer['username'], $msg);
|
||||
$msg = str_replace('[[plan]]', $package, $msg);
|
||||
$msg = str_replace('[[package]]', $package, $msg);
|
||||
$msg = str_replace('[[price]]', Lang::moneyFormat($price), $msg);
|
||||
$msg = str_replace('[[plan_type]]', $plan_type ? ucfirst(strtolower($plan_type)) : 'Internet', $msg);
|
||||
|
||||
// Add service-specific variables
|
||||
global $config;
|
||||
$msg = str_replace('[[service_portal]]', !empty($config['hotspot_url']) ? $config['hotspot_url'] : APP_URL, $msg);
|
||||
$msg = str_replace('[[support_contact]]', !empty($config['phone']) ? $config['phone'] : 'our support team', $msg);
|
||||
|
||||
list($bills, $add_cost) = User::getBills($customer['id']);
|
||||
if($add_cost>0){
|
||||
$note = "";
|
||||
foreach ($bills as $k => $v) {
|
||||
$note .= $k . " : " . Lang::moneyFormat($v) . "\n";
|
||||
}
|
||||
$note .= "Total : " . Lang::moneyFormat($add_cost+$price) . "\n";
|
||||
$msg = str_replace('[[bills]]', $note, $msg);
|
||||
}else{
|
||||
$msg = str_replace('[[bills]]', '', $msg);
|
||||
}
|
||||
if ($ds) {
|
||||
$msg = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($ds['expiration'], $ds['time']), $msg);
|
||||
}else{
|
||||
$msg = str_replace('[[expired_date]]', "", $msg);
|
||||
}
|
||||
if (
|
||||
!empty($customer['phonenumber']) && strlen($customer['phonenumber']) > 5
|
||||
&& !empty($template) && in_array($via, ['sms', 'wa'])
|
||||
) {
|
||||
if ($via == 'sms') {
|
||||
echo Message::sendSMS($customer['phonenumber'], $msg);
|
||||
} else if ($via == 'wa') {
|
||||
echo Message::sendWhatsapp($customer['phonenumber'], $msg);
|
||||
}
|
||||
}
|
||||
return "$via: $msg";
|
||||
}
|
||||
|
||||
public static function sendBalanceNotification($phone, $name, $balance, $balance_now, $message, $via)
|
||||
{
|
||||
$msg = str_replace('[[name]]', $name, $message);
|
||||
$msg = str_replace('[[current_balance]]', Lang::moneyFormat($balance_now), $msg);
|
||||
$msg = str_replace('[[balance]]', Lang::moneyFormat($balance), $msg);
|
||||
if (
|
||||
!empty($phone) && strlen($phone) > 5
|
||||
&& !empty($message) && in_array($via, ['sms', 'wa'])
|
||||
) {
|
||||
if ($via == 'sms') {
|
||||
Message::sendSMS($phone, $msg);
|
||||
} else if ($via == 'wa') {
|
||||
Message::sendWhatsapp($phone, $msg);
|
||||
}
|
||||
}
|
||||
return "$via: $msg";
|
||||
}
|
||||
|
||||
public static function sendInvoice($cust, $trx)
|
||||
{
|
||||
global $config;
|
||||
$textInvoice = Lang::getNotifText('invoice_paid');
|
||||
$textInvoice = str_replace('[[company_name]]', $config['CompanyName'], $textInvoice);
|
||||
$textInvoice = str_replace('[[address]]', $config['address'], $textInvoice);
|
||||
$textInvoice = str_replace('[[phone]]', $config['phone'], $textInvoice);
|
||||
$textInvoice = str_replace('[[invoice]]', $trx['invoice'], $textInvoice);
|
||||
$textInvoice = str_replace('[[date]]', Lang::dateAndTimeFormat($trx['recharged_on'], $trx['recharged_time']), $textInvoice);
|
||||
if (!empty($trx['note'])) {
|
||||
$textInvoice = str_replace('[[note]]', $trx['note'], $textInvoice);
|
||||
}
|
||||
$gc = explode("-", $trx['method']);
|
||||
$textInvoice = str_replace('[[payment_gateway]]', trim($gc[0]), $textInvoice);
|
||||
$textInvoice = str_replace('[[payment_channel]]', trim($gc[1]), $textInvoice);
|
||||
$textInvoice = str_replace('[[type]]', $trx['type'], $textInvoice);
|
||||
$textInvoice = str_replace('[[plan_name]]', $trx['plan_name'], $textInvoice);
|
||||
$textInvoice = str_replace('[[plan_price]]', Lang::moneyFormat($trx['price']), $textInvoice);
|
||||
$textInvoice = str_replace('[[name]]', $cust['fullname'], $textInvoice);
|
||||
$textInvoice = str_replace('[[note]]', $cust['note'], $textInvoice);
|
||||
$textInvoice = str_replace('[[user_name]]', $trx['username'], $textInvoice);
|
||||
$textInvoice = str_replace('[[user_password]]', $cust['password'], $textInvoice);
|
||||
$textInvoice = str_replace('[[username]]', $trx['username'], $textInvoice);
|
||||
$textInvoice = str_replace('[[password]]', $cust['password'], $textInvoice);
|
||||
$textInvoice = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($trx['expiration'], $trx['time']), $textInvoice);
|
||||
$textInvoice = str_replace('[[footer]]', $config['note'], $textInvoice);
|
||||
|
||||
if ($config['user_notification_payment'] == 'sms') {
|
||||
Message::sendSMS($cust['phonenumber'], $textInvoice);
|
||||
} else if ($config['user_notification_payment'] == 'wa') {
|
||||
Message::sendWhatsapp($cust['phonenumber'], $textInvoice);
|
||||
}
|
||||
}
|
||||
|
||||
public static function sendRegistrationNotification($customer)
|
||||
{
|
||||
global $config;
|
||||
|
||||
if(empty($customer['phonenumber']) || strlen($customer['phonenumber']) < 5){
|
||||
return "";
|
||||
}
|
||||
|
||||
$textRegistration = Lang::getNotifText('user_registration');
|
||||
if(empty($textRegistration)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$textRegistration = str_replace('[[company_name]]', $config['CompanyName'], $textRegistration);
|
||||
$textRegistration = str_replace('[[name]]', $customer['fullname'], $textRegistration);
|
||||
$textRegistration = str_replace('[[user_name]]', $customer['username'], $textRegistration);
|
||||
$textRegistration = str_replace('[[username]]', $customer['username'], $textRegistration);
|
||||
$textRegistration = str_replace('[[password]]', $customer['password'], $textRegistration);
|
||||
$textRegistration = str_replace('[[service_type]]', $customer['service_type'], $textRegistration);
|
||||
$textRegistration = str_replace('[[footer]]', $config['note'], $textRegistration);
|
||||
|
||||
// Use the same notification setting as recharge process
|
||||
if ($config['user_notification_payment'] == 'sms') {
|
||||
Message::sendSMS($customer['phonenumber'], $textRegistration);
|
||||
} else if ($config['user_notification_payment'] == 'wa') {
|
||||
Message::sendWhatsapp($customer['phonenumber'], $textRegistration);
|
||||
}
|
||||
|
||||
return $textRegistration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plan-specific template based on plan type
|
||||
* @param string $message_key The message key (e.g., 'expired', 'reminder_7_day')
|
||||
* @param string $plan_type The plan type ('Hotspot', 'PPPOE', etc.)
|
||||
* @return string The appropriate template
|
||||
*/
|
||||
public static function getPlanSpecificTemplate($message_key, $plan_type = null)
|
||||
{
|
||||
global $config;
|
||||
|
||||
// Load notification templates
|
||||
$notifications = self::loadNotificationTemplates();
|
||||
|
||||
// If no plan type provided or template doesn't exist, return original message
|
||||
if (empty($plan_type) || !isset($notifications[$message_key])) {
|
||||
return $message_key;
|
||||
}
|
||||
|
||||
// Check if it's a new structure with plan-specific templates
|
||||
if (is_array($notifications[$message_key])) {
|
||||
$plan_type_lower = strtolower($plan_type);
|
||||
|
||||
// Try to get plan-specific template
|
||||
if (isset($notifications[$message_key][$plan_type_lower])) {
|
||||
return $notifications[$message_key][$plan_type_lower];
|
||||
}
|
||||
|
||||
// Fallback to default template
|
||||
if (isset($notifications[$message_key]['default'])) {
|
||||
return $notifications[$message_key]['default'];
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to original message for backward compatibility
|
||||
return $notifications[$message_key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load notification templates from file
|
||||
* @return array Notification templates
|
||||
*/
|
||||
public static function loadNotificationTemplates()
|
||||
{
|
||||
global $root_path;
|
||||
$notifications_file = $root_path . 'system/uploads/notifications.default.json';
|
||||
|
||||
if (file_exists($notifications_file)) {
|
||||
$content = file_get_contents($notifications_file);
|
||||
$notifications = json_decode($content, true);
|
||||
return $notifications ?: [];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect plan type from customer and package information
|
||||
* @param array $customer Customer information
|
||||
* @param string $package Package name
|
||||
* @return string|null Plan type
|
||||
*/
|
||||
public static function detectPlanType($customer, $package)
|
||||
{
|
||||
// Try to get plan type from customer's service_type
|
||||
if (!empty($customer['service_type']) && in_array($customer['service_type'], ['Hotspot', 'PPPoE'])) {
|
||||
return $customer['service_type'];
|
||||
}
|
||||
|
||||
// Try to detect from package name (basic detection)
|
||||
$package_lower = strtolower($package);
|
||||
if (strpos($package_lower, 'hotspot') !== false) {
|
||||
return 'Hotspot';
|
||||
} elseif (strpos($package_lower, 'pppoe') !== false) {
|
||||
return 'PPPOE';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
118
system/autoload/Meta.php
Normal file
118
system/autoload/Meta.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
*
|
||||
* Meta is for save adtional information in database for every existing table
|
||||
*
|
||||
* * Good for Plugin, so plugin don't need to modify existing tables
|
||||
*
|
||||
* Example to put data
|
||||
* if plan need to add point value for loyalty poin
|
||||
* Meta::for("tbl_plans")->set(1, 'point', '24');
|
||||
* it means tbl_plans with id 1 have point value 24, customer will get 24 point for loyalty if buy plan with id 1
|
||||
* You need to create the logic for that, Meta only hold the data
|
||||
*
|
||||
* Example to get data
|
||||
* $point = Meta::for("tbl_plans")->get(1, 'point');
|
||||
* this will return the point value of plan with id 1
|
||||
*
|
||||
* to get all key value
|
||||
* $metas = Meta::for("tbl_plans")->getAll(1);
|
||||
*
|
||||
* to delete 1 data
|
||||
* Meta::for("tbl_plans")->delete(1, 'point');
|
||||
*
|
||||
* to delete all data
|
||||
* Meta::for("tbl_plans")->deleteAll(1);
|
||||
**/
|
||||
|
||||
|
||||
class Meta
|
||||
{
|
||||
protected $table = '';
|
||||
|
||||
protected function __construct($table)
|
||||
{
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
public static function for($table)
|
||||
{
|
||||
return new self($table);
|
||||
}
|
||||
|
||||
public function get($id, $key)
|
||||
{
|
||||
// get the Value
|
||||
return ORM::for_table('tbl_meta')
|
||||
->select('value')
|
||||
->where('tbl', $this->table)
|
||||
->where('tbl_id', $id)
|
||||
->where('name', $key)
|
||||
->find_one()['value'];
|
||||
}
|
||||
|
||||
public function getAll($id)
|
||||
{
|
||||
//get all key Value
|
||||
$metas = [];
|
||||
$result = ORM::for_table('tbl_meta')
|
||||
->select('name')
|
||||
->select('value')
|
||||
->where('tbl', $this->table)
|
||||
->where('tbl_id', $id)
|
||||
->find_array();
|
||||
foreach ($result as $value) {
|
||||
$metas[$value['name']] = $value['value'];
|
||||
}
|
||||
return $metas;
|
||||
}
|
||||
|
||||
public function set($id, $key, $value = '')
|
||||
{
|
||||
$meta = ORM::for_table('tbl_meta')
|
||||
->where('tbl', $this->table)
|
||||
->where('tbl_id', $id)
|
||||
->where('name', $key)
|
||||
->find_one();
|
||||
if (!$meta) {
|
||||
$meta = ORM::for_table('tbl_meta')->create();
|
||||
$meta->tbl = $this->table;
|
||||
$meta->tbl_id = $id;
|
||||
$meta->name = $key;
|
||||
$meta->value = $value;
|
||||
$meta->save();
|
||||
$result = $meta->id();
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
$meta->value = $value;
|
||||
$meta->save();
|
||||
return $meta['id'];
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($id, $key = '')
|
||||
{
|
||||
// get the Value
|
||||
return ORM::for_table('tbl_meta')
|
||||
->select('value')
|
||||
->where('tbl', $this->table)
|
||||
->where('tbl_id', $id)
|
||||
->where('name', $key)
|
||||
->delete();
|
||||
}
|
||||
|
||||
public function deleteAll($id)
|
||||
{
|
||||
//get all key Value
|
||||
return ORM::for_table('tbl_meta')
|
||||
->select('value')
|
||||
->where('tbl', $this->table)
|
||||
->where('tbl_id', $id)
|
||||
->delete_many();
|
||||
}
|
||||
}
|
||||
562
system/autoload/Mikrotik.php
Normal file
562
system/autoload/Mikrotik.php
Normal file
@@ -0,0 +1,562 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
|
||||
class Mikrotik
|
||||
{
|
||||
public static function info($name)
|
||||
{
|
||||
return ORM::for_table('tbl_routers')->where('name', $name)->find_one();
|
||||
}
|
||||
|
||||
public static function getClient($ip, $user, $pass, $timeout = 10)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$iport = explode(":", $ip);
|
||||
return new RouterOS\Client($iport[0], $user, $pass, ($iport[1]) ? $iport[1] : null, false, $timeout);
|
||||
}
|
||||
|
||||
public static function isUserLogin($client, $username)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip hotspot active print',
|
||||
RouterOS\Query::where('user', $username)
|
||||
);
|
||||
return $client->sendSync($printRequest)->getProperty('.id');
|
||||
}
|
||||
|
||||
public static function logMeIn($client, $user, $pass, $ip, $mac)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$addRequest = new RouterOS\Request('/ip/hotspot/active/login');
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('user', $user)
|
||||
->setArgument('password', $pass)
|
||||
->setArgument('ip', $ip)
|
||||
->setArgument('mac-address', $mac)
|
||||
);
|
||||
}
|
||||
|
||||
public static function logMeOut($client, $user)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip hotspot active print',
|
||||
RouterOS\Query::where('user', $user)
|
||||
);
|
||||
$id = $client->sendSync($printRequest)->getProperty('.id');
|
||||
$removeRequest = new RouterOS\Request('/ip/hotspot/active/remove');
|
||||
$client->sendSync(
|
||||
$removeRequest
|
||||
->setArgument('numbers', $id)
|
||||
);
|
||||
}
|
||||
|
||||
public static function addHotspotPlan($client, $name, $sharedusers, $rate)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$addRequest = new RouterOS\Request('/ip/hotspot/user/profile/add');
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('name', $name)
|
||||
->setArgument('shared-users', $sharedusers)
|
||||
->setArgument('rate-limit', $rate)
|
||||
);
|
||||
}
|
||||
|
||||
public static function setHotspotPlan($client, $name, $sharedusers, $rate)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip hotspot user profile print .proplist=.id',
|
||||
RouterOS\Query::where('name', $name)
|
||||
);
|
||||
$profileID = $client->sendSync($printRequest)->getProperty('.id');
|
||||
if (empty($profileID)) {
|
||||
Mikrotik::addHotspotPlan($client, $name, $sharedusers, $rate);
|
||||
} else {
|
||||
$setRequest = new RouterOS\Request('/ip/hotspot/user/profile/set');
|
||||
$client->sendSync(
|
||||
$setRequest
|
||||
->setArgument('numbers', $profileID)
|
||||
->setArgument('shared-users', $sharedusers)
|
||||
->setArgument('rate-limit', $rate)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function setHotspotExpiredPlan($client, $name, $pool)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip hotspot user profile print .proplist=.id',
|
||||
RouterOS\Query::where('name', $name)
|
||||
);
|
||||
$profileID = $client->sendSync($printRequest)->getProperty('.id');
|
||||
if (empty($profileID)) {
|
||||
$addRequest = new RouterOS\Request('/ip/hotspot/user/profile/add');
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('name', $name)
|
||||
->setArgument('shared-users', 3)
|
||||
->setArgument('address-pool', $pool)
|
||||
->setArgument('rate-limit', '512K/512K')
|
||||
);
|
||||
} else {
|
||||
$setRequest = new RouterOS\Request('/ip/hotspot/user/profile/set');
|
||||
$client->sendSync(
|
||||
$setRequest
|
||||
->setArgument('numbers', $profileID)
|
||||
->setArgument('shared-users', 3)
|
||||
->setArgument('address-pool', $pool)
|
||||
->setArgument('rate-limit', '512K/512K')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function removeHotspotPlan($client, $name)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip hotspot user profile print .proplist=.id',
|
||||
RouterOS\Query::where('name', $name)
|
||||
);
|
||||
$profileID = $client->sendSync($printRequest)->getProperty('.id');
|
||||
|
||||
$removeRequest = new RouterOS\Request('/ip/hotspot/user/profile/remove');
|
||||
$client->sendSync(
|
||||
$removeRequest
|
||||
->setArgument('numbers', $profileID)
|
||||
);
|
||||
}
|
||||
|
||||
public static function removeHotspotUser($client, $username)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip hotspot user print .proplist=.id',
|
||||
RouterOS\Query::where('name', $username)
|
||||
);
|
||||
$userID = $client->sendSync($printRequest)->getProperty('.id');
|
||||
$removeRequest = new RouterOS\Request('/ip/hotspot/user/remove');
|
||||
$client->sendSync(
|
||||
$removeRequest
|
||||
->setArgument('numbers', $userID)
|
||||
);
|
||||
}
|
||||
|
||||
public static function addHotspotUser($client, $plan, $customer)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$addRequest = new RouterOS\Request('/ip/hotspot/user/add');
|
||||
if ($plan['typebp'] == "Limited") {
|
||||
if ($plan['limit_type'] == "Time_Limit") {
|
||||
if ($plan['time_unit'] == 'Hrs')
|
||||
$timelimit = $plan['time_limit'] . ":00:00";
|
||||
else
|
||||
$timelimit = "00:" . $plan['time_limit'] . ":00";
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('name', $customer['username'])
|
||||
->setArgument('profile', $plan['name_plan'])
|
||||
->setArgument('password', $customer['password'])
|
||||
->setArgument('comment', $customer['fullname'])
|
||||
->setArgument('email', $customer['email'])
|
||||
->setArgument('limit-uptime', $timelimit)
|
||||
);
|
||||
} else if ($plan['limit_type'] == "Data_Limit") {
|
||||
if ($plan['data_unit'] == 'GB')
|
||||
$datalimit = $plan['data_limit'] . "000000000";
|
||||
else
|
||||
$datalimit = $plan['data_limit'] . "000000";
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('name', $customer['username'])
|
||||
->setArgument('profile', $plan['name_plan'])
|
||||
->setArgument('password', $customer['password'])
|
||||
->setArgument('comment', $customer['fullname'])
|
||||
->setArgument('email', $customer['email'])
|
||||
->setArgument('limit-bytes-total', $datalimit)
|
||||
);
|
||||
} else if ($plan['limit_type'] == "Both_Limit") {
|
||||
if ($plan['time_unit'] == 'Hrs')
|
||||
$timelimit = $plan['time_limit'] . ":00:00";
|
||||
else
|
||||
$timelimit = "00:" . $plan['time_limit'] . ":00";
|
||||
if ($plan['data_unit'] == 'GB')
|
||||
$datalimit = $plan['data_limit'] . "000000000";
|
||||
else
|
||||
$datalimit = $plan['data_limit'] . "000000";
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('name', $customer['username'])
|
||||
->setArgument('profile', $plan['name_plan'])
|
||||
->setArgument('password', $customer['password'])
|
||||
->setArgument('comment', $customer['fullname'])
|
||||
->setArgument('email', $customer['email'])
|
||||
->setArgument('limit-uptime', $timelimit)
|
||||
->setArgument('limit-bytes-total', $datalimit)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('name', $customer['username'])
|
||||
->setArgument('profile', $plan['name_plan'])
|
||||
->setArgument('comment', $customer['fullname'])
|
||||
->setArgument('email', $customer['email'])
|
||||
->setArgument('password', $customer['password'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function setHotspotUser($client, $user, $pass)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request('/ip/hotspot/user/print');
|
||||
$printRequest->setArgument('.proplist', '.id');
|
||||
$printRequest->setQuery(RouterOS\Query::where('name', $user));
|
||||
$id = $client->sendSync($printRequest)->getProperty('.id');
|
||||
|
||||
$setRequest = new RouterOS\Request('/ip/hotspot/user/set');
|
||||
$setRequest->setArgument('numbers', $id);
|
||||
$setRequest->setArgument('password', $pass);
|
||||
$client->sendSync($setRequest);
|
||||
}
|
||||
|
||||
public static function setHotspotUserPackage($client, $user, $plan)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request('/ip/hotspot/user/print');
|
||||
$printRequest->setArgument('.proplist', '.id');
|
||||
$printRequest->setQuery(RouterOS\Query::where('name', $user));
|
||||
$id = $client->sendSync($printRequest)->getProperty('.id');
|
||||
|
||||
$setRequest = new RouterOS\Request('/ip/hotspot/user/set');
|
||||
$setRequest->setArgument('numbers', $id);
|
||||
$setRequest->setArgument('profile', $plan);
|
||||
$client->sendSync($setRequest);
|
||||
}
|
||||
|
||||
public static function removeHotspotActiveUser($client, $username)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$onlineRequest = new RouterOS\Request('/ip/hotspot/active/print');
|
||||
$onlineRequest->setArgument('.proplist', '.id');
|
||||
$onlineRequest->setQuery(RouterOS\Query::where('user', $username));
|
||||
$id = $client->sendSync($onlineRequest)->getProperty('.id');
|
||||
|
||||
$removeRequest = new RouterOS\Request('/ip/hotspot/active/remove');
|
||||
$removeRequest->setArgument('numbers', $id);
|
||||
$client->sendSync($removeRequest);
|
||||
}
|
||||
|
||||
public static function removePpoeUser($client, $username)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request('/ppp/secret/print');
|
||||
//$printRequest->setArgument('.proplist', '.id');
|
||||
$printRequest->setQuery(RouterOS\Query::where('name', $username));
|
||||
$id = $client->sendSync($printRequest)->getProperty('.id');
|
||||
$removeRequest = new RouterOS\Request('/ppp/secret/remove');
|
||||
$removeRequest->setArgument('numbers', $id);
|
||||
$client->sendSync($removeRequest);
|
||||
}
|
||||
|
||||
public static function addPpoeUser($client, $plan, $customer)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$addRequest = new RouterOS\Request('/ppp/secret/add');
|
||||
if (!empty($customer['pppoe_password'])) {
|
||||
$pass = $customer['pppoe_password'];
|
||||
} else {
|
||||
$pass = $customer['password'];
|
||||
}
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('name', $customer['username'])
|
||||
->setArgument('service', 'pppoe')
|
||||
->setArgument('profile', $plan['name_plan'])
|
||||
->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email'])
|
||||
->setArgument('password', $pass)
|
||||
);
|
||||
}
|
||||
|
||||
public static function setPpoeUser($client, $user, $pass)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request('/ppp/secret/print');
|
||||
$printRequest->setArgument('.proplist', '.id');
|
||||
$printRequest->setQuery(RouterOS\Query::where('name', $user));
|
||||
$id = $client->sendSync($printRequest)->getProperty('.id');
|
||||
|
||||
$setRequest = new RouterOS\Request('/ppp/secret/set');
|
||||
$setRequest->setArgument('numbers', $id);
|
||||
$setRequest->setArgument('password', $pass);
|
||||
$client->sendSync($setRequest);
|
||||
}
|
||||
|
||||
public static function setPpoeUserPlan($client, $user, $plan)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request('/ppp/secret/print');
|
||||
$printRequest->setArgument('.proplist', '.id');
|
||||
$printRequest->setQuery(RouterOS\Query::where('name', $user));
|
||||
$id = $client->sendSync($printRequest)->getProperty('.id');
|
||||
|
||||
$setRequest = new RouterOS\Request('/ppp/secret/set');
|
||||
$setRequest->setArgument('numbers', $id);
|
||||
$setRequest->setArgument('profile', $plan);
|
||||
$client->sendSync($setRequest);
|
||||
}
|
||||
|
||||
public static function removePpoeActive($client, $username)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$onlineRequest = new RouterOS\Request('/ppp/active/print');
|
||||
$onlineRequest->setArgument('.proplist', '.id');
|
||||
$onlineRequest->setQuery(RouterOS\Query::where('name', $username));
|
||||
$id = $client->sendSync($onlineRequest)->getProperty('.id');
|
||||
|
||||
$removeRequest = new RouterOS\Request('/ppp/active/remove');
|
||||
$removeRequest->setArgument('numbers', $id);
|
||||
$client->sendSync($removeRequest);
|
||||
}
|
||||
|
||||
public static function removePool($client, $name)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip pool print .proplist=.id',
|
||||
RouterOS\Query::where('name', $name)
|
||||
);
|
||||
$poolID = $client->sendSync($printRequest)->getProperty('.id');
|
||||
|
||||
$removeRequest = new RouterOS\Request('/ip/pool/remove');
|
||||
$client->sendSync(
|
||||
$removeRequest
|
||||
->setArgument('numbers', $poolID)
|
||||
);
|
||||
}
|
||||
|
||||
public static function addPool($client, $name, $ip_address)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$addRequest = new RouterOS\Request('/ip/pool/add');
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('name', $name)
|
||||
->setArgument('ranges', $ip_address)
|
||||
);
|
||||
}
|
||||
|
||||
public static function setPool($client, $name, $ip_address)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip pool print .proplist=.id',
|
||||
RouterOS\Query::where('name', $name)
|
||||
);
|
||||
$poolID = $client->sendSync($printRequest)->getProperty('.id');
|
||||
|
||||
if (empty($poolID)) {
|
||||
self::addPool($client, $name, $ip_address);
|
||||
} else {
|
||||
$setRequest = new RouterOS\Request('/ip/pool/set');
|
||||
$client->sendSync(
|
||||
$setRequest
|
||||
->setArgument('numbers', $poolID)
|
||||
->setArgument('ranges', $ip_address)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function addPpoePlan($client, $name, $pool, $rate)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$addRequest = new RouterOS\Request('/ppp/profile/add');
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('name', $name)
|
||||
->setArgument('local-address', $pool)
|
||||
->setArgument('remote-address', $pool)
|
||||
->setArgument('rate-limit', $rate)
|
||||
);
|
||||
}
|
||||
|
||||
public static function setPpoePlan($client, $name, $pool, $rate)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ppp profile print .proplist=.id',
|
||||
RouterOS\Query::where('name', $name)
|
||||
);
|
||||
$profileID = $client->sendSync($printRequest)->getProperty('.id');
|
||||
if (empty($profileID)) {
|
||||
self::addPpoePlan($client, $name, $pool, $rate);
|
||||
} else {
|
||||
$setRequest = new RouterOS\Request('/ppp/profile/set');
|
||||
$client->sendSync(
|
||||
$setRequest
|
||||
->setArgument('numbers', $profileID)
|
||||
->setArgument('local-address', $pool)
|
||||
->setArgument('remote-address', $pool)
|
||||
->setArgument('rate-limit', $rate)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function removePpoePlan($client, $name)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ppp profile print .proplist=.id',
|
||||
RouterOS\Query::where('name', $name)
|
||||
);
|
||||
$profileID = $client->sendSync($printRequest)->getProperty('.id');
|
||||
|
||||
$removeRequest = new RouterOS\Request('/ppp/profile/remove');
|
||||
$client->sendSync(
|
||||
$removeRequest
|
||||
->setArgument('numbers', $profileID)
|
||||
);
|
||||
}
|
||||
|
||||
public static function sendSMS($client, $to, $message)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$smsRequest = new RouterOS\Request('/tool sms send');
|
||||
$smsRequest
|
||||
->setArgument('phone-number', $to)
|
||||
->setArgument('message', $message);
|
||||
$client->sendSync($smsRequest);
|
||||
}
|
||||
|
||||
public static function getIpHotspotUser($client, $username){
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip hotspot active print',
|
||||
RouterOS\Query::where('user', $username)
|
||||
);
|
||||
return $client->sendSync($printRequest)->getProperty('address');
|
||||
}
|
||||
|
||||
public static function addIpToAddressList($client, $ip, $listName, $comment = '')
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$addRequest = new RouterOS\Request('/ip/firewall/address-list/add');
|
||||
$client->sendSync(
|
||||
$addRequest
|
||||
->setArgument('address', $ip)
|
||||
->setArgument('comment', $comment)
|
||||
->setArgument('list', $listName)
|
||||
);
|
||||
}
|
||||
|
||||
public static function removeIpFromAddressList($client, $ip)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
$printRequest = new RouterOS\Request(
|
||||
'/ip firewall address-list print .proplist=.id',
|
||||
RouterOS\Query::where('address', $ip)
|
||||
);
|
||||
$id = $client->sendSync($printRequest)->getProperty('.id');
|
||||
$removeRequest = new RouterOS\Request('/ip/firewall/address-list/remove');
|
||||
$client->sendSync(
|
||||
$removeRequest
|
||||
->setArgument('numbers', $id)
|
||||
);
|
||||
}
|
||||
}
|
||||
817
system/autoload/Package.php
Normal file
817
system/autoload/Package.php
Normal file
@@ -0,0 +1,817 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
|
||||
class Package
|
||||
{
|
||||
/**
|
||||
* @param int $id_customer String user identifier
|
||||
* @param string $router_name router name for this package
|
||||
* @param int $plan_id plan id for this package
|
||||
* @param string $gateway payment gateway name
|
||||
* @param string $channel channel payment gateway
|
||||
* @param array $pgids payment gateway ids
|
||||
* @return boolean
|
||||
*/
|
||||
public static function rechargeUser($id_customer, $router_name, $plan_id, $gateway, $channel, $note = '')
|
||||
{
|
||||
global $config, $admin, $c, $p, $b, $t, $d, $zero, $trx;
|
||||
$date_now = date("Y-m-d H:i:s");
|
||||
$date_only = date("Y-m-d");
|
||||
$time_only = date("H:i:s");
|
||||
$time = date("H:i:s");
|
||||
$inv = "";
|
||||
|
||||
if ($id_customer == '' or $router_name == '' or $plan_id == '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one();
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $plan_id)->find_one();
|
||||
|
||||
if ($c['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($c['status']), 'danger', "");
|
||||
}
|
||||
|
||||
$add_cost = 0;
|
||||
$bills = [];
|
||||
// Zero cost recharge
|
||||
if (isset($zero) && $zero == 1) {
|
||||
$p['price'] = 0;
|
||||
} else {
|
||||
// Additional cost
|
||||
list($bills, $add_cost) = User::getBills($id_customer);
|
||||
if ($add_cost > 0 && $router_name != 'balance') {
|
||||
foreach ($bills as $k => $v) {
|
||||
$note .= $k . " : " . Lang::moneyFormat($v) . "\n";
|
||||
}
|
||||
$note .= $p['name_plan'] . " : " . Lang::moneyFormat($p['price']) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!$p['enabled']) {
|
||||
if (!isset($admin) || !isset($admin['id']) || empty($admin['id'])) {
|
||||
r2(U . 'home', 'e', Lang::T('Plan Not found'));
|
||||
}
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
r2(U . 'dashboard', 'e', Lang::T('Plan Not found'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($p['validity_unit'] == 'Period') {
|
||||
$day_exp = User::getAttribute("Expired Date", $c['id']); //ORM::for_table('tbl_customers_fields')->where('field_name', 'Expired Date')->where('customer_id', $c['id'])->find_one();
|
||||
if (!$day_exp) {
|
||||
$day_exp = 20;
|
||||
// $day_exp = date('d', strtotime($c['created_at']));
|
||||
// if (empty($day_exp) || $day_exp > 28) {
|
||||
// $day_exp = 1;
|
||||
// }
|
||||
$f = ORM::for_table('tbl_customers_fields')->create();
|
||||
$f->customer_id = $c['id'];
|
||||
$f->field_name = 'Expired Date';
|
||||
$f->field_value = $day_exp;
|
||||
$f->save();
|
||||
}
|
||||
}
|
||||
|
||||
if ($router_name == 'balance') {
|
||||
// insert table transactions
|
||||
$t = ORM::for_table('tbl_transactions')->create();
|
||||
$t->invoice = $inv = "INV-" . Package::_raid();
|
||||
$t->username = $c['username'];
|
||||
$t->plan_name = $p['name_plan'];
|
||||
$t->price = $p['price'];
|
||||
$t->recharged_on = $date_only;
|
||||
$t->recharged_time = date("H:i:s");
|
||||
$t->expiration = $date_only;
|
||||
$t->time = $time;
|
||||
$t->method = "$gateway - $channel";
|
||||
$t->routers = $router_name;
|
||||
$t->type = "Balance";
|
||||
if ($admin) {
|
||||
$t->admin_id = ($admin['id']) ? $admin['id'] : '0';
|
||||
} else {
|
||||
$t->admin_id = '0';
|
||||
}
|
||||
$t->save();
|
||||
|
||||
$balance_before = $c['balance'];
|
||||
Balance::plus($id_customer, $p['price']);
|
||||
$balance = $c['balance'] + $p['price'];
|
||||
|
||||
$textInvoice = Lang::getNotifText('invoice_balance');
|
||||
$textInvoice = str_replace('[[company_name]]', $config['CompanyName'], $textInvoice);
|
||||
$textInvoice = str_replace('[[address]]', $config['address'], $textInvoice);
|
||||
$textInvoice = str_replace('[[phone]]', $config['phone'], $textInvoice);
|
||||
$textInvoice = str_replace('[[invoice]]', $inv, $textInvoice);
|
||||
$textInvoice = str_replace('[[date]]', Lang::dateTimeFormat($date_now), $textInvoice);
|
||||
$textInvoice = str_replace('[[payment_gateway]]', $gateway, $textInvoice);
|
||||
$textInvoice = str_replace('[[payment_channel]]', $channel, $textInvoice);
|
||||
$textInvoice = str_replace('[[type]]', 'Balance', $textInvoice);
|
||||
$textInvoice = str_replace('[[plan_name]]', $p['name_plan'], $textInvoice);
|
||||
$textInvoice = str_replace('[[plan_price]]', Lang::moneyFormat($p['price']), $textInvoice);
|
||||
$textInvoice = str_replace('[[name]]', $c['fullname'], $textInvoice);
|
||||
$textInvoice = str_replace('[[user_name]]', $c['username'], $textInvoice);
|
||||
$textInvoice = str_replace('[[user_password]]', $c['password'], $textInvoice);
|
||||
$textInvoice = str_replace('[[footer]]', $config['note'], $textInvoice);
|
||||
$textInvoice = str_replace('[[balance_before]]', Lang::moneyFormat($balance_before), $textInvoice);
|
||||
$textInvoice = str_replace('[[balance]]', Lang::moneyFormat($balance), $textInvoice);
|
||||
|
||||
if ($config['user_notification_payment'] == 'sms') {
|
||||
Message::sendSMS($c['phonenumber'], $textInvoice);
|
||||
} else if ($config['user_notification_payment'] == 'wa') {
|
||||
Message::sendWhatsapp($c['phonenumber'], $textInvoice);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1 Customer only can have 1 PPPOE and 1 Hotspot Plan, 1 prepaid and 1 postpaid
|
||||
*/
|
||||
$b = ORM::for_table('tbl_user_recharges')
|
||||
->select('tbl_user_recharges.id', 'id')
|
||||
->select('customer_id')
|
||||
->select('username')
|
||||
->select('plan_id')
|
||||
->select('namebp')
|
||||
->select('recharged_on')
|
||||
->select('recharged_time')
|
||||
->select('expiration')
|
||||
->select('time')
|
||||
->select('status')
|
||||
->select('method')
|
||||
->select('tbl_user_recharges.routers', 'routers')
|
||||
->select('tbl_user_recharges.type', 'type')
|
||||
->select('admin_id')
|
||||
->select('prepaid')
|
||||
->where('customer_id', $id_customer)
|
||||
->where('tbl_user_recharges.routers', $router_name)
|
||||
->where('tbl_user_recharges.Type', $p['type'])
|
||||
# PPPOE or Hotspot only can have 1 per customer prepaid or postpaid
|
||||
# because 1 customer can have 1 PPPOE and 1 Hotspot Plan in mikrotik
|
||||
//->where('prepaid', $p['prepaid'])
|
||||
->left_outer_join('tbl_plans', array('tbl_plans.id', '=', 'tbl_user_recharges.plan_id'))
|
||||
->find_one();
|
||||
|
||||
run_hook("recharge_user");
|
||||
|
||||
|
||||
$mikrotik = Mikrotik::info($router_name);
|
||||
if ($p['validity_unit'] == 'Months') {
|
||||
$date_exp = date("Y-m-d", strtotime('+' . $p['validity'] . ' month'));
|
||||
} else if ($p['validity_unit'] == 'Period') {
|
||||
$date_tmp = date("Y-m-$day_exp", strtotime('+' . $p['validity'] . ' month'));
|
||||
$dt1 = new DateTime("$date_only");
|
||||
$dt2 = new DateTime("$date_tmp");
|
||||
$diff = $dt2->diff($dt1);
|
||||
$sum = $diff->format("%a"); // => 453
|
||||
if ($sum >= 35 * $p['validity']) {
|
||||
$date_exp = date("Y-m-$day_exp", strtotime('+0 month'));
|
||||
} else {
|
||||
$date_exp = date("Y-m-$day_exp", strtotime('+' . $p['validity'] . ' month'));
|
||||
};
|
||||
$time = date("23:59:00");
|
||||
} else if ($p['validity_unit'] == 'Days') {
|
||||
$datetime = explode(' ', date("Y-m-d H:i:s", strtotime('+' . $p['validity'] . ' day')));
|
||||
$date_exp = $datetime[0];
|
||||
$time = $datetime[1];
|
||||
} else if ($p['validity_unit'] == 'Hrs') {
|
||||
$datetime = explode(' ', date("Y-m-d H:i:s", strtotime('+' . $p['validity'] . ' hour')));
|
||||
$date_exp = $datetime[0];
|
||||
$time = $datetime[1];
|
||||
} else if ($p['validity_unit'] == 'Mins') {
|
||||
$datetime = explode(' ', date("Y-m-d H:i:s", strtotime('+' . $p['validity'] . ' minute')));
|
||||
$date_exp = $datetime[0];
|
||||
$time = $datetime[1];
|
||||
}
|
||||
$isChangePlan = false;
|
||||
if ($p['type'] == 'Hotspot') {
|
||||
if ($b) {
|
||||
if ($plan_id != $b['plan_id']) {
|
||||
$isChangePlan = true;
|
||||
}
|
||||
if ($config['extend_expiry'] === 'yes') {
|
||||
if ($b['namebp'] == $p['name_plan'] && $b['status'] == 'on') {
|
||||
// if it same internet plan, expired will extend
|
||||
if ($p['validity_unit'] == 'Months') {
|
||||
$date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' months'));
|
||||
$time = $b['time'];
|
||||
} else if ($p['validity_unit'] == 'Period') {
|
||||
$date_exp = date("Y-m-$day_exp", strtotime($b['expiration'] . ' +' . $p['validity'] . ' months'));
|
||||
$time = date("23:59:00");
|
||||
} else if ($p['validity_unit'] == 'Days') {
|
||||
$date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' days'));
|
||||
$time = $b['time'];
|
||||
} else if ($p['validity_unit'] == 'Hrs') {
|
||||
$datetime = explode(' ', date("Y-m-d H:i:s", strtotime($b['expiration'] . ' ' . $b['time'] . ' +' . $p['validity'] . ' hours')));
|
||||
$date_exp = $datetime[0];
|
||||
$time = $datetime[1];
|
||||
} else if ($p['validity_unit'] == 'Mins') {
|
||||
$datetime = explode(' ', date("Y-m-d H:i:s", strtotime($b['expiration'] . ' ' . $b['time'] . ' +' . $p['validity'] . ' minutes')));
|
||||
$date_exp = $datetime[0];
|
||||
$time = $datetime[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($isChangePlan || $b['status'] == 'off') {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p, "$date_exp $time");
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removeHotspotUser($client, $c['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $c['username']);
|
||||
Mikrotik::addHotspotUser($client, $p, $c);
|
||||
}
|
||||
}
|
||||
|
||||
$b->customer_id = $id_customer;
|
||||
$b->username = $c['username'];
|
||||
$b->plan_id = $plan_id;
|
||||
$b->namebp = $p['name_plan'];
|
||||
$b->recharged_on = $date_only;
|
||||
$b->recharged_time = $time_only;
|
||||
$b->expiration = $date_exp;
|
||||
$b->time = $time;
|
||||
$b->status = "on";
|
||||
$b->method = "$gateway - $channel";
|
||||
$b->routers = $router_name;
|
||||
$b->type = "Hotspot";
|
||||
if ($admin) {
|
||||
$b->admin_id = ($admin['id']) ? $admin['id'] : '0';
|
||||
} else {
|
||||
$b->admin_id = '0';
|
||||
}
|
||||
$b->save();
|
||||
|
||||
// insert table transactions
|
||||
$t = ORM::for_table('tbl_transactions')->create();
|
||||
$t->invoice = $inv = "INV-" . Package::_raid();
|
||||
$t->username = $c['username'];
|
||||
$t->plan_name = $p['name_plan'];
|
||||
if ($p['validity_unit'] == 'Period') {
|
||||
// Postpaid price from field
|
||||
$add_inv = User::getAttribute("Invoice", $id_customer);
|
||||
if (empty($add_inv) or $add_inv == 0) {
|
||||
$t->price = $p['price'] + $add_cost;
|
||||
} else {
|
||||
$t->price = $add_inv + $add_cost;
|
||||
}
|
||||
} else {
|
||||
$t->price = $p['price'] + $add_cost;
|
||||
}
|
||||
$t->recharged_on = $date_only;
|
||||
$t->recharged_time = $time_only;
|
||||
$t->expiration = $date_exp;
|
||||
$t->time = $time;
|
||||
$t->method = "$gateway - $channel";
|
||||
$t->routers = $router_name;
|
||||
$t->note = $note;
|
||||
$t->type = "Hotspot";
|
||||
if ($admin) {
|
||||
$t->admin_id = ($admin['id']) ? $admin['id'] : '0';
|
||||
} else {
|
||||
$t->admin_id = '0';
|
||||
}
|
||||
$t->save();
|
||||
|
||||
if ($p['validity_unit'] == 'Period') {
|
||||
// insert price to fields for invoice next month
|
||||
$fl = ORM::for_table('tbl_customers_fields')->where('field_name', 'Invoice')->where('customer_id', $c['id'])->find_one();
|
||||
if (!$fl) {
|
||||
$fl = ORM::for_table('tbl_customers_fields')->create();
|
||||
$fl->customer_id = $c['id'];
|
||||
$fl->field_name = 'Invoice';
|
||||
$fl->field_value = $p['price'];
|
||||
$fl->save();
|
||||
} else {
|
||||
$fl->customer_id = $c['id'];
|
||||
$fl->field_value = $p['price'];
|
||||
$fl->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Message::sendTelegram("#u$c[username] $c[fullname] #recharge #Hotspot \n" . $p['name_plan'] .
|
||||
"\nRouter: " . $router_name .
|
||||
"\nGateway: " . $gateway .
|
||||
"\nChannel: " . $channel .
|
||||
"\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) .
|
||||
"\nNote:\n" . $note);
|
||||
} else {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p, "$date_exp $time");
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removeHotspotUser($client, $c['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $c['username']);
|
||||
Mikrotik::addHotspotUser($client, $p, $c);
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_user_recharges')->create();
|
||||
$d->customer_id = $id_customer;
|
||||
$d->username = $c['username'];
|
||||
$d->plan_id = $plan_id;
|
||||
$d->namebp = $p['name_plan'];
|
||||
$d->recharged_on = $date_only;
|
||||
$d->recharged_time = $time_only;
|
||||
$d->expiration = $date_exp;
|
||||
$d->time = $time;
|
||||
$d->status = "on";
|
||||
$d->method = "$gateway - $channel";
|
||||
$d->routers = $router_name;
|
||||
$d->type = "Hotspot";
|
||||
if ($admin) {
|
||||
$d->admin_id = ($admin['id']) ? $admin['id'] : '0';
|
||||
} else {
|
||||
$d->admin_id = '0';
|
||||
}
|
||||
$d->save();
|
||||
|
||||
// insert table transactions
|
||||
$t = ORM::for_table('tbl_transactions')->create();
|
||||
$t->invoice = $inv = "INV-" . Package::_raid();
|
||||
$t->username = $c['username'];
|
||||
$t->plan_name = $p['name_plan'];
|
||||
if ($p['validity_unit'] == 'Period') {
|
||||
// Postpaid price always zero for first time
|
||||
$t->price = 0 + $add_cost;
|
||||
} else {
|
||||
$t->price = $p['price'] + $add_cost;
|
||||
}
|
||||
$t->recharged_on = $date_only;
|
||||
$t->recharged_time = $time_only;
|
||||
$t->expiration = $date_exp;
|
||||
$t->time = $time;
|
||||
$t->method = "$gateway - $channel";
|
||||
$t->routers = $router_name;
|
||||
$t->note = $note;
|
||||
$t->type = "Hotspot";
|
||||
if ($admin) {
|
||||
$t->admin_id = ($admin['id']) ? $admin['id'] : '0';
|
||||
} else {
|
||||
$t->admin_id = '0';
|
||||
}
|
||||
$t->save();
|
||||
|
||||
if ($p['validity_unit'] == 'Period' && $p['price'] != 0) {
|
||||
// insert price to fields for invoice next month
|
||||
$fl = ORM::for_table('tbl_customers_fields')->where('field_name', 'Invoice')->where('customer_id', $c['id'])->find_one();
|
||||
if (!$fl) {
|
||||
$fl = ORM::for_table('tbl_customers_fields')->create();
|
||||
$fl->customer_id = $c['id'];
|
||||
$fl->field_name = 'Invoice';
|
||||
// Calculating Price
|
||||
$sd = new DateTime("$date_only");
|
||||
$ed = new DateTime("$date_exp");
|
||||
$td = $ed->diff($sd);
|
||||
$fd = $td->format("%a");
|
||||
$gi = ($p['price'] / (30 * $p['validity'])) * $fd;
|
||||
if ($gi > $p['price']) {
|
||||
$fl->field_value = $p['price'];
|
||||
} else {
|
||||
$fl->field_value = $gi;
|
||||
}
|
||||
$fl->save();
|
||||
} else {
|
||||
$fl->customer_id = $c['id'];
|
||||
$fl->field_value = $p['price'];
|
||||
$fl->save();
|
||||
}
|
||||
}
|
||||
|
||||
Message::sendTelegram("#u$c[username] $c[fullname] #buy #Hotspot \n" . $p['name_plan'] .
|
||||
"\nRouter: " . $router_name .
|
||||
"\nGateway: " . $gateway .
|
||||
"\nChannel: " . $channel .
|
||||
"\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) .
|
||||
"\nNote:\n" . $note);
|
||||
}
|
||||
} else {
|
||||
|
||||
if ($b) {
|
||||
if ($plan_id != $b['plan_id']) {
|
||||
$isChangePlan = true;
|
||||
}
|
||||
if ($config['extend_expiry'] === 'yes') {
|
||||
if ($b['namebp'] == $p['name_plan'] && $b['status'] == 'on') {
|
||||
// if it same internet plan, expired will extend
|
||||
if ($p['validity_unit'] == 'Months') {
|
||||
$date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' months'));
|
||||
$time = $b['time'];
|
||||
} else if ($p['validity_unit'] == 'Period') {
|
||||
$date_exp = date("Y-m-$day_exp", strtotime($b['expiration'] . ' +' . $p['validity'] . ' months'));
|
||||
$time = date("23:59:00");
|
||||
} else if ($p['validity_unit'] == 'Days') {
|
||||
$date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' days'));
|
||||
$time = $b['time'];
|
||||
} else if ($p['validity_unit'] == 'Hrs') {
|
||||
$datetime = explode(' ', date("Y-m-d H:i:s", strtotime($b['expiration'] . ' ' . $b['time'] . ' +' . $p['validity'] . ' hours')));
|
||||
$date_exp = $datetime[0];
|
||||
$time = $datetime[1];
|
||||
} else if ($p['validity_unit'] == 'Mins') {
|
||||
$datetime = explode(' ', date("Y-m-d H:i:s", strtotime($b['expiration'] . ' ' . $b['time'] . ' +' . $p['validity'] . ' minutes')));
|
||||
$date_exp = $datetime[0];
|
||||
$time = $datetime[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isChangePlan || $b['status'] == 'off') {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p, "$date_exp $time");
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removePpoeUser($client, $c['username']);
|
||||
Mikrotik::removePpoeActive($client, $c['username']);
|
||||
Mikrotik::addPpoeUser($client, $p, $c);
|
||||
}
|
||||
}
|
||||
|
||||
$b->customer_id = $id_customer;
|
||||
$b->username = $c['username'];
|
||||
$b->plan_id = $plan_id;
|
||||
$b->namebp = $p['name_plan'];
|
||||
$b->recharged_on = $date_only;
|
||||
$b->recharged_time = $time_only;
|
||||
$b->expiration = $date_exp;
|
||||
$b->time = $time;
|
||||
$b->status = "on";
|
||||
$b->method = "$gateway - $channel";
|
||||
$b->routers = $router_name;
|
||||
$b->type = "PPPOE";
|
||||
if ($admin) {
|
||||
$b->admin_id = ($admin['id']) ? $admin['id'] : '0';
|
||||
} else {
|
||||
$b->admin_id = '0';
|
||||
}
|
||||
$b->save();
|
||||
|
||||
// insert table transactions
|
||||
$t = ORM::for_table('tbl_transactions')->create();
|
||||
$t->invoice = $inv = "INV-" . Package::_raid();
|
||||
$t->username = $c['username'];
|
||||
$t->plan_name = $p['name_plan'];
|
||||
if ($p['validity_unit'] == 'Period') {
|
||||
// Postpaid price from field
|
||||
$add_inv = User::getAttribute("Invoice", $id_customer);
|
||||
if (empty($add_inv) or $add_inv == 0) {
|
||||
$t->price = $p['price'] + $add_cost;
|
||||
} else {
|
||||
$t->price = $add_inv + $add_cost;
|
||||
}
|
||||
} else {
|
||||
$t->price = $p['price'] + $add_cost;
|
||||
}
|
||||
$t->recharged_on = $date_only;
|
||||
$t->recharged_time = $time_only;
|
||||
$t->expiration = $date_exp;
|
||||
$t->time = $time;
|
||||
$t->method = "$gateway - $channel";
|
||||
$t->routers = $router_name;
|
||||
$t->note = $note;
|
||||
$t->type = "PPPOE";
|
||||
if ($admin) {
|
||||
$t->admin_id = ($admin['id']) ? $admin['id'] : '0';
|
||||
} else {
|
||||
$t->admin_id = '0';
|
||||
}
|
||||
$t->save();
|
||||
|
||||
if ($p['validity_unit'] == 'Period' && $p['price'] != 0) {
|
||||
// insert price to fields for invoice next month
|
||||
$fl = ORM::for_table('tbl_customers_fields')->where('field_name', 'Invoice')->where('customer_id', $c['id'])->find_one();
|
||||
if (!$fl) {
|
||||
$fl = ORM::for_table('tbl_customers_fields')->create();
|
||||
$fl->customer_id = $c['id'];
|
||||
$fl->field_name = 'Invoice';
|
||||
$fl->field_value = $p['price'];
|
||||
$fl->save();
|
||||
} else {
|
||||
$fl->customer_id = $c['id'];
|
||||
$fl->field_value = $p['price'];
|
||||
$fl->save();
|
||||
}
|
||||
}
|
||||
|
||||
Message::sendTelegram("#u$c[username] $c[fullname] #recharge #PPPOE \n" . $p['name_plan'] .
|
||||
"\nRouter: " . $router_name .
|
||||
"\nGateway: " . $gateway .
|
||||
"\nChannel: " . $channel .
|
||||
"\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) .
|
||||
"\nNote:\n" . $note);
|
||||
} else {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p, "$date_exp $time");
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removePpoeUser($client, $c['username']);
|
||||
Mikrotik::removePpoeActive($client, $c['username']);
|
||||
Mikrotik::addPpoeUser($client, $p, $c);
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_user_recharges')->create();
|
||||
$d->customer_id = $id_customer;
|
||||
$d->username = $c['username'];
|
||||
$d->plan_id = $plan_id;
|
||||
$d->namebp = $p['name_plan'];
|
||||
$d->recharged_on = $date_only;
|
||||
$d->recharged_time = $time_only;
|
||||
$d->expiration = $date_exp;
|
||||
$d->time = $time;
|
||||
$d->status = "on";
|
||||
$d->method = "$gateway - $channel";
|
||||
$d->routers = $router_name;
|
||||
$d->type = "PPPOE";
|
||||
if ($admin) {
|
||||
$d->admin_id = ($admin['id']) ? $admin['id'] : '0';
|
||||
} else {
|
||||
$d->admin_id = '0';
|
||||
}
|
||||
$d->save();
|
||||
|
||||
// insert table transactions
|
||||
$t = ORM::for_table('tbl_transactions')->create();
|
||||
$t->invoice = $inv = "INV-" . Package::_raid();
|
||||
$t->username = $c['username'];
|
||||
$t->plan_name = $p['name_plan'];
|
||||
if ($p['validity_unit'] == 'Period') {
|
||||
// Postpaid price always zero for first time
|
||||
$note = '';
|
||||
$bills = [];
|
||||
$t->price = 0;
|
||||
} else {
|
||||
$t->price = $p['price'] + $add_cost;
|
||||
}
|
||||
$t->recharged_on = $date_only;
|
||||
$t->recharged_time = $time_only;
|
||||
$t->expiration = $date_exp;
|
||||
$t->time = $time;
|
||||
$t->method = "$gateway - $channel";
|
||||
$t->note = $note;
|
||||
$t->routers = $router_name;
|
||||
if ($admin) {
|
||||
$t->admin_id = ($admin['id']) ? $admin['id'] : '0';
|
||||
} else {
|
||||
$t->admin_id = '0';
|
||||
}
|
||||
$t->type = "PPPOE";
|
||||
$t->save();
|
||||
|
||||
if ($p['validity_unit'] == 'Period' && $p['price'] != 0) {
|
||||
// insert price to fields for invoice next month
|
||||
$fl = ORM::for_table('tbl_customers_fields')->where('field_name', 'Invoice')->where('customer_id', $c['id'])->find_one();
|
||||
if (!$fl) {
|
||||
$fl = ORM::for_table('tbl_customers_fields')->create();
|
||||
$fl->customer_id = $c['id'];
|
||||
$fl->field_name = 'Invoice';
|
||||
// Calculating Price
|
||||
$sd = new DateTime("$date_only");
|
||||
$ed = new DateTime("$date_exp");
|
||||
$td = $ed->diff($sd);
|
||||
$fd = $td->format("%a");
|
||||
$gi = ($p['price'] / (30 * $p['validity'])) * $fd;
|
||||
if ($gi > $p['price']) {
|
||||
$fl->field_value = $p['price'];
|
||||
} else {
|
||||
$fl->field_value = $gi;
|
||||
}
|
||||
$fl->save();
|
||||
} else {
|
||||
$fl->customer_id = $c['id'];
|
||||
$fl->field_value = $p['price'];
|
||||
$fl->save();
|
||||
}
|
||||
}
|
||||
|
||||
Message::sendTelegram("#u$c[username] $c[fullname] #buy #PPPOE \n" . $p['name_plan'] .
|
||||
"\nRouter: " . $router_name .
|
||||
"\nGateway: " . $gateway .
|
||||
"\nChannel: " . $channel .
|
||||
"\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) .
|
||||
"\nNote:\n" . $note);
|
||||
}
|
||||
}
|
||||
if (is_array($bills) && count($bills) > 0) {
|
||||
User::billsPaid($bills, $id_customer);
|
||||
}
|
||||
run_hook("recharge_user_finish");
|
||||
Message::sendInvoice($c, $t);
|
||||
if ($trx) {
|
||||
$trx->trx_invoice = $inv;
|
||||
}
|
||||
return $inv;
|
||||
}
|
||||
|
||||
public static function changeTo($username, $plan_id, $from_id)
|
||||
{
|
||||
$c = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $plan_id)->find_one();
|
||||
$b = ORM::for_table('tbl_user_recharges')->find_one($from_id);
|
||||
if ($p['routers'] == $b['routers'] && $b['routers'] != 'radius') {
|
||||
$mikrotik = Mikrotik::info($p['routers']);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($b['routers']);
|
||||
}
|
||||
// delete first
|
||||
if ($p['type'] == 'Hotspot') {
|
||||
if ($b) {
|
||||
if (!$p['is_radius']) {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removeHotspotUser($client, $c['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $c['username']);
|
||||
}
|
||||
} else {
|
||||
if (!$p['is_radius']) {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removeHotspotUser($client, $c['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $c['username']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($b) {
|
||||
if (!$p['is_radius']) {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removePpoeUser($client, $c['username']);
|
||||
Mikrotik::removePpoeActive($client, $c['username']);
|
||||
}
|
||||
} else {
|
||||
if (!$p['is_radius']) {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removePpoeUser($client, $c['username']);
|
||||
Mikrotik::removePpoeActive($client, $c['username']);
|
||||
}
|
||||
}
|
||||
}
|
||||
// call the next mikrotik
|
||||
if ($p['routers'] != $b['routers'] && $p['routers'] != 'radius') {
|
||||
$mikrotik = Mikrotik::info($p['routers']);
|
||||
}
|
||||
if ($p['type'] == 'Hotspot') {
|
||||
if ($b) {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p, $b['expiration'] . '' . $b['time']);
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::addHotspotUser($client, $p, $c);
|
||||
}
|
||||
} else {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p, $b['expiration'] . '' . $b['time']);
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::addHotspotUser($client, $p, $c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($b) {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p);
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::addPpoeUser($client, $p, $c);
|
||||
}
|
||||
} else {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p);
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::addPpoeUser($client, $p, $c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function _raid()
|
||||
{
|
||||
return ORM::for_table('tbl_transactions')->max('id') + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in tbl_transactions
|
||||
* @param string $router_name router name for this package
|
||||
* @param int $plan_id plan id for this package
|
||||
* @param string $gateway payment gateway name
|
||||
* @param string $channel channel payment gateway
|
||||
* @return boolean
|
||||
*/
|
||||
public static function createInvoice($in)
|
||||
{
|
||||
global $config, $admin, $ui;
|
||||
$date = Lang::dateAndTimeFormat($in['recharged_on'], $in['recharged_time']);
|
||||
if ($admin['id'] != $in['admin_id'] && $in['admin_id'] > 0) {
|
||||
$_admin = Admin::_info($in['admin_id']);
|
||||
// if admin not deleted
|
||||
if ($_admin) $admin = $_admin;
|
||||
} else {
|
||||
$admin['fullname'] = 'Customer';
|
||||
}
|
||||
$cust = ORM::for_table('tbl_customers')->where('username', $in['username'])->findOne();
|
||||
|
||||
$note = '';
|
||||
//print
|
||||
$invoice = Lang::pad($config['CompanyName'], ' ', 2) . "\n";
|
||||
$invoice .= Lang::pad($config['address'], ' ', 2) . "\n";
|
||||
$invoice .= Lang::pad($config['phone'], ' ', 2) . "\n";
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
$invoice .= Lang::pads("Invoice", $in['invoice'], ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Date'), $date, ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Sales'), $admin['fullname'], ' ') . "\n";
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Type'), $in['type'], ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Plan Name'), $in['plan_name'], ' ') . "\n";
|
||||
if (!empty($in['note'])) {
|
||||
$in['note'] = str_replace("\r", "", $in['note']);
|
||||
$tmp = explode("\n", $in['note']);
|
||||
foreach ($tmp as $t) {
|
||||
if (strpos($t, " : ") === false) {
|
||||
if (!empty($t)) {
|
||||
$note .= "$t\n";
|
||||
}
|
||||
} else {
|
||||
$tmp2 = explode(" : ", $t);
|
||||
$invoice .= Lang::pads($tmp2[0], $tmp2[1], ' ') . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$invoice .= Lang::pads(Lang::T('Total'), Lang::moneyFormat($in['price']), ' ') . "\n";
|
||||
$method = explode("-", $in['method']);
|
||||
$invoice .= Lang::pads($method[0], $method[1], ' ') . "\n";
|
||||
if (!empty($note)) {
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
$invoice .= Lang::pad($note, ' ', 2) . "\n";
|
||||
}
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
if ($cust) {
|
||||
$invoice .= Lang::pads(Lang::T('Full Name'), $cust['fullname'], ' ') . "\n";
|
||||
}
|
||||
$invoice .= Lang::pads(Lang::T('Username'), $in['username'], ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Password'), '**********', ' ') . "\n";
|
||||
if ($in['type'] != 'Balance') {
|
||||
$invoice .= Lang::pads(Lang::T('Created On'), Lang::dateAndTimeFormat($in['recharged_on'], $in['recharged_time']), ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Expires On'), Lang::dateAndTimeFormat($in['expiration'], $in['time']), ' ') . "\n";
|
||||
}
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
$invoice .= Lang::pad($config['note'], ' ', 2) . "\n";
|
||||
$ui->assign('invoice', $invoice);
|
||||
$config['printer_cols'] = 30;
|
||||
//whatsapp
|
||||
$invoice = Lang::pad($config['CompanyName'], ' ', 2) . "\n";
|
||||
$invoice .= Lang::pad($config['address'], ' ', 2) . "\n";
|
||||
$invoice .= Lang::pad($config['phone'], ' ', 2) . "\n";
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
$invoice .= Lang::pads("Invoice", $in['invoice'], ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Date'), $date, ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Sales'), $admin['fullname'], ' ') . "\n";
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Type'), $in['type'], ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Plan Name'), $in['plan_name'], ' ') . "\n";
|
||||
if (!empty($in['note'])) {
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
foreach ($tmp as $t) {
|
||||
if (strpos($t, " : ") === false) {
|
||||
if (!empty($t)) {
|
||||
$invoice .= Lang::pad($t, ' ', 2) . "\n";
|
||||
}
|
||||
} else {
|
||||
$tmp2 = explode(" : ", $t);
|
||||
$invoice .= Lang::pads($tmp2[0], $tmp2[1], ' ') . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$invoice .= Lang::pads(Lang::T('Total'), Lang::moneyFormat($in['price']), ' ') . "\n";
|
||||
$invoice .= Lang::pads($method[0], $method[1], ' ') . "\n";
|
||||
if (!empty($note)) {
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
$invoice .= Lang::pad($note, ' ', 2) . "\n";
|
||||
}
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
if ($cust) {
|
||||
$invoice .= Lang::pads(Lang::T('Full Name'), $cust['fullname'], ' ') . "\n";
|
||||
}
|
||||
$invoice .= Lang::pads(Lang::T('Username'), $in['username'], ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Password'), '**********', ' ') . "\n";
|
||||
if ($in['type'] != 'Balance') {
|
||||
$invoice .= Lang::pads(Lang::T('Created On'), Lang::dateAndTimeFormat($in['recharged_on'], $in['recharged_time']), ' ') . "\n";
|
||||
$invoice .= Lang::pads(Lang::T('Expires On'), Lang::dateAndTimeFormat($in['expiration'], $in['time']), ' ') . "\n";
|
||||
}
|
||||
$invoice .= Lang::pad("", '=') . "\n";
|
||||
$invoice .= Lang::pad($config['note'], ' ', 2) . "\n";
|
||||
$ui->assign('whatsapp', urlencode("```$invoice```"));
|
||||
$ui->assign('in', $in);
|
||||
}
|
||||
public static function tax($price, $tax_rate = 1)
|
||||
{
|
||||
// Convert tax rate to decimal
|
||||
$tax_rate_decimal = $tax_rate / 100;
|
||||
$tax = $price * $tax_rate_decimal;
|
||||
return $tax;
|
||||
}
|
||||
}
|
||||
366
system/autoload/Paginator.php
Normal file
366
system/autoload/Paginator.php
Normal file
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/SiberTech/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
class Paginator
|
||||
{
|
||||
public static function findMany($query, $search = [], $per_page = '10')
|
||||
{
|
||||
global $routes, $ui;
|
||||
$adjacents = "2";
|
||||
$page = _get('p', 1);
|
||||
$page = (empty($page) ? 1 : $page);
|
||||
$url = U . implode('/', $routes);
|
||||
if (count($search) > 0) {
|
||||
$url .= '&' . http_build_query($search);
|
||||
}
|
||||
$url .= '&p=';
|
||||
$totalReq = $query->count();
|
||||
$lastpage = ceil($totalReq / $per_page);
|
||||
$lpm1 = $lastpage - 1;
|
||||
$limit = $per_page;
|
||||
$startpoint = ($page * $limit) - $limit;
|
||||
if ($lastpage >= 1) {
|
||||
$pages = [];
|
||||
if ($lastpage < 7 + ($adjacents * 2)) {
|
||||
for ($counter = 1; $counter <= $lastpage; $counter++) {
|
||||
$pages[] = $counter;
|
||||
}
|
||||
} elseif ($lastpage > 5 + ($adjacents * 2)) {
|
||||
if ($page < 1 + ($adjacents * 2)) {
|
||||
for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++) {
|
||||
$pages[] = $counter;
|
||||
}
|
||||
$pages[] = "...";
|
||||
$pages[] = $lpm1;
|
||||
$pages[] = $lastpage;
|
||||
} elseif ($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2)) {
|
||||
$pages[] = "1";
|
||||
$pages[] = "2";
|
||||
$pages[] = "...";
|
||||
for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++) {
|
||||
$pages[] = $counter;
|
||||
}
|
||||
$pages[] = "...";
|
||||
$pages[] = $lpm1;
|
||||
$pages[] = $lastpage;
|
||||
} else {
|
||||
$pages[] = "1";
|
||||
$pages[] = "2";
|
||||
$pages[] = "...";
|
||||
for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++) {
|
||||
$pages[] = $counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = [
|
||||
'count' => $lastpage,
|
||||
'limit' => $per_page,
|
||||
'startpoint' => $startpoint,
|
||||
'url' => $url,
|
||||
'page' => $page,
|
||||
'pages' => $pages,
|
||||
'prev' => ($page > 0) ? ($page - 1) : "0",
|
||||
'next' => ($page >= $lastpage) ? $lastpage : $page + 1
|
||||
];
|
||||
if ($ui) {
|
||||
$ui->assign('paginator', $result);
|
||||
}
|
||||
return $query->offset($startpoint)->limit($per_page)->find_many();
|
||||
}
|
||||
}
|
||||
|
||||
public static function build($table, $colVal = [], $query = '', $per_page = '10')
|
||||
{
|
||||
global $routes;
|
||||
global $_L;
|
||||
$url = U . implode('/', $routes);
|
||||
$query = urlencode($query);
|
||||
$adjacents = "2";
|
||||
$page = (int)(empty(_get('p')) ? 1 : _get('p'));
|
||||
$pagination = "";
|
||||
foreach ($colVal as $k => $v) {
|
||||
if (!is_array($v) && strpos($v, '%') === false) {
|
||||
$table = $table->where($k, $v);
|
||||
} else {
|
||||
if (is_array($v)) {
|
||||
$table = $table->where_in($k, $v);
|
||||
} else {
|
||||
$table = $table->where_like($k, $v);
|
||||
}
|
||||
}
|
||||
}
|
||||
$totalReq = $table->count();
|
||||
$page = ($page == 0 ? 1 : $page);
|
||||
$next = $page + 1;
|
||||
$lastpage = ceil($totalReq / $per_page);
|
||||
$lpm1 = $lastpage - 1;
|
||||
$limit = $per_page;
|
||||
$startpoint = ($page * $limit) - $limit;
|
||||
if ($lastpage >= 1) {
|
||||
$pagination .= '<ul class="pagination">';
|
||||
if ($lastpage < 7 + ($adjacents * 2)) {
|
||||
for ($counter = 1; $counter <= $lastpage; $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-link' href='javascript:void(0);'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$counter&q=$query'>$counter</a></li>";
|
||||
}
|
||||
} elseif ($lastpage > 5 + ($adjacents * 2)) {
|
||||
if ($page < 1 + ($adjacents * 2)) {
|
||||
for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-link' href='javascript:void(0);'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$counter&q=$query'>$counter</a></li>";
|
||||
}
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link' href='#'>...</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$lpm1&q=$query'>$lpm1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$lastpage&q=$query'>$lastpage</a></li>";
|
||||
} elseif ($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2)) {
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=1&q=$query'>1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=2&q=$query'>2</a></li>";
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link' href='#'>...</a></li>";
|
||||
for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-link' href='javascript:void(0);'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$counter&q=$query'>$counter</a></li>";
|
||||
}
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link' href='#'>...</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$lpm1&q=$query'>$lpm1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$lastpage&q=$query'>$lastpage</a></li>";
|
||||
} else {
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=1&q=$query'>1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=2&q=$query'>2</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='#'>...</a></li>";
|
||||
for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item'><a class='page-link disabled'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$counter&q=$query'>$counter</a></li>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($page < $counter - 1) {
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$next&q=$query'>" . Lang::T('Next') . "</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}&p=$lastpage&q=$query'>" . Lang::T('Last') . "</a></li>";
|
||||
} else {
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link disabled'>" . Lang::T('Next') . "</a></li>";
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link disabled'>" . Lang::T('Last') . "</a></li>";
|
||||
}
|
||||
$pagination .= "</ul>";
|
||||
$pagination = '<nav>' . $pagination . '</nav>';
|
||||
return array("startpoint" => $startpoint, "limit" => $limit, "found" => $totalReq, "page" => $page, "lastpage" => $lastpage, "contents" => $pagination);
|
||||
}
|
||||
}
|
||||
|
||||
public static function bootstrap($table, $w1 = '', $c1 = '', $w2 = '', $c2 = '', $w3 = '', $c3 = '', $w4 = '', $c4 = '', $per_page = '10')
|
||||
{
|
||||
global $routes;
|
||||
global $_L;
|
||||
$url = U . $routes['0'] . '/' . $routes['1'] . '/';
|
||||
$adjacents = "2";
|
||||
$page = (int)(!isset($routes['2']) ? 1 : $routes['2']);
|
||||
$pagination = "";
|
||||
|
||||
if (is_object($table)) {
|
||||
if ($w1 != '') {
|
||||
$totalReq = $table->where($w1, $c1)->count();
|
||||
} elseif ($w2 != '') {
|
||||
$totalReq = $table->where($w1, $c1)->where($w2, $c2)->count();
|
||||
} elseif ($w3 != '') {
|
||||
$totalReq = $table->where($w1, $c1)->where($w2, $c2)->where($w3, $c3)->count();
|
||||
} elseif ($w4 != '') {
|
||||
$totalReq = $table->where($w1, $c1)->where($w2, $c2)->where($w3, $c3)->where($w4, $c4)->count();
|
||||
} else {
|
||||
$totalReq = $table->count();
|
||||
}
|
||||
} else {
|
||||
if ($w1 != '') {
|
||||
$totalReq = ORM::for_table($table)->where($w1, $c1)->count();
|
||||
} elseif ($w2 != '') {
|
||||
$totalReq = ORM::for_table($table)->where($w1, $c1)->where($w2, $c2)->count();
|
||||
} elseif ($w3 != '') {
|
||||
$totalReq = ORM::for_table($table)->where($w1, $c1)->where($w2, $c2)->where($w3, $c3)->count();
|
||||
} elseif ($w4 != '') {
|
||||
$totalReq = ORM::for_table($table)->where($w1, $c1)->where($w2, $c2)->where($w3, $c3)->where($w4, $c4)->count();
|
||||
} else {
|
||||
$totalReq = ORM::for_table($table)->count();
|
||||
}
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$page = ($page == 0 ? 1 : $page);
|
||||
$start = ($page - 1) * $per_page;
|
||||
|
||||
$prev = $page - 1;
|
||||
$next = $page + 1;
|
||||
$lastpage = ceil($totalReq / $per_page);
|
||||
|
||||
$lpm1 = $lastpage - 1;
|
||||
$limit = $per_page;
|
||||
$startpoint = ($page * $limit) - $limit;
|
||||
|
||||
if ($lastpage >= 1) {
|
||||
$pagination .= '<ul class="pagination">';
|
||||
if ($lastpage < 7 + ($adjacents * 2)) {
|
||||
for ($counter = 1; $counter <= $lastpage; $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-link' href='javascript:void(0);'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$counter'>$counter</a></li>";
|
||||
}
|
||||
} elseif ($lastpage > 5 + ($adjacents * 2)) {
|
||||
if ($page < 1 + ($adjacents * 2)) {
|
||||
for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-link' href='javascript:void(0);'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$counter'>$counter</a></li>";
|
||||
}
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link' href='#'>...</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lpm1'>$lpm1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lastpage'>$lastpage</a></li>";
|
||||
} elseif ($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2)) {
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}1'>1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}2'>2</a></li>";
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link' href='#'>...</a></li>";
|
||||
for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-link' href='javascript:void(0);'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$counter'>$counter</a></li>";
|
||||
}
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link' href='#'>...</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lpm1'>$lpm1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lastpage'>$lastpage</a></li>";
|
||||
} else {
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}1'>1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}2'>2</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='#'>...</a></li>";
|
||||
for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item'><a class='page-link disabled'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$counter'>$counter</a></li>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($page < $counter - 1) {
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$next'>" . Lang::T('Next') . "</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lastpage'>" . Lang::T('Last') . "</a></li>";
|
||||
} else {
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link disabled'>" . Lang::T('Next') . "</a></li>";
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link disabled'>" . Lang::T('Last') . "</a></li>";
|
||||
}
|
||||
$pagination .= "</ul>";
|
||||
$pagination = '<nav>' . $pagination . '</nav>';
|
||||
|
||||
$gen = array("startpoint" => $startpoint, "limit" => $limit, "found" => $totalReq, "page" => $page, "lastpage" => $lastpage, "contents" => $pagination);
|
||||
return $gen;
|
||||
}
|
||||
}
|
||||
|
||||
public static function bootstrapRaw($table, $w1 = '', $c1 = [], $per_page = '10')
|
||||
{
|
||||
global $routes;
|
||||
global $_L;
|
||||
$url = U . $routes['0'] . '/' . $routes['1'] . '/';
|
||||
$adjacents = "2";
|
||||
$page = (int)(!isset($routes['2']) ? 1 : $routes['2']);
|
||||
$pagination = "";
|
||||
if (is_object($table)) {
|
||||
if ($w1 != '') {
|
||||
$totalReq = $table->where_raw($w1, $c1)->count();
|
||||
} else {
|
||||
$totalReq = $table->count();
|
||||
}
|
||||
} else {
|
||||
if ($w1 != '') {
|
||||
$totalReq = ORM::for_table($table)->where_raw($w1, $c1)->count();
|
||||
} else {
|
||||
$totalReq = ORM::for_table($table)->count();
|
||||
}
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$page = ($page == 0 ? 1 : $page);
|
||||
$start = ($page - 1) * $per_page;
|
||||
|
||||
$prev = $page - 1;
|
||||
$next = $page + 1;
|
||||
$lastpage = ceil($totalReq / $per_page);
|
||||
|
||||
$lpm1 = $lastpage - 1;
|
||||
$limit = $per_page;
|
||||
$startpoint = ($page * $limit) - $limit;
|
||||
|
||||
if ($lastpage >= 1) {
|
||||
$pagination .= '<ul class="pagination">';
|
||||
if ($lastpage < 7 + ($adjacents * 2)) {
|
||||
for ($counter = 1; $counter <= $lastpage; $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-link' href='javascript:void(0);'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$counter'>$counter</a></li>";
|
||||
}
|
||||
} elseif ($lastpage > 5 + ($adjacents * 2)) {
|
||||
if ($page < 1 + ($adjacents * 2)) {
|
||||
for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-link' href='javascript:void(0);'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$counter'>$counter</a></li>";
|
||||
}
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link' href='#'>...</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lpm1'>$lpm1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lastpage'>$lastpage</a></li>";
|
||||
} elseif ($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2)) {
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}1'>1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}2'>2</a></li>";
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link' href='#'>...</a></li>";
|
||||
for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-link' href='javascript:void(0);'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$counter'>$counter</a></li>";
|
||||
}
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-link' href='#'>...</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lpm1'>$lpm1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lastpage'>$lastpage</a></li>";
|
||||
} else {
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}1'>1</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}2'>2</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='#'>...</a></li>";
|
||||
for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++) {
|
||||
if ($counter == $page)
|
||||
$pagination .= "<li class='page-item active'><a class='page-item disabled'>$counter</a></li>";
|
||||
else
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$counter'>$counter</a></li>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($page < $counter - 1) {
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$next'>" . Lang::T('Next') . "</a></li>";
|
||||
$pagination .= "<li class='page-item'><a class='page-link' href='{$url}$lastpage'>" . Lang::T('Last') . "</a></li>";
|
||||
} else {
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-item disabled'>" . Lang::T('Next') . "</a></li>";
|
||||
$pagination .= "<li class='page-item disabled'><a class='page-item disabled'>" . Lang::T('Last') . "</a></li>";
|
||||
}
|
||||
$pagination .= "</ul>";
|
||||
$pagination = '<nav>' . $pagination . '</nav>';
|
||||
|
||||
$gen = array("startpoint" => $startpoint, "limit" => $limit, "found" => $totalReq, "page" => $page, "lastpage" => $lastpage, "contents" => $pagination);
|
||||
return $gen;
|
||||
}
|
||||
}
|
||||
}
|
||||
1994
system/autoload/Parsedown.php
Normal file
1994
system/autoload/Parsedown.php
Normal file
File diff suppressed because it is too large
Load Diff
35
system/autoload/Password.php
Normal file
35
system/autoload/Password.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
class Password
|
||||
{
|
||||
|
||||
public static function _crypt($password)
|
||||
{
|
||||
return sha1($password);
|
||||
}
|
||||
|
||||
public static function _verify($user_input, $hashed_password)
|
||||
{
|
||||
if (sha1($user_input) == $hashed_password) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static function _uverify($user_input, $hashed_password)
|
||||
{
|
||||
if ($user_input == $hashed_password) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static function _gen()
|
||||
{
|
||||
$pass = substr(str_shuffle(str_repeat('ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@#!123456789', 8)), 0, 8);
|
||||
return $pass;
|
||||
}
|
||||
}
|
||||
349
system/autoload/Radius.php
Normal file
349
system/autoload/Radius.php
Normal file
@@ -0,0 +1,349 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
/**
|
||||
* Radius Class
|
||||
* based https://gist.github.com/nasirhafeez/6669b24aab0bda545f60f9da5ed14f25
|
||||
*/
|
||||
class Radius
|
||||
{
|
||||
|
||||
public static function getClient()
|
||||
{
|
||||
global $config;
|
||||
if (empty($config['radius_client'])) {
|
||||
if (function_exists("shell_exec")) {
|
||||
shell_exec('which radclient');
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
$config['radius_client'];
|
||||
}
|
||||
}
|
||||
|
||||
public static function getTableNas()
|
||||
{
|
||||
return ORM::for_table('nas', 'radius');
|
||||
}
|
||||
public static function getTableAcct()
|
||||
{
|
||||
return ORM::for_table('radacct', 'radius');
|
||||
}
|
||||
public static function getTableCustomer()
|
||||
{
|
||||
return ORM::for_table('radcheck', 'radius');
|
||||
}
|
||||
|
||||
public static function getTableCustomerAttr()
|
||||
{
|
||||
return ORM::for_table('radreply', 'radius');
|
||||
}
|
||||
|
||||
public static function getTablePackage()
|
||||
{
|
||||
return ORM::for_table('radgroupreply', 'radius');
|
||||
}
|
||||
|
||||
public static function getTableUserPackage()
|
||||
{
|
||||
return ORM::for_table('radusergroup', 'radius');
|
||||
}
|
||||
|
||||
public static function nasAdd($name, $ip, $ports, $secret, $routers = "", $description = "", $type = 'other', $server = null, $community = null)
|
||||
{
|
||||
$n = Radius::getTableNas()->create();
|
||||
$n->nasname = $ip;
|
||||
$n->shortname = $name;
|
||||
$n->type = $type;
|
||||
$n->ports = $ports;
|
||||
$n->secret = $secret;
|
||||
$n->description = $description;
|
||||
$n->server = $server;
|
||||
$n->community = $community;
|
||||
$n->routers = $routers;
|
||||
$n->save();
|
||||
return $n->id();
|
||||
}
|
||||
|
||||
public static function nasUpdate($id, $name, $ip, $ports, $secret, $routers = "", $description = "", $type = 'other', $server = null, $community = null)
|
||||
{
|
||||
$n = Radius::getTableNas()->find_one($id);
|
||||
if (empty($n)) {
|
||||
return false;
|
||||
}
|
||||
$n->nasname = $ip;
|
||||
$n->shortname = $name;
|
||||
$n->type = $type;
|
||||
$n->ports = $ports;
|
||||
$n->secret = $secret;
|
||||
$n->description = $description;
|
||||
$n->server = $server;
|
||||
$n->community = $community;
|
||||
$n->routers = $routers;
|
||||
return $n->save();
|
||||
}
|
||||
|
||||
public static function planUpSert($plan_id, $rate, $pool = null)
|
||||
{
|
||||
$rates = explode('/', $rate);
|
||||
##burst fixed
|
||||
if (strpos($rate, ' ')) {
|
||||
$ratos = $rates[0] . '/' . $rates[1] . ' ' . $rates[2] . '/' . $rates[3] . '/' . $rates[4] . '/' . $rates[5] . '/' . $rates[6];
|
||||
} else {
|
||||
$ratos = $rates[0] . '/' . $rates[1];
|
||||
}
|
||||
|
||||
Radius::upsertPackage($plan_id, 'Ascend-Data-Rate', $rates[1], ':=');
|
||||
Radius::upsertPackage($plan_id, 'Ascend-Xmit-Rate', $rates[0], ':=');
|
||||
Radius::upsertPackage($plan_id, 'Mikrotik-Rate-Limit', $ratos, ':=');
|
||||
// if ($pool != null) {
|
||||
// Radius::upsertPackage($plan_id, 'Framed-Pool', $pool, ':=');
|
||||
// }
|
||||
}
|
||||
|
||||
public static function planDelete($plan_id)
|
||||
{
|
||||
// Delete Plan
|
||||
Radius::getTablePackage()->where_equal('plan_id', "plan_" . $plan_id)->delete_many();
|
||||
// Reset User Plan
|
||||
$c = Radius::getTableUserPackage()->where_equal('groupname', "plan_" . $plan_id)->findMany();
|
||||
if ($c) {
|
||||
foreach ($c as $u) {
|
||||
$u->groupname = '';
|
||||
$u->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function customerChangeUsername($from, $to)
|
||||
{
|
||||
$c = Radius::getTableCustomer()->where_equal('username', $from)->findMany();
|
||||
if ($c) {
|
||||
foreach ($c as $u) {
|
||||
$u->username = $to;
|
||||
$u->save();
|
||||
}
|
||||
}
|
||||
$c = Radius::getTableUserPackage()->where_equal('username', $from)->findMany();
|
||||
if ($c) {
|
||||
foreach ($c as $u) {
|
||||
$u->username = $to;
|
||||
$u->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function customerDeactivate($username, $radiusDisconnect = true)
|
||||
{ {
|
||||
global $radius_pass;
|
||||
$r = Radius::getTableCustomer()->where_equal('username', $username)->whereEqual('attribute', 'Cleartext-Password')->findOne();
|
||||
if ($r) {
|
||||
// no need to delete, because it will make ID got higher
|
||||
// we just change the password
|
||||
$r->value = md5(time() . $username . $radius_pass);
|
||||
$r->save();
|
||||
if ($radiusDisconnect)
|
||||
return Radius::disconnectCustomer($username);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function customerDelete($username)
|
||||
{
|
||||
Radius::getTableCustomer()->where_equal('username', $username)->delete_many();
|
||||
Radius::getTableUserPackage()->where_equal('username', $username)->delete_many();
|
||||
}
|
||||
|
||||
/**
|
||||
* When add a plan to Customer, use this
|
||||
*/
|
||||
public static function customerAddPlan($customer, $plan, $expired = null)
|
||||
{
|
||||
global $config;
|
||||
if (Radius::customerUpsert($customer, $plan)) {
|
||||
$p = Radius::getTableUserPackage()->where_equal('username', $customer['username'])->findOne();
|
||||
if ($p) {
|
||||
// if exists
|
||||
Radius::delAtribute(Radius::getTableCustomer(), 'Max-All-Session', 'username', $customer['username']);
|
||||
Radius::delAtribute(Radius::getTableCustomer(), 'Max-Volume', 'username', $customer['username']);
|
||||
$p->groupname = "plan_" . $plan['id'];
|
||||
$p->save();
|
||||
} else {
|
||||
$p = Radius::getTableUserPackage()->create();
|
||||
$p->username = $customer['username'];
|
||||
$p->groupname = "plan_" . $plan['id'];
|
||||
$p->priority = 1;
|
||||
$p->save();
|
||||
}
|
||||
if ($plan['type'] == 'Hotspot' && $plan['typebp'] == "Limited") {
|
||||
if ($plan['limit_type'] == "Time_Limit") {
|
||||
if ($plan['time_unit'] == 'Hrs')
|
||||
$timelimit = $plan['time_limit'] * 60 * 60;
|
||||
else
|
||||
$timelimit = $plan['time_limit'] * 60;
|
||||
Radius::upsertCustomer($customer['username'], 'Max-All-Session', $timelimit);
|
||||
//Radius::upsertCustomer($customer['username'], 'Expire-After', $timelimit);
|
||||
} else if ($plan['limit_type'] == "Data_Limit") {
|
||||
if ($plan['data_unit'] == 'GB')
|
||||
$datalimit = $plan['data_limit'] . "000000000";
|
||||
else
|
||||
$datalimit = $plan['data_limit'] . "000000";
|
||||
Radius::upsertCustomer($customer['username'], 'Max-Volume', $datalimit);
|
||||
// Mikrotik Spesific
|
||||
//Radius::upsertCustomer($customer['username'], 'Max-Data', $datalimit);
|
||||
//Radius::upsertCustomer($customer['username'], 'Mikrotik-Total-Limit', $datalimit);
|
||||
} else if ($plan['limit_type'] == "Both_Limit") {
|
||||
if ($plan['time_unit'] == 'Hrs')
|
||||
$timelimit = $plan['time_limit'] * 60 * 60;
|
||||
else
|
||||
$timelimit = $plan['time_limit'] * 60;
|
||||
if ($plan['data_unit'] == 'GB')
|
||||
$datalimit = $plan['data_limit'] . "000000000";
|
||||
else
|
||||
$datalimit = $plan['data_limit'] . "000000";
|
||||
Radius::upsertCustomer($customer['username'], 'Max-Volume', $datalimit);
|
||||
Radius::upsertCustomer($customer['username'], 'Max-All-Session', $timelimit);
|
||||
// Mikrotik Spesific
|
||||
//Radius::upsertCustomer($customer['username'], 'Max-Data', $datalimit);
|
||||
//Radius::upsertCustomer($customer['username'], 'Mikrotik-Total-Limit', $datalimit);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
Radius::delAtribute(Radius::getTableCustomer(), 'Max-Volume', 'username', $customer['username']);
|
||||
Radius::delAtribute(Radius::getTableCustomer(), 'Max-All-Session', 'username', $customer['username']);
|
||||
//Radius::delAtribute(Radius::getTableCustomer(), 'Max-Data', 'username', $customer['username']);
|
||||
}
|
||||
|
||||
Radius::disconnectCustomer($customer['username']);
|
||||
Radius::getTableAcct()->where_equal('username', $customer['username'])->delete_many();
|
||||
|
||||
|
||||
// expired user
|
||||
if ($expired != null) {
|
||||
//Radius::upsertCustomer($customer['username'], 'access-period', strtotime($expired) - time());
|
||||
Radius::upsertCustomer($customer['username'], 'Max-All-Session', strtotime($expired) - time());
|
||||
//Radius::upsertCustomer($customer['username'], 'expiration', date('d M Y H:i:s', strtotime($expired)));
|
||||
// Mikrotik Spesific
|
||||
Radius::upsertCustomer(
|
||||
$customer['username'],
|
||||
'WISPr-Session-Terminate-Time',
|
||||
date('Y-m-d', strtotime($expired)) . 'T' . date('H:i:s', strtotime($expired)) . Timezone::getTimeOffset($config['timezone'])
|
||||
);
|
||||
} else {
|
||||
Radius::delAtribute(Radius::getTableCustomer(), 'Max-All-Session', 'username', $customer['username']);
|
||||
//Radius::delAtribute(Radius::getTableCustomer(), 'access-period', 'username', $customer['username']);
|
||||
//Radius::delAtribute(Radius::getTableCustomer(), 'expiration', 'username', $customer['username']);
|
||||
}
|
||||
|
||||
if ($plan['type'] == 'PPPOE') {
|
||||
Radius::upsertCustomerAttr($customer['username'], 'Framed-Pool', $plan['pool'], ':=');
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function customerUpsert($customer, $plan)
|
||||
{
|
||||
if ($plan['type'] == 'PPPOE') {
|
||||
Radius::upsertCustomer($customer['username'], 'Cleartext-Password', (empty($customer['pppoe_password'])) ? $customer['password'] : $customer['pppoe_password']);
|
||||
} else {
|
||||
Radius::upsertCustomer($customer['username'], 'Cleartext-Password', $customer['password']);
|
||||
}
|
||||
Radius::upsertCustomer($customer['username'], 'Simultaneous-Use', ($plan['type'] == 'PPPOE') ? 1 : $plan['shared_users']);
|
||||
// Mikrotik Spesific
|
||||
Radius::upsertCustomer($customer['username'], 'Port-Limit', ($plan['type'] == 'PPPOE') ? 1 : $plan['shared_users']);
|
||||
Radius::upsertCustomer($customer['username'], 'Mikrotik-Wireless-Comment', $customer['fullname']);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static function delAtribute($tabel, $attribute, $key, $value)
|
||||
{
|
||||
$r = $tabel->where_equal($key, $value)->whereEqual('attribute', $attribute)->findOne();
|
||||
if ($r) $r->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* To insert or update existing plan
|
||||
*/
|
||||
private static function upsertPackage($plan_id, $attr, $value, $op = ':=')
|
||||
{
|
||||
$r = Radius::getTablePackage()->where_equal('plan_id', $plan_id)->whereEqual('attribute', $attr)->find_one();
|
||||
if (!$r) {
|
||||
$r = Radius::getTablePackage()->create();
|
||||
$r->groupname = "plan_" . $plan_id;
|
||||
$r->plan_id = $plan_id;
|
||||
}
|
||||
$r->attribute = $attr;
|
||||
$r->op = $op;
|
||||
$r->value = $value;
|
||||
return $r->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* To insert or update existing customer
|
||||
*/
|
||||
public static function upsertCustomer($username, $attr, $value, $op = ':=')
|
||||
{
|
||||
$r = Radius::getTableCustomer()->where_equal('username', $username)->whereEqual('attribute', $attr)->find_one();
|
||||
if (!$r) {
|
||||
$r = Radius::getTableCustomer()->create();
|
||||
$r->username = $username;
|
||||
}
|
||||
$r->attribute = $attr;
|
||||
$r->op = $op;
|
||||
$r->value = $value;
|
||||
return $r->save();
|
||||
}
|
||||
/**
|
||||
* To insert or update existing customer Attribute
|
||||
*/
|
||||
public static function upsertCustomerAttr($username, $attr, $value, $op = ':=')
|
||||
{
|
||||
$r = Radius::getTableCustomerAttr()->where_equal('username', $username)->whereEqual('attribute', $attr)->find_one();
|
||||
if (!$r) {
|
||||
$r = Radius::getTableCustomerAttr()->create();
|
||||
$r->username = $username;
|
||||
}
|
||||
$r->attribute = $attr;
|
||||
$r->op = $op;
|
||||
$r->value = $value;
|
||||
return $r->save();
|
||||
}
|
||||
|
||||
public static function disconnectCustomer($username)
|
||||
{
|
||||
global $_app_stage;
|
||||
if ($_app_stage == 'demo') {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Fix loop to all Nas but still detecting Hotspot Multylogin from other Nas
|
||||
*/
|
||||
$act = ORM::for_table('radacct')->where_raw("acctstoptime IS NULL")->where('username', $username)->find_one();
|
||||
$nas = Radius::getTableNas()->where('nasname', $act['nasipaddress'])->find_many();
|
||||
$count = count($nas) * 15;
|
||||
set_time_limit($count);
|
||||
$result = [];
|
||||
foreach ($nas as $n) {
|
||||
$port = 3799;
|
||||
if (!empty($n['ports'])) {
|
||||
$port = $n['ports'];
|
||||
}
|
||||
$result[] = $n['nasname'] . ': ' . @shell_exec("echo 'User-Name = $username,Framed-IP-Address = " . $act['framedipaddress'] . "' | radclient -x " . trim($n['nasname']) . ":$port disconnect '" . $n['secret'] . "'");
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
64
system/autoload/Text.php
Normal file
64
system/autoload/Text.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
*
|
||||
* This file is for Text Transformation
|
||||
**/
|
||||
|
||||
class Text
|
||||
{
|
||||
|
||||
public static function toHex($string)
|
||||
{
|
||||
return "\x" . implode("\x", str_split(array_shift(unpack('H*', $string)), 2));
|
||||
}
|
||||
|
||||
public static function alphanumeric($str, $tambahan = "")
|
||||
{
|
||||
return preg_replace("/[^a-zA-Z0-9" . $tambahan . "]+/", "", $str);
|
||||
}
|
||||
|
||||
public static function numeric($str)
|
||||
{
|
||||
return preg_replace("/[^0-9]+/", "", $str);
|
||||
}
|
||||
|
||||
public static function ucWords($text)
|
||||
{
|
||||
return ucwords(str_replace('_', ' ', $text));
|
||||
}
|
||||
|
||||
public static function randomUpLowCase($text)
|
||||
{
|
||||
$jml = strlen($text);
|
||||
$result = '';
|
||||
for ($i = 0; $i < $jml; $i++) {
|
||||
if (rand(0, 99) % 2) {
|
||||
$result .= strtolower(substr($text, $i, 1));
|
||||
} else {
|
||||
$result .= substr($text, $i, 1);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function maskText($text){
|
||||
$len = strlen($text);
|
||||
if($len < 3){
|
||||
return "***";
|
||||
}else if($len<5){
|
||||
return substr($text,0,1)."***".substr($text,-1,1);
|
||||
}else if($len<8){
|
||||
return substr($text,0,2)."***".substr($text,-2,2);
|
||||
}else{
|
||||
return substr($text,0,4)."******".substr($text,-3,3);
|
||||
}
|
||||
}
|
||||
|
||||
public static function sanitize($str)
|
||||
{
|
||||
return preg_replace("/[^A-Za-z0-9]/", '_', $str);;
|
||||
}
|
||||
}
|
||||
50
system/autoload/Timezone.php
Normal file
50
system/autoload/Timezone.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
class Timezone {
|
||||
public static function timezoneList()
|
||||
{
|
||||
$timezoneIdentifiers = DateTimeZone::listIdentifiers();
|
||||
$utcTime = new DateTime('now', new DateTimeZone('UTC'));
|
||||
|
||||
$tempTimezones = array();
|
||||
foreach ($timezoneIdentifiers as $timezoneIdentifier) {
|
||||
$currentTimezone = new DateTimeZone($timezoneIdentifier);
|
||||
|
||||
$tempTimezones[] = array(
|
||||
'offset' => (int)$currentTimezone->getOffset($utcTime),
|
||||
'identifier' => $timezoneIdentifier
|
||||
);
|
||||
}
|
||||
|
||||
// Sort the array by offset,identifier ascending
|
||||
usort($tempTimezones, function($a, $b) {
|
||||
return ($a['offset'] == $b['offset'])
|
||||
? strcmp($a['identifier'], $b['identifier'])
|
||||
: $a['offset'] - $b['offset'];
|
||||
});
|
||||
|
||||
$timezoneList = array();
|
||||
foreach ($tempTimezones as $tz) {
|
||||
$sign = ($tz['offset'] > 0) ? '+' : '-';
|
||||
$offset = gmdate('H:i', abs($tz['offset']));
|
||||
$timezoneList[$tz['identifier']] = '(UTC ' . $sign . $offset . ') ' .
|
||||
$tz['identifier'];
|
||||
}
|
||||
|
||||
return $timezoneList;
|
||||
}
|
||||
|
||||
public static function getTimeOffset($tz = 'Asia/Jakarta'){
|
||||
$utcTime = new DateTime('now', new DateTimeZone('UTC'));
|
||||
$currentTimezone = new DateTimeZone($tz);
|
||||
$offset = $currentTimezone->getOffset($utcTime);
|
||||
$sign = ($offset > 0) ? '+' : '-';
|
||||
$offset = gmdate('H:i', abs($offset));
|
||||
return $sign.$offset;
|
||||
}
|
||||
}
|
||||
202
system/autoload/User.php
Normal file
202
system/autoload/User.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
class User
|
||||
{
|
||||
public static function getID()
|
||||
{
|
||||
global $db_password;
|
||||
if (isset($_SESSION['uid']) && !empty($_SESSION['uid'])) {
|
||||
return $_SESSION['uid'];
|
||||
} else if (isset($_COOKIE['uid'])) {
|
||||
// id.time.sha1
|
||||
$tmp = explode('.', $_COOKIE['uid']);
|
||||
if (sha1($tmp[0] . '.' . $tmp[1] . '.' . $db_password) == $tmp[2]) {
|
||||
if (time() - $tmp[1] < 86400 * 30) {
|
||||
$_SESSION['uid'] = $tmp[0];
|
||||
return $tmp[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function getBills($id = 0)
|
||||
{
|
||||
if (!$id) {
|
||||
$id = User::getID();
|
||||
if (!$id) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
$addcost = 0;
|
||||
$bills = [];
|
||||
$attrs = User::getAttributes('Bill', $id);
|
||||
foreach ($attrs as $k => $v) {
|
||||
// if has : then its an installment
|
||||
if (strpos($v, ":") === false) {
|
||||
// Not installment
|
||||
$bills[$k] = $v;
|
||||
$addcost += $v;
|
||||
} else {
|
||||
// installment
|
||||
list($cost, $rem) = explode(":", $v);
|
||||
// :0 installment is done
|
||||
if (!empty($rem)) {
|
||||
$bills[$k] = $cost;
|
||||
$addcost += $cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$bills, $addcost];
|
||||
}
|
||||
|
||||
public static function billsPaid($bills, $id = 0)
|
||||
{
|
||||
if (!$id) {
|
||||
$id = User::getID();
|
||||
if (!$id) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
foreach ($bills as $k => $v) {
|
||||
// if has : then its an installment
|
||||
$v = User::getAttribute($k, $id);
|
||||
if (strpos($v, ":") === false) {
|
||||
// Not installment, no need decrement
|
||||
} else {
|
||||
// installment
|
||||
list($cost, $rem) = explode(":", $v);
|
||||
// :0 installment is done
|
||||
if ($rem != 0) {
|
||||
User::setAttribute($k, "$cost:" . ($rem - 1), $id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function setAttribute($name, $value, $id = 0)
|
||||
{
|
||||
if (!$id) {
|
||||
$id = User::getID();
|
||||
if (!$id) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
$f = ORM::for_table('tbl_customers_fields')->where('field_name', $name)->where('customer_id', $id)->find_one();
|
||||
if (!$f) {
|
||||
$f = ORM::for_table('tbl_customers_fields')->create();
|
||||
$f->customer_id = $id;
|
||||
$f->field_name = $name;
|
||||
$f->field_value = $value;
|
||||
$f->save();
|
||||
$result = $f->id();
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
$f->field_value = $value;
|
||||
$f->save();
|
||||
return $f['id'];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function getAttribute($name, $id = 0)
|
||||
{
|
||||
if (!$id) {
|
||||
$id = User::getID();
|
||||
if (!$id) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
$f = ORM::for_table('tbl_customers_fields')->where('field_name', $name)->where('customer_id', $id)->find_one();
|
||||
if ($f) {
|
||||
return $f['field_value'];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function getAttributes($endWith, $id = 0)
|
||||
{
|
||||
if (!$id) {
|
||||
$id = User::getID();
|
||||
if (!$id) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
$attrs = [];
|
||||
$f = ORM::for_table('tbl_customers_fields')->where_like('field_name', "%$endWith")->where('customer_id', $id)->find_many();
|
||||
if ($f) {
|
||||
foreach ($f as $k) {
|
||||
$attrs[$k['field_name']] = $k['field_value'];
|
||||
}
|
||||
return $attrs;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function setCookie($uid)
|
||||
{
|
||||
global $db_password;
|
||||
if (isset($uid)) {
|
||||
$time = time();
|
||||
setcookie('uid', $uid . '.' . $time . '.' . sha1($uid . '.' . $time . '.' . $db_password), time() + 86400 * 30);
|
||||
}
|
||||
}
|
||||
|
||||
public static function removeCookie()
|
||||
{
|
||||
if (isset($_COOKIE['uid'])) {
|
||||
setcookie('uid', '', time() - 86400);
|
||||
}
|
||||
}
|
||||
|
||||
public static function _info($id = 0)
|
||||
{
|
||||
global $config;
|
||||
if ($config['maintenance_mode'] == true) {
|
||||
if ($config['maintenance_mode_logout'] == true) {
|
||||
r2(U . 'logout', 'd', '');
|
||||
} else {
|
||||
displayMaintenanceMessage();
|
||||
}
|
||||
}
|
||||
if (!$id) {
|
||||
$id = User::getID();
|
||||
}
|
||||
$d = ORM::for_table('tbl_customers')->find_one($id);
|
||||
if ($d['status'] == 'Banned') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($d['status']), 'danger', "logout");
|
||||
}
|
||||
if (empty($d['username'])) {
|
||||
r2(U . 'logout', 'd', '');
|
||||
}
|
||||
return $d;
|
||||
}
|
||||
|
||||
public static function _billing($id = 0)
|
||||
{
|
||||
if (!$id) {
|
||||
$id = User::getID();
|
||||
}
|
||||
$d = ORM::for_table('tbl_user_recharges')
|
||||
->select('tbl_user_recharges.id', 'id')
|
||||
->selects([
|
||||
'customer_id', 'username', 'plan_id', 'namebp', 'recharged_on', 'recharged_time', 'expiration', 'time',
|
||||
'status', 'method', 'plan_type',
|
||||
['tbl_user_recharges.routers', 'routers'],
|
||||
['tbl_user_recharges.type', 'type'],
|
||||
'admin_id', 'prepaid'
|
||||
])
|
||||
->where('customer_id', $id)
|
||||
->left_outer_join('tbl_plans', array('tbl_plans.id', '=', 'tbl_user_recharges.plan_id'))
|
||||
->find_many();
|
||||
return $d;
|
||||
}
|
||||
}
|
||||
323
system/autoload/Validator.php
Normal file
323
system/autoload/Validator.php
Normal file
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
/**
|
||||
* Validator class
|
||||
*/
|
||||
class Validator
|
||||
{
|
||||
|
||||
/**
|
||||
* String text finder
|
||||
*
|
||||
* @access private
|
||||
* @param string $string
|
||||
* @param array $hits
|
||||
* @return void
|
||||
*/
|
||||
public static function textHit($string, $exclude = "")
|
||||
{
|
||||
if (empty($exclude)) return false;
|
||||
if (is_array($exclude)) {
|
||||
foreach ($exclude as $text) {
|
||||
if (strstr($string, $text)) return true;
|
||||
}
|
||||
} else {
|
||||
if (strstr($string, $exclude)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number compare
|
||||
*
|
||||
* @access private
|
||||
* @param int $integer
|
||||
* @param int $max
|
||||
* @param int $min
|
||||
* @return bool
|
||||
*/
|
||||
private static function numberBetween($integer, $max = null, $min = 0)
|
||||
{
|
||||
if (is_numeric($min) && $integer <= $min) return false;
|
||||
if (is_numeric($max) && $integer >= $max) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Email addres check
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @param array $exclude
|
||||
* @return bool
|
||||
*/
|
||||
public static function Email($string, $exclude = "")
|
||||
{
|
||||
if (self::textHit($string, $exclude)) return false;
|
||||
return (bool)preg_match("/^([a-z0-9])(([-a-z0-9._])*([a-z0-9]))*\@([a-z0-9])(([a-z0-9-])*([a-z0-9]))+(\.([a-z0-9])([-a-z0-9_-])?([a-z0-9])+)+$/i", $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL check
|
||||
*
|
||||
* @access public
|
||||
* @param strin $string
|
||||
* @return bool
|
||||
*/
|
||||
public static function Url($string, $exclude = "")
|
||||
{
|
||||
if (self::textHit($string, $exclude)) return false;
|
||||
return (bool)preg_match("/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i", $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* IP
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @return void
|
||||
*/
|
||||
public static function Ip($string)
|
||||
{
|
||||
return (bool)preg_match("/^(1?\d{1,2}|2([0-4]\d|5[0-5]))(\.(1?\d{1,2}|2([0-4]\d|5[0-5]))){3}$/", $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if it is an number
|
||||
*
|
||||
* @access public
|
||||
* @param int $integer
|
||||
* @param int $max
|
||||
* @param int $min
|
||||
* @return bool
|
||||
*/
|
||||
public static function Number($integer, $max = null, $min = 0)
|
||||
{
|
||||
if (preg_match("/^\-?\+?[0-9e1-9]+$/", $integer)) {
|
||||
if (!self::numberBetween($integer, $max, $min)) return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if it is an unsigned number
|
||||
*
|
||||
* @access public
|
||||
* @param int $integer
|
||||
* @return bool
|
||||
*/
|
||||
public static function UnsignedNumber($integer)
|
||||
{
|
||||
return (bool)preg_match("/^\+?[0-9]+$/", $integer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Float
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @return bool
|
||||
*/
|
||||
public static function Float($string)
|
||||
{
|
||||
return (bool)($string == strval(floatval($string))) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alpha check
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @return void
|
||||
*/
|
||||
public static function Alpha($string)
|
||||
{
|
||||
return (bool)preg_match("/^[a-zA-Z]+$/", $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alpha numeric check
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @return void
|
||||
*/
|
||||
public static function AlphaNumeric($string)
|
||||
{
|
||||
return (bool)preg_match("/^[0-9a-zA-Z]+$/", $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific chars check
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @param array $allowed
|
||||
* @return void
|
||||
*/
|
||||
public static function Chars($string, $allowed = array("a-z"))
|
||||
{
|
||||
return (bool)preg_match("/^[" . implode("", $allowed) . "]+$/", $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check length of an string
|
||||
*
|
||||
* @access public
|
||||
* @param string $stirng
|
||||
* @param int $max
|
||||
* @param int $min
|
||||
* @return bool
|
||||
*/
|
||||
public static function Length($string, $max = null, $min = 0)
|
||||
{
|
||||
$length = strlen($string);
|
||||
if (!self::numberBetween($length, $max, $min)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hex color check
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @return void
|
||||
*/
|
||||
public static function HexColor($string)
|
||||
{
|
||||
return (bool)preg_match("/^(#)?([0-9a-f]{1,2}){3}$/i", $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data validation
|
||||
*
|
||||
* Does'nt matter how you provide the date
|
||||
* dd/mm/yyyy
|
||||
* dd-mm-yyyy
|
||||
* yyyy/mm/dd
|
||||
* yyyy-mm-dd
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @return bool
|
||||
*/
|
||||
public static function Date($string)
|
||||
{
|
||||
$date = date('Y', strtotime($string));
|
||||
return ($date == "1970" || $date == '') ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Older than check
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @param int $age
|
||||
* @return bool
|
||||
*/
|
||||
public static function OlderThan($string, $age)
|
||||
{
|
||||
$date = date('Y', strtotime($string));
|
||||
if ($date == "1970" || $date == '') return false;
|
||||
return (date('Y') - $date) > $age ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML valid
|
||||
*
|
||||
* @access public
|
||||
* @param string $string
|
||||
* @return bool
|
||||
*/
|
||||
public static function Xml($string)
|
||||
{
|
||||
$Xml = @simplexml_load_string($string);
|
||||
return ($Xml === false) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is filesize between
|
||||
*
|
||||
* @access public
|
||||
* @param string $file
|
||||
* @param int $max
|
||||
* @param int $min
|
||||
* @return bool
|
||||
*/
|
||||
public static function FilesizeBetween($file, $max = null, $min = 0)
|
||||
{
|
||||
$filesize = filesize($file);
|
||||
return self::numberBetween($filesize, $max, $min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is image width between
|
||||
*
|
||||
* @access public
|
||||
* @param string $image
|
||||
* @param int $max_width
|
||||
* @param int $min_width
|
||||
* @param int $max_height
|
||||
* @param int $min_height
|
||||
* @return void
|
||||
*/
|
||||
public static function ImageSizeBetween($image, $max_width = "", $min_width = 0, $max_height = "", $min_height = 0)
|
||||
{
|
||||
$size = getimagesize($image);
|
||||
if (!self::numberBetween($size[0], $max_width, $min_width)) return false;
|
||||
if (!self::numberBetween($size[1], $max_height, $min_height)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Phone numbers
|
||||
*
|
||||
* @access public
|
||||
* @param string $phone
|
||||
* @return bool
|
||||
*/
|
||||
public static function Phone($phone)
|
||||
{
|
||||
$formats = array(
|
||||
'###-###-####',
|
||||
'####-###-###',
|
||||
'(###) ###-###',
|
||||
'####-####-####',
|
||||
'##-###-####-####',
|
||||
'####-####',
|
||||
'###-###-###',
|
||||
'#####-###-###',
|
||||
'##########',
|
||||
'####-##-##-##'
|
||||
);
|
||||
$format = trim(preg_replace("/[0-9]/", "#", $phone));
|
||||
return (bool)in_array($format, $formats);
|
||||
}
|
||||
|
||||
public static function countRouterPlan($plans, $router){
|
||||
$n = 0;
|
||||
foreach ($plans as $plan){
|
||||
if($plan['routers'] == $router){
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
|
||||
public static function isRouterHasPlan($plans, $router){
|
||||
foreach ($plans as $plan){
|
||||
if($plan['routers'] == $router){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
50
system/autoload/Widget.php
Normal file
50
system/autoload/Widget.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
/**
|
||||
* Validator class
|
||||
*/
|
||||
class Widget
|
||||
{
|
||||
|
||||
public static function rows($rows, $result){
|
||||
$result .= '<div class="row">';
|
||||
foreach($rows as $row){
|
||||
|
||||
}
|
||||
$result .= '</div>';
|
||||
}
|
||||
|
||||
public static function columns($cols, $result){
|
||||
$c = count($cols);
|
||||
switch($c){
|
||||
case 1:
|
||||
$result .= '<div class="col-md-12">';
|
||||
break;
|
||||
case 2:
|
||||
$result .= '<div class="col-md-6">';
|
||||
break;
|
||||
case 3:
|
||||
$result .= '<div class="col-md-4">';
|
||||
break;
|
||||
case 4:
|
||||
$result .= '<div class="col-md-4">';
|
||||
break;
|
||||
case 5:
|
||||
$result .= '<div class="col-md-4">';
|
||||
break;
|
||||
default:
|
||||
$result .= '<div class="col-md-1">';
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($cols as $col){
|
||||
}
|
||||
$result .= '</div>';
|
||||
}
|
||||
}
|
||||
8
system/autoload/index.html
Normal file
8
system/autoload/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
||||
169
system/boot.php
Normal file
169
system/boot.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
|
||||
**/
|
||||
|
||||
try {
|
||||
require_once 'init.php';
|
||||
} catch (Throwable $e) {
|
||||
$ui = new Smarty();
|
||||
$ui->setTemplateDir([
|
||||
'custom' => File::pathFixer($UI_PATH . '/ui_custom/'),
|
||||
'default' => File::pathFixer($UI_PATH . '/ui/')
|
||||
]);
|
||||
$ui->assign('_url', APP_URL . '/index.php?_route=');
|
||||
$ui->setCompileDir(File::pathFixer($UI_PATH . '/compiled/'));
|
||||
$ui->setConfigDir(File::pathFixer($UI_PATH . '/conf/'));
|
||||
$ui->setCacheDir(File::pathFixer($UI_PATH . '/cache/'));
|
||||
$ui->assign("error_title", "PHPNuxBill Crash");
|
||||
if (_auth()) {
|
||||
$ui->assign("error_message", $e->getMessage() . '<br>');
|
||||
} else {
|
||||
$ui->assign("error_message", $e->getMessage() . '<br><pre>' . $e->getTraceAsString() . '</pre>');
|
||||
}
|
||||
$ui->display('router-error.tpl');
|
||||
die();
|
||||
} catch (Exception $e) {
|
||||
$ui = new Smarty();
|
||||
$ui->setTemplateDir([
|
||||
'custom' => File::pathFixer($UI_PATH . '/ui_custom/'),
|
||||
'default' => File::pathFixer($UI_PATH . '/ui/')
|
||||
]);
|
||||
$ui->assign('_url', APP_URL . '/index.php?_route=');
|
||||
$ui->setCompileDir(File::pathFixer($UI_PATH . '/compiled/'));
|
||||
$ui->setConfigDir(File::pathFixer($UI_PATH . '/conf/'));
|
||||
$ui->setCacheDir(File::pathFixer($UI_PATH . '/cache/'));
|
||||
$ui->assign("error_title", "PHPNuxBill Crash");
|
||||
if (_auth()) {
|
||||
$ui->assign("error_message", $e->getMessage() . '<br>');
|
||||
} else {
|
||||
$ui->assign("error_message", $e->getMessage() . '<br><pre>' . $e->getTraceAsString() . '</pre>');
|
||||
}
|
||||
$ui->display('router-error.tpl');
|
||||
die();
|
||||
}
|
||||
|
||||
function _notify($msg, $type = 'e')
|
||||
{
|
||||
$_SESSION['ntype'] = $type;
|
||||
$_SESSION['notify'] = $msg;
|
||||
}
|
||||
|
||||
$ui = new Smarty();
|
||||
$ui->assign('_kolaps', $_COOKIE['kolaps']);
|
||||
if (!empty($config['theme']) && $config['theme'] != 'default') {
|
||||
$_theme = APP_URL . '/' . $UI_PATH . '/themes/' . $config['theme'];
|
||||
$ui->setTemplateDir([
|
||||
'custom' => File::pathFixer($UI_PATH . '/ui_custom/'),
|
||||
'theme' => File::pathFixer($UI_PATH . '/themes/' . $config['theme']),
|
||||
'default' => File::pathFixer($UI_PATH . '/ui/')
|
||||
]);
|
||||
} else {
|
||||
$_theme = APP_URL . '/' . $UI_PATH . '/ui';
|
||||
$ui->setTemplateDir([
|
||||
'custom' => File::pathFixer($UI_PATH . '/ui_custom/'),
|
||||
'default' => File::pathFixer($UI_PATH . '/ui/')
|
||||
]);
|
||||
}
|
||||
$ui->assign('_theme', $_theme);
|
||||
$ui->addTemplateDir($PAYMENTGATEWAY_PATH . File::pathFixer('/ui/'), 'pg');
|
||||
$ui->addTemplateDir($PLUGIN_PATH . File::pathFixer('/ui/'), 'plugin');
|
||||
$ui->setCompileDir(File::pathFixer($UI_PATH . '/compiled/'));
|
||||
$ui->setConfigDir(File::pathFixer($UI_PATH . '/conf/'));
|
||||
$ui->setCacheDir(File::pathFixer($UI_PATH . '/cache/'));
|
||||
$ui->assign('app_url', APP_URL);
|
||||
$ui->assign('_domain', str_replace('www.', '', parse_url(APP_URL, PHP_URL_HOST)));
|
||||
$ui->assign('_url', APP_URL . '/index.php?_route=');
|
||||
$ui->assign('_path', __DIR__);
|
||||
$ui->assign('_c', $config);
|
||||
$ui->assign('UPLOAD_PATH', str_replace($root_path, '', $UPLOAD_PATH));
|
||||
$ui->assign('CACHE_PATH', str_replace($root_path, '', $CACHE_PATH));
|
||||
$ui->assign('PAGES_PATH', str_replace($root_path, '', $PAGES_PATH));
|
||||
$ui->assign('_system_menu', 'dashboard');
|
||||
|
||||
function _msglog($type, $msg)
|
||||
{
|
||||
$_SESSION['ntype'] = $type;
|
||||
$_SESSION['notify'] = $msg;
|
||||
}
|
||||
|
||||
if (isset($_SESSION['notify'])) {
|
||||
$notify = $_SESSION['notify'];
|
||||
$ntype = $_SESSION['ntype'];
|
||||
$ui->assign('notify', $notify);
|
||||
$ui->assign('notify_t', $ntype);
|
||||
unset($_SESSION['notify']);
|
||||
unset($_SESSION['ntype']);
|
||||
}
|
||||
|
||||
// Routing Engine
|
||||
$req = _get('_route');
|
||||
global $routes;
|
||||
$routes = explode('/', $req);
|
||||
$ui->assign('_routes', $routes);
|
||||
$handler = $routes[0];
|
||||
if ($handler == '') {
|
||||
$handler = 'default';
|
||||
}
|
||||
$admin = Admin::_info();
|
||||
try {
|
||||
$sys_render = $root_path . File::pathFixer('system/controllers/' . $handler . '.php');
|
||||
if (file_exists($sys_render)) {
|
||||
$menus = array();
|
||||
// "name" => $name,
|
||||
// "admin" => $admin,
|
||||
// "position" => $position,
|
||||
// "function" => $function
|
||||
$ui->assign('_system_menu', $routes[0]);
|
||||
foreach ($menu_registered as $menu) {
|
||||
if ($menu['admin'] && _admin(false)) {
|
||||
if (count($menu['auth']) == 0 || in_array($admin['user_type'], $menu['auth'])) {
|
||||
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . U . 'plugin/' . $menu['function'] . '">';
|
||||
if (!empty($menu['icon'])) {
|
||||
$menus[$menu['position']] .= '<i class="' . $menu['icon'] . '"></i>';
|
||||
}
|
||||
if (!empty($menu['label'])) {
|
||||
$menus[$menu['position']] .= '<span class="pull-right-container">';
|
||||
$menus[$menu['position']] .= '<small class="label pull-right bg-' . $menu['color'] . '">' . $menu['label'] . '</small></span>';
|
||||
}
|
||||
$menus[$menu['position']] .= '<span class="text">' . $menu['name'] . '</span></a></li>';
|
||||
}
|
||||
} else if (!$menu['admin'] && _auth(false)) {
|
||||
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . U . 'plugin/' . $menu['function'] . '">';
|
||||
if (!empty($menu['icon'])) {
|
||||
$menus[$menu['position']] .= '<i class="' . $menu['icon'] . '"></i>';
|
||||
}
|
||||
if (!empty($menu['label'])) {
|
||||
$menus[$menu['position']] .= '<span class="pull-right-container">';
|
||||
$menus[$menu['position']] .= '<small class="label pull-right bg-' . $menu['color'] . '">' . $menu['label'] . '</small></span>';
|
||||
}
|
||||
$menus[$menu['position']] .= '<span class="text">' . $menu['name'] . '</span></a></li>';
|
||||
}
|
||||
}
|
||||
foreach ($menus as $k => $v) {
|
||||
$ui->assign('_MENU_' . $k, $v);
|
||||
}
|
||||
unset($menus, $menu_registered);
|
||||
include($sys_render);
|
||||
} else {
|
||||
r2(U . 'dashboard', 'e', 'not found');
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
if (!Admin::getID()) {
|
||||
r2(U . 'home', 'e', $e->getMessage());
|
||||
}
|
||||
$ui->assign("error_message", $e->getMessage() . '<br><pre>' . $e->getTraceAsString() . '</pre>');
|
||||
$ui->assign("error_title", "PHPNuxBill Crash");
|
||||
$ui->display('router-error.tpl');
|
||||
die();
|
||||
} catch (Exception $e) {
|
||||
if (!Admin::getID()) {
|
||||
r2(U . 'home', 'e', $e->getMessage());
|
||||
}
|
||||
$ui->assign("error_message", $e->getMessage() . '<br><pre>' . $e->getTraceAsString() . '</pre>');
|
||||
$ui->assign("error_title", "PHPNuxBill Crash");
|
||||
$ui->display('router-error.tpl');
|
||||
die();
|
||||
}
|
||||
6
system/composer.json
Normal file
6
system/composer.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"require": {
|
||||
"mpdf/mpdf": "^8.1",
|
||||
"smarty/smarty": "^4.3"
|
||||
}
|
||||
}
|
||||
490
system/composer.lock
generated
Normal file
490
system/composer.lock
generated
Normal file
@@ -0,0 +1,490 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "81c1d3c4b2673fdd2922ac32768d59f1",
|
||||
"packages": [
|
||||
{
|
||||
"name": "mpdf/mpdf",
|
||||
"version": "v8.1.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mpdf/mpdf.git",
|
||||
"reference": "146c7c1dfd21c826b9d5bbfe3c15e52fd933c90f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mpdf/mpdf/zipball/146c7c1dfd21c826b9d5bbfe3c15e52fd933c90f",
|
||||
"reference": "146c7c1dfd21c826b9d5bbfe3c15e52fd933c90f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-gd": "*",
|
||||
"ext-mbstring": "*",
|
||||
"mpdf/psr-log-aware-trait": "^2.0 || ^3.0",
|
||||
"myclabs/deep-copy": "^1.7",
|
||||
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
|
||||
"php": "^5.6 || ^7.0 || ~8.0.0 || ~8.1.0 || ~8.2.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||
"setasign/fpdi": "^2.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.0",
|
||||
"mpdf/qrcode": "^1.1.0",
|
||||
"squizlabs/php_codesniffer": "^3.5.0",
|
||||
"tracy/tracy": "~2.5",
|
||||
"yoast/phpunit-polyfills": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "Needed for generation of some types of barcodes",
|
||||
"ext-xml": "Needed mainly for SVG manipulation",
|
||||
"ext-zlib": "Needed for compression of embedded resources, such as fonts"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mpdf\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-2.0-only"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Matěj Humpál",
|
||||
"role": "Developer, maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Ian Back",
|
||||
"role": "Developer (retired)"
|
||||
}
|
||||
],
|
||||
"description": "PHP library generating PDF files from UTF-8 encoded HTML",
|
||||
"homepage": "https://mpdf.github.io",
|
||||
"keywords": [
|
||||
"pdf",
|
||||
"php",
|
||||
"utf-8"
|
||||
],
|
||||
"support": {
|
||||
"docs": "http://mpdf.github.io",
|
||||
"issues": "https://github.com/mpdf/mpdf/issues",
|
||||
"source": "https://github.com/mpdf/mpdf"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.paypal.me/mpdf",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2023-05-03T19:36:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mpdf/psr-log-aware-trait",
|
||||
"version": "v2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mpdf/psr-log-aware-trait.git",
|
||||
"reference": "7a077416e8f39eb626dee4246e0af99dd9ace275"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mpdf/psr-log-aware-trait/zipball/7a077416e8f39eb626dee4246e0af99dd9ace275",
|
||||
"reference": "7a077416e8f39eb626dee4246e0af99dd9ace275",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"psr/log": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mpdf\\PsrLogAwareTrait\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Dorison",
|
||||
"email": "mark@chromatichq.com"
|
||||
},
|
||||
{
|
||||
"name": "Kristofer Widholm",
|
||||
"email": "kristofer@chromatichq.com"
|
||||
}
|
||||
],
|
||||
"description": "Trait to allow support of different psr/log versions.",
|
||||
"support": {
|
||||
"issues": "https://github.com/mpdf/psr-log-aware-trait/issues",
|
||||
"source": "https://github.com/mpdf/psr-log-aware-trait/tree/v2.0.0"
|
||||
},
|
||||
"time": "2023-05-03T06:18:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.11.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/collections": "<1.6.8",
|
||||
"doctrine/common": "<2.13.3 || >=3,<3.2.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.6.8",
|
||||
"doctrine/common": "^2.13.3 || ^3.2.2",
|
||||
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/DeepCopy/deep_copy.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"DeepCopy\\": "src/DeepCopy/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Create deep copies (clones) of your objects",
|
||||
"keywords": [
|
||||
"clone",
|
||||
"copy",
|
||||
"duplicate",
|
||||
"object",
|
||||
"object graph"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-03-08T13:26:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "v9.99.100",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
|
||||
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">= 7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*|5.*",
|
||||
"vimeo/psalm": "^1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||
"keywords": [
|
||||
"csprng",
|
||||
"polyfill",
|
||||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"support": {
|
||||
"email": "info@paragonie.com",
|
||||
"issues": "https://github.com/paragonie/random_compat/issues",
|
||||
"source": "https://github.com/paragonie/random_compat"
|
||||
},
|
||||
"time": "2020-10-15T08:29:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/1.1"
|
||||
},
|
||||
"time": "2023-04-04T09:50:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Log\\": "Psr/Log/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for logging libraries",
|
||||
"homepage": "https://github.com/php-fig/log",
|
||||
"keywords": [
|
||||
"log",
|
||||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/log/tree/1.1.4"
|
||||
},
|
||||
"time": "2021-05-03T11:20:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "setasign/fpdi",
|
||||
"version": "v2.3.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Setasign/FPDI.git",
|
||||
"reference": "bccc892d5fa1f48c43f8ba7db5ed4ba6f30c8c05"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Setasign/FPDI/zipball/bccc892d5fa1f48c43f8ba7db5ed4ba6f30c8c05",
|
||||
"reference": "bccc892d5fa1f48c43f8ba7db5ed4ba6f30c8c05",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-zlib": "*",
|
||||
"php": "^5.6 || ^7.0 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"setasign/tfpdf": "<1.31"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~5.7",
|
||||
"setasign/fpdf": "~1.8",
|
||||
"setasign/tfpdf": "1.31",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"tecnickcom/tcpdf": "~6.2"
|
||||
},
|
||||
"suggest": {
|
||||
"setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"setasign\\Fpdi\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jan Slabon",
|
||||
"email": "jan.slabon@setasign.com",
|
||||
"homepage": "https://www.setasign.com"
|
||||
},
|
||||
{
|
||||
"name": "Maximilian Kresse",
|
||||
"email": "maximilian.kresse@setasign.com",
|
||||
"homepage": "https://www.setasign.com"
|
||||
}
|
||||
],
|
||||
"description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.",
|
||||
"homepage": "https://www.setasign.com/fpdi",
|
||||
"keywords": [
|
||||
"fpdf",
|
||||
"fpdi",
|
||||
"pdf"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Setasign/FPDI/issues",
|
||||
"source": "https://github.com/Setasign/FPDI/tree/v2.3.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/setasign/fpdi",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-09T10:38:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "smarty/smarty",
|
||||
"version": "v4.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/smarty-php/smarty.git",
|
||||
"reference": "e28cb0915b4e3749bf57d4ebae2984e25395cfe5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/e28cb0915b4e3749bf57d4ebae2984e25395cfe5",
|
||||
"reference": "e28cb0915b4e3749bf57d4ebae2984e25395cfe5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5 || ^7.5",
|
||||
"smarty/smarty-lexer": "^3.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"libs/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Monte Ohrt",
|
||||
"email": "monte@ohrt.com"
|
||||
},
|
||||
{
|
||||
"name": "Uwe Tews",
|
||||
"email": "uwe.tews@googlemail.com"
|
||||
},
|
||||
{
|
||||
"name": "Rodney Rehm",
|
||||
"email": "rodney.rehm@medialize.de"
|
||||
},
|
||||
{
|
||||
"name": "Simon Wisselink",
|
||||
"homepage": "https://www.iwink.nl/"
|
||||
}
|
||||
],
|
||||
"description": "Smarty - the compiling PHP template engine",
|
||||
"homepage": "https://smarty-php.github.io/smarty/",
|
||||
"keywords": [
|
||||
"templating"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://github.com/smarty-php/smarty/discussions",
|
||||
"issues": "https://github.com/smarty-php/smarty/issues",
|
||||
"source": "https://github.com/smarty-php/smarty/tree/v4.3.1"
|
||||
},
|
||||
"time": "2023-03-28T19:47:03+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
254
system/controllers/accounts.php
Normal file
254
system/controllers/accounts.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
_auth();
|
||||
$ui->assign('_title', Lang::T('My Account'));
|
||||
$ui->assign('_system_menu', 'accounts');
|
||||
|
||||
$action = $routes['1'];
|
||||
$user = User::_info();
|
||||
$ui->assign('_user', $user);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'change-password':
|
||||
run_hook('customer_view_change_password'); #HOOK
|
||||
$ui->display('user-change-password.tpl');
|
||||
break;
|
||||
|
||||
case 'change-password-post':
|
||||
$password = _post('password');
|
||||
run_hook('customer_change_password'); #HOOK
|
||||
if ($password != '') {
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $user['username'])->find_one();
|
||||
if ($d) {
|
||||
$d_pass = $d['password'];
|
||||
$npass = _post('npass');
|
||||
$cnpass = _post('cnpass');
|
||||
|
||||
if (Password::_uverify($password, $d_pass) == true) {
|
||||
if (!Validator::Length($npass, 15, 2)) {
|
||||
r2(U . 'accounts/change-password', 'e', 'New Password must be 3 to 14 character');
|
||||
}
|
||||
if ($npass != $cnpass) {
|
||||
r2(U . 'accounts/change-password', 'e', 'Both Password should be same');
|
||||
}
|
||||
|
||||
$c = ORM::for_table('tbl_user_recharges')->where('username', $user['username'])->find_one();
|
||||
if ($c) {
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $c['plan_id'])->find_one();
|
||||
if ($p['is_radius']) {
|
||||
if ($c['type'] == 'Hotspot' || ($c['type'] == 'PPPOE' && empty($d['pppoe_password']))) {
|
||||
Radius::customerUpsert($d, $p);
|
||||
}
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($c['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($c['type'] == 'Hotspot') {
|
||||
Mikrotik::setHotspotUser($client, $c['username'], $npass);
|
||||
Mikrotik::removeHotspotActiveUser($client, $user['username']);
|
||||
} else if (empty($d['pppoe_password'])) {
|
||||
// only change when pppoe_password empty
|
||||
Mikrotik::setPpoeUser($client, $c['username'], $npass);
|
||||
Mikrotik::removePpoeActive($client, $user['username']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$d->password = $npass;
|
||||
$d->save();
|
||||
|
||||
_msglog('s', Lang::T('Password changed successfully, Please login again'));
|
||||
_log('[' . $user['username'] . ']: Password changed successfully', 'User', $user['id']);
|
||||
|
||||
r2(U . 'login');
|
||||
} else {
|
||||
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
|
||||
}
|
||||
} else {
|
||||
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
|
||||
}
|
||||
} else {
|
||||
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'profile':
|
||||
$d = ORM::for_table('tbl_customers')->find_one($user['id']);
|
||||
if ($d) {
|
||||
run_hook('customer_view_edit_profile'); #HOOK
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('user-profile.tpl');
|
||||
} else {
|
||||
r2(U . 'home', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit-profile-post':
|
||||
$fullname = _post('fullname');
|
||||
$address = _post('address');
|
||||
$email = _post('email');
|
||||
$phonenumber = _post('phonenumber');
|
||||
run_hook('customer_edit_profile'); #HOOK
|
||||
$msg = '';
|
||||
if (Validator::Length($fullname, 31, 2) == false) {
|
||||
$msg .= 'Full Name should be between 3 to 30 characters' . '<br>';
|
||||
}
|
||||
if (Validator::UnsignedNumber($phonenumber) == false) {
|
||||
$msg .= 'Phone Number must be a number' . '<br>';
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_customers')->find_one($user['id']);
|
||||
if ($d) {
|
||||
} else {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
$d->fullname = $fullname;
|
||||
$d->address = $address;
|
||||
$d->email = $email;
|
||||
$d->phonenumber = $phonenumber;
|
||||
$d->save();
|
||||
|
||||
_log('[' . $user['username'] . ']: ' . Lang::T('User Updated Successfully'), 'User', $user['id']);
|
||||
r2(U . 'accounts/profile', 's', Lang::T('User Updated Successfully'));
|
||||
} else {
|
||||
r2(U . 'accounts/profile', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'phone-update':
|
||||
|
||||
$d = ORM::for_table('tbl_customers')->find_one($user['id']);
|
||||
if ($d) {
|
||||
//run_hook('customer_view_edit_profile'); #HOOK
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('user-phone-update.tpl');
|
||||
} else {
|
||||
r2(U . 'home', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'phone-update-otp':
|
||||
$phone = _post('phone');
|
||||
$username = $user['username'];
|
||||
$otpPath = $CACHE_PATH . '/sms/';
|
||||
|
||||
// Validate the phone number format
|
||||
if (!preg_match('/^[0-9]{10,}$/', $phone)) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
|
||||
}
|
||||
|
||||
if (empty($config['sms_url'])) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not Available, Please try again later'));
|
||||
}
|
||||
|
||||
if (!empty($config['sms_url'])) {
|
||||
if (!empty($phone)) {
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->where('phonenumber', $phone)->find_one();
|
||||
if ($d) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('You cannot use your current phone number'));
|
||||
}
|
||||
if (!file_exists($otpPath)) {
|
||||
mkdir($otpPath);
|
||||
touch($otpPath . 'index.html');
|
||||
}
|
||||
$otpFile = $otpPath . sha1($username . $db_password) . ".txt";
|
||||
$phoneFile = $otpPath . sha1($username . $db_password) . "_phone.txt";
|
||||
|
||||
// expired 10 minutes
|
||||
if (file_exists($otpFile) && time() - filemtime($otpFile) < 1200) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Please wait ' . (1200 - (time() - filemtime($otpFile))) . ' seconds before sending another SMS'));
|
||||
} else {
|
||||
$otp = rand(100000, 999999);
|
||||
file_put_contents($otpFile, $otp);
|
||||
file_put_contents($phoneFile, $phone);
|
||||
// send send OTP to user
|
||||
if ($config['phone_otp_type'] === 'sms') {
|
||||
Message::sendSMS($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
|
||||
} elseif ($config['phone_otp_type'] === 'whatsapp') {
|
||||
Message::sendWhatsapp($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
|
||||
} elseif ($config['phone_otp_type'] === 'both') {
|
||||
Message::sendSMS($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
|
||||
Message::sendWhatsapp($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
|
||||
}
|
||||
//redirect after sending OTP
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code has been sent to your phone'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'phone-update-post':
|
||||
$phone = _post('phone');
|
||||
$otp_code = _post('otp');
|
||||
$username = $user['username'];
|
||||
$otpPath = $CACHE_PATH . '/sms/';
|
||||
|
||||
// Validate the phone number format
|
||||
if (!preg_match('/^[0-9]{10,}$/', $phone)) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!empty($config['sms_url'])) {
|
||||
$otpFile = $otpPath . sha1($username . $db_password) . ".txt";
|
||||
$phoneFile = $otpPath . sha1($username . $db_password) . "_phone.txt";
|
||||
|
||||
// Check if OTP file exists
|
||||
if (!file_exists($otpFile)) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Please request OTP first'));
|
||||
exit();
|
||||
}
|
||||
|
||||
// expired 10 minutes
|
||||
if (time() - filemtime($otpFile) > 1200) {
|
||||
unlink($otpFile);
|
||||
unlink($phoneFile);
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code expired'));
|
||||
exit();
|
||||
} else {
|
||||
$code = file_get_contents($otpFile);
|
||||
|
||||
// Check if OTP code matches
|
||||
if ($code != $otp_code) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('Wrong Verification code'));
|
||||
exit();
|
||||
}
|
||||
|
||||
// Check if the phone number matches the one that requested the OTP
|
||||
$savedPhone = file_get_contents($phoneFile);
|
||||
if ($savedPhone !== $phone) {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('The phone number does not match the one that requested the OTP'));
|
||||
exit();
|
||||
}
|
||||
|
||||
// OTP verification successful, delete OTP and phone number files
|
||||
unlink($otpFile);
|
||||
unlink($phoneFile);
|
||||
}
|
||||
} else {
|
||||
r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not available'));
|
||||
exit();
|
||||
}
|
||||
|
||||
// Update the phone number in the database
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$d->phonenumber = Lang::phoneFormat($phone);
|
||||
$d->save();
|
||||
}
|
||||
|
||||
r2(U . 'accounts/profile', 's', Lang::T('Phone number updated successfully'));
|
||||
break;
|
||||
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
57
system/controllers/admin.php
Normal file
57
system/controllers/admin.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
if(Admin::getID()){
|
||||
r2(U.'dashboard', "s", Lang::T("You are already logged in"));
|
||||
}
|
||||
|
||||
if (isset($routes['1'])) {
|
||||
$do = $routes['1'];
|
||||
} else {
|
||||
$do = 'login-display';
|
||||
}
|
||||
|
||||
switch ($do) {
|
||||
case 'post':
|
||||
$username = _post('username');
|
||||
$password = _post('password');
|
||||
run_hook('admin_login'); #HOOK
|
||||
if ($username != '' and $password != '') {
|
||||
$d = ORM::for_table('tbl_users')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$d_pass = $d['password'];
|
||||
if (Password::_verify($password, $d_pass) == true) {
|
||||
$_SESSION['aid'] = $d['id'];
|
||||
$token = Admin::setCookie($d['id']);
|
||||
$d->last_login = date('Y-m-d H:i:s');
|
||||
$d->save();
|
||||
_log($username . ' ' . Lang::T('Login Successful'), $d['user_type'], $d['id']);
|
||||
if ($isApi) {
|
||||
if ($token) {
|
||||
showResult(true, Lang::T('Login Successful'), ['token' => "a.".$token]);
|
||||
} else {
|
||||
showResult(false, Lang::T('Invalid Username or Password'));
|
||||
}
|
||||
}
|
||||
_alert(Lang::T('Login Successful'),'success', "dashboard");
|
||||
} else {
|
||||
_log($username . ' ' . Lang::T('Failed Login'), $d['user_type']);
|
||||
_alert(Lang::T('Invalid Username or Password').".",'danger', "admin");
|
||||
}
|
||||
} else {
|
||||
_alert(Lang::T('Invalid Username or Password')."..",'danger', "admin");
|
||||
}
|
||||
} else {
|
||||
_alert(Lang::T('Invalid Username or Password')."...",'danger', "admin");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
run_hook('view_login'); #HOOK
|
||||
$ui->display('admin-login.tpl');
|
||||
break;
|
||||
}
|
||||
119
system/controllers/autoload.php
Normal file
119
system/controllers/autoload.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
/**
|
||||
* used for ajax
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Network'));
|
||||
$ui->assign('_system_menu', 'network');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
switch ($action) {
|
||||
case 'pool':
|
||||
$routers = _get('routers');
|
||||
if(empty($routers)){
|
||||
$d = ORM::for_table('tbl_pool')->find_many();
|
||||
}else{
|
||||
$d = ORM::for_table('tbl_pool')->where('routers', $routers)->find_many();
|
||||
}
|
||||
$ui->assign('routers', $routers);
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('autoload-pool.tpl');
|
||||
break;
|
||||
|
||||
case 'server':
|
||||
$d = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
|
||||
$ui->assign('d', $d);
|
||||
|
||||
$ui->display('autoload-server.tpl');
|
||||
break;
|
||||
|
||||
case 'plan':
|
||||
$server = _post('server');
|
||||
$jenis = _post('jenis');
|
||||
if(in_array($admin['user_type'], array('SuperAdmin', 'Admin'))){
|
||||
if($server=='radius'){
|
||||
$d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->find_many();
|
||||
}else{
|
||||
$d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many();
|
||||
}
|
||||
}else{
|
||||
if($server=='radius'){
|
||||
$d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->where('enabled', '1')->find_many();
|
||||
}else{
|
||||
$d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->where('enabled', '1')->find_many();
|
||||
}
|
||||
}
|
||||
$ui->assign('d', $d);
|
||||
|
||||
$ui->display('autoload.tpl');
|
||||
break;
|
||||
case 'customer_is_active':
|
||||
$d = ORM::for_table('tbl_user_recharges')->where('customer_id', $routes['2'])->findOne();
|
||||
if ($d) {
|
||||
if ($d['status'] == 'on') {
|
||||
die('<span class="label label-success" title="Expired ' . Lang::dateAndTimeFormat($d['expiration'], $d['time']) . '">'.$d['namebp'].'</span>');
|
||||
} else {
|
||||
die('<span class="label label-danger" title="Expired ' . Lang::dateAndTimeFormat($d['expiration'], $d['time']) . '">'.$d['namebp'].'</span>');
|
||||
}
|
||||
} else {
|
||||
die('<span class="label label-danger">•</span>');
|
||||
}
|
||||
break;
|
||||
case 'customer_select2':
|
||||
|
||||
$s = addslashes(_get('s'));
|
||||
if (empty($s)) {
|
||||
$c = ORM::for_table('tbl_customers')->limit(30)->find_many();
|
||||
} else {
|
||||
$c = ORM::for_table('tbl_customers')->where_raw("(`username` LIKE '%$s%' OR `fullname` LIKE '%$s%' OR `phonenumber` LIKE '%$s%' OR `email` LIKE '%$s%')")->limit(30)->find_many();
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
foreach ($c as $cust) {
|
||||
$json[] = [
|
||||
'id' => $cust['id'],
|
||||
'text' => $cust['username'] . ' - ' . $cust['fullname'] . ' - ' . $cust['email']
|
||||
];
|
||||
}
|
||||
echo json_encode(['results' => $json]);
|
||||
die();
|
||||
case 'customer_info':
|
||||
$customerId = _post('id');
|
||||
|
||||
if (empty($customerId)) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => 'Customer ID is required']);
|
||||
die();
|
||||
}
|
||||
|
||||
$customer = ORM::for_table('tbl_customers')->find_one($customerId);
|
||||
if (!$customer) {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'message' => 'Customer not found']);
|
||||
die();
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'id' => $customer['id'],
|
||||
'username' => $customer['username'],
|
||||
'fullname' => $customer['fullname'],
|
||||
'phonenumber' => $customer['phonenumber'],
|
||||
'email' => $customer['email'],
|
||||
'address' => $customer['address'],
|
||||
'balance' => $customer['balance']
|
||||
]
|
||||
]);
|
||||
die();
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
37
system/controllers/autoload_user.php
Normal file
37
system/controllers/autoload_user.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
/**
|
||||
* used for ajax
|
||||
**/
|
||||
|
||||
_auth();
|
||||
|
||||
$action = $routes['1'];
|
||||
$user = User::_info();
|
||||
|
||||
switch ($action) {
|
||||
case 'isLogin':
|
||||
$bill = ORM::for_table('tbl_user_recharges')->where('id', $routes['2'])->where('username', $user['username'])->findOne();
|
||||
if ($bill['type'] == 'Hotspot' && $bill['status'] == 'on') {
|
||||
$m = Mikrotik::info($bill['routers']);
|
||||
$client = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
if (Mikrotik::isUserLogin($client, $user['username'])) {
|
||||
die('<a href="' . U . 'home&mikrotik=logout&id='.$bill['id'].'" onclick="return confirm(\''.Lang::T('Disconnect Internet?').'\')" class="btn btn-success btn-xs btn-block">'.Lang::T('You are Online, Logout?').'</a>');
|
||||
} else {
|
||||
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
|
||||
die('<a href="' . U . 'home&mikrotik=login&id='.$bill['id'].'" onclick="return confirm(\''.Lang::T('Connect to Internet?').'\')" class="btn btn-danger btn-xs btn-block">'.Lang::T('Not Online, Login now?').'</a>');
|
||||
}else{
|
||||
die(Lang::T('Your account not connected to internet'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
die('--');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$ui->display('404.tpl');
|
||||
}
|
||||
190
system/controllers/bandwidth.php
Normal file
190
system/controllers/bandwidth.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Bandwidth Plans'));
|
||||
$ui->assign('_system_menu', 'services');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
r2(U . "dashboard", 'e', Lang::T('You do not have permission to access this page'));
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'list':
|
||||
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/bandwidth.js"></script>');
|
||||
run_hook('view_list_bandwidth'); #HOOK
|
||||
$name = _post('name');
|
||||
if ($name != '') {
|
||||
$query = ORM::for_table('tbl_bandwidth')->where_like('name_bw', '%' . $name . '%')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query, ['name' => $name]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_bandwidth')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('bandwidth.tpl');
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
run_hook('view_add_bandwidth'); #HOOK
|
||||
$ui->display('bandwidth-add.tpl');
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id = $routes['2'];
|
||||
run_hook('view_edit_bandwith'); #HOOK
|
||||
$d = ORM::for_table('tbl_bandwidth')->find_one($id);
|
||||
if ($d) {
|
||||
$ui->assign('burst', explode(" ", $d['burst']));
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('bandwidth-edit.tpl');
|
||||
} else {
|
||||
r2(U . 'bandwidth/list', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id = $routes['2'];
|
||||
run_hook('delete_bandwidth'); #HOOK
|
||||
$d = ORM::for_table('tbl_bandwidth')->find_one($id);
|
||||
if ($d) {
|
||||
$d->delete();
|
||||
r2(U . 'bandwidth/list', 's', Lang::T('Data Deleted Successfully'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'add-post':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$name = _post('name');
|
||||
$rate_down = _post('rate_down');
|
||||
$rate_down_unit = _post('rate_down_unit');
|
||||
$rate_up = _post('rate_up');
|
||||
$rate_up_unit = _post('rate_up_unit');
|
||||
run_hook('add_bandwidth'); #HOOK
|
||||
$isBurst = true;
|
||||
$burst = "";
|
||||
if (isset($_POST['burst'])) {
|
||||
foreach ($_POST['burst'] as $b) {
|
||||
if (empty($b)) {
|
||||
$isBurst = false;
|
||||
}
|
||||
}
|
||||
if ($isBurst) {
|
||||
$burst = implode(' ', $_POST['burst']);
|
||||
};
|
||||
}
|
||||
$msg = '';
|
||||
if (Validator::Length($name, 16, 4) == false) {
|
||||
$msg .= 'Name should be between 5 to 15 characters' . '<br>';
|
||||
}
|
||||
|
||||
if ($rate_down_unit == 'Kbps') {
|
||||
$unit_rate_down = $rate_down * 1024;
|
||||
} else {
|
||||
$unit_rate_down = $rate_down * 1048576;
|
||||
}
|
||||
if ($rate_up_unit == 'Kbps') {
|
||||
$unit_rate_up = $min_up * 1024;
|
||||
} else {
|
||||
$unit_rate_up = $min_up * 1048576;
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_bandwidth')->where('name_bw', $name)->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('Name Bandwidth Already Exist') . '<br>';
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
$d = ORM::for_table('tbl_bandwidth')->create();
|
||||
$d->name_bw = $name;
|
||||
$d->rate_down = $rate_down;
|
||||
$d->rate_down_unit = $rate_down_unit;
|
||||
$d->rate_up = $rate_up;
|
||||
$d->rate_up_unit = $rate_up_unit;
|
||||
$d->burst = $burst;
|
||||
$d->save();
|
||||
|
||||
r2(U . 'bandwidth/list', 's', Lang::T('Data Created Successfully'));
|
||||
} else {
|
||||
r2(U . 'bandwidth/add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit-post':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$name = _post('name');
|
||||
$rate_down = _post('rate_down');
|
||||
$rate_down_unit = _post('rate_down_unit');
|
||||
$rate_up = _post('rate_up');
|
||||
$rate_up_unit = _post('rate_up_unit');
|
||||
run_hook('edit_bandwidth'); #HOOK
|
||||
$isBurst = true;
|
||||
$burst = "";
|
||||
if (isset($_POST['burst'])) {
|
||||
foreach ($_POST['burst'] as $b) {
|
||||
if (empty($b)) {
|
||||
$isBurst = false;
|
||||
}
|
||||
}
|
||||
if ($isBurst) {
|
||||
$burst = implode(' ', $_POST['burst']);
|
||||
};
|
||||
}
|
||||
$msg = '';
|
||||
if (Validator::Length($name, 16, 4) == false) {
|
||||
$msg .= 'Name should be between 5 to 15 characters' . '<br>';
|
||||
}
|
||||
|
||||
$id = _post('id');
|
||||
$d = ORM::for_table('tbl_bandwidth')->find_one($id);
|
||||
if ($d) {
|
||||
} else {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
|
||||
if ($d['name_bw'] != $name) {
|
||||
$c = ORM::for_table('tbl_bandwidth')->where('name_bw', $name)->find_one();
|
||||
if ($c) {
|
||||
$msg .= Lang::T('Name Bandwidth Already Exist') . '<br>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
$d->name_bw = $name;
|
||||
$d->rate_down = $rate_down;
|
||||
$d->rate_down_unit = $rate_down_unit;
|
||||
$d->rate_up = $rate_up;
|
||||
$d->rate_up_unit = $rate_up_unit;
|
||||
$d->burst = $burst;
|
||||
$d->save();
|
||||
|
||||
r2(U . 'bandwidth/list', 's', Lang::T('Data Updated Successfully'));
|
||||
} else {
|
||||
r2(U . 'bandwidth/edit/' . $id, 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
481
system/controllers/banks.php
Normal file
481
system/controllers/banks.php
Normal file
@@ -0,0 +1,481 @@
|
||||
<?php
|
||||
/**
|
||||
* Bank Management Controller
|
||||
*
|
||||
* Handles CRUD operations for bank accounts including
|
||||
* STK Push configuration and management.
|
||||
*/
|
||||
|
||||
// Register bank management menu
|
||||
register_menu("Bank Management", true, "banks_list", 'SERVICES', 'fa fa-university', '', 'info');
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Bank Management'));
|
||||
$ui->assign('_system_menu', 'services');
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
// Check permissions
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
r2(U . "dashboard", 'e', Lang::T('You do not have permission to access this page'));
|
||||
}
|
||||
|
||||
$action = $routes['1'] ?? 'list';
|
||||
|
||||
switch ($action) {
|
||||
case 'list':
|
||||
// List all banks
|
||||
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/banks.js"></script>');
|
||||
|
||||
// Search functionality
|
||||
$search = _post('search');
|
||||
$filter_stk = _post('filter_stk');
|
||||
$filter_active = _post('filter_active');
|
||||
|
||||
// Alternative approach: Use direct PDO query for better performance
|
||||
$use_direct_query = true; // Set to true for direct query, false for ORM
|
||||
|
||||
if ($use_direct_query) {
|
||||
// Direct PDO approach - faster and more reliable
|
||||
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$sql = "SELECT * FROM tbl_banks WHERE 1=1";
|
||||
$params = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$sql .= " AND (name LIKE ? OR account_number LIKE ? OR account_name LIKE ?)";
|
||||
$search_term = '%' . $search . '%';
|
||||
$params[] = $search_term;
|
||||
$params[] = $search_term;
|
||||
$params[] = $search_term;
|
||||
}
|
||||
|
||||
if ($filter_stk !== '') {
|
||||
$sql .= " AND supports_stk_push = ?";
|
||||
$params[] = $filter_stk;
|
||||
}
|
||||
|
||||
if ($filter_active !== '') {
|
||||
$sql .= " AND is_active = ?";
|
||||
$params[] = $filter_active;
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY is_default DESC, name ASC";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$banks = $stmt->fetchAll(PDO::FETCH_OBJ);
|
||||
} else {
|
||||
// Original ORM approach
|
||||
$query = ORM::for_table('tbl_banks');
|
||||
|
||||
if (!empty($search)) {
|
||||
$query->where_raw('(name LIKE ? OR account_number LIKE ? OR account_name LIKE ?)',
|
||||
['%' . $search . '%', '%' . $search . '%', '%' . $search . '%']);
|
||||
}
|
||||
|
||||
if ($filter_stk !== '') {
|
||||
$query->where('supports_stk_push', $filter_stk);
|
||||
}
|
||||
|
||||
if ($filter_active !== '') {
|
||||
$query->where('is_active', $filter_active);
|
||||
}
|
||||
|
||||
$query->order_by_desc('is_default')->order_by_asc('name');
|
||||
$banks = Paginator::findMany($query, [
|
||||
'search' => $search,
|
||||
'filter_stk' => $filter_stk,
|
||||
'filter_active' => $filter_active
|
||||
]);
|
||||
|
||||
// Convert ORM object to array for template compatibility
|
||||
if ($banks && method_exists($banks, 'as_array')) {
|
||||
$banks = $banks->as_array();
|
||||
} elseif ($banks && is_object($banks)) {
|
||||
$banks_array = [];
|
||||
foreach ($banks as $bank) {
|
||||
$banks_array[] = $bank;
|
||||
}
|
||||
$banks = $banks_array;
|
||||
}
|
||||
|
||||
// Fallback: If banks is still not an array, use direct database query
|
||||
if (!is_array($banks) || empty($banks)) {
|
||||
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$sql = "SELECT * FROM tbl_banks WHERE 1=1";
|
||||
$params = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$sql .= " AND (name LIKE ? OR account_number LIKE ? OR account_name LIKE ?)";
|
||||
$search_term = '%' . $search . '%';
|
||||
$params[] = $search_term;
|
||||
$params[] = $search_term;
|
||||
$params[] = $search_term;
|
||||
}
|
||||
|
||||
if ($filter_stk !== '') {
|
||||
$sql .= " AND supports_stk_push = ?";
|
||||
$params[] = $filter_stk;
|
||||
}
|
||||
|
||||
if ($filter_active !== '') {
|
||||
$sql .= " AND is_active = ?";
|
||||
$params[] = $filter_active;
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY is_default DESC, name ASC";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$banks = $stmt->fetchAll(PDO::FETCH_OBJ);
|
||||
}
|
||||
}
|
||||
|
||||
$ui->assign('banks', $banks);
|
||||
$ui->assign('search', $search);
|
||||
$ui->assign('filter_stk', $filter_stk);
|
||||
$ui->assign('filter_active', $filter_active);
|
||||
|
||||
run_hook('view_list_banks');
|
||||
$ui->display('banks_basic.tpl');
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
// Show add bank form
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
// Add JavaScript for bank add form
|
||||
$xfooter = '
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Form validation
|
||||
$("form").on("submit", function(e) {
|
||||
var name = $("input[name=\'name\']").val().trim();
|
||||
var accountNumber = $("input[name=\'account_number\']").val().trim();
|
||||
var accountName = $("input[name=\'account_name\']").val().trim();
|
||||
|
||||
if (!name || !accountNumber || !accountName) {
|
||||
e.preventDefault();
|
||||
alert("' . Lang::T("Please fill in all required fields") . '");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-generate account name from bank name
|
||||
$("input[name=\'name\']").on("blur", function() {
|
||||
var bankName = $(this).val().trim();
|
||||
if (bankName && !$("input[name=\'account_name\']").val()) {
|
||||
$("input[name=\'account_name\']").val(bankName + " Account");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>';
|
||||
|
||||
$ui->assign('xfooter', $xfooter);
|
||||
run_hook('view_add_bank');
|
||||
$ui->display('banks-add.tpl');
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
// Show edit bank form
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$id = $routes['2'] ?? 0;
|
||||
if (!$id) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Invalid Bank ID'));
|
||||
}
|
||||
|
||||
$bank = ORM::for_table('tbl_banks')->find_one($id);
|
||||
if (!$bank) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Bank Not Found'));
|
||||
}
|
||||
|
||||
// Add JavaScript for bank edit form
|
||||
$xfooter = '
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Form validation
|
||||
$("form").on("submit", function(e) {
|
||||
var name = $("input[name=\'name\']").val().trim();
|
||||
var accountNumber = $("input[name=\'account_number\']").val().trim();
|
||||
var accountName = $("input[name=\'account_name\']").val().trim();
|
||||
|
||||
if (!name || !accountNumber || !accountName) {
|
||||
e.preventDefault();
|
||||
alert("' . Lang::T("Please fill in all required fields") . '");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Warn if disabling STK Push support on default bank
|
||||
$("input[name=\'supports_stk_push\']").on("change", function() {
|
||||
if (!$(this).is(":checked") && $("input[name=\'is_default\']").is(":checked")) {
|
||||
if (confirm("' . Lang::T("This bank is set as default. Disabling STK Push support will also remove it as default. Continue?") . '")) {
|
||||
$("input[name=\'is_default\']").prop("checked", false);
|
||||
} else {
|
||||
$(this).prop("checked", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Warn if deactivating default bank
|
||||
$("input[name=\'is_active\']").on("change", function() {
|
||||
if (!$(this).is(":checked") && $("input[name=\'is_default\']").is(":checked")) {
|
||||
if (confirm("' . Lang::T("This bank is set as default. Deactivating it will also remove it as default. Continue?") . '")) {
|
||||
$("input[name=\'is_default\']").prop("checked", false);
|
||||
} else {
|
||||
$(this).prop("checked", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>';
|
||||
|
||||
$ui->assign('bank', $bank);
|
||||
$ui->assign('xfooter', $xfooter);
|
||||
run_hook('view_edit_bank');
|
||||
$ui->display('banks-edit.tpl');
|
||||
break;
|
||||
|
||||
case 'save':
|
||||
// Save bank data (add or edit)
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$id = _post('id');
|
||||
$name = _post('name');
|
||||
$account_number = _post('account_number');
|
||||
$account_name = _post('account_name');
|
||||
$bank_code = _post('bank_code');
|
||||
$paybill = _post('paybill');
|
||||
$supports_stk_push = _post('supports_stk_push') ? 1 : 0;
|
||||
$is_active = _post('is_active') ? 1 : 0;
|
||||
$is_default = _post('is_default') ? 1 : 0;
|
||||
|
||||
// Validation
|
||||
$msg = '';
|
||||
if (empty($name)) {
|
||||
$msg .= Lang::T('Bank name is required') . '<br>';
|
||||
}
|
||||
if (empty($account_number)) {
|
||||
$msg .= Lang::T('Account number is required') . '<br>';
|
||||
}
|
||||
if (empty($account_name)) {
|
||||
$msg .= Lang::T('Account name is required') . '<br>';
|
||||
}
|
||||
|
||||
// Check for duplicate account number
|
||||
$duplicate_query = ORM::for_table('tbl_banks')->where('account_number', $account_number);
|
||||
if ($id) {
|
||||
$duplicate_query->where_not_equal('id', $id);
|
||||
}
|
||||
$duplicate = $duplicate_query->find_one();
|
||||
|
||||
if ($duplicate) {
|
||||
$msg .= Lang::T('Account number already exists') . '<br>';
|
||||
}
|
||||
|
||||
// If setting as default, unset other defaults
|
||||
if ($is_default) {
|
||||
ORM::for_table('tbl_banks')->where('is_default', 1)->find_result_set()->set('is_default', 0)->save();
|
||||
}
|
||||
|
||||
if ($msg) {
|
||||
$redirect_url = $id ? U . 'banks/edit/' . $id : U . 'banks/add';
|
||||
r2($redirect_url, 'e', $msg);
|
||||
}
|
||||
|
||||
// Save bank
|
||||
if ($id) {
|
||||
$bank = ORM::for_table('tbl_banks')->find_one($id);
|
||||
if (!$bank) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Bank Not Found'));
|
||||
}
|
||||
} else {
|
||||
$bank = ORM::for_table('tbl_banks')->create();
|
||||
}
|
||||
|
||||
$bank->name = $name;
|
||||
$bank->account_number = $account_number;
|
||||
$bank->account_name = $account_name;
|
||||
$bank->bank_code = $bank_code;
|
||||
$bank->paybill = $paybill;
|
||||
$bank->supports_stk_push = $supports_stk_push;
|
||||
$bank->is_active = $is_active;
|
||||
$bank->is_default = $is_default;
|
||||
$bank->save();
|
||||
|
||||
$action_text = $id ? 'updated' : 'created';
|
||||
_log('[' . $admin['username'] . ']: Bank ' . $action_text . ' - ' . $name, 'Admin', $admin['id']);
|
||||
|
||||
r2(U . 'banks/list', 's', Lang::T('Bank ' . ucfirst($action_text) . ' Successfully'));
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
// Delete bank
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$id = $routes['2'] ?? 0;
|
||||
if (!$id) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Invalid Bank ID'));
|
||||
}
|
||||
|
||||
$bank = ORM::for_table('tbl_banks')->find_one($id);
|
||||
if (!$bank) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Bank Not Found'));
|
||||
}
|
||||
|
||||
// Check if bank is being used in appconfig
|
||||
$stk_config = ORM::for_table('tbl_appconfig')->where('setting', 'Stkbankacc')->find_one();
|
||||
if ($stk_config && $stk_config->value == $id) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Cannot delete bank that is currently selected for STK Push'));
|
||||
}
|
||||
|
||||
$bank_name = $bank->name;
|
||||
$bank->delete();
|
||||
|
||||
_log('[' . $admin['username'] . ']: Bank deleted - ' . $bank_name, 'Admin', $admin['id']);
|
||||
r2(U . 'banks/list', 's', Lang::T('Bank Deleted Successfully'));
|
||||
break;
|
||||
|
||||
case 'toggle-status':
|
||||
// Toggle bank active status
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$id = $routes['2'] ?? 0;
|
||||
if (!$id) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Invalid Bank ID'));
|
||||
}
|
||||
|
||||
$bank = ORM::for_table('tbl_banks')->find_one($id);
|
||||
if (!$bank) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Bank Not Found'));
|
||||
}
|
||||
|
||||
$bank->is_active = $bank->is_active ? 0 : 1;
|
||||
$bank->save();
|
||||
|
||||
$status_text = $bank->is_active ? 'activated' : 'deactivated';
|
||||
_log('[' . $admin['username'] . ']: Bank ' . $status_text . ' - ' . $bank->name, 'Admin', $admin['id']);
|
||||
|
||||
r2(U . 'banks/list', 's', Lang::T('Bank ' . ucfirst($status_text) . ' Successfully'));
|
||||
break;
|
||||
|
||||
case 'set-default':
|
||||
// Set bank as default for STK Push
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$id = $routes['2'] ?? 0;
|
||||
if (!$id) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Invalid Bank ID'));
|
||||
}
|
||||
|
||||
$bank = ORM::for_table('tbl_banks')->find_one($id);
|
||||
if (!$bank) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Bank Not Found'));
|
||||
}
|
||||
|
||||
if (!$bank->supports_stk_push) {
|
||||
r2(U . 'banks/list', 'e', Lang::T('Bank does not support STK Push'));
|
||||
}
|
||||
|
||||
// Unset other defaults
|
||||
ORM::for_table('tbl_banks')->where('is_default', 1)->find_result_set()->set('is_default', 0)->save();
|
||||
|
||||
// Set this bank as default
|
||||
$bank->is_default = 1;
|
||||
$bank->save();
|
||||
|
||||
// Update appconfig
|
||||
$stk_config = ORM::for_table('tbl_appconfig')->where('setting', 'Stkbankacc')->find_one();
|
||||
if ($stk_config) {
|
||||
$stk_config->value = $bank->id;
|
||||
$stk_config->save();
|
||||
}
|
||||
|
||||
$stk_name_config = ORM::for_table('tbl_appconfig')->where('setting', 'Stkbankname')->find_one();
|
||||
if ($stk_name_config) {
|
||||
$stk_name_config->value = $bank->name;
|
||||
$stk_name_config->save();
|
||||
}
|
||||
|
||||
_log('[' . $admin['username'] . ']: Bank set as default for STK Push - ' . $bank->name, 'Admin', $admin['id']);
|
||||
r2(U . 'banks/list', 's', Lang::T('Bank Set as Default Successfully'));
|
||||
break;
|
||||
|
||||
case 'api':
|
||||
// API endpoint for AJAX requests
|
||||
$api_action = $routes['2'] ?? '';
|
||||
|
||||
switch ($api_action) {
|
||||
case 'get-active-banks':
|
||||
// Get banks that support STK Push and are active
|
||||
$banks = ORM::for_table('tbl_banks')
|
||||
->where('supports_stk_push', 1)
|
||||
->where('is_active', 1)
|
||||
->order_by_desc('is_default')
|
||||
->order_by_asc('name')
|
||||
->find_many();
|
||||
|
||||
$result = [];
|
||||
foreach ($banks as $bank) {
|
||||
$result[] = [
|
||||
'id' => $bank->id,
|
||||
'name' => $bank->name,
|
||||
'account_number' => $bank->account_number,
|
||||
'account_name' => $bank->account_name,
|
||||
'is_default' => $bank->is_default
|
||||
];
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
|
||||
case 'get-bank-details':
|
||||
// Get specific bank details
|
||||
$bank_id = $_GET['bank_id'] ?? 0;
|
||||
$bank = ORM::for_table('tbl_banks')->find_one($bank_id);
|
||||
|
||||
if ($bank) {
|
||||
$result = [
|
||||
'id' => $bank->id,
|
||||
'name' => $bank->name,
|
||||
'account_number' => $bank->account_number,
|
||||
'account_name' => $bank->account_name,
|
||||
'bank_code' => $bank->bank_code,
|
||||
'paybill' => $bank->paybill,
|
||||
'supports_stk_push' => $bank->supports_stk_push,
|
||||
'is_active' => $bank->is_active,
|
||||
'is_default' => $bank->is_default
|
||||
];
|
||||
} else {
|
||||
$result = ['error' => 'Bank not found'];
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
}
|
||||
|
||||
r2(U . 'banks/list', 'e', Lang::T('Invalid API Action'));
|
||||
break;
|
||||
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
?>
|
||||
22
system/controllers/callback.php
Normal file
22
system/controllers/callback.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
|
||||
$action = $routes['1'];
|
||||
|
||||
|
||||
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php')) {
|
||||
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php';
|
||||
if (function_exists($action . '_payment_notification')) {
|
||||
run_hook('callback_payment_notification'); #HOOK
|
||||
call_user_func($action . '_payment_notification');
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
header('HTTP/1.1 404 Not Found');
|
||||
echo 'Not Found';
|
||||
126
system/controllers/codecanyon.php
Normal file
126
system/controllers/codecanyon.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', 'CodeCanyon.net');
|
||||
$ui->assign('_system_menu', 'settings');
|
||||
|
||||
$plugin_repository = 'https://hotspotbilling.github.io/Plugin-Repository/repository.json';
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
$cache = File::pathFixer($CACHE_PATH . '/codecanyon.json');
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
if (empty($config['envato_token'])) {
|
||||
r2(U . 'settings/app', 'w', '<a href="' . U . 'settings/app#envato' . '">Envato Personal Access Token</a> is not set');
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'install':
|
||||
if (!is_writeable(File::pathFixer($CACHE_PATH . '/'))) {
|
||||
r2(U . "codecanyon", 'e', 'Folder system/cache/ is not writable');
|
||||
}
|
||||
if (!is_writeable($PLUGIN_PATH)) {
|
||||
r2(U . "codecanyon", 'e', 'Folder plugin/ is not writable');
|
||||
}
|
||||
if (!is_writeable($PAYMENTGATEWAY_PATH)) {
|
||||
r2(U . "codecanyon", 'e', 'Folder paymentgateway/ is not writable');
|
||||
}
|
||||
set_time_limit(-1);
|
||||
$item_id = $routes['2'];
|
||||
$tipe = $routes['3'];
|
||||
$result = Http::getData('https://api.envato.com/v3/market/buyer/download?item_id=' . $item_id, ['Authorization: Bearer ' . $config['envato_token']]);
|
||||
$json = json_decode($result, true);
|
||||
if (!isset($json['download_url'])) {
|
||||
r2(U . 'codecanyon', 'e', 'Failed to get download url. ' . $json['description']);
|
||||
}
|
||||
$file = File::pathFixer($CACHE_PATH . '/codecanyon/');
|
||||
if (!file_exists($file)) {
|
||||
mkdir($file);
|
||||
}
|
||||
$file .= $item_id . '.zip';
|
||||
if (file_exists($file))
|
||||
unlink($file);
|
||||
//download
|
||||
$fp = fopen($file, 'w+');
|
||||
$ch = curl_init($json['download_url']);
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
//extract
|
||||
$target = File::pathFixer($CACHE_PATH . '/codecanyon/' . $item_id . '/');
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($file);
|
||||
$zip->extractTo($target);
|
||||
$zip->close();
|
||||
//moving
|
||||
if (file_exists($target . 'plugin')) {
|
||||
File::copyFolder($target . 'plugin', $PLUGIN_PATH . DIRECTORY_SEPARATOR);
|
||||
} else if (file_exists($target . 'paymentgateway')) {
|
||||
File::copyFolder($target . 'paymentgateway', $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR);
|
||||
} else if (file_exists($target . 'theme')) {
|
||||
File::copyFolder($target . 'theme', File::pathFixer('ui/themes/'));
|
||||
}
|
||||
//Cleaning
|
||||
File::deleteFolder($target);
|
||||
unlink($file);
|
||||
r2(U . "codecanyon", 's', 'Installation success');
|
||||
case 'reload':
|
||||
if (file_exists($cache))
|
||||
unlink($cache);
|
||||
default:
|
||||
if (class_exists('ZipArchive')) {
|
||||
$zipExt = true;
|
||||
} else {
|
||||
$zipExt = false;
|
||||
}
|
||||
$ui->assign('zipExt', $zipExt);
|
||||
|
||||
if (file_exists($cache) && time() - filemtime($cache) < (24 * 60 * 60)) {
|
||||
$txt = file_get_contents($cache);
|
||||
$plugins = json_decode($txt, true);
|
||||
$ui->assign('chached_until', date($config['date_format'] . ' H:i', filemtime($cache) + (24 * 60 * 60)));
|
||||
if (count($plugins) == 0) {
|
||||
unlink($cache);
|
||||
r2(U . 'codecanyon');
|
||||
}
|
||||
} else {
|
||||
$plugins = [];
|
||||
$page = _get('page', 1);
|
||||
back:
|
||||
$result = Http::getData('https://api.envato.com/v3/market/buyer/list-purchases?&page=' . $page, ['Authorization: Bearer ' . $config['envato_token']]);
|
||||
$items = json_decode($result, true);
|
||||
if ($items && count($items['results']) > 0) {
|
||||
foreach ($items['results'] as $item) {
|
||||
$name = strtolower($item['item']['name']);
|
||||
if (strpos($name, 'phpnuxbill') !== false) {
|
||||
$plugins[] = $item;
|
||||
}
|
||||
}
|
||||
$page++;
|
||||
goto back;
|
||||
}
|
||||
if (count($plugins) > 0) {
|
||||
file_put_contents($cache, json_encode($plugins));
|
||||
if (file_exists($cache)) {
|
||||
$ui->assign('chached_until', date($config['date_format'] . ' H:i', filemtime($cache) + (24 * 60 * 60)));
|
||||
}
|
||||
}
|
||||
}
|
||||
$ui->assign('plugins', $plugins);
|
||||
$ui->display('codecanyon.tpl');
|
||||
}
|
||||
26
system/controllers/community.php
Normal file
26
system/controllers/community.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', 'Community');
|
||||
$ui->assign('_system_menu', 'community');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
switch ($action) {
|
||||
case 'rollback':
|
||||
$ui->assign('_title', 'Rollback Update');
|
||||
$masters = json_decode(Http::getData("https://api.github.com/repos/hotspotbilling/phpnuxbill/commits?per_page=100",['User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0']), true);
|
||||
$devs = json_decode(Http::getData("https://api.github.com/repos/hotspotbilling/phpnuxbill/commits?sha=Development&per_page=100",['User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0']), true);
|
||||
|
||||
$ui->assign('masters', $masters);
|
||||
$ui->assign('devs', $devs);
|
||||
$ui->display('community-rollback.tpl');
|
||||
break;
|
||||
default:
|
||||
$ui->display('community.tpl');
|
||||
}
|
||||
739
system/controllers/customers.php
Normal file
739
system/controllers/customers.php
Normal file
@@ -0,0 +1,739 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Customer'));
|
||||
$ui->assign('_system_menu', 'customers');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
|
||||
if (empty($action)) {
|
||||
$action = 'list';
|
||||
}
|
||||
|
||||
$leafletpickerHeader = <<<EOT
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css">
|
||||
EOT;
|
||||
|
||||
switch ($action) {
|
||||
case 'csv':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$cs = ORM::for_table('tbl_customers')
|
||||
->select('tbl_customers.id', 'id')
|
||||
->select('tbl_customers.username', 'username')
|
||||
->select('fullname')
|
||||
->select('address')
|
||||
->select('phonenumber')
|
||||
->select('email')
|
||||
->select('balance')
|
||||
->select('service_type')
|
||||
->order_by_asc('tbl_customers.id')
|
||||
->find_array();
|
||||
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="phpnuxbill_customers_' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
|
||||
$headers = [
|
||||
'id',
|
||||
'username',
|
||||
'fullname',
|
||||
'address',
|
||||
'phonenumber',
|
||||
'email',
|
||||
'balance',
|
||||
'service_type',
|
||||
];
|
||||
|
||||
if (!$h) {
|
||||
echo '"' . implode('","', $headers) . "\"\n";
|
||||
$h = true;
|
||||
}
|
||||
|
||||
foreach ($cs as $c) {
|
||||
$row = [
|
||||
$c['id'],
|
||||
$c['username'],
|
||||
$c['fullname'],
|
||||
$c['address'],
|
||||
$c['phonenumber'],
|
||||
$c['email'],
|
||||
$c['balance'],
|
||||
$c['service_type'],
|
||||
];
|
||||
echo '"' . implode('","', $row) . "\"\n";
|
||||
}
|
||||
break;
|
||||
//case csv-prepaid can be moved later to (plan.php) php file dealing with prepaid users
|
||||
case 'csv-prepaid':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$cs = ORM::for_table('tbl_customers')
|
||||
->select('tbl_customers.id', 'id')
|
||||
->select('tbl_customers.username', 'username')
|
||||
->select('fullname')
|
||||
->select('address')
|
||||
->select('phonenumber')
|
||||
->select('email')
|
||||
->select('balance')
|
||||
->select('service_type')
|
||||
->select('namebp')
|
||||
->select('routers')
|
||||
->select('status')
|
||||
->select('method', 'Payment')
|
||||
->left_outer_join('tbl_user_recharges', array('tbl_customers.id', '=', 'tbl_user_recharges.customer_id'))
|
||||
->order_by_asc('tbl_customers.id')
|
||||
->find_array();
|
||||
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="phpnuxbill_prepaid_users' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
|
||||
$headers = [
|
||||
'id',
|
||||
'username',
|
||||
'fullname',
|
||||
'address',
|
||||
'phonenumber',
|
||||
'email',
|
||||
'balance',
|
||||
'service_type',
|
||||
'namebp',
|
||||
'routers',
|
||||
'status',
|
||||
'Payment'
|
||||
];
|
||||
|
||||
if (!$h) {
|
||||
echo '"' . implode('","', $headers) . "\"\n";
|
||||
$h = true;
|
||||
}
|
||||
|
||||
foreach ($cs as $c) {
|
||||
$row = [
|
||||
$c['id'],
|
||||
$c['username'],
|
||||
$c['fullname'],
|
||||
$c['address'],
|
||||
$c['phonenumber'],
|
||||
$c['email'],
|
||||
$c['balance'],
|
||||
$c['service_type'],
|
||||
$c['namebp'],
|
||||
$c['routers'],
|
||||
$c['status'],
|
||||
$c['Payment']
|
||||
];
|
||||
echo '"' . implode('","', $row) . "\"\n";
|
||||
}
|
||||
break;
|
||||
case 'add':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$ui->assign('xheader', $leafletpickerHeader);
|
||||
run_hook('view_add_customer'); #HOOK
|
||||
$ui->display('customers-add.tpl');
|
||||
break;
|
||||
case 'recharge':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id_customer = $routes['2'];
|
||||
$plan_id = $routes['3'];
|
||||
$b = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('plan_id', $plan_id)->find_one();
|
||||
if ($b) {
|
||||
$gateway = 'Recharge';
|
||||
$channel = $admin['fullname'];
|
||||
$cust = User::_info($id_customer);
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($b['plan_id']);
|
||||
list($bills, $add_cost) = User::getBills($id_customer);
|
||||
if ($using == 'balance' && $config['enable_balance'] == 'yes') {
|
||||
if (!$cust) {
|
||||
r2(U . 'plan/recharge', 'e', Lang::T('Customer not found'));
|
||||
}
|
||||
if (!$plan) {
|
||||
r2(U . 'plan/recharge', 'e', Lang::T('Plan not found'));
|
||||
}
|
||||
if ($cust['balance'] < ($plan['price'] + $add_cost)) {
|
||||
r2(U . 'plan/recharge', 'e', Lang::T('insufficient balance'));
|
||||
}
|
||||
$gateway = 'Recharge Balance';
|
||||
}
|
||||
if ($using == 'zero') {
|
||||
$zero = 1;
|
||||
$gateway = 'Recharge Zero';
|
||||
}
|
||||
$usings = explode(',', $config['payment_usings']);
|
||||
$usings = array_filter(array_unique($usings));
|
||||
if (count($usings) == 0) {
|
||||
$usings[] = Lang::T('Cash');
|
||||
}
|
||||
$ui->assign('usings', $usings);
|
||||
$ui->assign('bills', $bills);
|
||||
$ui->assign('add_cost', $add_cost);
|
||||
$ui->assign('cust', $cust);
|
||||
$ui->assign('gateway', $gateway);
|
||||
$ui->assign('channel', $channel);
|
||||
$ui->assign('server', $b['routers']);
|
||||
$ui->assign('plan', $plan);
|
||||
$ui->display('recharge-confirm.tpl');
|
||||
} else {
|
||||
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
|
||||
}
|
||||
break;
|
||||
case 'deactivate':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id_customer = $routes['2'];
|
||||
$plan_id = $routes['3'];
|
||||
$b = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('plan_id', $plan_id)->find_one();
|
||||
if ($b) {
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $b['plan_id'])->find_one();
|
||||
if ($p) {
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerDeactivate($b['username']);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($b['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($b['type'] == 'Hotspot') {
|
||||
Mikrotik::removeHotspotUser($client, $b['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $b['username']);
|
||||
} else if ($b['type'] == 'PPPOE') {
|
||||
Mikrotik::removePpoeUser($client, $b['username']);
|
||||
Mikrotik::removePpoeActive($client, $b['username']);
|
||||
}
|
||||
}
|
||||
$b->status = 'off';
|
||||
$b->expiration = date('Y-m-d');
|
||||
$b->time = date('H:i:s');
|
||||
$b->save();
|
||||
_log('Admin ' . $admin['username'] . ' Deactivate ' . $b['namebp'] . ' for ' . $b['username'], 'User', $b['customer_id']);
|
||||
Message::sendTelegram('Admin ' . $admin['username'] . ' Deactivate ' . $b['namebp'] . ' for u' . $b['username']);
|
||||
r2(U . 'customers/view/' . $id_customer, 's', 'Success deactivate customer to Mikrotik');
|
||||
}
|
||||
}
|
||||
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
|
||||
break;
|
||||
case 'sync':
|
||||
$id_customer = $routes['2'];
|
||||
$bs = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('status', 'on')->findMany();
|
||||
if ($bs) {
|
||||
$routers = [];
|
||||
foreach ($bs as $b) {
|
||||
$c = ORM::for_table('tbl_customers')->find_one($id_customer);
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $b['plan_id'])->where('enabled', '1')->find_one();
|
||||
if ($p) {
|
||||
$routers[] = $b['routers'];
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerAddPlan($c, $p, $p['expiration'] . ' ' . $p['time']);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($b['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($b['type'] == 'Hotspot') {
|
||||
Mikrotik::addHotspotUser($client, $p, $c);
|
||||
} else if ($b['type'] == 'PPPOE') {
|
||||
Mikrotik::addPpoeUser($client, $p, $c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
r2(U . 'customers/view/' . $id_customer, 's', 'Sync success to ' . implode(", ", $routers));
|
||||
}
|
||||
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
|
||||
break;
|
||||
case 'viewu':
|
||||
$customer = ORM::for_table('tbl_customers')->where('username', $routes['2'])->find_one();
|
||||
case 'view':
|
||||
$id = $routes['2'];
|
||||
run_hook('view_customer'); #HOOK
|
||||
if (!$customer) {
|
||||
$customer = ORM::for_table('tbl_customers')->find_one($id);
|
||||
}
|
||||
if ($customer) {
|
||||
|
||||
|
||||
// Fetch the Customers Attributes values from the tbl_customer_custom_fields table
|
||||
$customFields = ORM::for_table('tbl_customers_fields')
|
||||
->where('customer_id', $customer['id'])
|
||||
->find_many();
|
||||
$v = $routes['3'];
|
||||
if (empty($v)) {
|
||||
$v = 'activation';
|
||||
}
|
||||
if ($v == 'order') {
|
||||
$v = 'order';
|
||||
$query = ORM::for_table('tbl_transactions')->where('username', $customer['username'])->order_by_desc('id');
|
||||
$order = Paginator::findMany($query);
|
||||
$ui->assign('order', $order);
|
||||
} else if ($v == 'activation') {
|
||||
$query = ORM::for_table('tbl_transactions')->where('username', $customer['username'])->order_by_desc('id');
|
||||
$activation = Paginator::findMany($query);
|
||||
$ui->assign('activation', $activation);
|
||||
}
|
||||
$ui->assign('packages', User::_billing($customer['id']));
|
||||
$ui->assign('v', $v);
|
||||
$ui->assign('d', $customer);
|
||||
$ui->assign('customFields', $customFields);
|
||||
$ui->assign('xheader', $leafletpickerHeader);
|
||||
$ui->display('customers-view.tpl');
|
||||
} else {
|
||||
r2(U . 'customers/list', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
case 'edit':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id = $routes['2'];
|
||||
run_hook('edit_customer'); #HOOK
|
||||
$d = ORM::for_table('tbl_customers')->find_one($id);
|
||||
// Fetch the Customers Attributes values from the tbl_customers_fields table
|
||||
$customFields = ORM::for_table('tbl_customers_fields')
|
||||
->where('customer_id', $id)
|
||||
->find_many();
|
||||
if ($d) {
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('statuses', ORM::for_table('tbl_customers')->getEnum("status"));
|
||||
$ui->assign('customFields', $customFields);
|
||||
$ui->assign('xheader', $leafletpickerHeader);
|
||||
$ui->display('customers-edit.tpl');
|
||||
} else {
|
||||
r2(U . 'customers/list', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
$id = $routes['2'];
|
||||
run_hook('delete_customer'); #HOOK
|
||||
$d = ORM::for_table('tbl_customers')->find_one($id);
|
||||
if ($d) {
|
||||
// Delete the associated Customers Attributes records from tbl_customer_custom_fields table
|
||||
ORM::for_table('tbl_customers_fields')->where('customer_id', $id)->delete_many();
|
||||
$c = ORM::for_table('tbl_user_recharges')->where('username', $d['username'])->find_one();
|
||||
if ($c) {
|
||||
$p = ORM::for_table('tbl_plans')->find_one($c['plan_id']);
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerDelete($d['username']);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($c['routers']);
|
||||
if ($c['type'] == 'Hotspot') {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removeHotspotUser($client, $d['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $d['username']);
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removePpoeUser($client, $d['username']);
|
||||
Mikrotik::removePpoeActive($client, $d['username']);
|
||||
}
|
||||
try {
|
||||
$d->delete();
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
}
|
||||
try {
|
||||
$c->delete();
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$d->delete();
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
}
|
||||
try {
|
||||
if ($c)
|
||||
$c->delete();
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
}
|
||||
}
|
||||
|
||||
r2(U . 'customers/list', 's', Lang::T('User deleted Successfully'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'add-post':
|
||||
$username = _post('username');
|
||||
$fullname = _post('fullname');
|
||||
$password = _post('password');
|
||||
$pppoe_password = _post('pppoe_password');
|
||||
$email = _post('email');
|
||||
$address = _post('address');
|
||||
$phonenumber = _post('phonenumber');
|
||||
$service_type = _post('service_type');
|
||||
$account_type = _post('account_type');
|
||||
$status = _post('status');
|
||||
// Validate status value and set default if invalid
|
||||
$validStatuses = ['Active', 'Banned', 'Disabled', 'Inactive', 'Limited', 'Suspended'];
|
||||
if (!in_array($status, $validStatuses)) {
|
||||
$status = 'Active'; // Default status
|
||||
}
|
||||
$coordinates = _post('coordinates');
|
||||
//post Customers Attributes
|
||||
$custom_field_names = (array) $_POST['custom_field_name'];
|
||||
$custom_field_values = (array) $_POST['custom_field_value'];
|
||||
//additional information
|
||||
$city = _post('city');
|
||||
$district = _post('district');
|
||||
$state = _post('state');
|
||||
$zip = _post('zip');
|
||||
|
||||
run_hook('add_customer'); #HOOK
|
||||
$msg = '';
|
||||
if (Validator::Length($username, 35, 2) == false) {
|
||||
$msg .= 'Username should be between 3 to 55 characters' . '<br>';
|
||||
}
|
||||
if (Validator::Length($fullname, 36, 2) == false) {
|
||||
$msg .= 'Full Name should be between 3 to 25 characters' . '<br>';
|
||||
}
|
||||
if (!Validator::Length($password, 36, 2)) {
|
||||
$msg .= 'Password should be between 3 to 35 characters' . '<br>';
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('Account already axist') . '<br>';
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
$d = ORM::for_table('tbl_customers')->create();
|
||||
$d->username = Lang::phoneFormat($username);
|
||||
$d->password = $password;
|
||||
$d->pppoe_password = $pppoe_password;
|
||||
$d->email = $email;
|
||||
$d->account_type = $account_type;
|
||||
$d->status = $status;
|
||||
$d->fullname = $fullname;
|
||||
$d->address = $address;
|
||||
$d->created_by = $admin['id'];
|
||||
$d->phonenumber = Lang::phoneFormat($phonenumber);
|
||||
$d->service_type = $service_type;
|
||||
$d->coordinates = $coordinates;
|
||||
$d->city = $city;
|
||||
$d->district = $district;
|
||||
$d->state = $state;
|
||||
$d->zip = $zip;
|
||||
$d->save();
|
||||
|
||||
// Retrieve the customer ID of the newly created customer
|
||||
$customerId = $d->id();
|
||||
// Save Customers Attributes details
|
||||
if (!empty($custom_field_names) && !empty($custom_field_values)) {
|
||||
$totalFields = min(count($custom_field_names), count($custom_field_values));
|
||||
for ($i = 0; $i < $totalFields; $i++) {
|
||||
$name = $custom_field_names[$i];
|
||||
$value = $custom_field_values[$i];
|
||||
|
||||
if (!empty($name)) {
|
||||
$customField = ORM::for_table('tbl_customers_fields')->create();
|
||||
$customField->customer_id = $customerId;
|
||||
$customField->field_name = $name;
|
||||
$customField->field_value = $value;
|
||||
$customField->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send registration notification SMS/WhatsApp
|
||||
if (!empty($phonenumber) && strlen($phonenumber) > 5) {
|
||||
$formattedPhone = Lang::phoneFormat($phonenumber);
|
||||
$customerData = [
|
||||
'fullname' => $fullname,
|
||||
'username' => Lang::phoneFormat($username),
|
||||
'password' => $password,
|
||||
'phonenumber' => $formattedPhone,
|
||||
'service_type' => $service_type
|
||||
];
|
||||
|
||||
Message::sendRegistrationNotification($customerData);
|
||||
}
|
||||
|
||||
r2(U . 'customers/list', 's', Lang::T('Account Created Successfully'));
|
||||
} else {
|
||||
r2(U . 'customers/add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'edit-post':
|
||||
$username = Lang::phoneFormat(_post('username'));
|
||||
$fullname = _post('fullname');
|
||||
$account_type = _post('account_type');
|
||||
$password = _post('password');
|
||||
$pppoe_password = _post('pppoe_password');
|
||||
$email = _post('email');
|
||||
$address = _post('address');
|
||||
$phonenumber = Lang::phoneFormat(_post('phonenumber'));
|
||||
$service_type = _post('service_type');
|
||||
$coordinates = _post('coordinates');
|
||||
$status = _post('status');
|
||||
// Validate status value and set default if invalid
|
||||
$validStatuses = ['Active', 'Banned', 'Disabled', 'Inactive', 'Limited', 'Suspended'];
|
||||
if (!in_array($status, $validStatuses)) {
|
||||
$status = 'Active'; // Default status
|
||||
}
|
||||
//additional information
|
||||
$city = _post('city');
|
||||
$district = _post('district');
|
||||
$state = _post('state');
|
||||
$zip = _post('zip');
|
||||
run_hook('edit_customer'); #HOOK
|
||||
$msg = '';
|
||||
if (Validator::Length($username, 35, 2) == false) {
|
||||
$msg .= 'Username should be between 3 to 15 characters' . '<br>';
|
||||
}
|
||||
if (Validator::Length($fullname, 36, 1) == false) {
|
||||
$msg .= 'Full Name should be between 2 to 25 characters' . '<br>';
|
||||
}
|
||||
if ($password != '') {
|
||||
if (!Validator::Length($password, 36, 2)) {
|
||||
$msg .= 'Password should be between 3 to 15 characters' . '<br>';
|
||||
}
|
||||
}
|
||||
|
||||
$id = _post('id');
|
||||
$d = ORM::for_table('tbl_customers')->find_one($id);
|
||||
|
||||
//lets find user Customers Attributes using id
|
||||
$customFields = ORM::for_table('tbl_customers_fields')
|
||||
->where('customer_id', $id)
|
||||
->find_many();
|
||||
|
||||
if (!$d) {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
|
||||
$oldusername = $d['username'];
|
||||
$oldPppoePassword = $d['password'];
|
||||
$oldPassPassword = $d['pppoe_password'];
|
||||
$userDiff = false;
|
||||
$pppoeDiff = false;
|
||||
$passDiff = false;
|
||||
if ($oldusername != $username) {
|
||||
$c = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($c) {
|
||||
$msg .= Lang::T('Account already exist') . '<br>';
|
||||
}
|
||||
$userDiff = true;
|
||||
}
|
||||
if ($oldPppoePassword != $pppoe_password) {
|
||||
$pppoeDiff = true;
|
||||
}
|
||||
if ($password != '' && $oldPassPassword != $password) {
|
||||
$passDiff = true;
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
if ($userDiff) {
|
||||
$d->username = $username;
|
||||
}
|
||||
if ($password != '') {
|
||||
$d->password = $password;
|
||||
}
|
||||
$d->pppoe_password = $pppoe_password;
|
||||
$d->fullname = $fullname;
|
||||
$d->email = $email;
|
||||
$d->account_type = $account_type;
|
||||
$d->address = $address;
|
||||
$d->status = $status;
|
||||
$d->phonenumber = $phonenumber;
|
||||
$d->service_type = $service_type;
|
||||
$d->coordinates = $coordinates;
|
||||
$d->city = $city;
|
||||
$d->district = $district;
|
||||
$d->state = $state;
|
||||
$d->zip = $zip;
|
||||
$d->save();
|
||||
|
||||
|
||||
// Update Customers Attributes values in tbl_customers_fields table
|
||||
foreach ($customFields as $customField) {
|
||||
$fieldName = $customField['field_name'];
|
||||
if (isset($_POST['custom_fields'][$fieldName])) {
|
||||
$customFieldValue = $_POST['custom_fields'][$fieldName];
|
||||
$customField->set('field_value', $customFieldValue);
|
||||
$customField->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Add new Customers Attributess
|
||||
if (isset($_POST['custom_field_name']) && isset($_POST['custom_field_value'])) {
|
||||
$newCustomFieldNames = $_POST['custom_field_name'];
|
||||
$newCustomFieldValues = $_POST['custom_field_value'];
|
||||
|
||||
// Check if the number of field names and values match
|
||||
if (count($newCustomFieldNames) == count($newCustomFieldValues)) {
|
||||
$numNewFields = count($newCustomFieldNames);
|
||||
|
||||
for ($i = 0; $i < $numNewFields; $i++) {
|
||||
$fieldName = $newCustomFieldNames[$i];
|
||||
$fieldValue = $newCustomFieldValues[$i];
|
||||
|
||||
// Insert the new Customers Attributes
|
||||
$newCustomField = ORM::for_table('tbl_customers_fields')->create();
|
||||
$newCustomField->set('customer_id', $id);
|
||||
$newCustomField->set('field_name', $fieldName);
|
||||
$newCustomField->set('field_value', $fieldValue);
|
||||
$newCustomField->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Customers Attributess
|
||||
if (isset($_POST['delete_custom_fields'])) {
|
||||
$fieldsToDelete = $_POST['delete_custom_fields'];
|
||||
foreach ($fieldsToDelete as $fieldName) {
|
||||
// Delete the Customers Attributes with the given field name
|
||||
ORM::for_table('tbl_customers_fields')
|
||||
->where('field_name', $fieldName)
|
||||
->where('customer_id', $id)
|
||||
->delete_many();
|
||||
}
|
||||
}
|
||||
|
||||
if ($userDiff || $pppoeDiff || $passDiff) {
|
||||
$c = ORM::for_table('tbl_user_recharges')->where('username', ($userDiff) ? $oldusername : $username)->find_one();
|
||||
if ($c) {
|
||||
$c->username = $username;
|
||||
$c->save();
|
||||
$p = ORM::for_table('tbl_plans')->find_one($c['plan_id']);
|
||||
if ($p['is_radius']) {
|
||||
if ($userDiff) {
|
||||
Radius::customerChangeUsername($oldusername, $username);
|
||||
}
|
||||
Radius::customerAddPlan($d, $p, $p['expiration'] . ' ' . $p['time']);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($c['routers']);
|
||||
if ($c['type'] == 'Hotspot') {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::setHotspotUser($client, $c['username'], $password);
|
||||
Mikrotik::removeHotspotActiveUser($client, $d['username']);
|
||||
} else {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if (!empty($d['pppoe_password'])) {
|
||||
Mikrotik::setPpoeUser($client, $c['username'], $d['pppoe_password']);
|
||||
} else {
|
||||
Mikrotik::setPpoeUser($client, $c['username'], $password);
|
||||
}
|
||||
Mikrotik::removePpoeActive($client, $d['username']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
r2(U . 'customers/view/' . $id, 's', 'User Updated Successfully');
|
||||
} else {
|
||||
r2(U . 'customers/edit/' . $id, 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
run_hook('list_customers'); #HOOK
|
||||
$search = _post('search');
|
||||
$order = _post('order', 'username');
|
||||
$filter = _post('filter', 'All');
|
||||
$orderby = _post('orderby', 'asc');
|
||||
$order_pos = [
|
||||
'username' => 0,
|
||||
'created_at' => 8,
|
||||
'balance' => 3,
|
||||
'status' => 7
|
||||
];
|
||||
|
||||
if ($search != '') {
|
||||
$query = ORM::for_table('tbl_customers')
|
||||
->whereRaw("(username LIKE '%$search%' OR fullname LIKE '%$search%' OR address LIKE '%$search%' " .
|
||||
"OR phonenumber LIKE '%$search%' OR email LIKE '%$search%')");
|
||||
if ($filter != 'All') {
|
||||
$query->where("status", $filter);
|
||||
}
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_customers');
|
||||
if ($filter != 'All') {
|
||||
$query->where("status", $filter);
|
||||
}
|
||||
}
|
||||
if ($orderby == 'asc') {
|
||||
$query->order_by_asc($order);
|
||||
} else {
|
||||
$query->order_by_desc($order);
|
||||
}
|
||||
$d = $query->findMany();
|
||||
if (_post('export', '') == 'csv') {
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="phpnuxbill_customers_' . $filter . '_' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
|
||||
$headers = [
|
||||
'id',
|
||||
'username',
|
||||
'fullname',
|
||||
'address',
|
||||
'phonenumber',
|
||||
'email',
|
||||
'balance',
|
||||
'service_type',
|
||||
];
|
||||
$fp = fopen('php://output', 'wb');
|
||||
if (!$h) {
|
||||
fputcsv($fp, $headers, ";");
|
||||
$h = true;
|
||||
}
|
||||
foreach ($d as $c) {
|
||||
$row = [
|
||||
$c['id'],
|
||||
$c['username'],
|
||||
$c['fullname'],
|
||||
str_replace("\n", " ", $c['address']),
|
||||
$c['phonenumber'],
|
||||
$c['email'],
|
||||
$c['balance'],
|
||||
$c['service_type'],
|
||||
];
|
||||
fputcsv($fp, $row, ";");
|
||||
}
|
||||
fclose($fp);
|
||||
die();
|
||||
}
|
||||
$ui->assign('xheader', '<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">');
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('statuses', ORM::for_table('tbl_customers')->getEnum("status"));
|
||||
$ui->assign('filter', $filter);
|
||||
$ui->assign('search', $search);
|
||||
$ui->assign('order', $order);
|
||||
$ui->assign('order_pos', $order_pos[$order]);
|
||||
$ui->assign('orderby', $orderby);
|
||||
$ui->display('customers.tpl');
|
||||
break;
|
||||
}
|
||||
207
system/controllers/dashboard.php
Normal file
207
system/controllers/dashboard.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Dashboard'));
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if(isset($_GET['refresh'])){
|
||||
$files = scandir($CACHE_PATH);
|
||||
foreach ($files as $file) {
|
||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||
if (is_file($CACHE_PATH . DIRECTORY_SEPARATOR . $file) && $ext == 'temp') {
|
||||
unlink($CACHE_PATH . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
}
|
||||
r2(U . 'dashboard', 's', 'Data Refreshed');
|
||||
}
|
||||
|
||||
$fdate = date('Y-m-01');
|
||||
$tdate = date('Y-m-t');
|
||||
//first day of month
|
||||
$first_day_month = date('Y-m-01');
|
||||
$mdate = date('Y-m-d');
|
||||
$month_n = date('n');
|
||||
|
||||
$iday = ORM::for_table('tbl_transactions')
|
||||
->where('recharged_on', $mdate)
|
||||
->where_not_equal('method', 'Customer - Balance')
|
||||
->where_not_equal('method', 'Recharge Balance - Administrator')
|
||||
->sum('price');
|
||||
|
||||
if ($iday == '') {
|
||||
$iday = '0.00';
|
||||
}
|
||||
$ui->assign('iday', $iday);
|
||||
|
||||
$imonth = ORM::for_table('tbl_transactions')->where_not_equal('method', 'Customer - Balance')->where_not_equal('method', 'Recharge Balance - Administrator')->where_gte('recharged_on', $first_day_month)->where_lte('recharged_on', $mdate)->sum('price');
|
||||
if ($imonth == '') {
|
||||
$imonth = '0.00';
|
||||
}
|
||||
$ui->assign('imonth', $imonth);
|
||||
|
||||
$u_act = ORM::for_table('tbl_user_recharges')->where('status', 'on')->count();
|
||||
if (empty($u_act)) {
|
||||
$u_act = '0';
|
||||
}
|
||||
$ui->assign('u_act', $u_act);
|
||||
|
||||
$u_all = ORM::for_table('tbl_user_recharges')->count();
|
||||
if (empty($u_all)) {
|
||||
$u_all = '0';
|
||||
}
|
||||
$ui->assign('u_all', $u_all);
|
||||
|
||||
|
||||
$c_all = ORM::for_table('tbl_customers')->count();
|
||||
if (empty($c_all)) {
|
||||
$c_all = '0';
|
||||
}
|
||||
$ui->assign('c_all', $c_all);
|
||||
|
||||
if ($config['hide_uet'] != 'yes') {
|
||||
//user expire
|
||||
$query = ORM::for_table('tbl_user_recharges')
|
||||
->select('tbl_user_recharges.*')
|
||||
->select('tbl_customers.fullname')
|
||||
->left_outer_join('tbl_customers', array('tbl_customers.id', '=', 'tbl_user_recharges.customer_id'))
|
||||
->where_lte('tbl_user_recharges.expiration', $mdate)
|
||||
->order_by_desc('tbl_user_recharges.expiration');
|
||||
$expire = Paginator::findMany($query);
|
||||
|
||||
// Get the total count of expired records for pagination
|
||||
$totalCount = ORM::for_table('tbl_user_recharges')
|
||||
->where_lte('expiration', $mdate)
|
||||
->count();
|
||||
|
||||
// Pass the total count and current page to the paginator
|
||||
$paginator['total_count'] = $totalCount;
|
||||
|
||||
// Assign the pagination HTML to the template variable
|
||||
$ui->assign('expire', $expire);
|
||||
}
|
||||
|
||||
//activity log
|
||||
$dlog = ORM::for_table('tbl_logs')->limit(5)->order_by_desc('id')->find_many();
|
||||
$ui->assign('dlog', $dlog);
|
||||
$log = ORM::for_table('tbl_logs')->count();
|
||||
$ui->assign('log', $log);
|
||||
|
||||
|
||||
if ($config['hide_vs'] != 'yes') {
|
||||
$cacheStocksfile = $CACHE_PATH . File::pathFixer('/VoucherStocks.temp');
|
||||
$cachePlanfile = $CACHE_PATH . File::pathFixer('/VoucherPlans.temp');
|
||||
//Cache for 5 minutes
|
||||
if (file_exists($cacheStocksfile) && time() - filemtime($cacheStocksfile) < 600) {
|
||||
$stocks = json_decode(file_get_contents($cacheStocksfile), true);
|
||||
$plans = json_decode(file_get_contents($cachePlanfile), true);
|
||||
} else {
|
||||
// Count stock
|
||||
$tmp = $v = ORM::for_table('tbl_plans')->select('id')->select('name_plan')->find_many();
|
||||
$plans = array();
|
||||
$stocks = array("used" => 0, "unused" => 0);
|
||||
$n = 0;
|
||||
foreach ($tmp as $plan) {
|
||||
$unused = ORM::for_table('tbl_voucher')
|
||||
->where('id_plan', $plan['id'])
|
||||
->where('status', 0)->count();
|
||||
$used = ORM::for_table('tbl_voucher')
|
||||
->where('id_plan', $plan['id'])
|
||||
->where('status', 1)->count();
|
||||
if ($unused > 0 || $used > 0) {
|
||||
$plans[$n]['name_plan'] = $plan['name_plan'];
|
||||
$plans[$n]['unused'] = $unused;
|
||||
$plans[$n]['used'] = $used;
|
||||
$stocks["unused"] += $unused;
|
||||
$stocks["used"] += $used;
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
file_put_contents($cacheStocksfile, json_encode($stocks));
|
||||
file_put_contents($cachePlanfile, json_encode($plans));
|
||||
}
|
||||
}
|
||||
|
||||
$cacheMRfile = File::pathFixer('/monthlyRegistered.temp');
|
||||
//Cache for 1 hour
|
||||
if (file_exists($cacheMRfile) && time() - filemtime($cacheMRfile) < 3600) {
|
||||
$monthlyRegistered = json_decode(file_get_contents($cacheMRfile), true);
|
||||
} else {
|
||||
//Monthly Registered Customers
|
||||
$result = ORM::for_table('tbl_customers')
|
||||
->select_expr('MONTH(created_at)', 'month')
|
||||
->select_expr('COUNT(*)', 'count')
|
||||
->where_raw('YEAR(created_at) = YEAR(NOW())')
|
||||
->group_by_expr('MONTH(created_at)')
|
||||
->find_many();
|
||||
|
||||
$monthlyRegistered = [];
|
||||
foreach ($result as $row) {
|
||||
$monthlyRegistered[] = [
|
||||
'date' => $row->month,
|
||||
'count' => $row->count
|
||||
];
|
||||
}
|
||||
file_put_contents($cacheMRfile, json_encode($monthlyRegistered));
|
||||
}
|
||||
|
||||
$cacheMSfile = $CACHE_PATH . File::pathFixer('/monthlySales.temp');
|
||||
//Cache for 12 hours
|
||||
if (file_exists($cacheMSfile) && time() - filemtime($cacheMSfile) < 43200) {
|
||||
$monthlySales = json_decode(file_get_contents($cacheMSfile), true);
|
||||
} else {
|
||||
// Query to retrieve monthly data
|
||||
$results = ORM::for_table('tbl_transactions')
|
||||
->select_expr('MONTH(recharged_on)', 'month')
|
||||
->select_expr('SUM(price)', 'total')
|
||||
->where_raw("YEAR(recharged_on) = YEAR(CURRENT_DATE())") // Filter by the current year
|
||||
->where_not_equal('method', 'Customer - Balance')
|
||||
->where_not_equal('method', 'Recharge Balance - Administrator')
|
||||
->group_by_expr('MONTH(recharged_on)')
|
||||
->find_many();
|
||||
|
||||
// Create an array to hold the monthly sales data
|
||||
$monthlySales = array();
|
||||
|
||||
// Iterate over the results and populate the array
|
||||
foreach ($results as $result) {
|
||||
$month = $result->month;
|
||||
$totalSales = $result->total;
|
||||
|
||||
$monthlySales[$month] = array(
|
||||
'month' => $month,
|
||||
'totalSales' => $totalSales
|
||||
);
|
||||
}
|
||||
|
||||
// Fill in missing months with zero sales
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
if (!isset($monthlySales[$month])) {
|
||||
$monthlySales[$month] = array(
|
||||
'month' => $month,
|
||||
'totalSales' => 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the array by month
|
||||
ksort($monthlySales);
|
||||
|
||||
// Reindex the array
|
||||
$monthlySales = array_values($monthlySales);
|
||||
file_put_contents($cacheMSfile, json_encode($monthlySales));
|
||||
}
|
||||
|
||||
// Assign the monthly sales data to Smarty
|
||||
$ui->assign('monthlySales', $monthlySales);
|
||||
$ui->assign('xfooter', '');
|
||||
$ui->assign('monthlyRegistered', $monthlyRegistered);
|
||||
$ui->assign('stocks', $stocks);
|
||||
$ui->assign('plans', $plans);
|
||||
|
||||
run_hook('view_dashboard'); #HOOK
|
||||
$ui->display('dashboard.tpl');
|
||||
13
system/controllers/default.php
Normal file
13
system/controllers/default.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
if(Admin::getID()){
|
||||
r2(U.'dashboard');
|
||||
}if(User::getID()){
|
||||
r2(U.'home');
|
||||
}else{
|
||||
r2(U.'login');
|
||||
}
|
||||
356
system/controllers/export.php
Normal file
356
system/controllers/export.php
Normal file
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Reports'));
|
||||
$ui->assign('_sysfrm_menu', 'reports');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
$mdate = date('Y-m-d');
|
||||
$tdate = date('Y-m-d', strtotime('today - 30 days'));
|
||||
|
||||
//first day of month
|
||||
$first_day_month = date('Y-m-01');
|
||||
//
|
||||
$this_week_start = date('Y-m-d', strtotime('previous sunday'));
|
||||
// 30 days before
|
||||
$before_30_days = date('Y-m-d', strtotime('today - 30 days'));
|
||||
//this month
|
||||
$month_n = date('n');
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'print-by-date':
|
||||
$mdate = date('Y-m-d');
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
$d->where('recharged_on', $mdate);
|
||||
$d->order_by_desc('id');
|
||||
$x = $d->find_many();
|
||||
|
||||
$dr = ORM::for_table('tbl_transactions');
|
||||
$dr->where('recharged_on', $mdate);
|
||||
$dr->order_by_desc('id');
|
||||
$xy = $dr->sum('price');
|
||||
|
||||
$ui->assign('d', $x);
|
||||
$ui->assign('dr', $xy);
|
||||
$ui->assign('mdate', $mdate);
|
||||
$ui->assign('recharged_on', $mdate);
|
||||
run_hook('print_by_date'); #HOOK
|
||||
$ui->display('print-by-date.tpl');
|
||||
break;
|
||||
|
||||
case 'pdf-by-date':
|
||||
$mdate = date('Y-m-d');
|
||||
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
$d->where('recharged_on', $mdate);
|
||||
$d->order_by_desc('id');
|
||||
$x = $d->find_many();
|
||||
|
||||
$dr = ORM::for_table('tbl_transactions');
|
||||
$dr->where('recharged_on', $mdate);
|
||||
$dr->order_by_desc('id');
|
||||
$xy = $dr->sum('price');
|
||||
|
||||
$title = ' Reports [' . $mdate . ']';
|
||||
$title = str_replace('-', ' ', $title);
|
||||
|
||||
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||
if (file_exists($UPLOAD_PATH . '/logo.png')) {
|
||||
$logo = $UPLOAD_URL_PATH . '/logo.png';
|
||||
} else {
|
||||
$logo = $UPLOAD_URL_PATH . '/logo.default.png';
|
||||
}
|
||||
|
||||
if ($x) {
|
||||
$html = '
|
||||
<div id="page-wrap">
|
||||
<div id="address">
|
||||
<h3>' . $config['CompanyName'] . '</h3>
|
||||
' . $config['address'] . '<br>
|
||||
' . Lang::T('Phone Number') . ': ' . $config['phone'] . '<br>
|
||||
</div>
|
||||
<div id="logo"><img id="image" src="' . $logo . '" alt="logo" /></div>
|
||||
</div>
|
||||
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . date($config['date_format'], strtotime($mdate)) . '</div>
|
||||
<table id="customers">
|
||||
<tr>
|
||||
<th>' . Lang::T('Username') . '</th>
|
||||
<th>' . Lang::T('Plan Name') . '</th>
|
||||
<th>' . Lang::T('Type') . '</th>
|
||||
<th>' . Lang::T('Plan Price') . '</th>
|
||||
<th>' . Lang::T('Created On') . '</th>
|
||||
<th>' . Lang::T('Expires On') . '</th>
|
||||
<th>' . Lang::T('Method') . '</th>
|
||||
<th>' . Lang::T('Routers') . '</th>
|
||||
</tr>';
|
||||
$c = true;
|
||||
foreach ($x as $value) {
|
||||
|
||||
$username = $value['username'];
|
||||
$plan_name = $value['plan_name'];
|
||||
$type = $value['type'];
|
||||
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
||||
$recharged_on = date($config['date_format'], strtotime($value['recharged_on']));
|
||||
$expiration = date($config['date_format'], strtotime($value['expiration']));
|
||||
$time = $value['time'];
|
||||
$method = $value['method'];
|
||||
$routers = $value['routers'];
|
||||
|
||||
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
||||
<td>$username</td>
|
||||
<td>$plan_name</td>
|
||||
<td>$type</td>
|
||||
<td align='right'>$price</td>
|
||||
<td>$recharged_on</td>
|
||||
<td>$expiration $time </td>
|
||||
<td>$method</td>
|
||||
<td>$routers</td>
|
||||
</tr>";
|
||||
}
|
||||
$html .= '</table>
|
||||
<h4 class="text-uppercase text-bold">' . Lang::T('Total Income') . ':</h4>
|
||||
<h3 class="sum">' . $config['currency_code'] . ' ' . number_format($xy, 2, $config['dec_point'], $config['thousands_sep']) . '</h3>';
|
||||
run_hook('print_pdf_by_date'); #HOOK
|
||||
|
||||
$mpdf = new \Mpdf\Mpdf();
|
||||
$mpdf->SetProtection(array('print'));
|
||||
$mpdf->SetTitle($config['CompanyName'] . ' Reports');
|
||||
$mpdf->SetAuthor($config['CompanyName']);
|
||||
$mpdf->SetWatermarkText($d['price']);
|
||||
$mpdf->showWatermarkText = true;
|
||||
$mpdf->watermark_font = 'Helvetica';
|
||||
$mpdf->watermarkTextAlpha = 0.1;
|
||||
$mpdf->SetDisplayMode('fullpage');
|
||||
|
||||
$style = '<style>
|
||||
#page-wrap { width: 100%; margin: 0 auto; }
|
||||
#header { text-align: center; position: relative; color: black; font: bold 15px Helvetica, Sans-Serif; margin-top: 10px; margin-bottom: 10px;}
|
||||
|
||||
#address { width: 300px; float: left; }
|
||||
#logo { text-align: right; float: right; position: relative; margin-top: 15px; border: 5px solid #fff; overflow: hidden; }
|
||||
|
||||
#customers
|
||||
{
|
||||
font-family: Helvetica, sans-serif;
|
||||
width:100%;
|
||||
border-collapse:collapse;
|
||||
}
|
||||
#customers td, #customers th
|
||||
{
|
||||
font-size:0.8em;
|
||||
border:1px solid #98bf21;
|
||||
padding:3px 5px 2px 5px;
|
||||
}
|
||||
#customers th
|
||||
{
|
||||
font-size:0.8em;
|
||||
text-align:left;
|
||||
padding-top:5px;
|
||||
padding-bottom:4px;
|
||||
background-color:#A7C942;
|
||||
color:#fff;
|
||||
}
|
||||
#customers tr.alt td
|
||||
{
|
||||
color:#000;
|
||||
background-color:#EAF2D3;
|
||||
}
|
||||
</style>';
|
||||
|
||||
$nhtml = <<<EOF
|
||||
$style
|
||||
$html
|
||||
EOF;
|
||||
$mpdf->WriteHTML($nhtml);
|
||||
$mpdf->Output(date('Ymd_His') . '.pdf', 'D');
|
||||
} else {
|
||||
echo 'No Data';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'print-by-period':
|
||||
$fdate = _post('fdate');
|
||||
$tdate = _post('tdate');
|
||||
$stype = _post('stype');
|
||||
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$d->where('type', $stype);
|
||||
}
|
||||
$d->where_gte('recharged_on', $fdate);
|
||||
$d->where_lte('recharged_on', $tdate);
|
||||
$d->order_by_desc('id');
|
||||
$x = $d->find_many();
|
||||
|
||||
$dr = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$dr->where('type', $stype);
|
||||
}
|
||||
|
||||
$dr->where_gte('recharged_on', $fdate);
|
||||
$dr->where_lte('recharged_on', $tdate);
|
||||
$xy = $dr->sum('price');
|
||||
|
||||
$ui->assign('d', $x);
|
||||
$ui->assign('dr', $xy);
|
||||
$ui->assign('fdate', $fdate);
|
||||
$ui->assign('tdate', $tdate);
|
||||
$ui->assign('stype', $stype);
|
||||
run_hook('print_by_period'); #HOOK
|
||||
$ui->display('print-by-period.tpl');
|
||||
break;
|
||||
|
||||
|
||||
case 'pdf-by-period':
|
||||
$fdate = _post('fdate');
|
||||
$tdate = _post('tdate');
|
||||
$stype = _post('stype');
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$d->where('type', $stype);
|
||||
}
|
||||
|
||||
$d->where_gte('recharged_on', $fdate);
|
||||
$d->where_lte('recharged_on', $tdate);
|
||||
$d->order_by_desc('id');
|
||||
$x = $d->find_many();
|
||||
|
||||
$dr = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$dr->where('type', $stype);
|
||||
}
|
||||
|
||||
$dr->where_gte('recharged_on', $fdate);
|
||||
$dr->where_lte('recharged_on', $tdate);
|
||||
$xy = $dr->sum('price');
|
||||
|
||||
$title = ' Reports [' . $mdate . ']';
|
||||
$title = str_replace('-', ' ', $title);
|
||||
|
||||
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
|
||||
if (file_exists($UPLOAD_PATH . '/logo.png')) {
|
||||
$logo = $UPLOAD_URL_PATH . '/logo.png';
|
||||
} else {
|
||||
$logo = $UPLOAD_URL_PATH . '/logo.default.png';
|
||||
}
|
||||
|
||||
if ($x) {
|
||||
$html = '
|
||||
<div id="page-wrap">
|
||||
<div id="address">
|
||||
<h3>' . $config['CompanyName'] . '</h3>
|
||||
' . $config['address'] . '<br>
|
||||
' . Lang::T('Phone Number') . ': ' . $config['phone'] . '<br>
|
||||
</div>
|
||||
<div id="logo"><img id="image" src="' . $logo . '" alt="logo" /></div>
|
||||
</div>
|
||||
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . date($config['date_format'], strtotime($fdate)) . ' - ' . date($config['date_format'], strtotime($tdate)) . '</div>
|
||||
<table id="customers">
|
||||
<tr>
|
||||
<th>' . Lang::T('Username') . '</th>
|
||||
<th>' . Lang::T('Plan Name') . '</th>
|
||||
<th>' . Lang::T('Type') . '</th>
|
||||
<th>' . Lang::T('Plan Price') . '</th>
|
||||
<th>' . Lang::T('Created On') . '</th>
|
||||
<th>' . Lang::T('Expires On') . '</th>
|
||||
<th>' . Lang::T('Method') . '</th>
|
||||
<th>' . Lang::T('Routers') . '</th>
|
||||
</tr>';
|
||||
$c = true;
|
||||
foreach ($x as $value) {
|
||||
|
||||
$username = $value['username'];
|
||||
$plan_name = $value['plan_name'];
|
||||
$type = $value['type'];
|
||||
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
|
||||
$recharged_on = date($config['date_format'], strtotime($value['recharged_on']));
|
||||
$expiration = date($config['date_format'], strtotime($value['expiration']));
|
||||
$time = $value['time'];
|
||||
$method = $value['method'];
|
||||
$routers = $value['routers'];
|
||||
|
||||
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
|
||||
<td>$username</td>
|
||||
<td>$plan_name</td>
|
||||
<td>$type</td>
|
||||
<td align='right'>$price</td>
|
||||
<td>$recharged_on </td>
|
||||
<td>$expiration $time </td>
|
||||
<td>$method</td>
|
||||
<td>$routers</td>
|
||||
</tr>";
|
||||
}
|
||||
$html .= '</table>
|
||||
<h4 class="text-uppercase text-bold">' . Lang::T('Total Income') . ':</h4>
|
||||
<h3 class="sum">' . $config['currency_code'] . ' ' . number_format($xy, 2, $config['dec_point'], $config['thousands_sep']) . '</h3>';
|
||||
|
||||
run_hook('pdf_by_period'); #HOOK
|
||||
$mpdf = new \Mpdf\Mpdf();
|
||||
$mpdf->SetProtection(array('print'));
|
||||
$mpdf->SetTitle($config['CompanyName'] . ' Reports');
|
||||
$mpdf->SetAuthor($config['CompanyName']);
|
||||
$mpdf->SetWatermarkText($d['price']);
|
||||
$mpdf->showWatermarkText = true;
|
||||
$mpdf->watermark_font = 'Helvetica';
|
||||
$mpdf->watermarkTextAlpha = 0.1;
|
||||
$mpdf->SetDisplayMode('fullpage');
|
||||
|
||||
$style = '<style>
|
||||
#page-wrap { width: 100%; margin: 0 auto; }
|
||||
#header { text-align: center; position: relative; color: black; font: bold 15px Helvetica, Sans-Serif; margin-top: 10px; margin-bottom: 10px;}
|
||||
|
||||
#address { width: 300px; float: left; }
|
||||
#logo { text-align: right; float: right; position: relative; margin-top: 15px; border: 5px solid #fff; overflow: hidden; }
|
||||
|
||||
#customers
|
||||
{
|
||||
font-family: Helvetica, sans-serif;
|
||||
width:100%;
|
||||
border-collapse:collapse;
|
||||
}
|
||||
#customers td, #customers th
|
||||
{
|
||||
font-size:0.8em;
|
||||
border:1px solid #98bf21;
|
||||
padding:3px 5px 2px 5px;
|
||||
}
|
||||
#customers th
|
||||
{
|
||||
font-size:0.8em;
|
||||
text-align:left;
|
||||
padding-top:5px;
|
||||
padding-bottom:4px;
|
||||
background-color:#A7C942;
|
||||
color:#fff;
|
||||
}
|
||||
#customers tr.alt td
|
||||
{
|
||||
color:#000;
|
||||
background-color:#EAF2D3;
|
||||
}
|
||||
</style>';
|
||||
|
||||
$nhtml = <<<EOF
|
||||
$style
|
||||
$html
|
||||
EOF;
|
||||
$mpdf->WriteHTML($nhtml);
|
||||
$mpdf->Output(date('Ymd_His') . '.pdf', 'D');
|
||||
} else {
|
||||
echo 'No Data';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
252
system/controllers/home.php
Normal file
252
system/controllers/home.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_auth();
|
||||
$ui->assign('_title', Lang::T('Dashboard'));
|
||||
|
||||
$user = User::_info();
|
||||
$ui->assign('_user', $user);
|
||||
|
||||
if (isset($_GET['renewal'])) {
|
||||
$user->auto_renewal = $_GET['renewal'];
|
||||
$user->save();
|
||||
}
|
||||
|
||||
if (_post('send') == 'balance') {
|
||||
if ($config['enable_balance'] == 'yes' && $config['allow_balance_transfer'] == 'yes') {
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
$target = ORM::for_table('tbl_customers')->where('username', _post('username'))->find_one();
|
||||
if (!$target) {
|
||||
r2(U . 'home', 'd', Lang::T('Username not found'));
|
||||
}
|
||||
$username = _post('username');
|
||||
$balance = _post('balance');
|
||||
if ($user['balance'] < $balance) {
|
||||
r2(U . 'home', 'd', Lang::T('insufficient balance'));
|
||||
}
|
||||
if (!empty($config['minimum_transfer']) && intval($balance) < intval($config['minimum_transfer'])) {
|
||||
r2(U . 'home', 'd', Lang::T('Minimum Transfer') . ' ' . Lang::moneyFormat($config['minimum_transfer']));
|
||||
}
|
||||
if ($user['username'] == $target['username']) {
|
||||
r2(U . 'home', 'd', Lang::T('Cannot send to yourself'));
|
||||
}
|
||||
if (Balance::transfer($user['id'], $username, $balance)) {
|
||||
//sender
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $user['username'];
|
||||
$d->gateway = $target['username'];
|
||||
$d->plan_id = 0;
|
||||
$d->plan_name = 'Send Balance';
|
||||
$d->routers_id = 0;
|
||||
$d->routers = 'balance';
|
||||
$d->price = $balance;
|
||||
$d->payment_method = "Customer";
|
||||
$d->payment_channel = "Balance";
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = 'balance';
|
||||
$d->status = 2;
|
||||
$d->save();
|
||||
//receiver
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $target['username'];
|
||||
$d->gateway = $user['username'];
|
||||
$d->plan_id = 0;
|
||||
$d->plan_name = 'Receive Balance';
|
||||
$d->routers_id = 0;
|
||||
$d->routers = 'balance';
|
||||
$d->payment_method = "Customer";
|
||||
$d->payment_channel = "Balance";
|
||||
$d->price = $balance;
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = 'balance';
|
||||
$d->status = 2;
|
||||
$d->save();
|
||||
Message::sendBalanceNotification($user['phonenumber'], $target['fullname'] . ' (' . $target['username'] . ')', $balance, ($user['balance'] - $balance), Lang::getNotifText('balance_send'), $config['user_notification_payment']);
|
||||
Message::sendBalanceNotification($target['phonenumber'], $user['fullname'] . ' (' . $user['username'] . ')', $balance, ($target['balance'] + $balance), Lang::getNotifText('balance_received'), $config['user_notification_payment']);
|
||||
Message::sendTelegram("#u$user[username] send balance to #u$target[username] \n" . Lang::moneyFormat($balance));
|
||||
r2(U . 'home', 's', Lang::T('Sending balance success'));
|
||||
}
|
||||
} else {
|
||||
r2(U . 'home', 'd', Lang::T('Failed, balance is not available'));
|
||||
}
|
||||
} else if (_post('send') == 'plan') {
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
$actives = ORM::for_table('tbl_user_recharges')
|
||||
->where('username', _post('username'))
|
||||
->find_many();
|
||||
foreach ($actives as $active) {
|
||||
$router = ORM::for_table('tbl_routers')->where('name', $active['routers'])->find_one();
|
||||
if ($router) {
|
||||
r2(U . "order/send/$router[id]/$active[plan_id]&u=" . trim(_post('username')), 's', Lang::T('Review package before recharge'));
|
||||
}
|
||||
}
|
||||
r2(U . 'home', 'w', Lang::T('Your friend do not have active package'));
|
||||
}
|
||||
|
||||
$ui->assign('_bills', User::_billing());
|
||||
|
||||
if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
if (!empty(App::getTokenValue(_get('stoken')))) {
|
||||
r2(U . "voucher/invoice/");
|
||||
die();
|
||||
}
|
||||
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['recharge'])->where('username', $user['username'])->findOne();
|
||||
if ($bill) {
|
||||
if ($bill['routers'] == 'radius') {
|
||||
$router = 'radius';
|
||||
} else {
|
||||
$routers = ORM::for_table('tbl_routers')->where('name', $bill['routers'])->find_one();
|
||||
$router = $routers['id'];
|
||||
}
|
||||
if ($config['enable_balance'] == 'yes') {
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($bill['plan_id']);
|
||||
if (!$plan['enabled']) {
|
||||
r2(U . "home", 'e', 'Plan is not exists');
|
||||
}
|
||||
if ($user['balance'] > $plan['price']) {
|
||||
r2(U . "order/pay/$router/$bill[plan_id]&stoken=" . _get('stoken'), 'e', 'Order Plan');
|
||||
} else {
|
||||
r2(U . "order/buy/$router/$bill[plan_id]", 'e', 'Order Plan');
|
||||
}
|
||||
} else {
|
||||
r2(U . "order/buy/$router/$bill[plan_id]", 'e', 'Order Plan');
|
||||
}
|
||||
}
|
||||
} else if (!empty(_get('extend'))) {
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
if (!$config['extend_expired']) {
|
||||
r2(U . 'home', 'e', "cannot extend");
|
||||
}
|
||||
if (!empty(App::getTokenValue(_get('stoken')))) {
|
||||
r2(U . 'home', 'e', "You already extend");
|
||||
}
|
||||
$id = _get('extend');
|
||||
$tur = ORM::for_table('tbl_user_recharges')->where('customer_id', $user['id'])->where('id', $id)->find_one();
|
||||
if ($tur) {
|
||||
$m = date("m");
|
||||
$path = $CACHE_PATH . DIRECTORY_SEPARATOR . "extends" . DIRECTORY_SEPARATOR;
|
||||
if (!file_exists($path)) {
|
||||
mkdir($path);
|
||||
}
|
||||
$path .= $user['id'] . ".txt";
|
||||
if (file_exists($path)) {
|
||||
// is already extend
|
||||
$last = file_get_contents($path);
|
||||
if ($last == $m) {
|
||||
r2(U . 'home', 'e', "You already extend for this month");
|
||||
}
|
||||
}
|
||||
if ($tur['status'] != 'on') {
|
||||
if ($tur['routers'] != 'radius') {
|
||||
$mikrotik = Mikrotik::info($tur['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$router = $tur['routers'];
|
||||
}
|
||||
$p = ORM::for_table('tbl_plans')->findOne($tur['plan_id']);
|
||||
if (!$p) {
|
||||
r2(U . 'home', '3', "Plan Not Found");
|
||||
}
|
||||
if ($tur['routers'] == 'radius') {
|
||||
Radius::customerAddPlan($user, $p, $tur['expiration'] . ' ' . $tur['time']);
|
||||
} else {
|
||||
if ($tur['type'] == 'Hotspot') {
|
||||
Mikrotik::removeHotspotUser($client, $user['username']);
|
||||
Mikrotik::addHotspotUser($client, $p, $user);
|
||||
} else if ($tur['type'] == 'PPPOE') {
|
||||
Mikrotik::removePpoeUser($client, $user['username']);
|
||||
Mikrotik::addPpoeUser($client, $p, $user);
|
||||
}
|
||||
}
|
||||
// make customer cannot extend again
|
||||
$days = $config['extend_days'];
|
||||
$expiration = date('Y-m-d', strtotime(" +$days day"));
|
||||
$tur->expiration = $expiration;
|
||||
$tur->status = "on";
|
||||
$tur->save();
|
||||
App::setToken(_get('stoken'), $id);
|
||||
file_put_contents($path, $m);
|
||||
_log("Customer $tur[customer_id] $tur[username] extend for $days days", "Customer", $user['id']);
|
||||
Message::sendTelegram("#u$user[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
|
||||
"\nLocation: " . $p['routers'] .
|
||||
"\nCustomer: " . $user['fullname'] .
|
||||
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
|
||||
r2(U . 'home', 's', "Extend until $expiration");
|
||||
} else {
|
||||
r2(U . 'home', 'e', "Plan is not expired");
|
||||
}
|
||||
} else {
|
||||
r2(U . 'home', 'e', "Plan Not Found or Not Active");
|
||||
}
|
||||
} else if (isset($_GET['deactivate']) && !empty($_GET['deactivate'])) {
|
||||
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['deactivate'])->where('username', $user['username'])->findOne();
|
||||
if ($bill) {
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $bill['plan_id'])->find_one();
|
||||
if ($p['is_radius']) {
|
||||
Radius::customerDeactivate($user['username']);
|
||||
} else {
|
||||
try {
|
||||
$mikrotik = Mikrotik::info($bill['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($bill['type'] == 'Hotspot') {
|
||||
Mikrotik::removeHotspotUser($client, $bill['username']);
|
||||
Mikrotik::removeHotspotActiveUser($client, $bill['username']);
|
||||
} else if ($bill['type'] == 'PPPOE') {
|
||||
Mikrotik::removePpoeUser($client, $bill['username']);
|
||||
Mikrotik::removePpoeActive($client, $bill['username']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
//ignore it maybe mikrotik has been deleted
|
||||
}
|
||||
}
|
||||
$bill->status = 'off';
|
||||
$bill->expiration = date('Y-m-d');
|
||||
$bill->time = date('H:i:s');
|
||||
$bill->save();
|
||||
_log('User ' . $bill['username'] . ' Deactivate ' . $bill['namebp'], 'Customer', $bill['customer_id']);
|
||||
Message::sendTelegram('User u' . $bill['username'] . ' Deactivate ' . $bill['namebp']);
|
||||
r2(U . 'home', 's', 'Success deactivate ' . $bill['namebp']);
|
||||
} else {
|
||||
r2(U . 'home', 'e', 'No Active Plan');
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
|
||||
$ui->assign('nux_mac', $_SESSION['nux-mac']);
|
||||
$ui->assign('nux_ip', $_SESSION['nux-ip']);
|
||||
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['id'])->where('username', $user['username'])->findOne();
|
||||
if ($_GET['mikrotik'] == 'login') {
|
||||
$m = Mikrotik::info($bill['routers']);
|
||||
$c = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
Mikrotik::logMeIn($c, $user['username'], $user['password'], $_SESSION['nux-ip'], $_SESSION['nux-mac']);
|
||||
r2(U . 'home', 's', Lang::T('Login Request successfully'));
|
||||
} else if ($_GET['mikrotik'] == 'logout') {
|
||||
$m = Mikrotik::info($bill['routers']);
|
||||
$c = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
Mikrotik::logMeOut($c, $user['username']);
|
||||
r2(U . 'home', 's', Lang::T('Logout Request successfully'));
|
||||
}
|
||||
}
|
||||
|
||||
$ui->assign('unpaid', ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->where('status', 1)
|
||||
->find_one());
|
||||
run_hook('view_customer_dashboard'); #HOOK
|
||||
$ui->display('user-dashboard.tpl');
|
||||
8
system/controllers/index.html
Normal file
8
system/controllers/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
||||
195
system/controllers/login.php
Normal file
195
system/controllers/login.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
$maintenance_mode = $config['maintenance_mode'];
|
||||
if ($maintenance_mode == true) {
|
||||
displayMaintenanceMessage();
|
||||
}
|
||||
|
||||
if (Admin::getID()) {
|
||||
r2(U . 'dashboard');
|
||||
}
|
||||
|
||||
if (User::getID()) {
|
||||
r2(U . 'home');
|
||||
}
|
||||
|
||||
if (isset($routes['1'])) {
|
||||
$do = $routes['1'];
|
||||
} else {
|
||||
$do = 'login-display';
|
||||
}
|
||||
|
||||
switch ($do) {
|
||||
case 'post':
|
||||
$username = _post('username');
|
||||
$password = _post('password');
|
||||
run_hook('customer_login'); #HOOK
|
||||
if ($username != '' and $password != '') {
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$d_pass = $d['password'];
|
||||
if ($d['status'] == 'Banned') {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('This account status') . ': ' . Lang::T($d['status']) . '</div>';
|
||||
}
|
||||
if (Password::_uverify($password, $d_pass) == true) {
|
||||
$_SESSION['uid'] = $d['id'];
|
||||
User::setCookie($d['id']);
|
||||
$d->last_login = date('Y-m-d H:i:s');
|
||||
$d->save();
|
||||
_log($username . ' ' . Lang::T('Login Successful'), 'User', $d['id']);
|
||||
echo '<div class="alert alert-success">' . Lang::T('Login Successful') . '</div>';
|
||||
r2(U . 'home');
|
||||
} else {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('Invalid Username or Password') . '</div>';
|
||||
_log($username . ' ' . Lang::T('Failed Login'), 'User');
|
||||
r2(U . 'login');
|
||||
}
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_users')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$d_pass = $d['password'];
|
||||
if (Password::_verify($password, $d_pass) == true) {
|
||||
$_SESSION['aid'] = $d['id'];
|
||||
$token = Admin::setCookie($d['id']);
|
||||
$d->last_login = date('Y-m-d H:i:s');
|
||||
$d->save();
|
||||
_log($username . ' ' . Lang::T('Login Successful'), $d['user_type'], $d['id']);
|
||||
echo '<div class="alert alert-success">' . Lang::T('Login Successful') . '</div>';
|
||||
r2(U . 'dashboard');
|
||||
} else {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('Invalid Username or Password') . '</div>';
|
||||
_log($username . ' ' . Lang::T('Failed Login'), $d['user_type']);
|
||||
r2(U . 'login');
|
||||
}
|
||||
} else {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('Invalid Username or Password') . '</div>';
|
||||
r2(U . 'login');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo '<div class="alert alert-danger">' . Lang::T('Invalid Username or Password') . '</div>';
|
||||
r2(U . 'login');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'activation':
|
||||
$voucher = _post('voucher');
|
||||
$username = _post('username');
|
||||
$v1 = ORM::for_table('tbl_voucher')->where('code', $voucher)->find_one();
|
||||
if ($v1) {
|
||||
// voucher exists, check customer exists or not
|
||||
$user = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if (!$user) {
|
||||
$d = ORM::for_table('tbl_customers')->create();
|
||||
$d->username = alphanumeric($username, "+_.");
|
||||
$d->password = $voucher;
|
||||
$d->fullname = '';
|
||||
$d->address = '';
|
||||
$d->email = '';
|
||||
$d->phonenumber = (strlen($username) < 21) ? $username : '';
|
||||
if ($d->save()) {
|
||||
$user = ORM::for_table('tbl_customers')->where('username', $username)->find_one($d->id());
|
||||
if (!$user) {
|
||||
r2(U . 'login', 'e', Lang::T('Voucher activation failed'));
|
||||
}
|
||||
} else {
|
||||
_alert(Lang::T('Login Successful'), 'success', "dashboard");
|
||||
r2(U . 'login', 'e', Lang::T('Voucher activation failed') . '.');
|
||||
}
|
||||
}
|
||||
if ($v1['status'] == 0) {
|
||||
$oldPass = $user['password'];
|
||||
// change customer password to voucher code
|
||||
$user->password = $voucher;
|
||||
$user->save();
|
||||
// voucher activation
|
||||
if (Package::rechargeUser($user['id'], $v1['routers'], $v1['id_plan'], "Voucher", $voucher)) {
|
||||
$v1->status = "1";
|
||||
$v1->user = $user['username'];
|
||||
$v1->save();
|
||||
$user->last_login = date('Y-m-d H:i:s');
|
||||
$user->save();
|
||||
// add customer to mikrotik
|
||||
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
|
||||
try {
|
||||
$m = Mikrotik::info($v1['routers']);
|
||||
$c = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
Mikrotik::logMeIn($c, $user['username'], $user['password'], $_SESSION['nux-ip'], $_SESSION['nux-mac']);
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, you are connected to internet"));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
} else {
|
||||
// if failed to recharge, restore old password
|
||||
$user->password = $oldPass;
|
||||
$user->save();
|
||||
r2(U . 'login', 'e', Lang::T("Failed to activate voucher"));
|
||||
}
|
||||
} else {
|
||||
// used voucher
|
||||
// check if voucher used by this username
|
||||
if ($v1['user'] == $user['username']) {
|
||||
$user->last_login = date('Y-m-d H:i:s');
|
||||
$user->save();
|
||||
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
|
||||
try {
|
||||
$m = Mikrotik::info($v1['routers']);
|
||||
$c = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
Mikrotik::logMeIn($c, $user['username'], $user['password'], $_SESSION['nux-ip'], $_SESSION['nux-mac']);
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!empty($config['voucher_redirect'])) {
|
||||
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
|
||||
} else {
|
||||
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// voucher used by other customer
|
||||
r2(U . 'login', 'e', Lang::T('Voucher Not Valid'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_msglog('e', Lang::T('Invalid Username or Password'));
|
||||
r2(U . 'login');
|
||||
}
|
||||
default:
|
||||
run_hook('customer_view_login'); #HOOK
|
||||
if ($config['disable_registration'] == 'yes') {
|
||||
$ui->display('user-login-noreg.tpl');
|
||||
} else {
|
||||
$ui->display('user-login.tpl');
|
||||
}
|
||||
break;
|
||||
}
|
||||
12
system/controllers/logout.php
Normal file
12
system/controllers/logout.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
run_hook('customer_logout'); #HOOK
|
||||
if (session_status() == PHP_SESSION_NONE) session_start();
|
||||
Admin::removeCookie();
|
||||
User::removeCookie();
|
||||
session_destroy();
|
||||
_alert(Lang::T('Logout Successful'),'warning', "login");
|
||||
160
system/controllers/logs.php
Normal file
160
system/controllers/logs.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', 'PHPNuxBill Logs');
|
||||
$ui->assign('_system_menu', 'logs');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
|
||||
switch ($action) {
|
||||
case 'list-csv':
|
||||
$logs = ORM::for_table('tbl_logs')
|
||||
->select('id')
|
||||
->select('date')
|
||||
->select('type')
|
||||
->select('description')
|
||||
->select('userid')
|
||||
->select('ip')
|
||||
->order_by_asc('id')->find_array();
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="activity-logs_' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
foreach ($logs as $log) {
|
||||
$ks = [];
|
||||
$vs = [];
|
||||
foreach ($log as $k => $v) {
|
||||
$ks[] = $k;
|
||||
$vs[] = $v;
|
||||
}
|
||||
if (!$h) {
|
||||
echo '"' . implode('";"', $ks) . "\"\n";
|
||||
$h = true;
|
||||
}
|
||||
echo '"' . implode('";"', $vs) . "\"\n";
|
||||
}
|
||||
break;
|
||||
case 'radius-csv':
|
||||
$logs = ORM::for_table('radpostauth')
|
||||
->select('id')
|
||||
->select('username')
|
||||
->select('pass')
|
||||
->select('reply')
|
||||
->select('authdate')
|
||||
->order_by_asc('id')->find_array();
|
||||
$h = false;
|
||||
set_time_limit(-1);
|
||||
header('Pragma: public');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header("Content-type: text/csv");
|
||||
header('Content-Disposition: attachment;filename="radius-logs_' . date('Y-m-d_H_i') . '.csv"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
foreach ($logs as $log) {
|
||||
$ks = [];
|
||||
$vs = [];
|
||||
foreach ($log as $k => $v) {
|
||||
$ks[] = $k;
|
||||
$vs[] = $v;
|
||||
}
|
||||
if (!$h) {
|
||||
echo '"' . implode('";"', $ks) . "\"\n";
|
||||
$h = true;
|
||||
}
|
||||
echo '"' . implode('";"', $vs) . "\"\n";
|
||||
}
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
$q = (_post('q') ? _post('q') : _get('q'));
|
||||
$keep = _post('keep');
|
||||
if (!empty($keep)) {
|
||||
ORM::raw_execute("DELETE FROM tbl_logs WHERE UNIX_TIMESTAMP(date) < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
|
||||
r2(U . "logs/list/", 's', "Delete logs older than $keep days");
|
||||
}
|
||||
if ($q != '') {
|
||||
$query = ORM::for_table('tbl_logs')->where_like('description', '%' . $q . '%')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query, ['q' => $q]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_logs')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('q', $q);
|
||||
$ui->display('logs.tpl');
|
||||
break;
|
||||
case 'radius':
|
||||
$q = (_post('q') ? _post('q') : _get('q'));
|
||||
$keep = _post('keep');
|
||||
if (!empty($keep)) {
|
||||
ORM::raw_execute("DELETE FROM radpostauth WHERE UNIX_TIMESTAMP(authdate) < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))", [], 'radius');
|
||||
r2(U . "logs/radius/", 's', "Delete logs older than $keep days");
|
||||
}
|
||||
if ($q != '') {
|
||||
$query = ORM::for_table('radpostauth', 'radius')->where_like('username', '%' . $q . '%')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query, ['q' => $q]);
|
||||
} else {
|
||||
$query = ORM::for_table('radpostauth', 'radius')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('q', $q);
|
||||
$ui->assign('_title', 'Radius Logs');
|
||||
$ui->display('logs-radius.tpl');
|
||||
break;
|
||||
|
||||
case 'cron':
|
||||
$q = (_post('q') ? _post('q') : _get('q'));
|
||||
$cron_type = (_post('cron_type') ? _post('cron_type') : _get('cron_type'));
|
||||
$status = (_post('status') ? _post('status') : _get('status'));
|
||||
|
||||
// Build query
|
||||
$query = ORM::for_table('tbl_cron_logs');
|
||||
|
||||
if (!empty($q)) {
|
||||
$query->where_like('error_message', '%' . $q . '%');
|
||||
}
|
||||
|
||||
if (!empty($cron_type)) {
|
||||
$query->where('cron_type', $cron_type);
|
||||
}
|
||||
|
||||
if (!empty($status)) {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
|
||||
$query->order_by_desc('started_at');
|
||||
$d = Paginator::findMany($query, ['q' => $q, 'cron_type' => $cron_type, 'status' => $status]);
|
||||
|
||||
// Get statistics
|
||||
$stats = CronLog::getStats(7);
|
||||
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('q', $q);
|
||||
$ui->assign('cron_type', $cron_type);
|
||||
$ui->assign('status', $status);
|
||||
$ui->assign('stats', $stats);
|
||||
$ui->assign('_title', 'Cron Job Logs');
|
||||
$ui->display('logs-cron.tpl');
|
||||
break;
|
||||
|
||||
default:
|
||||
r2(U . 'logs/list/', 's', '');
|
||||
}
|
||||
54
system/controllers/map.php
Normal file
54
system/controllers/map.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_system_menu', 'map');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (empty($action)) {
|
||||
$action = 'customer';
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'customer':
|
||||
if(!empty(_req('search'))){
|
||||
$search = _req('search');
|
||||
$query = ORM::for_table('tbl_customers')->whereRaw("coordinates != '' AND fullname LIKE '%$search%' OR username LIKE '%$search%' OR email LIKE '%$search%' OR phonenumber LIKE '%$search%'")->order_by_desc('fullname');
|
||||
$c = Paginator::findMany($query, ['search' => $search], 50);
|
||||
}else{
|
||||
$query = ORM::for_table('tbl_customers')->where_not_equal('coordinates','');
|
||||
$c = Paginator::findMany($query, ['search'=>''], 50);
|
||||
}
|
||||
$customerData = [];
|
||||
|
||||
foreach ($c as $customer) {
|
||||
if (!empty($customer->coordinates)) {
|
||||
$customerData[] = [
|
||||
'id' => $customer->id,
|
||||
'name' => $customer->fullname,
|
||||
'balance' => $customer->balance,
|
||||
'address' => $customer->address,
|
||||
'direction' => $customer->coordinates,
|
||||
'info' => Lang::T("Username") . ": " . $customer->username . " - " . Lang::T("Full Name") . ": " . $customer->fullname . " - " . Lang::T("Email") . ": " . $customer->email . " - " . Lang::T("Phone") . ": " . $customer->phonenumber . " - " . Lang::T("Service Type") . ": " . $customer->service_type,
|
||||
'coordinates' => '[' . $customer->coordinates . ']',
|
||||
];
|
||||
}
|
||||
}
|
||||
$ui->assign('search', $search);
|
||||
$ui->assign('customers', $customerData);
|
||||
$ui->assign('xheader', '<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css">');
|
||||
$ui->assign('_title', Lang::T('Customer Geo Location Information'));
|
||||
$ui->assign('xfooter', '<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>');
|
||||
$ui->display('customers-map.tpl');
|
||||
break;
|
||||
|
||||
default:
|
||||
r2(U . 'map/customer', 'e', 'action not defined');
|
||||
break;
|
||||
}
|
||||
242
system/controllers/message.php
Normal file
242
system/controllers/message.php
Normal file
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Send Message'));
|
||||
$ui->assign('_system_menu', 'message');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (empty($action)) {
|
||||
$action = 'send';
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'send':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$select2_customer = <<<EOT
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
$('#personSelect').select2({
|
||||
theme: "bootstrap",
|
||||
ajax: {
|
||||
url: function(params) {
|
||||
if(params.term != undefined){
|
||||
return './index.php?_route=autoload/customer_select2&s='+params.term;
|
||||
}else{
|
||||
return './index.php?_route=autoload/customer_select2';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
EOT;
|
||||
if (isset($routes['2']) && !empty($routes['2'])) {
|
||||
$ui->assign('cust', ORM::for_table('tbl_customers')->find_one($routes['2']));
|
||||
}
|
||||
$id = $routes['2'];
|
||||
$ui->assign('id', $id);
|
||||
$ui->assign('xfooter', $select2_customer);
|
||||
$ui->display('message.tpl');
|
||||
break;
|
||||
|
||||
case 'send-post':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
// Get form data
|
||||
$id_customer = $_POST['id_customer'];
|
||||
$message = $_POST['message'];
|
||||
$via = $_POST['via'];
|
||||
|
||||
// Check if fields are empty
|
||||
if ($id_customer == '' or $message == '' or $via == '') {
|
||||
r2(U . 'message/send', 'e', Lang::T('All field is required'));
|
||||
} 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
|
||||
$message = str_replace('[[name]]', $c['fullname'], $message);
|
||||
$message = str_replace('[[user_name]]', $c['username'], $message);
|
||||
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
|
||||
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
|
||||
|
||||
// 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(U . 'message/send', 's', Lang::T('Message Sent Successfully'));
|
||||
} else {
|
||||
r2(U . 'message/send', 'e', Lang::T('Failed to send message'));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'send_bulk':
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
// Get form data
|
||||
$group = $_POST['group'];
|
||||
$message = $_POST['message'];
|
||||
$via = $_POST['via'];
|
||||
$test = isset($_POST['test']) && $_POST['test'] === 'on' ? 'yes' : 'no';
|
||||
$batch = $_POST['batch'];
|
||||
$delay = $_POST['delay'];
|
||||
|
||||
// Initialize counters
|
||||
$totalSMSSent = 0;
|
||||
$totalSMSFailed = 0;
|
||||
$totalWhatsappSent = 0;
|
||||
$totalWhatsappFailed = 0;
|
||||
$batchStatus = [];
|
||||
|
||||
if (_req('send') == 'now') {
|
||||
// Check if fields are empty
|
||||
if ($group == '' || $message == '' || $via == '') {
|
||||
r2(U . 'message/send_bulk', 'e', Lang::T('All fields are required'));
|
||||
} else {
|
||||
// Get customer details from the database based on the selected group
|
||||
if ($group == 'all') {
|
||||
$customers = ORM::for_table('tbl_customers')->find_many()->as_array();
|
||||
} elseif ($group == 'new') {
|
||||
// Get customers created just a month ago
|
||||
$customers = ORM::for_table('tbl_customers')->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)")->find_many()->as_array();
|
||||
} elseif ($group == 'expired') {
|
||||
// Get expired user recharges where status is 'off'
|
||||
$expired = ORM::for_table('tbl_user_recharges')->where('status', 'off')->find_many();
|
||||
$customer_ids = [];
|
||||
foreach ($expired as $recharge) {
|
||||
$customer_ids[] = $recharge->customer_id;
|
||||
}
|
||||
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_many()->as_array();
|
||||
} elseif ($group == 'active') {
|
||||
// Get active user recharges where status is 'on'
|
||||
$active = ORM::for_table('tbl_user_recharges')->where('status', 'on')->find_many();
|
||||
$customer_ids = [];
|
||||
foreach ($active as $recharge) {
|
||||
$customer_ids[] = $recharge->customer_id;
|
||||
}
|
||||
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_many()->as_array();
|
||||
} elseif ($group == 'pppoe') {
|
||||
// Get customers with PPPoE service type
|
||||
$customers = ORM::for_table('tbl_customers')->where('service_type', 'PPPoE')->find_many()->as_array();
|
||||
} elseif ($group == 'hotspot') {
|
||||
// Get customers with Hotspot service type
|
||||
$customers = ORM::for_table('tbl_customers')->where('service_type', 'Hotspot')->find_many()->as_array();
|
||||
}
|
||||
|
||||
// Set the batch size
|
||||
$batchSize = $batch;
|
||||
|
||||
// Calculate the number of batches
|
||||
$totalCustomers = count($customers);
|
||||
$totalBatches = ceil($totalCustomers / $batchSize);
|
||||
|
||||
// Loop through batches
|
||||
for ($batchIndex = 0; $batchIndex < $totalBatches; $batchIndex++) {
|
||||
// Get the starting and ending index for the current batch
|
||||
$start = $batchIndex * $batchSize;
|
||||
$end = min(($batchIndex + 1) * $batchSize, $totalCustomers);
|
||||
$batchCustomers = array_slice($customers, $start, $end - $start);
|
||||
|
||||
// Loop through customers in the current batch and send messages
|
||||
foreach ($batchCustomers as $customer) {
|
||||
// Create a copy of the original message for each customer and save it as currentMessage
|
||||
$currentMessage = $message;
|
||||
$currentMessage = str_replace('[[name]]', $customer['fullname'], $currentMessage);
|
||||
$currentMessage = str_replace('[[user_name]]', $customer['username'], $currentMessage);
|
||||
$currentMessage = str_replace('[[phone]]', $customer['phonenumber'], $currentMessage);
|
||||
$currentMessage = str_replace('[[company_name]]', $config['CompanyName'], $currentMessage);
|
||||
|
||||
// Send the message based on the selected method
|
||||
if ($test === 'yes') {
|
||||
// Only for testing, do not send messages to customers
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'Test Mode - Message not sent'
|
||||
];
|
||||
} else {
|
||||
// Send the actual messages
|
||||
if ($via == 'sms' || $via == 'both') {
|
||||
$smsSent = Message::sendSMS($customer['phonenumber'], $currentMessage);
|
||||
if ($smsSent) {
|
||||
$totalSMSSent++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'SMS Message Sent'
|
||||
];
|
||||
} else {
|
||||
$totalSMSFailed++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'SMS Message Failed'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($via == 'wa' || $via == 'both') {
|
||||
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentMessage);
|
||||
if ($waSent) {
|
||||
$totalWhatsappSent++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'WhatsApp Message Sent'
|
||||
];
|
||||
} else {
|
||||
$totalWhatsappFailed++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'WhatsApp Message Failed'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Introduce a delay between each batch
|
||||
if ($batchIndex < $totalBatches - 1) {
|
||||
sleep($delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$ui->assign('batchStatus', $batchStatus);
|
||||
$ui->assign('totalSMSSent', $totalSMSSent);
|
||||
$ui->assign('totalSMSFailed', $totalSMSFailed);
|
||||
$ui->assign('totalWhatsappSent', $totalWhatsappSent);
|
||||
$ui->assign('totalWhatsappFailed', $totalWhatsappFailed);
|
||||
$ui->display('message-bulk.tpl');
|
||||
break;
|
||||
|
||||
default:
|
||||
r2(U . 'message/send_sms', 'e', 'action not defined');
|
||||
}
|
||||
22
system/controllers/messages.php
Normal file
22
system/controllers/messages.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Messaging System
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Messages'));
|
||||
$ui->assign('_system_menu', 'messages');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
// Fetch all messages
|
||||
$msgs = ORM::for_table('tbl_message')
|
||||
->order_by_desc('date')
|
||||
->find_array();
|
||||
|
||||
$ui->assign('messages', $msgs);
|
||||
$ui->display('message-list.tpl');
|
||||
|
||||
?>
|
||||
212
system/controllers/monitoring.php
Normal file
212
system/controllers/monitoring.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
/**
|
||||
* Router Monitoring API Controller
|
||||
* Provides monitoring data for dashboard
|
||||
*/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Router Monitoring'));
|
||||
$ui->assign('_system_menu', 'monitoring');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'dashboard':
|
||||
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/monitoring.js"></script>');
|
||||
$ui->display('monitoring.tpl');
|
||||
break;
|
||||
|
||||
case 'get_router_status':
|
||||
$routerId = $_GET['router_id'] ?? null;
|
||||
$hours = $_GET['hours'] ?? 24;
|
||||
|
||||
if ($routerId) {
|
||||
$data = getRouterMonitoringData($routerId, $hours);
|
||||
} else {
|
||||
$data = getAllRoutersMonitoringData($hours);
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
break;
|
||||
|
||||
case 'get_router_history':
|
||||
$routerId = $_GET['router_id'] ?? null;
|
||||
$days = $_GET['days'] ?? 7;
|
||||
|
||||
if (!$routerId) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Router ID required']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = getRouterHistoryData($routerId, $days);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
break;
|
||||
|
||||
case 'get_alerts':
|
||||
$hours = $_GET['hours'] ?? 24;
|
||||
$data = getRecentAlerts($hours);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
break;
|
||||
|
||||
default:
|
||||
r2(U . 'monitoring/dashboard/', 's', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get monitoring data for specific router
|
||||
*/
|
||||
function getRouterMonitoringData($routerId, $hours = 24) {
|
||||
$cutoffTime = date('Y-m-d H:i:s', strtotime("-$hours hours"));
|
||||
|
||||
$router = ORM::for_table('tbl_routers')->find_one($routerId);
|
||||
if (!$router) {
|
||||
return ['error' => 'Router not found'];
|
||||
}
|
||||
|
||||
$monitoringData = ORM::for_table('tbl_router_monitoring')
|
||||
->where('router_id', $routerId)
|
||||
->where_gte('timestamp', $cutoffTime)
|
||||
->order_by_desc('timestamp')
|
||||
->find_many();
|
||||
|
||||
$data = [
|
||||
'router' => [
|
||||
'id' => $router['id'],
|
||||
'name' => $router['name'],
|
||||
'ip_address' => $router['ip_address'],
|
||||
'status' => $router['status'],
|
||||
'last_check' => $router['last_check'],
|
||||
'last_error' => $router['last_error']
|
||||
],
|
||||
'monitoring' => []
|
||||
];
|
||||
|
||||
foreach ($monitoringData as $record) {
|
||||
$data['monitoring'][] = [
|
||||
'timestamp' => $record['timestamp'],
|
||||
'ping_status' => (bool)$record['ping_status'],
|
||||
'api_status' => (bool)$record['api_status'],
|
||||
'uptime' => $record['uptime'],
|
||||
'free_memory' => (int)$record['free_memory'],
|
||||
'total_memory' => (int)$record['total_memory'],
|
||||
'cpu_load' => (int)$record['cpu_load'],
|
||||
'temperature' => $record['temperature'],
|
||||
'voltage' => $record['voltage'],
|
||||
'error' => $record['error']
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get monitoring data for all routers
|
||||
*/
|
||||
function getAllRoutersMonitoringData($hours = 24) {
|
||||
$cutoffTime = date('Y-m-d H:i:s', strtotime("-$hours hours"));
|
||||
|
||||
$routers = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
|
||||
$data = [];
|
||||
|
||||
foreach ($routers as $router) {
|
||||
$latestData = ORM::for_table('tbl_router_monitoring')
|
||||
->where('router_id', $router['id'])
|
||||
->where_gte('timestamp', $cutoffTime)
|
||||
->order_by_desc('timestamp')
|
||||
->find_one();
|
||||
|
||||
$data[] = [
|
||||
'router' => [
|
||||
'id' => $router['id'],
|
||||
'name' => $router['name'],
|
||||
'ip_address' => $router['ip_address'],
|
||||
'status' => $router['status'],
|
||||
'last_check' => $router['last_check'],
|
||||
'last_error' => $router['last_error']
|
||||
],
|
||||
'latest_data' => $latestData ? [
|
||||
'timestamp' => $latestData['timestamp'],
|
||||
'ping_status' => (bool)$latestData['ping_status'],
|
||||
'api_status' => (bool)$latestData['api_status'],
|
||||
'uptime' => $latestData['uptime'],
|
||||
'free_memory' => (int)$latestData['free_memory'],
|
||||
'total_memory' => (int)$latestData['total_memory'],
|
||||
'cpu_load' => (int)$latestData['cpu_load'],
|
||||
'temperature' => $latestData['temperature'],
|
||||
'voltage' => $latestData['voltage'],
|
||||
'error' => $latestData['error']
|
||||
] : null
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get historical data for router
|
||||
*/
|
||||
function getRouterHistoryData($routerId, $days = 7) {
|
||||
$cutoffTime = date('Y-m-d H:i:s', strtotime("-$days days"));
|
||||
|
||||
$data = ORM::for_table('tbl_router_monitoring')
|
||||
->where('router_id', $routerId)
|
||||
->where_gte('timestamp', $cutoffTime)
|
||||
->order_by_asc('timestamp')
|
||||
->find_many();
|
||||
|
||||
$result = [];
|
||||
foreach ($data as $record) {
|
||||
$result[] = [
|
||||
'timestamp' => $record['timestamp'],
|
||||
'ping_status' => (bool)$record['ping_status'],
|
||||
'api_status' => (bool)$record['api_status'],
|
||||
'cpu_load' => (int)$record['cpu_load'],
|
||||
'memory_usage' => $record['total_memory'] ?
|
||||
round((($record['total_memory'] - $record['free_memory']) / $record['total_memory']) * 100, 1) : 0,
|
||||
'temperature' => $record['temperature'],
|
||||
'voltage' => $record['voltage']
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent alerts
|
||||
*/
|
||||
function getRecentAlerts($hours = 24) {
|
||||
$cutoffTime = date('Y-m-d H:i:s', strtotime("-$hours hours"));
|
||||
|
||||
$alerts = ORM::for_table('tbl_router_monitoring')
|
||||
->where_gte('timestamp', $cutoffTime)
|
||||
->where_not_null('error')
|
||||
->order_by_desc('timestamp')
|
||||
->find_many();
|
||||
|
||||
$result = [];
|
||||
foreach ($alerts as $alert) {
|
||||
$router = ORM::for_table('tbl_routers')->find_one($alert['router_id']);
|
||||
$result[] = [
|
||||
'timestamp' => $alert['timestamp'],
|
||||
'router_name' => $router ? $router['name'] : 'Unknown',
|
||||
'error' => $alert['error'],
|
||||
'cpu_load' => $alert['cpu_load'],
|
||||
'temperature' => $alert['temperature']
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
?>
|
||||
130
system/controllers/onlinehotspot.php
Normal file
130
system/controllers/onlinehotspot.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('online'));
|
||||
$ui->assign('_system_menu', 'onlineusers');
|
||||
$ui->assign('onlineusers', $online);
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
|
||||
function handle_action($action)
|
||||
{
|
||||
switch ($action) {
|
||||
case 'get_hotspot_online_users':
|
||||
mikrotik_get_hotspot_online_users();
|
||||
break;
|
||||
case 'disconnect_online_user':
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
mikrotik_disconnect_online_user($_POST['router'], $_POST['username'], $_POST['userType']);
|
||||
}
|
||||
break;
|
||||
case 'list':
|
||||
// Assuming you have a function to fetch the data for the online hotspot users list
|
||||
$onlineHotspotUsers = fetch_online_hotspot_users();
|
||||
$ui->assign('onlineHotspotUsers', $onlineHotspotUsers);
|
||||
$ui->display('onlinehotspot.tpl');
|
||||
break;
|
||||
default:
|
||||
// Handle default case, maybe return an error or redirect
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function mikrotik_get_hotspot_online_users()
|
||||
{
|
||||
global $routes;
|
||||
$router = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$hotspotActive = $client->sendSync(new RouterOS\Request('/ip/hotspot/active/print'));
|
||||
$hotspotList = [];
|
||||
|
||||
foreach ($hotspotActive as $hotspot) {
|
||||
$username = $hotspot->getProperty('user');
|
||||
$address = $hotspot->getProperty('address');
|
||||
$uptime = $hotspot->getProperty('uptime');
|
||||
$server = $hotspot->getProperty('server');
|
||||
$mac = $hotspot->getProperty('mac-address');
|
||||
$sessionTime = $hotspot->getProperty('session-time-left');
|
||||
$rxBytes = $hotspot->getProperty('bytes-in');
|
||||
$txBytes = $hotspot->getProperty('bytes-out');
|
||||
$hotspotList[] = [
|
||||
'username' => $username,
|
||||
'address' => $address,
|
||||
'uptime' => $uptime,
|
||||
'server' => $server,
|
||||
'mac' => $mac,
|
||||
'session_time' => $sessionTime,
|
||||
'rx_bytes' => mikrotik_formatBytes($rxBytes),
|
||||
'tx_bytes' => mikrotik_formatBytes($txBytes),
|
||||
'total' => mikrotik_formatBytes($txBytes + $rxBytes),
|
||||
];
|
||||
}
|
||||
|
||||
// Return the Hotspot online user list as JSON
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($hotspotList);
|
||||
}
|
||||
|
||||
function mikrotik_disconnect_online_user($router, $username, $userType)
|
||||
{
|
||||
// Check if the form was submitted
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Retrieve the form data
|
||||
$router = $_POST['router'];
|
||||
$username = $_POST['username'];
|
||||
$userType = $_POST['userType'];
|
||||
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
if (!$mikrotik) {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($userType == 'hotspot') {
|
||||
Mikrotik::removeHotspotActiveUser($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} elseif ($userType == 'pppoe') {
|
||||
Mikrotik::removePpoeActive($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} else {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Handle the error response or redirection
|
||||
} finally {
|
||||
// Disconnect from the MikroTik router
|
||||
if (isset($client)) {
|
||||
$client->disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to format bytes
|
||||
function mikrotik_formatBytes($bytes)
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$factor = floor((strlen($bytes) - 1) / 3);
|
||||
return sprintf("%.2f %s", $bytes / pow(1024, $factor), @$units[$factor]);
|
||||
}
|
||||
|
||||
// Call the main function with the action provided in the URL
|
||||
$action = $routes['1'] ?? ''; // Assuming $routes is defined elsewhere
|
||||
handle_action($action);
|
||||
|
||||
$ui->assign('onlineusers', $online);
|
||||
|
||||
$ui->display('onlinehotspot.tpl');
|
||||
?>
|
||||
347
system/controllers/onlineusers.php
Normal file
347
system/controllers/onlineusers.php
Normal file
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
// Include necessary files and functions here
|
||||
// ...
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Online Users'));
|
||||
$ui->assign('_system_menu', 'onlineusers');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
|
||||
require_once 'system/autoload/PEAR2/Autoload.php';
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
// Handle cases for hotspot users and PPP users
|
||||
switch ($action) {
|
||||
case 'hotspot':
|
||||
$ui->display('hotspot_users.tpl');
|
||||
break;
|
||||
case 'hotspot_users':
|
||||
$hotspotUsers = mikrotik_get_hotspot_online_users();
|
||||
|
||||
// Filter out entries where all values are null
|
||||
$filteredHotspotUsers = array_filter($hotspotUsers, function($user) {
|
||||
// Check if all specified fields are null
|
||||
return !(
|
||||
is_null($user['username']) &&
|
||||
is_null($user['address']) &&
|
||||
is_null($user['uptime']) &&
|
||||
is_null($user['server']) &&
|
||||
is_null($user['mac']) &&
|
||||
is_null($user['session_time']) &&
|
||||
$user['rx_bytes'] === '0 B' &&
|
||||
$user['tx_bytes'] === '0 B' &&
|
||||
$user['total'] === '0 B'
|
||||
);
|
||||
});
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($filteredHotspotUsers);
|
||||
exit;
|
||||
break;
|
||||
|
||||
case 'pppoe':
|
||||
$ui->display('ppp_users.tpl');
|
||||
break;
|
||||
|
||||
case 'ppp_users':
|
||||
$pppUsers = mikrotik_get_ppp_online_users();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($pppUsers);
|
||||
exit;
|
||||
break;
|
||||
|
||||
case 'disconnect':
|
||||
$routerId = $routes['2'];
|
||||
$username = $routes['3'];
|
||||
$userType = $routes['4'];
|
||||
mikrotik_disconnect_online_user($routerId, $username, $userType);
|
||||
// Redirect or handle the response as needed
|
||||
break;
|
||||
|
||||
case 'summary':
|
||||
// Fetch summary of online users and total bytes used
|
||||
$summary = mikrotik_get_online_users_summary();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($summary);
|
||||
exit;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Handle default case or invalid action
|
||||
break;
|
||||
}
|
||||
|
||||
// Function to round the value and append the appropriate unit
|
||||
function mikrotik_formatBytes($bytes, $precision = 2)
|
||||
{
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= pow(1024, $pow);
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
function filter_null_users($users) {
|
||||
return array_filter($users, function($user) {
|
||||
return array_reduce($user, function($carry, $value) {
|
||||
return $carry || $value !== null;
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
function mikrotik_get_hotspot_online_users()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$hotspotActive = $client->sendSync(new RouterOS\Request('/ip/hotspot/active/print'));
|
||||
|
||||
$hotspotList = [];
|
||||
foreach ($hotspotActive as $hotspot) {
|
||||
$username = $hotspot->getProperty('user');
|
||||
$address = $hotspot->getProperty('address');
|
||||
$uptime = $hotspot->getProperty('uptime');
|
||||
$server = $hotspot->getProperty('server');
|
||||
$mac = $hotspot->getProperty('mac-address');
|
||||
$sessionTime = $hotspot->getProperty('session-time-left');
|
||||
$rxBytes = $hotspot->getProperty('bytes-in');
|
||||
$txBytes = $hotspot->getProperty('bytes-out');
|
||||
|
||||
$hotspotList[] = [
|
||||
'username' => $username,
|
||||
'address' => $address,
|
||||
'uptime' => $uptime,
|
||||
'server' => $server,
|
||||
'mac' => $mac,
|
||||
'session_time' => $sessionTime,
|
||||
'rx_bytes' => mikrotik_formatBytes($rxBytes),
|
||||
'tx_bytes' => mikrotik_formatBytes($txBytes),
|
||||
'total' => mikrotik_formatBytes($rxBytes + $txBytes),
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out users with all null properties
|
||||
$filteredHotspotList = filter_null_users($hotspotList);
|
||||
|
||||
// Return an empty array if no users are left after filtering
|
||||
return empty($filteredHotspotList) ? [] : $filteredHotspotList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function mikrotik_get_ppp_online_users()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$pppUsers = $client->sendSync(new RouterOS\Request('/ppp/active/print'));
|
||||
|
||||
$userList = [];
|
||||
foreach ($pppUsers as $pppUser) {
|
||||
$username = $pppUser->getProperty('name');
|
||||
$address = $pppUser->getProperty('address');
|
||||
$uptime = $pppUser->getProperty('uptime');
|
||||
$service = $pppUser->getProperty('service');
|
||||
$callerid = $pppUser->getProperty('caller-id');
|
||||
$bytes_in = $pppUser->getProperty('limit-bytes-in');
|
||||
$bytes_out = $pppUser->getProperty('limit-bytes-out');
|
||||
|
||||
$userList[] = [
|
||||
'username' => $username,
|
||||
'address' => $address,
|
||||
'uptime' => $uptime,
|
||||
'service' => $service,
|
||||
'caller_id' => $callerid,
|
||||
'bytes_in' => $bytes_in,
|
||||
'bytes_out' => $bytes_out,
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out users with all null properties
|
||||
return filter_null_users($userList);
|
||||
}
|
||||
|
||||
function save_data_usage($username, $bytes_in, $bytes_out, $connection_type) {
|
||||
if (!$username) {
|
||||
error_log("Error: Missing username in save_data_usage()");
|
||||
return;
|
||||
}
|
||||
|
||||
$currentTime = date('Y-m-d H:i:s');
|
||||
$currentDate = date('Y-m-d');
|
||||
|
||||
// Check if there's an existing record for this user today
|
||||
$existingRecord = ORM::for_table('tbl_user_data_usage')
|
||||
->where('username', $username)
|
||||
->where('connection_type', $connection_type)
|
||||
->where_raw('DATE(timestamp) = ?', [$currentDate])
|
||||
->find_one();
|
||||
|
||||
if ($existingRecord) {
|
||||
// Update existing record for today
|
||||
$existingRecord->bytes_in = ($bytes_in ?: 0);
|
||||
$existingRecord->bytes_out = ($bytes_out ?: 0);
|
||||
$existingRecord->last_updated = $currentTime;
|
||||
$existingRecord->save();
|
||||
} else {
|
||||
// Create new record for today
|
||||
$newRecord = ORM::for_table('tbl_user_data_usage')->create();
|
||||
$newRecord->username = $username;
|
||||
$newRecord->bytes_in = ($bytes_in ?: 0);
|
||||
$newRecord->bytes_out = ($bytes_out ?: 0);
|
||||
$newRecord->connection_type = $connection_type;
|
||||
$newRecord->timestamp = $currentTime;
|
||||
$newRecord->last_updated = $currentTime;
|
||||
$newRecord->save();
|
||||
}
|
||||
}
|
||||
|
||||
function mikrotik_get_online_users_summary()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
// Get Hotspot users
|
||||
$hotspotActive = $client->sendSync(new RouterOS\Request('/ip/hotspot/active/print'));
|
||||
$hotspotList = [];
|
||||
$totalHotspotUsage = 0;
|
||||
foreach ($hotspotActive as $hotspot) {
|
||||
$rxBytes = $hotspot->getProperty('bytes-in');
|
||||
$txBytes = $hotspot->getProperty('bytes-out');
|
||||
$totalHotspotUsage += $rxBytes + $txBytes;
|
||||
$username = $hotspot->getProperty('user');
|
||||
save_data_usage($username, $rxBytes, $txBytes, 'hotspot');
|
||||
|
||||
$hotspotList[] = [
|
||||
'username' => $username,
|
||||
'address' => $hotspot->getProperty('address'),
|
||||
'uptime' => $hotspot->getProperty('uptime'),
|
||||
'server' => $hotspot->getProperty('server'),
|
||||
'mac' => $hotspot->getProperty('mac-address'),
|
||||
'session_time' => $hotspot->getProperty('session-time-left'),
|
||||
'rx_bytes' => mikrotik_formatBytes($rxBytes),
|
||||
'tx_bytes' => mikrotik_formatBytes($txBytes),
|
||||
'total' => mikrotik_formatBytes($rxBytes + $txBytes),
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out null hotspot users
|
||||
$hotspotList = array_filter($hotspotList, function($user) {
|
||||
return !(
|
||||
is_null($user['username']) &&
|
||||
is_null($user['address']) &&
|
||||
is_null($user['uptime']) &&
|
||||
is_null($user['server']) &&
|
||||
is_null($user['mac']) &&
|
||||
is_null($user['session_time']) &&
|
||||
$user['rx_bytes'] === '0 B' &&
|
||||
$user['tx_bytes'] === '0 B' &&
|
||||
$user['total'] === '0 B'
|
||||
);
|
||||
});
|
||||
|
||||
// Get PPPoE users
|
||||
$pppUsers = $client->sendSync(new RouterOS\Request('/ppp/active/print'));
|
||||
$pppoeList = [];
|
||||
$totalPPPoEUsage = 0;
|
||||
foreach ($pppUsers as $pppUser) {
|
||||
$bytes_in = $pppUser->getProperty('limit-bytes-in');
|
||||
$bytes_out = $pppUser->getProperty('limit-bytes-out');
|
||||
$totalPPPoEUsage += $bytes_in + $bytes_out;
|
||||
$username = $pppUser->getProperty('name');
|
||||
save_data_usage($username, $bytes_in, $bytes_out, 'pppoe');
|
||||
|
||||
$pppoeList[] = [
|
||||
'username' => $username,
|
||||
'address' => $pppUser->getProperty('address'),
|
||||
'uptime' => $pppUser->getProperty('uptime'),
|
||||
'service' => $pppUser->getProperty('service'),
|
||||
'caller_id' => $pppUser->getProperty('caller-id'),
|
||||
'bytes_in' => mikrotik_formatBytes($bytes_in),
|
||||
'bytes_out' => mikrotik_formatBytes($bytes_out),
|
||||
'total' => mikrotik_formatBytes($bytes_in + $bytes_out),
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out null PPPoE users
|
||||
$pppoeList = array_filter($pppoeList, function($user) {
|
||||
return !(
|
||||
is_null($user['username']) &&
|
||||
is_null($user['address']) &&
|
||||
is_null($user['uptime']) &&
|
||||
is_null($user['service']) &&
|
||||
is_null($user['caller_id']) &&
|
||||
$user['bytes_in'] === '0 B' &&
|
||||
$user['bytes_out'] === '0 B' &&
|
||||
$user['total'] === '0 B'
|
||||
);
|
||||
});
|
||||
// Calculate total data usage
|
||||
$totalDataUsage = $totalHotspotUsage + $totalPPPoEUsage;
|
||||
|
||||
// Calculate total users
|
||||
$totalHotspotUsers = count($hotspotList);
|
||||
$totalPPPoEUsers = count($pppoeList);
|
||||
$totalUsers = $totalHotspotUsers + $totalPPPoEUsers;
|
||||
|
||||
return [
|
||||
'hotspot_users' => $totalHotspotUsers,
|
||||
'ppp_users' => $totalPPPoEUsers,
|
||||
'total_users' => $totalUsers,
|
||||
'total_bytes' => mikrotik_formatBytes($totalDataUsage),
|
||||
];
|
||||
}
|
||||
|
||||
function mikrotik_disconnect_online_user($router, $username, $userType)
|
||||
{
|
||||
// Check if the form was submitted
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Retrieve the form data
|
||||
$router = $_POST['router'];
|
||||
$username = $_POST['username'];
|
||||
$userType = $_POST['userType'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
if (!$mikrotik) {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($userType == 'hotspot') {
|
||||
Mikrotik::removeHotspotActiveUser($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} elseif ($userType == 'pppoe') {
|
||||
Mikrotik::removePpoeActive($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} else {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Handle the error response or redirection
|
||||
} finally {
|
||||
// Disconnect from the MikroTik router
|
||||
if (isset($client)) {
|
||||
$client->disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
347
system/controllers/onlineusers_backup.php
Normal file
347
system/controllers/onlineusers_backup.php
Normal file
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
// Include necessary files and functions here
|
||||
// ...
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Online Users'));
|
||||
$ui->assign('_system_menu', 'onlineusers');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
|
||||
require_once 'system/autoload/PEAR2/Autoload.php';
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
// Handle cases for hotspot users and PPP users
|
||||
switch ($action) {
|
||||
case 'hotspot':
|
||||
$ui->display('hotspot_users.tpl');
|
||||
break;
|
||||
case 'hotspot_users':
|
||||
$hotspotUsers = mikrotik_get_hotspot_online_users();
|
||||
|
||||
// Filter out entries where all values are null
|
||||
$filteredHotspotUsers = array_filter($hotspotUsers, function($user) {
|
||||
// Check if all specified fields are null
|
||||
return !(
|
||||
is_null($user['username']) &&
|
||||
is_null($user['address']) &&
|
||||
is_null($user['uptime']) &&
|
||||
is_null($user['server']) &&
|
||||
is_null($user['mac']) &&
|
||||
is_null($user['session_time']) &&
|
||||
$user['rx_bytes'] === '0 B' &&
|
||||
$user['tx_bytes'] === '0 B' &&
|
||||
$user['total'] === '0 B'
|
||||
);
|
||||
});
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($filteredHotspotUsers);
|
||||
exit;
|
||||
break;
|
||||
|
||||
case 'pppoe':
|
||||
$ui->display('ppp_users.tpl');
|
||||
break;
|
||||
|
||||
case 'ppp_users':
|
||||
$pppUsers = mikrotik_get_ppp_online_users();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($pppUsers);
|
||||
exit;
|
||||
break;
|
||||
|
||||
case 'disconnect':
|
||||
$routerId = $routes['2'];
|
||||
$username = $routes['3'];
|
||||
$userType = $routes['4'];
|
||||
mikrotik_disconnect_online_user($routerId, $username, $userType);
|
||||
// Redirect or handle the response as needed
|
||||
break;
|
||||
|
||||
case 'summary':
|
||||
// Fetch summary of online users and total bytes used
|
||||
$summary = mikrotik_get_online_users_summary();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($summary);
|
||||
exit;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Handle default case or invalid action
|
||||
break;
|
||||
}
|
||||
|
||||
// Function to round the value and append the appropriate unit
|
||||
function mikrotik_formatBytes($bytes, $precision = 2)
|
||||
{
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= pow(1024, $pow);
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
function filter_null_users($users) {
|
||||
return array_filter($users, function($user) {
|
||||
return array_reduce($user, function($carry, $value) {
|
||||
return $carry || $value !== null;
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
function mikrotik_get_hotspot_online_users()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$hotspotActive = $client->sendSync(new RouterOS\Request('/ip/hotspot/active/print'));
|
||||
|
||||
$hotspotList = [];
|
||||
foreach ($hotspotActive as $hotspot) {
|
||||
$username = $hotspot->getProperty('user');
|
||||
$address = $hotspot->getProperty('address');
|
||||
$uptime = $hotspot->getProperty('uptime');
|
||||
$server = $hotspot->getProperty('server');
|
||||
$mac = $hotspot->getProperty('mac-address');
|
||||
$sessionTime = $hotspot->getProperty('session-time-left');
|
||||
$rxBytes = $hotspot->getProperty('bytes-in');
|
||||
$txBytes = $hotspot->getProperty('bytes-out');
|
||||
|
||||
$hotspotList[] = [
|
||||
'username' => $username,
|
||||
'address' => $address,
|
||||
'uptime' => $uptime,
|
||||
'server' => $server,
|
||||
'mac' => $mac,
|
||||
'session_time' => $sessionTime,
|
||||
'rx_bytes' => mikrotik_formatBytes($rxBytes),
|
||||
'tx_bytes' => mikrotik_formatBytes($txBytes),
|
||||
'total' => mikrotik_formatBytes($rxBytes + $txBytes),
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out users with all null properties
|
||||
$filteredHotspotList = filter_null_users($hotspotList);
|
||||
|
||||
// Return an empty array if no users are left after filtering
|
||||
return empty($filteredHotspotList) ? [] : $filteredHotspotList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function mikrotik_get_ppp_online_users()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$pppUsers = $client->sendSync(new RouterOS\Request('/ppp/active/print'));
|
||||
|
||||
$userList = [];
|
||||
foreach ($pppUsers as $pppUser) {
|
||||
$username = $pppUser->getProperty('name');
|
||||
$address = $pppUser->getProperty('address');
|
||||
$uptime = $pppUser->getProperty('uptime');
|
||||
$service = $pppUser->getProperty('service');
|
||||
$callerid = $pppUser->getProperty('caller-id');
|
||||
$bytes_in = $pppUser->getProperty('limit-bytes-in');
|
||||
$bytes_out = $pppUser->getProperty('limit-bytes-out');
|
||||
|
||||
$userList[] = [
|
||||
'username' => $username,
|
||||
'address' => $address,
|
||||
'uptime' => $uptime,
|
||||
'service' => $service,
|
||||
'caller_id' => $callerid,
|
||||
'bytes_in' => $bytes_in,
|
||||
'bytes_out' => $bytes_out,
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out users with all null properties
|
||||
return filter_null_users($userList);
|
||||
}
|
||||
|
||||
function save_data_usage($username, $bytes_in, $bytes_out, $connection_type) {
|
||||
if (!$username) {
|
||||
error_log("Error: Missing username in save_data_usage()");
|
||||
return;
|
||||
}
|
||||
|
||||
$currentTime = date('Y-m-d H:i:s');
|
||||
$currentDate = date('Y-m-d');
|
||||
|
||||
// Check if there's an existing record for this user today
|
||||
$existingRecord = ORM::for_table('tbl_user_data_usage')
|
||||
->where('username', $username)
|
||||
->where('connection_type', $connection_type)
|
||||
->where_raw('DATE(timestamp) = ?', [$currentDate])
|
||||
->find_one();
|
||||
|
||||
if ($existingRecord) {
|
||||
// Update existing record for today
|
||||
$existingRecord->bytes_in = ($bytes_in ?: 0);
|
||||
$existingRecord->bytes_out = ($bytes_out ?: 0);
|
||||
$existingRecord->last_updated = $currentTime;
|
||||
$existingRecord->save();
|
||||
} else {
|
||||
// Create new record for today
|
||||
$newRecord = ORM::for_table('tbl_user_data_usage')->create();
|
||||
$newRecord->username = $username;
|
||||
$newRecord->bytes_in = ($bytes_in ?: 0);
|
||||
$newRecord->bytes_out = ($bytes_out ?: 0);
|
||||
$newRecord->connection_type = $connection_type;
|
||||
$newRecord->timestamp = $currentTime;
|
||||
$newRecord->last_updated = $currentTime;
|
||||
$newRecord->save();
|
||||
}
|
||||
}
|
||||
|
||||
function mikrotik_get_online_users_summary()
|
||||
{
|
||||
global $routes;
|
||||
$routerId = $routes['2'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
// Get Hotspot users
|
||||
$hotspotActive = $client->sendSync(new RouterOS\Request('/ip/hotspot/active/print'));
|
||||
$hotspotList = [];
|
||||
$totalHotspotUsage = 0;
|
||||
foreach ($hotspotActive as $hotspot) {
|
||||
$rxBytes = $hotspot->getProperty('bytes-in');
|
||||
$txBytes = $hotspot->getProperty('bytes-out');
|
||||
$totalHotspotUsage += $rxBytes + $txBytes;
|
||||
$username = $hotspot->getProperty('user');
|
||||
save_data_usage($username, $rxBytes, $txBytes, 'hotspot');
|
||||
|
||||
$hotspotList[] = [
|
||||
'username' => $username,
|
||||
'address' => $hotspot->getProperty('address'),
|
||||
'uptime' => $hotspot->getProperty('uptime'),
|
||||
'server' => $hotspot->getProperty('server'),
|
||||
'mac' => $hotspot->getProperty('mac-address'),
|
||||
'session_time' => $hotspot->getProperty('session-time-left'),
|
||||
'rx_bytes' => mikrotik_formatBytes($rxBytes),
|
||||
'tx_bytes' => mikrotik_formatBytes($txBytes),
|
||||
'total' => mikrotik_formatBytes($rxBytes + $txBytes),
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out null hotspot users
|
||||
$hotspotList = array_filter($hotspotList, function($user) {
|
||||
return !(
|
||||
is_null($user['username']) &&
|
||||
is_null($user['address']) &&
|
||||
is_null($user['uptime']) &&
|
||||
is_null($user['server']) &&
|
||||
is_null($user['mac']) &&
|
||||
is_null($user['session_time']) &&
|
||||
$user['rx_bytes'] === '0 B' &&
|
||||
$user['tx_bytes'] === '0 B' &&
|
||||
$user['total'] === '0 B'
|
||||
);
|
||||
});
|
||||
|
||||
// Get PPPoE users
|
||||
$pppUsers = $client->sendSync(new RouterOS\Request('/ppp/active/print'));
|
||||
$pppoeList = [];
|
||||
$totalPPPoEUsage = 0;
|
||||
foreach ($pppUsers as $pppUser) {
|
||||
$bytes_in = $pppUser->getProperty('limit-bytes-in');
|
||||
$bytes_out = $pppUser->getProperty('limit-bytes-out');
|
||||
$totalPPPoEUsage += $bytes_in + $bytes_out;
|
||||
$username = $pppUser->getProperty('name');
|
||||
save_data_usage($username, $bytes_in, $bytes_out, 'pppoe');
|
||||
|
||||
$pppoeList[] = [
|
||||
'username' => $username,
|
||||
'address' => $pppUser->getProperty('address'),
|
||||
'uptime' => $pppUser->getProperty('uptime'),
|
||||
'service' => $pppUser->getProperty('service'),
|
||||
'caller_id' => $pppUser->getProperty('caller-id'),
|
||||
'bytes_in' => mikrotik_formatBytes($bytes_in),
|
||||
'bytes_out' => mikrotik_formatBytes($bytes_out),
|
||||
'total' => mikrotik_formatBytes($bytes_in + $bytes_out),
|
||||
];
|
||||
}
|
||||
|
||||
// Filter out null PPPoE users
|
||||
$pppoeList = array_filter($pppoeList, function($user) {
|
||||
return !(
|
||||
is_null($user['username']) &&
|
||||
is_null($user['address']) &&
|
||||
is_null($user['uptime']) &&
|
||||
is_null($user['service']) &&
|
||||
is_null($user['caller_id']) &&
|
||||
$user['bytes_in'] === '0 B' &&
|
||||
$user['bytes_out'] === '0 B' &&
|
||||
$user['total'] === '0 B'
|
||||
);
|
||||
});
|
||||
// Calculate total data usage
|
||||
$totalDataUsage = $totalHotspotUsage + $totalPPPoEUsage;
|
||||
|
||||
// Calculate total users
|
||||
$totalHotspotUsers = count($hotspotList);
|
||||
$totalPPPoEUsers = count($pppoeList);
|
||||
$totalUsers = $totalHotspotUsers + $totalPPPoEUsers;
|
||||
|
||||
return [
|
||||
'hotspot_users' => $totalHotspotUsers,
|
||||
'ppp_users' => $totalPPPoEUsers,
|
||||
'total_users' => $totalUsers,
|
||||
'total_bytes' => mikrotik_formatBytes($totalDataUsage),
|
||||
];
|
||||
}
|
||||
|
||||
function mikrotik_disconnect_online_user($router, $username, $userType)
|
||||
{
|
||||
// Check if the form was submitted
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Retrieve the form data
|
||||
$router = $_POST['router'];
|
||||
$username = $_POST['username'];
|
||||
$userType = $_POST['userType'];
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($router);
|
||||
if (!$mikrotik) {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
if ($userType == 'hotspot') {
|
||||
Mikrotik::removeHotspotActiveUser($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} elseif ($userType == 'pppoe') {
|
||||
Mikrotik::removePpoeActive($client, $username);
|
||||
// Handle the success response or redirection
|
||||
} else {
|
||||
// Handle the error response or redirection
|
||||
return;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Handle the error response or redirection
|
||||
} finally {
|
||||
// Disconnect from the MikroTik router
|
||||
if (isset($client)) {
|
||||
$client->disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
493
system/controllers/order.php
Normal file
493
system/controllers/order.php
Normal file
@@ -0,0 +1,493 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_auth();
|
||||
$action = $routes['1'];
|
||||
$user = User::_info();
|
||||
$ui->assign('_user', $user);
|
||||
|
||||
switch ($action) {
|
||||
case 'voucher':
|
||||
$ui->assign('_system_menu', 'voucher');
|
||||
$ui->assign('_title', Lang::T('Order Voucher'));
|
||||
run_hook('customer_view_order'); #HOOK
|
||||
$ui->display('user-order.tpl');
|
||||
break;
|
||||
case 'history':
|
||||
$ui->assign('_system_menu', 'history');
|
||||
$query = ORM::for_table('tbl_payment_gateway')->where('username', $user['username'])->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('_title', Lang::T('Order History'));
|
||||
run_hook('customer_view_order_history'); #HOOK
|
||||
$ui->display('user-orderHistory.tpl');
|
||||
break;
|
||||
case 'balance':
|
||||
if (strpos($user['email'], '@') === false) {
|
||||
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
|
||||
}
|
||||
$ui->assign('_title', 'Top Up');
|
||||
$ui->assign('_system_menu', 'balance');
|
||||
$plans_balance = ORM::for_table('tbl_plans')->where('enabled', '1')->where('type', 'Balance')->where('prepaid', 'yes')->find_many();
|
||||
$ui->assign('plans_balance', $plans_balance);
|
||||
$ui->display('user-orderBalance.tpl');
|
||||
break;
|
||||
case 'package':
|
||||
if (strpos($user['email'], '@') === false) {
|
||||
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
|
||||
}
|
||||
$ui->assign('_title', 'Order Plan');
|
||||
$ui->assign('_system_menu', 'package');
|
||||
$account_type = $user['account_type'];
|
||||
if (empty($account_type)) {
|
||||
$account_type = 'Personal';
|
||||
}
|
||||
if (!empty($_SESSION['nux-router'])) {
|
||||
if ($_SESSION['nux-router'] == 'radius') {
|
||||
$radius_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
|
||||
$radius_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
|
||||
} else {
|
||||
$routers = ORM::for_table('tbl_routers')->where('id', $_SESSION['nux-router'])->find_many();
|
||||
$rs = [];
|
||||
foreach ($routers as $r) {
|
||||
$rs[] = $r['name'];
|
||||
}
|
||||
$plans_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where_in('routers', $rs)->where('is_radius', 0)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
|
||||
$plans_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where_in('routers', $rs)->where('is_radius', 0)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
|
||||
}
|
||||
} else {
|
||||
$radius_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
|
||||
$radius_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
|
||||
|
||||
$routers = ORM::for_table('tbl_routers')->find_many();
|
||||
$plans_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 0)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
|
||||
$plans_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 0)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
|
||||
}
|
||||
$ui->assign('routers', $routers);
|
||||
$ui->assign('radius_pppoe', $radius_pppoe);
|
||||
$ui->assign('radius_hotspot', $radius_hotspot);
|
||||
$ui->assign('plans_pppoe', $plans_pppoe);
|
||||
$ui->assign('plans_hotspot', $plans_hotspot);
|
||||
run_hook('customer_view_order_plan'); #HOOK
|
||||
$ui->display('user-orderPlan.tpl');
|
||||
break;
|
||||
case 'unpaid':
|
||||
$d = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->where('status', 1)
|
||||
->find_one();
|
||||
run_hook('custome
|
||||
r_find_unpaid'); #HOOK
|
||||
if ($d) {
|
||||
if (empty($d['pg_url_payment'])) {
|
||||
r2(U . "order/buy/" . $trx['routers_id'] . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
|
||||
} else {
|
||||
r2(U . "order/view/" . $d['id'] . '/check/', 's', Lang::T("You have unpaid transaction"));
|
||||
}
|
||||
} else {
|
||||
r2(U . "order/package/", 's', Lang::T("You have no unpaid transaction"));
|
||||
}
|
||||
break;
|
||||
case 'view':
|
||||
$trxid = $routes['2'];
|
||||
$trx = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->find_one($trxid);
|
||||
run_hook('customer_view_payment'); #HOOK
|
||||
// jika tidak ditemukan, berarti punya orang lain
|
||||
if (empty($trx)) {
|
||||
r2(U . "order/package", 'w', Lang::T("Payment not found"));
|
||||
}
|
||||
// jika url kosong, balikin ke buy, kecuali cancel
|
||||
if (empty($trx['pg_url_payment']) && $routes['3'] != 'cancel') {
|
||||
r2(U . "order/buy/" . (($trx['routers_id'] == 0) ? $trx['routers'] : $trx['routers_id']) . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
|
||||
}
|
||||
if ($routes['3'] == 'check') {
|
||||
if (!file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $trx['gateway'] . '.php')) {
|
||||
r2(U . 'order/view/' . $trxid, 'e', Lang::T("No Payment Gateway Available"));
|
||||
}
|
||||
run_hook('customer_check_payment_status'); #HOOK
|
||||
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $trx['gateway'] . '.php';
|
||||
call_user_func($trx['gateway'] . '_validate_config');
|
||||
call_user_func($trx['gateway'] . '_get_status', $trx, $user);
|
||||
} else if ($routes['3'] == 'cancel') {
|
||||
run_hook('customer_cancel_payment'); #HOOK
|
||||
$trx->pg_paid_response = '{}';
|
||||
$trx->status = 4;
|
||||
$trx->paid_date = date('Y-m-d H:i:s');
|
||||
$trx->save();
|
||||
$trx = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->find_one($trxid);
|
||||
}
|
||||
if (empty($trx)) {
|
||||
r2(U . "order/package", 'e', Lang::T("Transaction Not found"));
|
||||
}
|
||||
|
||||
$router = Mikrotik::info($trx['routers']);
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($trx['plan_id']);
|
||||
$bandw = ORM::for_table('tbl_bandwidth')->find_one($plan['id_bw']);
|
||||
$invoice = ORM::for_table('tbl_transactions')->where("invoice", $trx['trx_invoice'])->find_one();
|
||||
$ui->assign('invoice', $invoice);
|
||||
$ui->assign('trx', $trx);
|
||||
$ui->assign('router', $router);
|
||||
$ui->assign('plan', $plan);
|
||||
$ui->assign('bandw', $bandw);
|
||||
$ui->assign('_title', 'TRX #' . $trxid);
|
||||
$ui->display('user-orderView.tpl');
|
||||
break;
|
||||
case 'pay':
|
||||
if ($config['enable_balance'] != 'yes') {
|
||||
r2(U . "order/package", 'e', Lang::T("Balance not enabled"));
|
||||
}
|
||||
if (!empty(App::getTokenValue($_GET['stoken']))) {
|
||||
r2(U . "voucher/invoice/");
|
||||
die();
|
||||
}
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
$plan = ORM::for_table('tbl_plans')->where('enabled', '1')->find_one($routes['3']);
|
||||
if (empty($plan)) {
|
||||
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
|
||||
}
|
||||
if (!$plan['enabled']) {
|
||||
r2(U . "home", 'e', 'Plan is not exists');
|
||||
}
|
||||
if ($routes['2'] == 'radius') {
|
||||
$router_name = 'radius';
|
||||
} else {
|
||||
$router_name = $plan['routers'];
|
||||
}
|
||||
|
||||
list($bills, $add_cost) = User::getBills($id_customer);
|
||||
|
||||
// Tax calculation start
|
||||
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
|
||||
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
|
||||
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
|
||||
|
||||
if ($tax_rate_setting === 'custom') {
|
||||
$tax_rate = $custom_tax_rate;
|
||||
} else {
|
||||
$tax_rate = $tax_rate_setting;
|
||||
}
|
||||
|
||||
if ($tax_enable === 'yes') {
|
||||
$tax = Package::tax($plan['price'], $tax_rate);
|
||||
} else {
|
||||
$tax = 0;
|
||||
}
|
||||
// Tax calculation stop
|
||||
|
||||
if ($plan && $plan['enabled'] && $user['balance'] >= $plan['price'] + $tax) {
|
||||
if (Package::rechargeUser($user['id'], $router_name, $plan['id'], 'Customer', 'Balance')) {
|
||||
// if success, then get the balance
|
||||
Balance::min($user['id'], $plan['price'] + $add_cost + $tax);
|
||||
App::setToken($_GET['stoken'], "success");
|
||||
r2(U . "voucher/invoice/", 's', Lang::T("Success to buy package"));
|
||||
} else {
|
||||
r2(U . "order/package", 'e', Lang::T("Failed to buy package"));
|
||||
Message::sendTelegram("Buy Package with Balance Failed\n\n#u$c[username] #buy \n" . $plan['name_plan'] .
|
||||
"\nRouter: " . $router_name .
|
||||
"\nPrice: " . $plan['price'] + $tax);
|
||||
}
|
||||
} else {
|
||||
r2(U . "home", 'e', 'Plan is not exists');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'send':
|
||||
if ($config['enable_balance'] != 'yes') {
|
||||
r2(U . "order/package", 'e', Lang::T("Balance not enabled"));
|
||||
}
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
$ui->assign('_title', Lang::T('Buy for friend'));
|
||||
$ui->assign('_system_menu', 'package');
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($routes['3']);
|
||||
if (empty($plan)) {
|
||||
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
|
||||
}
|
||||
if (!$plan['enabled']) {
|
||||
r2(U . "home", 'e', 'Plan is not exists');
|
||||
}
|
||||
if ($routes['2'] == 'radius') {
|
||||
$router_name = 'radius';
|
||||
} else {
|
||||
$router_name = $plan['routers'];
|
||||
}
|
||||
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
|
||||
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
|
||||
|
||||
if ($tax_rate_setting === 'custom') {
|
||||
$tax_rate = $custom_tax_rate;
|
||||
} else {
|
||||
$tax_rate = $tax_rate_setting;
|
||||
}
|
||||
|
||||
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
|
||||
|
||||
if ($tax_enable === 'yes') {
|
||||
$tax = Package::tax($plan['price'], $tax_rate);
|
||||
$ui->assign('tax', $tax);
|
||||
} else {
|
||||
$tax = 0;
|
||||
}
|
||||
|
||||
// Add tax to plan price
|
||||
$plan['price'] += $tax;
|
||||
|
||||
if (isset($_POST['send']) && $_POST['send'] == 'plan') {
|
||||
$target = ORM::for_table('tbl_customers')->where('username', _post('username'))->find_one();
|
||||
list($bills, $add_cost) = User::getBills($target['id']);
|
||||
if (!empty($add_cost)) {
|
||||
$ui->assign('bills', $bills);
|
||||
$ui->assign('add_cost', $add_cost);
|
||||
$plan['price'] += $add_cost;
|
||||
}
|
||||
|
||||
if (!$target) {
|
||||
r2(U . 'home', 'd', Lang::T('Username not found'));
|
||||
}
|
||||
if ($user['balance'] < $plan['price']) {
|
||||
r2(U . 'home', 'd', Lang::T('insufficient balance'));
|
||||
}
|
||||
if ($user['username'] == $target['username']) {
|
||||
r2(U . "order/pay/$routes[2]/$routes[3]", 's', '^_^ v');
|
||||
}
|
||||
$active = ORM::for_table('tbl_user_recharges')
|
||||
->where('username', _post('username'))
|
||||
->where('status', 'on')
|
||||
->find_one();
|
||||
|
||||
if ($active && $active['plan_id'] != $plan['id']) {
|
||||
r2(U . "order/package", 'e', Lang::T("Target has active plan, different with current plant.") . " [ <b>$active[namebp]</b> ]");
|
||||
}
|
||||
$result = Package::rechargeUser($target['id'], $router_name, $plan['id'], $user['username'], 'Balance');
|
||||
if (!empty($result)) {
|
||||
// if success, then get the balance
|
||||
Balance::min($user['id'], $plan['price']);
|
||||
//sender
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $user['username'];
|
||||
$d->gateway = $target['username'];
|
||||
$d->plan_id = $plan['id'];
|
||||
$d->plan_name = $plan['name_plan'];
|
||||
$d->routers_id = $routes['2'];
|
||||
$d->routers = $router_name;
|
||||
$d->price = $plan['price'];
|
||||
$d->payment_method = "Balance";
|
||||
$d->payment_channel = "Send Plan";
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = 'balance';
|
||||
$d->trx_invoice = $result;
|
||||
$d->status = 2;
|
||||
$d->save();
|
||||
$trx_id = $d->id();
|
||||
//receiver
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $target['username'];
|
||||
$d->gateway = $user['username'];
|
||||
$d->plan_id = $plan['id'];
|
||||
$d->plan_name = $plan['name_plan'];
|
||||
$d->routers_id = $routes['2'];
|
||||
$d->routers = $router_name;
|
||||
$d->price = $plan['price'];
|
||||
$d->payment_method = "Balance";
|
||||
$d->payment_channel = "Received Plan";
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = 'balance';
|
||||
$d->trx_invoice = $result;
|
||||
$d->status = 2;
|
||||
$d->save();
|
||||
r2(U . "order/view/$trx_id", 's', Lang::T("Success to send package"));
|
||||
} else {
|
||||
$errorMessage = "Send Package with Balance Failed\n\n#u$user[username] #send \n" . $plan['name_plan'] .
|
||||
"\nRouter: " . $router_name .
|
||||
"\nPrice: " . $plan['price'];
|
||||
|
||||
if ($tax_enable === 'yes') {
|
||||
$errorMessage .= "\nTax: " . $tax;
|
||||
}
|
||||
|
||||
r2(U . "order/package", 'e', Lang::T("Failed to Send package"));
|
||||
Message::sendTelegram($errorMessage);
|
||||
}
|
||||
}
|
||||
$ui->assign('username', $_GET['u']);
|
||||
$ui->assign('router', $router_name);
|
||||
$ui->assign('plan', $plan);
|
||||
$ui->assign('tax', $tax);
|
||||
$ui->display('user-sendPlan.tpl');
|
||||
break;
|
||||
case 'gateway':
|
||||
$ui->assign('_title', Lang::T('Select Payment Gateway'));
|
||||
$ui->assign('_system_menu', 'package');
|
||||
if (strpos($user['email'], '@') === false) {
|
||||
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
|
||||
}
|
||||
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
|
||||
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
|
||||
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
|
||||
if ($tax_rate_setting === 'custom') {
|
||||
$tax_rate = $custom_tax_rate;
|
||||
} else {
|
||||
$tax_rate = $tax_rate_setting;
|
||||
}
|
||||
$plan = ORM::for_table('tbl_plans')->find_one($routes['3']);
|
||||
$tax = Package::tax($plan['price'], $tax_rate);
|
||||
$pgs = array_values(explode(',', $config['payment_gateway']));
|
||||
if (count($pgs) == 0) {
|
||||
sendTelegram("Payment Gateway not set, please set it in Settings");
|
||||
_log(Lang::T("Payment Gateway not set, please set it in Settings"));
|
||||
r2(U . "home", 'e', Lang::T("Failed to create Transaction.."));
|
||||
}
|
||||
if (count($pgs) > 1) {
|
||||
$ui->assign('pgs', $pgs);
|
||||
if ($tax_enable === 'yes') {
|
||||
$ui->assign('tax', $tax);
|
||||
}
|
||||
$ui->assign('route2', $routes[2]);
|
||||
$ui->assign('route3', $routes[3]);
|
||||
$ui->assign('plan', $plan);
|
||||
$ui->display('user-selectGateway.tpl');
|
||||
break;
|
||||
} else {
|
||||
if (empty($pgs[0])) {
|
||||
sendTelegram("Payment Gateway not set, please set it in Settings");
|
||||
_log(Lang::T("Payment Gateway not set, please set it in Settings"));
|
||||
r2(U . "home", 'e', Lang::T("Failed to create Transaction.."));
|
||||
} else {
|
||||
$_POST['gateway'] = $pgs[0];
|
||||
}
|
||||
}
|
||||
case 'buy':
|
||||
$gateway = _post('gateway');
|
||||
if (empty($gateway) && !empty($_SESSION['gateway'])) {
|
||||
$gateway = $_SESSION['gateway'];
|
||||
} else if (!empty($gateway)) {
|
||||
$_SESSION['gateway'] = $gateway;
|
||||
}
|
||||
if ($user['status'] != 'Active') {
|
||||
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
|
||||
}
|
||||
if (empty($gateway)) {
|
||||
r2(U . 'order/gateway/' . $routes[2] . '/' . $routes[3], 'w', Lang::T("Please select Payment Gateway"));
|
||||
}
|
||||
run_hook('customer_buy_plan'); #HOOK
|
||||
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $gateway . '.php';
|
||||
call_user_func($gateway . '_validate_config');
|
||||
|
||||
if ($routes['2'] == 'radius') {
|
||||
$router['id'] = 0;
|
||||
$router['name'] = 'radius';
|
||||
} else if ($routes['2'] > 0) {
|
||||
$router = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routes['2']);
|
||||
} else {
|
||||
$router['id'] = 0;
|
||||
$router['name'] = 'balance';
|
||||
}
|
||||
$plan = ORM::for_table('tbl_plans')->where('enabled', '1')->find_one($routes['3']);
|
||||
if (empty($router) || empty($plan)) {
|
||||
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
|
||||
}
|
||||
$d = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $user['username'])
|
||||
->where('status', 1)
|
||||
->find_one();
|
||||
if ($d) {
|
||||
if ($d['pg_url_payment']) {
|
||||
r2(U . "order/view/" . $d['id'], 'w', Lang::T("You already have unpaid transaction, cancel it or pay it."));
|
||||
} else {
|
||||
if ($gateway == $d['gateway']) {
|
||||
$id = $d['id'];
|
||||
} else {
|
||||
$d->status = 4;
|
||||
$d->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
$add_cost = 0;
|
||||
$tax = 0;
|
||||
if ($router['name'] != 'balance') {
|
||||
list($bills, $add_cost) = User::getBills($id_customer);
|
||||
}
|
||||
// Tax calculation start
|
||||
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
|
||||
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
|
||||
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
|
||||
if ($tax_rate_setting === 'custom') {
|
||||
$tax_rate = $custom_tax_rate;
|
||||
} else {
|
||||
$tax_rate = $tax_rate_setting;
|
||||
}
|
||||
if ($tax_enable === 'yes') {
|
||||
$tax = Package::tax($plan['price'], $tax_rate);
|
||||
}
|
||||
// Tax calculation stop
|
||||
if (empty($id)) {
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $user['username'];
|
||||
$d->gateway = $gateway;
|
||||
$d->plan_id = $plan['id'];
|
||||
$d->plan_name = $plan['name_plan'];
|
||||
$d->routers_id = $router['id'];
|
||||
$d->routers = $router['name'];
|
||||
if ($plan['validity_unit'] == 'Period') {
|
||||
// Postpaid price from field
|
||||
$add_inv = User::getAttribute("Invoice", $id_customer);
|
||||
if (empty($add_inv) or $add_inv == 0) {
|
||||
$d->price = ($plan['price'] + $add_cost + $tax);
|
||||
} else {
|
||||
$d->price = ($add_inv + $add_cost + $tax);
|
||||
}
|
||||
} else {
|
||||
$d->price = ($plan['price'] + $add_cost + $tax);
|
||||
}
|
||||
//$d->price = ($plan['price'] + $add_cost);
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->status = 1;
|
||||
$d->save();
|
||||
$id = $d->id();
|
||||
} else {
|
||||
$d->username = $user['username'];
|
||||
$d->gateway = $gateway;
|
||||
$d->plan_id = $plan['id'];
|
||||
$d->plan_name = $plan['name_plan'];
|
||||
$d->routers_id = $router['id'];
|
||||
$d->routers = $router['name'];
|
||||
if ($plan['validity_unit'] == 'Period') {
|
||||
// Postpaid price from field
|
||||
$add_inv = User::getAttribute("Invoice", $id_customer);
|
||||
if (empty($add_inv) or $add_inv == 0) {
|
||||
$d->price = ($plan['price'] + $add_cost + $tax);
|
||||
} else {
|
||||
$d->price = ($add_inv + $add_cost + $tax);
|
||||
}
|
||||
} else {
|
||||
$d->price = ($plan['price'] + $add_cost + $tax);
|
||||
}
|
||||
//$d->price = ($plan['price'] + $add_cost);
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->status = 1;
|
||||
$d->save();
|
||||
}
|
||||
if (!$id) {
|
||||
r2(U . "order/package/" . $d['id'], 'e', Lang::T("Failed to create Transaction.."));
|
||||
} else {
|
||||
call_user_func($gateway . '_create_transaction', $d, $user);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
r2(U . "order/package/", 's', '');
|
||||
}
|
||||
21
system/controllers/page.php
Normal file
21
system/controllers/page.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_auth();
|
||||
$ui->assign('_title', Lang::T('Order Voucher'));
|
||||
$ui->assign('_system_menu', 'order');
|
||||
|
||||
$action = $routes['1'];
|
||||
$user = User::_info();
|
||||
$ui->assign('_user', $user);
|
||||
|
||||
if(file_exists(__DIR__."/../../pages/".str_replace(".","",$action).".html")){
|
||||
$ui->assign("PageFile",$action);
|
||||
$ui->assign("pageHeader",$action);
|
||||
run_hook('customer_view_page'); #HOOK
|
||||
$ui->display('user-pages.tpl');
|
||||
}else
|
||||
$ui->display('404.tpl');
|
||||
71
system/controllers/pages.php
Normal file
71
system/controllers/pages.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', 'Pages');
|
||||
$ui->assign('_system_menu', 'pages');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if(strpos($action,"-reset")!==false){
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
|
||||
}
|
||||
$action = str_replace("-reset","",$action);
|
||||
$path = "pages/".str_replace(".","",$action).".html";
|
||||
$temp = "pages_template/".str_replace(".","",$action).".html";
|
||||
if(file_exists($temp)){
|
||||
if(!copy($temp, $path)){
|
||||
file_put_contents($path, Http::getData('https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/pages_template/'.$action.'.html'));
|
||||
}
|
||||
}else{
|
||||
file_put_contents($path, Http::getData('https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/pages_template/'.$action.'.html'));
|
||||
}
|
||||
r2(U . 'pages/'.$action);
|
||||
}else if(strpos($action,"-post")===false){
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
|
||||
}
|
||||
$path = "pages/".str_replace(".","",$action).".html";
|
||||
//echo $path;
|
||||
run_hook('view_edit_pages'); #HOOK
|
||||
if(!file_exists($path)){
|
||||
$temp = "pages_template/".str_replace(".","",$action).".html";
|
||||
if(file_exists($temp)){
|
||||
if(!copy($temp, $path)){
|
||||
touch($path);
|
||||
}
|
||||
}else{
|
||||
touch($path);
|
||||
}
|
||||
}
|
||||
if(file_exists($path)){
|
||||
$html = file_get_contents($path);
|
||||
$ui->assign("htmls",str_replace(["<div","</div>"],"",$html));
|
||||
$ui->assign("writeable",is_writable($path));
|
||||
$ui->assign("pageHeader",str_replace('_', ' ', $action));
|
||||
$ui->assign("PageFile",$action);
|
||||
$ui->display('page-edit.tpl');
|
||||
}else
|
||||
$ui->display('a404.tpl');
|
||||
}else{
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
|
||||
}
|
||||
$action = str_replace("-post","",$action);
|
||||
$path = "pages/".str_replace(".","",$action).".html";
|
||||
if(file_exists($path)){
|
||||
$html = _post("html");
|
||||
run_hook('save_pages'); #HOOK
|
||||
if(file_put_contents($path, str_replace(["<div","</div>"],"",$html))){
|
||||
r2(U . 'pages/'.$action, 's', Lang::T("Saving page success"));
|
||||
}else{
|
||||
r2(U . 'pages/'.$action, 'e', Lang::T("Failed to save page, make sure i can write to folder pages, <i>chmod 664 pages/*.html<i>"));
|
||||
}
|
||||
}else
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
83
system/controllers/paymentgateway.php
Normal file
83
system/controllers/paymentgateway.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_system_menu', 'paymentgateway');
|
||||
|
||||
$action = alphanumeric($routes[1]);
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if ($action == 'delete') {
|
||||
$pg = alphanumeric($routes[2]);
|
||||
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $pg . '.php')) {
|
||||
deleteFile($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR, $pg);
|
||||
}
|
||||
r2(U . 'paymentgateway', 's', Lang::T('Payment Gateway Deleted'));
|
||||
}
|
||||
|
||||
if (_post('save') == 'actives') {
|
||||
$pgs = '';
|
||||
if(is_array($_POST['pgs'])){
|
||||
$pgs = implode(',', $_POST['pgs']);
|
||||
}
|
||||
$d = ORM::for_table('tbl_appconfig')->where('setting', 'payment_gateway')->find_one();
|
||||
if ($d) {
|
||||
$d->value = $pgs;
|
||||
$d->save();
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = 'payment_gateway';
|
||||
$d->value = $pgs;
|
||||
$d->save();
|
||||
}
|
||||
r2(U . 'paymentgateway', 's', Lang::T('Payment Gateway saved successfully'));
|
||||
}
|
||||
|
||||
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php')) {
|
||||
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php';
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (function_exists($action . '_save_config')) {
|
||||
call_user_func($action . '_save_config');
|
||||
} else {
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
} else {
|
||||
if (function_exists($action . '_show_config')) {
|
||||
call_user_func($action . '_show_config');
|
||||
} else {
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!empty($action)) {
|
||||
r2(U . 'paymentgateway', 'w', Lang::T('Payment Gateway Not Found'));
|
||||
} else {
|
||||
$files = scandir($PAYMENTGATEWAY_PATH);
|
||||
foreach ($files as $file) {
|
||||
if (pathinfo($file, PATHINFO_EXTENSION) == 'php') {
|
||||
$pgs[] = str_replace('.php', '', $file);
|
||||
}
|
||||
}
|
||||
$ui->assign('_title', 'Payment Gateway Settings');
|
||||
$ui->assign('pgs', $pgs);
|
||||
$ui->assign('actives', explode(',', $config['payment_gateway']));
|
||||
$ui->display('paymentgateway.tpl');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function deleteFile($path, $name)
|
||||
{
|
||||
$files = scandir($path);
|
||||
foreach ($files as $file) {
|
||||
if (is_file($path . $file) && strpos($file, $name) !== false) {
|
||||
unlink($path . $file);
|
||||
} else if (is_dir($path . $file) && !in_array($file, ['.', '..'])) {
|
||||
deleteFile($path . $file . DIRECTORY_SEPARATOR, $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
1042
system/controllers/plan.php
Normal file
1042
system/controllers/plan.php
Normal file
File diff suppressed because it is too large
Load Diff
11
system/controllers/plugin.php
Normal file
11
system/controllers/plugin.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
if(function_exists($routes[1])){
|
||||
call_user_func($routes[1]);
|
||||
}else{
|
||||
r2(U.'dashboard', 'e', 'Function not found');
|
||||
}
|
||||
196
system/controllers/pluginmanager.php
Normal file
196
system/controllers/pluginmanager.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', 'Plugin Manager');
|
||||
$ui->assign('_system_menu', 'settings');
|
||||
|
||||
$plugin_repository = 'https://hotspotbilling.github.io/Plugin-Repository/repository.json';
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$cache = $CACHE_PATH . File::pathFixer('/plugin_repository.json');
|
||||
if (file_exists($cache) && time() - filemtime($cache) < (24 * 60 * 60)) {
|
||||
$txt = file_get_contents($cache);
|
||||
$json = json_decode($txt, true);
|
||||
if (empty($json['plugins']) && empty($json['payment_gateway'])) {
|
||||
unlink($cache);
|
||||
r2(U . 'dashboard', 'd', $txt);
|
||||
}
|
||||
} else {
|
||||
$data = Http::getData($plugin_repository);
|
||||
file_put_contents($cache, $data);
|
||||
$json = json_decode($data, true);
|
||||
}
|
||||
switch ($action) {
|
||||
case 'delete':
|
||||
if (!is_writeable($CACHE_PATH)) {
|
||||
r2(U . "pluginmanager", 'e', 'Folder cache/ is not writable');
|
||||
}
|
||||
if (!is_writeable($PLUGIN_PATH)) {
|
||||
r2(U . "pluginmanager", 'e', 'Folder plugin/ is not writable');
|
||||
}
|
||||
set_time_limit(-1);
|
||||
$tipe = $routes['2'];
|
||||
$plugin = $routes['3'];
|
||||
$file = $CACHE_PATH . DIRECTORY_SEPARATOR . $plugin . '.zip';
|
||||
if (file_exists($file)) unlink($file);
|
||||
if ($tipe == 'plugin') {
|
||||
foreach ($json['plugins'] as $plg) {
|
||||
if ($plg['id'] == $plugin) {
|
||||
$fp = fopen($file, 'w+');
|
||||
$ch = curl_init($plg['github'] . '/archive/refs/heads/master.zip');
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($file);
|
||||
$zip->extractTo($CACHE_PATH);
|
||||
$zip->close();
|
||||
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-main/');
|
||||
if (!file_exists($folder)) {
|
||||
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
|
||||
}
|
||||
if (!file_exists($folder)) {
|
||||
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
|
||||
}
|
||||
scanAndRemovePath($folder, $PLUGIN_PATH . DIRECTORY_SEPARATOR);
|
||||
File::deleteFolder($folder);
|
||||
unlink($file);
|
||||
r2(U . "pluginmanager", 's', 'Plugin ' . $plugin . ' has been deleted');
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'install':
|
||||
if (!is_writeable($CACHE_PATH)) {
|
||||
r2(U . "pluginmanager", 'e', 'Folder cache/ is not writable');
|
||||
}
|
||||
if (!is_writeable($PLUGIN_PATH)) {
|
||||
r2(U . "pluginmanager", 'e', 'Folder plugin/ is not writable');
|
||||
}
|
||||
set_time_limit(-1);
|
||||
$tipe = $routes['2'];
|
||||
$plugin = $routes['3'];
|
||||
$file = $CACHE_PATH . DIRECTORY_SEPARATOR . $plugin . '.zip';
|
||||
if (file_exists($file)) unlink($file);
|
||||
if ($tipe == 'plugin') {
|
||||
foreach ($json['plugins'] as $plg) {
|
||||
if ($plg['id'] == $plugin) {
|
||||
$fp = fopen($file, 'w+');
|
||||
$ch = curl_init($plg['github'] . '/archive/refs/heads/master.zip');
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($file);
|
||||
$zip->extractTo($CACHE_PATH);
|
||||
$zip->close();
|
||||
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-main/');
|
||||
if (!file_exists($folder)) {
|
||||
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
|
||||
}
|
||||
if (!file_exists($folder)) {
|
||||
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
|
||||
}
|
||||
File::copyFolder($folder, $PLUGIN_PATH . DIRECTORY_SEPARATOR, ['README.md', 'LICENSE']);
|
||||
File::deleteFolder($folder);
|
||||
unlink($file);
|
||||
r2(U . "pluginmanager", 's', 'Plugin ' . $plugin . ' has been installed');
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if ($tipe == 'payment') {
|
||||
foreach ($json['payment_gateway'] as $plg) {
|
||||
if ($plg['id'] == $plugin) {
|
||||
$fp = fopen($file, 'w+');
|
||||
$ch = curl_init($plg['github'] . '/archive/refs/heads/master.zip');
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_exec($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($file);
|
||||
$zip->extractTo($CACHE_PATH);
|
||||
$zip->close();
|
||||
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-main/');
|
||||
if (!file_exists($folder)) {
|
||||
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
|
||||
}
|
||||
if (!file_exists($folder)) {
|
||||
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
|
||||
}
|
||||
File::copyFolder($folder, $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR, ['README.md', 'LICENSE']);
|
||||
File::deleteFolder($folder);
|
||||
unlink($file);
|
||||
r2(U . "paymentgateway", 's', 'Payment Gateway ' . $plugin . ' has been installed');
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (class_exists('ZipArchive')) {
|
||||
$zipExt = true;
|
||||
} else {
|
||||
$zipExt = false;
|
||||
}
|
||||
$ui->assign('zipExt', $zipExt);
|
||||
$ui->assign('plugins', $json['plugins']);
|
||||
$ui->assign('pgs', $json['payment_gateway']);
|
||||
$ui->display('plugin-manager.tpl');
|
||||
}
|
||||
|
||||
|
||||
function scanAndRemovePath($source, $target)
|
||||
{
|
||||
$files = scandir($source);
|
||||
foreach ($files as $file) {
|
||||
if (is_file($source . $file)) {
|
||||
if(file_exists($target.$file)){
|
||||
unlink($target . $file);
|
||||
}
|
||||
} else if (is_dir($source . $file) && !in_array($file, ['.', '..'])) {
|
||||
scanAndRemovePath($source. $file. DIRECTORY_SEPARATOR, $target. $file. DIRECTORY_SEPARATOR);
|
||||
if(file_exists($target.$file)){
|
||||
rmdir($target . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(file_exists($target)){
|
||||
rmdir($target);
|
||||
}
|
||||
}
|
||||
165
system/controllers/pool.php
Normal file
165
system/controllers/pool.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Network'));
|
||||
$ui->assign('_system_menu', 'network');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
|
||||
}
|
||||
|
||||
|
||||
switch ($action) {
|
||||
case 'list':
|
||||
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/pool.js"></script>');
|
||||
|
||||
$name = _post('name');
|
||||
if ($name != '') {
|
||||
$query = ORM::for_table('tbl_pool')->where_like('pool_name', '%' . $name . '%')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query, ['name' => $name]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_pool')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
run_hook('view_pool'); #HOOK
|
||||
$ui->display('pool.tpl');
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
$r = ORM::for_table('tbl_routers')->find_many();
|
||||
$ui->assign('r', $r);
|
||||
run_hook('view_add_pool'); #HOOK
|
||||
$ui->display('pool-add.tpl');
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
$id = $routes['2'];
|
||||
$d = ORM::for_table('tbl_pool')->find_one($id);
|
||||
if ($d) {
|
||||
$ui->assign('d', $d);
|
||||
run_hook('view_edit_pool'); #HOOK
|
||||
$ui->display('pool-edit.tpl');
|
||||
} else {
|
||||
r2(U . 'pool/list', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$id = $routes['2'];
|
||||
run_hook('delete_pool'); #HOOK
|
||||
$d = ORM::for_table('tbl_pool')->find_one($id);
|
||||
if ($d) {
|
||||
if ($d['routers'] != 'radius') {
|
||||
try {
|
||||
$mikrotik = Mikrotik::info($d['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removePool($client, $d['pool_name']);
|
||||
} catch (Exception $e) {
|
||||
//ignore exception, it means router has already deleted
|
||||
} catch(Throwable $e){
|
||||
//ignore exception, it means router has already deleted
|
||||
}
|
||||
}
|
||||
$d->delete();
|
||||
|
||||
r2(U . 'pool/list', 's', Lang::T('Data Deleted Successfully'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'sync':
|
||||
$pools = ORM::for_table('tbl_pool')->find_many();
|
||||
$log = '';
|
||||
foreach ($pools as $pool) {
|
||||
if ($pool['routers'] != 'radius') {
|
||||
$mikrotik = Mikrotik::info($pool['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::addPool($client, $pool['pool_name'], $pool['range_ip']);
|
||||
$log .= 'DONE: ' . $pool['pool_name'] . ': ' . $pool['range_ip'] . '<br>';
|
||||
}
|
||||
}
|
||||
r2(U . 'pool/list', 's', $log);
|
||||
break;
|
||||
case 'add-post':
|
||||
$name = _post('name');
|
||||
$ip_address = _post('ip_address');
|
||||
$routers = _post('routers');
|
||||
run_hook('add_pool'); #HOOK
|
||||
$msg = '';
|
||||
if (Validator::Length($name, 30, 2) == false) {
|
||||
$msg .= 'Name should be between 3 to 30 characters' . '<br>';
|
||||
}
|
||||
if ($ip_address == '' or $routers == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_pool')->where('pool_name', $name)->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('Pool Name Already Exist') . '<br>';
|
||||
}
|
||||
if ($msg == '') {
|
||||
if ($routers != 'radius') {
|
||||
$mikrotik = Mikrotik::info($routers);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::addPool($client, $name, $ip_address);
|
||||
}
|
||||
|
||||
$b = ORM::for_table('tbl_pool')->create();
|
||||
$b->pool_name = $name;
|
||||
$b->range_ip = $ip_address;
|
||||
$b->routers = $routers;
|
||||
$b->save();
|
||||
|
||||
r2(U . 'pool/list', 's', Lang::T('Data Created Successfully'));
|
||||
} else {
|
||||
r2(U . 'pool/add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'edit-post':
|
||||
$ip_address = _post('ip_address');
|
||||
$routers = _post('routers');
|
||||
run_hook('edit_pool'); #HOOK
|
||||
$msg = '';
|
||||
|
||||
if ($ip_address == '' or $routers == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
|
||||
$id = _post('id');
|
||||
$d = ORM::for_table('tbl_pool')->find_one($id);
|
||||
if ($d) {
|
||||
} else {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
if ($routers != 'radius') {
|
||||
$mikrotik = Mikrotik::info($routers);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::setPool($client, $d['pool_name'], $ip_address);
|
||||
}
|
||||
|
||||
$d->range_ip = $ip_address;
|
||||
$d->routers = $routers;
|
||||
$d->save();
|
||||
|
||||
r2(U . 'pool/list', 's', Lang::T('Data Updated Successfully'));
|
||||
} else {
|
||||
r2(U . 'pool/edit/' . $id, 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
r2(U . 'pool/list/', 's', '');
|
||||
}
|
||||
150
system/controllers/radius.php
Normal file
150
system/controllers/radius.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Plugin Manager'));
|
||||
$ui->assign('_system_menu', 'settings');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'nas-add':
|
||||
$ui->assign('_system_menu', 'radius');
|
||||
$ui->assign('_title', "Network Access Server");
|
||||
$ui->assign('routers', ORM::for_table('tbl_routers')->find_many());
|
||||
$ui->display('radius-nas-add.tpl');
|
||||
break;
|
||||
case 'nas-add-post':
|
||||
$shortname = _post('shortname');
|
||||
$nasname = _post('nasname');
|
||||
$secret = _post('secret');
|
||||
$ports = _post('ports', null);
|
||||
$type = _post('type', 'other');
|
||||
$server = _post('server', null);
|
||||
$community = _post('community', null);
|
||||
$description = _post('description');
|
||||
$routers = _post('routers');
|
||||
$msg = '';
|
||||
|
||||
if (Validator::Length($shortname, 30, 2) == false) {
|
||||
$msg .= 'Name should be between 3 to 30 characters' . '<br>';
|
||||
}
|
||||
if (empty($ports)) {
|
||||
$ports = null;
|
||||
}
|
||||
if (empty($server)) {
|
||||
$server = null;
|
||||
}
|
||||
if (empty($community)) {
|
||||
$community = null;
|
||||
}
|
||||
if (empty($type)) {
|
||||
$type = null;
|
||||
}
|
||||
$d = ORM::for_table('nas', 'radius')->where('nasname', $nasname)->find_one();
|
||||
if ($d) {
|
||||
$msg .= 'NAS IP Exists<br>';
|
||||
}
|
||||
if ($msg == '') {
|
||||
$id = Radius::nasAdd($shortname, $nasname, $ports, $secret, $routers, $description, $type, $server, $community);
|
||||
if ($id > 0) {
|
||||
r2(U . 'radius/nas-list/', 's', "NAS Added");
|
||||
} else {
|
||||
r2(U . 'radius/nas-add/', 'e', "NAS Added Failed");
|
||||
}
|
||||
} else {
|
||||
r2(U . 'radius/nas-add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
case 'nas-edit':
|
||||
$ui->assign('_system_menu', 'radius');
|
||||
$ui->assign('_title', "Network Access Server");
|
||||
|
||||
$id = $routes['2'];
|
||||
$d = ORM::for_table('nas', 'radius')->find_one($id);
|
||||
if (!$d) {
|
||||
$d = ORM::for_table('nas', 'radius')->where_equal('shortname', _get('name'))->find_one();
|
||||
}
|
||||
if ($d) {
|
||||
$ui->assign('routers', ORM::for_table('tbl_routers')->find_many());
|
||||
$ui->assign('d', $d);
|
||||
$ui->display('radius-nas-edit.tpl');
|
||||
} else {
|
||||
r2(U . 'radius/list', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
|
||||
break;
|
||||
case 'nas-edit-post':
|
||||
$id = $routes['2'];
|
||||
$shortname = _post('shortname');
|
||||
$nasname = _post('nasname');
|
||||
$secret = _post('secret');
|
||||
$ports = _post('ports', null);
|
||||
$type = _post('type', 'other');
|
||||
$server = _post('server', null);
|
||||
$community = _post('community', null);
|
||||
$description = _post('description');
|
||||
$routers = _post('routers');
|
||||
$msg = '';
|
||||
|
||||
if (Validator::Length($shortname, 30, 2) == false) {
|
||||
$msg .= 'Name should be between 3 to 30 characters' . '<br>';
|
||||
}
|
||||
if (empty($ports)) {
|
||||
$ports = null;
|
||||
}
|
||||
if (empty($server)) {
|
||||
$server = null;
|
||||
}
|
||||
if (empty($community)) {
|
||||
$community = null;
|
||||
}
|
||||
if (empty($type)) {
|
||||
$type = null;
|
||||
}
|
||||
if ($msg == '') {
|
||||
if (Radius::nasUpdate($id, $shortname, $nasname, $ports, $secret, $routers, $description, $type, $server, $community)) {
|
||||
r2(U . 'radius/list/', 's', "NAS Saved");
|
||||
} else {
|
||||
r2(U . 'radius/nas-add', 'e', 'NAS NOT Exists');
|
||||
}
|
||||
} else {
|
||||
r2(U . 'radius/nas-add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
case 'nas-delete':
|
||||
$id = $routes['2'];
|
||||
$d = ORM::for_table('nas', 'radius')->find_one($id);
|
||||
if ($d) {
|
||||
$d->delete();
|
||||
} else {
|
||||
r2(U . 'radius/nas-list', 'e', 'NAS Not found');
|
||||
}
|
||||
default:
|
||||
$ui->assign('_system_menu', 'radius');
|
||||
$ui->assign('_title', "Network Access Server");
|
||||
$name = _post('name');
|
||||
if (empty($name)) {
|
||||
$query = ORM::for_table('nas', 'radius');
|
||||
$nas = Paginator::findMany($query);
|
||||
} else {
|
||||
$query = ORM::for_table('nas', 'radius')
|
||||
->where_like('nasname', $search)
|
||||
->where_like('shortname', $search)
|
||||
->where_like('description', $search);
|
||||
$nas = Paginator::findMany($query, ['name' => $name]);
|
||||
}
|
||||
$ui->assign('name', $name);
|
||||
$ui->assign('nas', $nas);
|
||||
$ui->display('radius-nas.tpl');
|
||||
}
|
||||
169
system/controllers/register.php
Normal file
169
system/controllers/register.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
if (isset($routes['1'])) {
|
||||
$do = $routes['1'];
|
||||
} else {
|
||||
$do = 'register-display';
|
||||
}
|
||||
|
||||
$otpPath = $CACHE_PATH . File::pathFixer('/sms/');
|
||||
|
||||
switch ($do) {
|
||||
case 'post':
|
||||
$otp_code = _post('otp_code');
|
||||
$username = alphanumeric(_post('username'), "+_.");
|
||||
$email = _post('email');
|
||||
$fullname = _post('fullname');
|
||||
$password = _post('password');
|
||||
$cpassword = _post('cpassword');
|
||||
$address = _post('address');
|
||||
if (!empty($config['sms_url'])) {
|
||||
$phonenumber = Lang::phoneFormat($username);
|
||||
$username = $phonenumber;
|
||||
} else if (strlen($username) < 21) {
|
||||
$phonenumber = $username;
|
||||
}
|
||||
$msg = '';
|
||||
if (Validator::Length($username, 35, 2) == false) {
|
||||
$msg .= 'Username should be between 3 to 55 characters' . '<br>';
|
||||
}
|
||||
if (Validator::Length($fullname, 36, 2) == false) {
|
||||
$msg .= 'Full Name should be between 3 to 25 characters' . '<br>';
|
||||
}
|
||||
if (!Validator::Length($password, 35, 2)) {
|
||||
$msg .= 'Password should be between 3 to 35 characters' . '<br>';
|
||||
}
|
||||
if (!Validator::Email($email)) {
|
||||
$msg .= 'Email is not Valid<br>';
|
||||
}
|
||||
if ($password != $cpassword) {
|
||||
$msg .= Lang::T('Passwords does not match') . '<br>';
|
||||
}
|
||||
|
||||
if (!empty($config['sms_url'])) {
|
||||
$otpPath .= sha1($username . $db_password) . ".txt";
|
||||
run_hook('validate_otp'); #HOOK
|
||||
//expired 10 minutes
|
||||
if (file_exists($otpPath) && time() - filemtime($otpPath) > 1200) {
|
||||
unlink($otpPath);
|
||||
r2(U . 'register', 's', 'Verification code expired');
|
||||
} else if (file_exists($otpPath)) {
|
||||
$code = file_get_contents($otpPath);
|
||||
if ($code != $otp_code) {
|
||||
$ui->assign('username', $username);
|
||||
$ui->assign('fullname', $fullname);
|
||||
$ui->assign('address', $address);
|
||||
$ui->assign('email', $email);
|
||||
$ui->assign('phonenumber', $phonenumber);
|
||||
$ui->assign('notify', 'Wrong Verification code');
|
||||
$ui->assign('notify_t', 'd');
|
||||
$ui->display('register-otp.tpl');
|
||||
exit();
|
||||
} else {
|
||||
unlink($otpPath);
|
||||
}
|
||||
} else {
|
||||
r2(U . 'register', 's', 'No Verification code');
|
||||
}
|
||||
}
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('Account already axist') . '<br>';
|
||||
}
|
||||
if ($msg == '') {
|
||||
run_hook('register_user'); #HOOK
|
||||
$d = ORM::for_table('tbl_customers')->create();
|
||||
$d->username = alphanumeric($username, "+_.");
|
||||
$d->password = $password;
|
||||
$d->fullname = $fullname;
|
||||
$d->address = $address;
|
||||
$d->email = $email;
|
||||
$d->phonenumber = $phonenumber;
|
||||
if ($d->save()) {
|
||||
$user = $d->id();
|
||||
|
||||
// Send registration notification SMS/WhatsApp
|
||||
if (!empty($phonenumber) && strlen($phonenumber) > 5) {
|
||||
$customerData = [
|
||||
'fullname' => $fullname,
|
||||
'username' => alphanumeric($username, "+_."),
|
||||
'password' => $password,
|
||||
'phonenumber' => $phonenumber,
|
||||
'service_type' => 'Hotspot' // Default for public registration
|
||||
];
|
||||
|
||||
Message::sendRegistrationNotification($customerData);
|
||||
}
|
||||
|
||||
r2(U . 'login', 's', Lang::T('Register Success! You can login now'));
|
||||
} else {
|
||||
$ui->assign('username', $username);
|
||||
$ui->assign('fullname', $fullname);
|
||||
$ui->assign('address', $address);
|
||||
$ui->assign('email', $email);
|
||||
$ui->assign('phonenumber', $phonenumber);
|
||||
$ui->assign('notify', 'Failed to register');
|
||||
$ui->assign('notify_t', 'd');
|
||||
run_hook('view_otp_register'); #HOOK
|
||||
$ui->display('register-rotp.tpl');
|
||||
}
|
||||
} else {
|
||||
$ui->assign('username', $username);
|
||||
$ui->assign('fullname', $fullname);
|
||||
$ui->assign('address', $address);
|
||||
$ui->assign('email', $email);
|
||||
$ui->assign('phonenumber', $phonenumber);
|
||||
$ui->assign('notify', $msg);
|
||||
$ui->assign('notify_t', 'd');
|
||||
$ui->display('register.tpl');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!empty($config['sms_url'])) {
|
||||
$username = _post('username');
|
||||
if (!empty($username)) {
|
||||
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
|
||||
if ($d) {
|
||||
r2(U . 'register', 's', Lang::T('Account already axist'));
|
||||
}
|
||||
if (!file_exists($otpPath)) {
|
||||
mkdir($otpPath);
|
||||
touch($otpPath . 'index.html');
|
||||
}
|
||||
$otpPath .= sha1($username . $db_password) . ".txt";
|
||||
//expired 10 minutes
|
||||
if (file_exists($otpPath) && time() - filemtime($otpPath) < 1200) {
|
||||
$ui->assign('username', $username);
|
||||
$ui->assign('notify', 'Please wait ' . (1200 - (time() - filemtime($otpPath))) . ' seconds before sending another SMS');
|
||||
$ui->assign('notify_t', 'd');
|
||||
$ui->display('register-otp.tpl');
|
||||
} else {
|
||||
$otp = rand(100000, 999999);
|
||||
file_put_contents($otpPath, $otp);
|
||||
Message::sendSMS($username, $config['CompanyName'] . "\nYour Verification code are: $otp");
|
||||
$ui->assign('username', $username);
|
||||
$ui->assign('notify', 'Verification code has been sent to your phone');
|
||||
$ui->assign('notify_t', 's');
|
||||
$ui->display('register-otp.tpl');
|
||||
}
|
||||
} else {
|
||||
run_hook('view_otp_register'); #HOOK
|
||||
$ui->display('register-rotp.tpl');
|
||||
}
|
||||
} else {
|
||||
$ui->assign('username', "");
|
||||
$ui->assign('fullname', "");
|
||||
$ui->assign('address', "");
|
||||
$ui->assign('email', "");
|
||||
$ui->assign('otp', false);
|
||||
run_hook('view_register'); #HOOK
|
||||
$ui->display('register.tpl');
|
||||
}
|
||||
break;
|
||||
}
|
||||
100
system/controllers/reports.php
Normal file
100
system/controllers/reports.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Reports'));
|
||||
$ui->assign('_system_menu', 'reports');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
$mdate = date('Y-m-d');
|
||||
$mtime = date('H:i:s');
|
||||
$tdate = date('Y-m-d', strtotime('today - 30 days'));
|
||||
$firs_day_month = date('Y-m-01');
|
||||
$this_week_start = date('Y-m-d', strtotime('previous sunday'));
|
||||
$before_30_days = date('Y-m-d', strtotime('today - 30 days'));
|
||||
$month_n = date('n');
|
||||
|
||||
switch ($action) {
|
||||
case 'by-date':
|
||||
case 'activation':
|
||||
$q = (_post('q') ? _post('q') : _get('q'));
|
||||
$keep = _post('keep');
|
||||
if (!empty($keep)) {
|
||||
ORM::raw_execute("DELETE FROM tbl_transactions WHERE date < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
|
||||
r2(U . "logs/list/", 's', "Delete logs older than $keep days");
|
||||
}
|
||||
if ($q != '') {
|
||||
$query = ORM::for_table('tbl_transactions')->where_like('invoice', '%' . $q . '%')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query, ['q' => $q]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_transactions')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('activation', $d);
|
||||
$ui->assign('q', $q);
|
||||
$ui->display('reports-activation.tpl');
|
||||
break;
|
||||
case 'daily-report':
|
||||
$query = ORM::for_table('tbl_transactions')->where('recharged_on', $mdate)->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
$dr = $query->sum('price');
|
||||
|
||||
$ui->assign('d', $d);
|
||||
$ui->assign('dr', $dr);
|
||||
$ui->assign('mdate', $mdate);
|
||||
$ui->assign('mtime', $mtime);
|
||||
run_hook('view_daily_reports'); #HOOK
|
||||
$ui->display('reports-daily.tpl');
|
||||
break;
|
||||
|
||||
case 'by-period':
|
||||
$ui->assign('mdate', $mdate);
|
||||
$ui->assign('mtime', $mtime);
|
||||
$ui->assign('tdate', $tdate);
|
||||
run_hook('view_reports_by_period'); #HOOK
|
||||
$ui->display('reports-period.tpl');
|
||||
break;
|
||||
|
||||
case 'period-view':
|
||||
$fdate = _post('fdate');
|
||||
$tdate = _post('tdate');
|
||||
$stype = _post('stype');
|
||||
|
||||
$d = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$d->where('type', $stype);
|
||||
}
|
||||
|
||||
$d->where_gte('recharged_on', $fdate);
|
||||
$d->where_lte('recharged_on', $tdate);
|
||||
$d->order_by_desc('id');
|
||||
$x = $d->find_many();
|
||||
|
||||
$dr = ORM::for_table('tbl_transactions');
|
||||
if ($stype != '') {
|
||||
$dr->where('type', $stype);
|
||||
}
|
||||
|
||||
$dr->where_gte('recharged_on', $fdate);
|
||||
$dr->where_lte('recharged_on', $tdate);
|
||||
$xy = $dr->sum('price');
|
||||
|
||||
$ui->assign('d', $x);
|
||||
$ui->assign('dr', $xy);
|
||||
$ui->assign('fdate', $fdate);
|
||||
$ui->assign('tdate', $tdate);
|
||||
$ui->assign('stype', $stype);
|
||||
run_hook('view_reports_period'); #HOOK
|
||||
$ui->display('reports-period-view.tpl');
|
||||
break;
|
||||
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
400
system/controllers/routers.php
Normal file
400
system/controllers/routers.php
Normal file
@@ -0,0 +1,400 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Network'));
|
||||
$ui->assign('_system_menu', 'network');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
|
||||
require_once 'system/autoload/PEAR2/Autoload.php';
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
use RouterOS\Exceptions\Socket\TimeoutException;
|
||||
use RouterOS\Exceptions\Socket\ConnectionException;
|
||||
|
||||
function mikrotik_get_resources($routerId)
|
||||
{
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
|
||||
if (!$mikrotik) {
|
||||
return [
|
||||
'status' => 'Offline',
|
||||
'error' => 'Router not found or disabled'
|
||||
];
|
||||
}
|
||||
|
||||
// Check cache first (30 second cache for successful connections)
|
||||
$cacheKey = 'router_resources_' . $routerId;
|
||||
$cachedData = null;
|
||||
if (function_exists('apcu_fetch')) {
|
||||
$cachedData = apcu_fetch($cacheKey);
|
||||
if ($cachedData && (time() - $cachedData['timestamp']) < 30) {
|
||||
return array_merge($cachedData, ['cached' => true]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if router is in circuit breaker state (too many recent failures)
|
||||
$circuitBreakerKey = 'router_failures_' . $routerId;
|
||||
$failureCount = 0;
|
||||
$lastFailure = 0;
|
||||
if (function_exists('apcu_fetch')) {
|
||||
$failureCount = apcu_fetch($circuitBreakerKey) ?: 0;
|
||||
$lastFailure = apcu_fetch($circuitBreakerKey . '_time') ?: 0;
|
||||
}
|
||||
|
||||
// If more than 3 failures in the last 5 minutes, skip connection attempt
|
||||
if ($failureCount >= 3 && (time() - $lastFailure) < 300) {
|
||||
return [
|
||||
'status' => 'Offline',
|
||||
'error' => 'Router temporarily unavailable (circuit breaker)',
|
||||
'cached' => true
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// Set a shorter timeout for the connection
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
if (!$client) {
|
||||
throw new Exception('Failed to create RouterOS client');
|
||||
}
|
||||
|
||||
// Set timeout for individual requests (5 seconds each)
|
||||
$health = $client->sendSync(new RouterOS\Request('/system health print'), 5);
|
||||
$res = $client->sendSync(new RouterOS\Request('/system resource print'), 5);
|
||||
|
||||
$resourceData = $res->getAllOfType(RouterOS\Response::TYPE_DATA)[0];
|
||||
$uptime = $resourceData->getProperty('uptime');
|
||||
$freeMemory = $resourceData->getProperty('free-memory');
|
||||
$totalMemory = $resourceData->getProperty('total-memory');
|
||||
$cpuLoad = $resourceData->getProperty('cpu-load');
|
||||
|
||||
$status = ($uptime !== null && $freeMemory !== null && $totalMemory !== null && $cpuLoad !== null) ? 'Online' : 'Offline';
|
||||
|
||||
$result = [
|
||||
'status' => $status,
|
||||
'uptime' => $uptime,
|
||||
'freeMemory' => mikrotik_formatSize($freeMemory),
|
||||
'totalMemory' => mikrotik_formatSize($totalMemory),
|
||||
'cpuLoad' => $cpuLoad . '%',
|
||||
'timestamp' => time()
|
||||
];
|
||||
|
||||
// Cache successful results
|
||||
if (function_exists('apcu_store') && $status === 'Online') {
|
||||
apcu_store($cacheKey, $result, 30);
|
||||
}
|
||||
|
||||
return $result;
|
||||
} catch (TimeoutException $e) {
|
||||
// Increment failure count for circuit breaker
|
||||
if (function_exists('apcu_store')) {
|
||||
apcu_store($circuitBreakerKey, $failureCount + 1, 300);
|
||||
apcu_store($circuitBreakerKey . '_time', time(), 300);
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'Offline',
|
||||
'error' => 'Connection timeout',
|
||||
'timeout' => true
|
||||
];
|
||||
} catch (ConnectionException $e) {
|
||||
// Increment failure count for circuit breaker
|
||||
if (function_exists('apcu_store')) {
|
||||
apcu_store($circuitBreakerKey, $failureCount + 1, 300);
|
||||
apcu_store($circuitBreakerKey . '_time', time(), 300);
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'Offline',
|
||||
'error' => 'Connection failed',
|
||||
'connection_error' => true
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
// Increment failure count for circuit breaker
|
||||
if (function_exists('apcu_store')) {
|
||||
apcu_store($circuitBreakerKey, $failureCount + 1, 300);
|
||||
apcu_store($circuitBreakerKey . '_time', time(), 300);
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'Offline',
|
||||
'error' => 'Unknown error: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function mikrotik_formatSize($size)
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB'];
|
||||
$unitIndex = 0;
|
||||
while ($size >= 1024 && $unitIndex < count($units) - 1) {
|
||||
$size /= 1024;
|
||||
$unitIndex++;
|
||||
}
|
||||
return round($size, 2) . ' ' . $units[$unitIndex];
|
||||
}
|
||||
|
||||
// Function to reboot MikroTik router
|
||||
function mikrotik_reboot($routerId)
|
||||
{
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
|
||||
if (!$mikrotik) {
|
||||
return [
|
||||
'status' => 'Offline',
|
||||
'message' => 'Router not found'
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
// Send the reboot command
|
||||
$client->sendSync(new RouterOS\Request('/system reboot'));
|
||||
|
||||
return [
|
||||
'status' => 'Rebooting',
|
||||
'message' => 'Router is rebooting'
|
||||
];
|
||||
} catch (TimeoutException | ConnectionException $e) {
|
||||
return [
|
||||
'status' => 'Error',
|
||||
'message' => 'Failed to connect to the router'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'list':
|
||||
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/routers.js"></script>');
|
||||
|
||||
$name = _post('name');
|
||||
if ($name != '') {
|
||||
$query = ORM::for_table('tbl_routers')->where_like('name', '%' . $name . '%')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query, ['name' => $name]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_routers')->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
run_hook('view_list_routers'); #HOOK
|
||||
$ui->display('routers.tpl');
|
||||
break;
|
||||
|
||||
case 'get_resources':
|
||||
if (isset($_GET['router_id'])) {
|
||||
$routerId = $_GET['router_id'];
|
||||
$resources = mikrotik_get_resources($routerId);
|
||||
echo json_encode($resources);
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'get_all_resources':
|
||||
// Get all enabled routers
|
||||
$routers = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
|
||||
$results = [];
|
||||
|
||||
foreach ($routers as $router) {
|
||||
$resources = mikrotik_get_resources($router['id']);
|
||||
$results[$router['id']] = $resources;
|
||||
}
|
||||
|
||||
echo json_encode($results);
|
||||
exit;
|
||||
break;
|
||||
|
||||
case 'reboot':
|
||||
if (isset($_GET['router_id'])) {
|
||||
$routerId = $_GET['router_id'];
|
||||
$result = mikrotik_reboot($routerId);
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
run_hook('view_add_routers'); #HOOK
|
||||
$ui->display('routers-add.tpl');
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
$id = $routes['2'];
|
||||
$d = ORM::for_table('tbl_routers')->find_one($id);
|
||||
if (!$d) {
|
||||
$d = ORM::for_table('tbl_routers')->where_equal('name', _get('name'))->find_one();
|
||||
}
|
||||
if ($d) {
|
||||
$ui->assign('d', $d);
|
||||
run_hook('view_router_edit'); #HOOK
|
||||
$ui->display('routers-edit.tpl');
|
||||
} else {
|
||||
r2(U . 'routers/list', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$id = $routes['2'];
|
||||
run_hook('router_delete'); #HOOK
|
||||
$d = ORM::for_table('tbl_routers')->find_one($id);
|
||||
if ($d) {
|
||||
$d->delete();
|
||||
r2(U . 'routers/list', 's', Lang::T('Data Deleted Successfully'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'add-post':
|
||||
$name = _post('name');
|
||||
$ip_address = _post('ip_address');
|
||||
$username = _post('username');
|
||||
$password = _post('password');
|
||||
$description = _post('description');
|
||||
$enabled = _post('enabled');
|
||||
|
||||
$msg = '';
|
||||
if (Validator::Length($name, 30, 4) == false) {
|
||||
$msg .= 'Name should be between 5 to 30 characters' . '<br>';
|
||||
}
|
||||
if ($ip_address == '' or $username == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_routers')->where('ip_address', $ip_address)->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('IP Router Already Exist') . '<br>';
|
||||
}
|
||||
if (strtolower($name) == 'radius') {
|
||||
$msg .= '<b>Radius</b> name is reserved<br>';
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
Mikrotik::getClient($ip_address, $username, $password);
|
||||
run_hook('add_router'); #HOOK
|
||||
$d = ORM::for_table('tbl_routers')->create();
|
||||
$d->name = $name;
|
||||
$d->ip_address = $ip_address;
|
||||
$d->username = $username;
|
||||
$d->password = $password;
|
||||
$d->description = $description;
|
||||
$d->enabled = $enabled;
|
||||
$d->save();
|
||||
|
||||
r2(U . 'routers/list', 's', Lang::T('Data Created Successfully'));
|
||||
} else {
|
||||
r2(U . 'routers/add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'download':
|
||||
$routerId = _post('router_id');
|
||||
$routerName = _post('router_name');
|
||||
|
||||
if ($routerId && $routerName) {
|
||||
$updateRouterIdStmt = $conn->prepare("UPDATE tbl_appconfig SET value = :router_id WHERE setting = 'router_id'");
|
||||
$updateRouterIdStmt->execute(['router_id' => $routerId]);
|
||||
|
||||
$updateRouterNameStmt = $conn->prepare("UPDATE tbl_appconfig SET value = :router_name WHERE setting = 'router_name'");
|
||||
$updateRouterNameStmt->execute(['router_name' => $routerName]);
|
||||
|
||||
header("Location: {$app_url}/system/plugin/download.php?download=1");
|
||||
exit;
|
||||
} else {
|
||||
r2(U . 'routers/list', 'e', Lang::T('Invalid router ID or name'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit-post':
|
||||
$name = _post('name');
|
||||
$ip_address = _post('ip_address');
|
||||
$username = _post('username');
|
||||
$password = _post('password');
|
||||
$description = _post('description');
|
||||
$enabled = $_POST['enabled'];
|
||||
$msg = '';
|
||||
if (Validator::Length($name, 30, 4) == false) {
|
||||
$msg .= 'Name should be between 5 to 30 characters' . '<br>';
|
||||
}
|
||||
if ($ip_address == '' or $username == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
|
||||
$id = _post('id');
|
||||
$d = ORM::for_table('tbl_routers')->find_one($id);
|
||||
if ($d) {
|
||||
} else {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
|
||||
if ($d['name'] != $name) {
|
||||
$c = ORM::for_table('tbl_routers')->where('name', $name)->where_not_equal('id', $id)->find_one();
|
||||
if ($c) {
|
||||
$msg .= 'Name Already Exists<br>';
|
||||
}
|
||||
}
|
||||
$oldname = $d['name'];
|
||||
|
||||
if ($d['ip_address'] != $ip_address) {
|
||||
$c = ORM::for_table('tbl_routers')->where('ip_address', $ip_address)->where_not_equal('id', $id)->find_one();
|
||||
if ($c) {
|
||||
$msg .= 'IP Already Exists<br>';
|
||||
}
|
||||
}
|
||||
|
||||
if (strtolower($name) == 'radius') {
|
||||
$msg .= '<b>Radius</b> name is reserved<br>';
|
||||
}
|
||||
|
||||
if ($msg == '') {
|
||||
Mikrotik::getClient($ip_address, $username, $password);
|
||||
run_hook('router_edit'); #HOOK
|
||||
$d->name = $name;
|
||||
$d->ip_address = $ip_address;
|
||||
$d->username = $username;
|
||||
$d->password = $password;
|
||||
$d->description = $description;
|
||||
$d->enabled = $enabled;
|
||||
$d->save();
|
||||
if ($name != $oldname) {
|
||||
$p = ORM::for_table('tbl_plans')->where('routers', $oldname)->find_result_set();
|
||||
$p->set('routers', $name);
|
||||
$p->save();
|
||||
$p = ORM::for_table('tbl_payment_gateway')->where('routers', $oldname)->find_result_set();
|
||||
$p->set('routers', $name);
|
||||
$p->save();
|
||||
$p = ORM::for_table('tbl_pool')->where('routers', $oldname)->find_result_set();
|
||||
$p->set('routers', $name);
|
||||
$p->save();
|
||||
$p = ORM::for_table('tbl_transactions')->where('routers', $oldname)->find_result_set();
|
||||
$p->set('routers', $name);
|
||||
$p->save();
|
||||
$p = ORM::for_table('tbl_user_recharges')->where('routers', $oldname)->find_result_set();
|
||||
$p->set('routers', $name);
|
||||
$p->save();
|
||||
$p = ORM::for_table('tbl_voucher')->where('routers', $oldname)->find_result_set();
|
||||
$p->set('routers', $name);
|
||||
$p->save();
|
||||
}
|
||||
r2(U . 'routers/list', 's', Lang::T('Data Updated Successfully'));
|
||||
} else {
|
||||
r2(U . 'routers/edit/' . $id, 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
r2(U . 'routers/list/', 's', '');
|
||||
}
|
||||
|
||||
?>
|
||||
750
system/controllers/services.php
Normal file
750
system/controllers/services.php
Normal file
@@ -0,0 +1,750 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Hotspot Plans'));
|
||||
$ui->assign('_system_menu', 'services');
|
||||
|
||||
$action = $routes['1'];
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
use PEAR2\Net\RouterOS;
|
||||
|
||||
require_once 'system/autoload/PEAR2/Autoload.php';
|
||||
|
||||
switch ($action) {
|
||||
case 'sync':
|
||||
set_time_limit(-1);
|
||||
if ($routes['2'] == 'hotspot') {
|
||||
$plans = ORM::for_table('tbl_bandwidth')->left_outer_join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type', 'Hotspot')->where('tbl_plans.enabled', '1')->find_many();
|
||||
$log = '';
|
||||
$router = '';
|
||||
foreach ($plans as $plan) {
|
||||
if ($plan['is_radius']) {
|
||||
if ($b['rate_down_unit'] == 'Kbps') {
|
||||
$raddown = '000';
|
||||
} else {
|
||||
$raddown = '000000';
|
||||
}
|
||||
if ($b['rate_up_unit'] == 'Kbps') {
|
||||
$radup = '000';
|
||||
} else {
|
||||
$radup = '000000';
|
||||
}
|
||||
$radiusRate = $plan['rate_up'] . $radup . '/' . $plan['rate_down'] . $raddown . '/' . $plan['burst'];
|
||||
Radius::planUpSert($plan['id'], $radiusRate);
|
||||
$log .= "DONE : Radius $plan[name_plan], $plan[shared_users], $radiusRate<br>";
|
||||
} else {
|
||||
if ($router != $plan['routers']) {
|
||||
$mikrotik = Mikrotik::info($plan['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$router = $plan['routers'];
|
||||
}
|
||||
if ($plan['rate_down_unit'] == 'Kbps') {
|
||||
$unitdown = 'K';
|
||||
} else {
|
||||
$unitdown = 'M';
|
||||
}
|
||||
if ($plan['rate_up_unit'] == 'Kbps') {
|
||||
$unitup = 'K';
|
||||
} else {
|
||||
$unitup = 'M';
|
||||
}
|
||||
$rate = $plan['rate_up'] . $unitup . "/" . $plan['rate_down'] . $unitdown;
|
||||
Mikrotik::addHotspotPlan($client, $plan['name_plan'], $plan['shared_users'], $rate);
|
||||
$log .= "DONE : $plan[name_plan], $plan[shared_users], $rate<br>";
|
||||
if (!empty($plan['pool_expired'])) {
|
||||
Mikrotik::setHotspotExpiredPlan($client, 'EXPIRED NUXBILL ' . $plan['pool_expired'], $plan['pool_expired']);
|
||||
$log .= "DONE Expired : EXPIRED NUXBILL $plan[pool_expired]<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
r2(U . 'services/hotspot', 's', $log);
|
||||
} else if ($routes['2'] == 'pppoe') {
|
||||
$plans = ORM::for_table('tbl_bandwidth')->left_outer_join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type', 'PPPOE')->where('tbl_plans.enabled', '1')->find_many();
|
||||
$log = '';
|
||||
$router = '';
|
||||
foreach ($plans as $plan) {
|
||||
if ($plan['is_radius']) {
|
||||
if ($b['rate_down_unit'] == 'Kbps') {
|
||||
$raddown = '000';
|
||||
} else {
|
||||
$raddown = '000000';
|
||||
}
|
||||
if ($b['rate_up_unit'] == 'Kbps') {
|
||||
$radup = '000';
|
||||
} else {
|
||||
$radup = '000000';
|
||||
}
|
||||
$radiusRate = $plan['rate_up'] . $radup . '/' . $plan['rate_down'] . $raddown . '/' . $plan['burst'];
|
||||
Radius::planUpSert($plan['id'], $radiusRate, $plan['pool']);
|
||||
$log .= "DONE : RADIUS $plan[name_plan], $plan[pool], $rate<br>";
|
||||
} else {
|
||||
if ($router != $plan['routers']) {
|
||||
$mikrotik = Mikrotik::info($plan['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
$router = $plan['routers'];
|
||||
}
|
||||
if ($plan['rate_down_unit'] == 'Kbps') {
|
||||
$unitdown = 'K';
|
||||
} else {
|
||||
$unitdown = 'M';
|
||||
}
|
||||
if ($plan['rate_up_unit'] == 'Kbps') {
|
||||
$unitup = 'K';
|
||||
} else {
|
||||
$unitup = 'M';
|
||||
}
|
||||
$rate = $plan['rate_up'] . $unitup . "/" . $plan['rate_down'] . $unitdown;
|
||||
Mikrotik::addPpoePlan($client, $plan['name_plan'], $plan['pool'], $rate);
|
||||
$log .= "DONE : $plan[name_plan], $plan[pool], $rate<br>";
|
||||
if (!empty($plan['pool_expired'])) {
|
||||
Mikrotik::setPpoePlan($client, 'EXPIRED NUXBILL ' . $plan['pool_expired'], $plan['pool_expired'], '512K/512K');
|
||||
$log .= "DONE Expired : EXPIRED NUXBILL $plan[pool_expired]<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
r2(U . 'services/pppoe', 's', $log);
|
||||
}
|
||||
r2(U . 'services/hotspot', 'w', 'Unknown command');
|
||||
case 'hotspot':
|
||||
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/hotspot.js"></script>');
|
||||
|
||||
$name = _post('name');
|
||||
if ($name != '') {
|
||||
$query = ORM::for_table('tbl_bandwidth')->left_outer_join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type', 'Hotspot')->where_like('tbl_plans.name_plan', '%' . $name . '%');
|
||||
$d = Paginator::findMany($query, ['name' => $name]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_bandwidth')->left_outer_join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type', 'Hotspot');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
run_hook('view_list_plans'); #HOOK
|
||||
$ui->display('hotspot.tpl');
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
$d = ORM::for_table('tbl_bandwidth')->find_many();
|
||||
$ui->assign('d', $d);
|
||||
$r = ORM::for_table('tbl_routers')->find_many();
|
||||
$ui->assign('r', $r);
|
||||
run_hook('view_add_plan'); #HOOK
|
||||
$ui->display('hotspot-add.tpl');
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
$id = $routes['2'];
|
||||
$d = ORM::for_table('tbl_plans')->find_one($id);
|
||||
if ($d) {
|
||||
$ui->assign('d', $d);
|
||||
$p = ORM::for_table('tbl_pool')->where('routers', $d['routers'])->find_many();
|
||||
$ui->assign('p', $p);
|
||||
$b = ORM::for_table('tbl_bandwidth')->find_many();
|
||||
$ui->assign('b', $b);
|
||||
run_hook('view_edit_plan'); #HOOK
|
||||
$ui->display('hotspot-edit.tpl');
|
||||
} else {
|
||||
r2(U . 'services/hotspot', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$id = $routes['2'];
|
||||
|
||||
$d = ORM::for_table('tbl_plans')->find_one($id);
|
||||
if ($d) {
|
||||
run_hook('delete_plan'); #HOOK
|
||||
if ($d['is_radius']) {
|
||||
Radius::planDelete($d['id']);
|
||||
} else {
|
||||
try {
|
||||
$mikrotik = Mikrotik::info($d['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removeHotspotPlan($client, $d['name_plan']);
|
||||
} catch (Exception $e) {
|
||||
//ignore exception, it means router has already deleted
|
||||
} catch (Throwable $e) {
|
||||
//ignore exception, it means router has already deleted
|
||||
}
|
||||
}
|
||||
|
||||
$d->delete();
|
||||
|
||||
r2(U . 'services/hotspot', 's', Lang::T('Data Deleted Successfully'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'add-post':
|
||||
$name = _post('name');
|
||||
$plan_type = _post('plan_type'); //Personal / Business
|
||||
$radius = _post('radius');
|
||||
$typebp = _post('typebp');
|
||||
$limit_type = _post('limit_type');
|
||||
$time_limit = _post('time_limit');
|
||||
$time_unit = _post('time_unit');
|
||||
$data_limit = _post('data_limit');
|
||||
$data_unit = _post('data_unit');
|
||||
$id_bw = _post('id_bw');
|
||||
$price = _post('price');
|
||||
$sharedusers = _post('sharedusers');
|
||||
$validity = _post('validity');
|
||||
$validity_unit = _post('validity_unit');
|
||||
$routers = _post('routers');
|
||||
$pool_expired = _post('pool_expired');
|
||||
$list_expired = _post('list_expired');
|
||||
$enabled = _post('enabled');
|
||||
$prepaid = _post('prepaid');
|
||||
|
||||
$msg = '';
|
||||
if (Validator::UnsignedNumber($validity) == false) {
|
||||
$msg .= 'The validity must be a number' . '<br>';
|
||||
}
|
||||
if (Validator::UnsignedNumber($price) == false) {
|
||||
$msg .= 'The price must be a number' . '<br>';
|
||||
}
|
||||
if ($name == '' or $id_bw == '' or $price == '' or $validity == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
if (empty($radius)) {
|
||||
if ($routers == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
}
|
||||
$d = ORM::for_table('tbl_plans')->where('name_plan', $name)->where('type', 'Hotspot')->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('Name Plan Already Exist') . '<br>';
|
||||
}
|
||||
|
||||
run_hook('add_plan'); #HOOK
|
||||
|
||||
if ($msg == '') {
|
||||
$b = ORM::for_table('tbl_bandwidth')->where('id', $id_bw)->find_one();
|
||||
if ($b['rate_down_unit'] == 'Kbps') {
|
||||
$unitdown = 'K';
|
||||
$raddown = '000';
|
||||
} else {
|
||||
$unitdown = 'M';
|
||||
$raddown = '000000';
|
||||
}
|
||||
if ($b['rate_up_unit'] == 'Kbps') {
|
||||
$unitup = 'K';
|
||||
$radup = '000';
|
||||
} else {
|
||||
$unitup = 'M';
|
||||
$radup = '000000';
|
||||
}
|
||||
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
|
||||
$radiusRate = $b['rate_up'] . $radup . '/' . $b['rate_down'] . $raddown . '/' . $b['burst'];
|
||||
$rate = trim($rate . " " . $b['burst']);
|
||||
// Create new plan
|
||||
$d = ORM::for_table('tbl_plans')->create();
|
||||
$d->name_plan = $name;
|
||||
$d->id_bw = $id_bw;
|
||||
$d->price = $price; // Set price with or without tax based on configuration
|
||||
$d->type = 'Hotspot';
|
||||
$d->typebp = $typebp;
|
||||
$d->plan_type = $plan_type;
|
||||
$d->limit_type = $limit_type;
|
||||
$d->time_limit = $time_limit;
|
||||
$d->time_unit = $time_unit;
|
||||
$d->data_limit = $data_limit;
|
||||
$d->data_unit = $data_unit;
|
||||
$d->validity = $validity;
|
||||
$d->validity_unit = $validity_unit;
|
||||
$d->shared_users = $sharedusers;
|
||||
if (!empty($radius)) {
|
||||
$d->is_radius = 1;
|
||||
$d->routers = '';
|
||||
} else {
|
||||
$d->is_radius = 0;
|
||||
$d->routers = $routers;
|
||||
}
|
||||
$d->pool_expired = $pool_expired;
|
||||
$d->list_expired = $list_expired;
|
||||
$d->enabled = $enabled;
|
||||
$d->prepaid = $prepaid;
|
||||
$d->save();
|
||||
$plan_id = $d->id();
|
||||
|
||||
if ($d['is_radius']) {
|
||||
Radius::planUpSert($plan_id, $radiusRate);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($routers);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::addHotspotPlan($client, $name, $sharedusers, $rate);
|
||||
if (!empty($pool_expired)) {
|
||||
Mikrotik::setHotspotExpiredPlan($client, 'EXPIRED NUXBILL ' . $pool_expired, $pool_expired);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
r2(U . 'services/hotspot', 's', Lang::T('Data Created Successfully'));
|
||||
} else {
|
||||
r2(U . 'services/add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'edit-post':
|
||||
$id = _post('id');
|
||||
$name = _post('name');
|
||||
$plan_type = _post('plan_type');
|
||||
$id_bw = _post('id_bw');
|
||||
$typebp = _post('typebp');
|
||||
$price = _post('price');
|
||||
$limit_type = _post('limit_type');
|
||||
$time_limit = _post('time_limit');
|
||||
$time_unit = _post('time_unit');
|
||||
$data_limit = _post('data_limit');
|
||||
$data_unit = _post('data_unit');
|
||||
$sharedusers = _post('sharedusers');
|
||||
$validity = _post('validity');
|
||||
$validity_unit = _post('validity_unit');
|
||||
$pool_expired = _post('pool_expired');
|
||||
$list_expired = _post('list_expired');
|
||||
$enabled = _post('enabled');
|
||||
$prepaid = _post('prepaid');
|
||||
$routers = _post('routers');
|
||||
$msg = '';
|
||||
if (Validator::UnsignedNumber($validity) == false) {
|
||||
$msg .= 'The validity must be a number' . '<br>';
|
||||
}
|
||||
if (Validator::UnsignedNumber($price) == false) {
|
||||
$msg .= 'The price must be a number' . '<br>';
|
||||
}
|
||||
if ($name == '' or $id_bw == '' or $price == '' or $validity == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
$d = ORM::for_table('tbl_plans')->where('id', $id)->find_one();
|
||||
if ($d) {
|
||||
} else {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
run_hook('edit_plan'); #HOOK
|
||||
if ($msg == '') {
|
||||
$b = ORM::for_table('tbl_bandwidth')->where('id', $id_bw)->find_one();
|
||||
if ($b['rate_down_unit'] == 'Kbps') {
|
||||
$unitdown = 'K';
|
||||
$raddown = '000';
|
||||
} else {
|
||||
$unitdown = 'M';
|
||||
$raddown = '000000';
|
||||
}
|
||||
if ($b['rate_up_unit'] == 'Kbps') {
|
||||
$unitup = 'K';
|
||||
$radup = '000';
|
||||
} else {
|
||||
$unitup = 'M';
|
||||
$radup = '000000';
|
||||
}
|
||||
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
|
||||
$radiusRate = $b['rate_up'] . $radup . '/' . $b['rate_down'] . $raddown . '/' . $b['burst'];
|
||||
|
||||
$rate = trim($rate . " " . $b['burst']);
|
||||
|
||||
if ($d['is_radius']) {
|
||||
Radius::planUpSert($id, $radiusRate);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($routers);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::setHotspotPlan($client, $name, $sharedusers, $rate);
|
||||
if (!empty($pool_expired)) {
|
||||
Mikrotik::setHotspotExpiredPlan($client, 'EXPIRED NUXBILL ' . $pool_expired, $pool_expired);
|
||||
}
|
||||
}
|
||||
$d->name_plan = $name;
|
||||
$d->id_bw = $id_bw;
|
||||
$d->price = $price; // Set price with or without tax based on configuration
|
||||
$d->typebp = $typebp;
|
||||
$d->limit_type = $limit_type;
|
||||
$d->time_limit = $time_limit;
|
||||
$d->time_unit = $time_unit;
|
||||
$d->data_limit = $data_limit;
|
||||
$d->plan_type = $plan_type;
|
||||
$d->data_unit = $data_unit;
|
||||
$d->validity = $validity;
|
||||
$d->validity_unit = $validity_unit;
|
||||
$d->shared_users = $sharedusers;
|
||||
$d->pool_expired = $pool_expired;
|
||||
$d->list_expired = $list_expired;
|
||||
$d->enabled = $enabled;
|
||||
$d->prepaid = $prepaid;
|
||||
$d->save();
|
||||
|
||||
r2(U . 'services/hotspot', 's', Lang::T('Data Updated Successfully'));
|
||||
} else {
|
||||
r2(U . 'services/edit/' . $id, 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'pppoe':
|
||||
$ui->assign('_title', Lang::T('PPPOE Plans'));
|
||||
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/pppoe.js"></script>');
|
||||
|
||||
$name = _post('name');
|
||||
if ($name != '') {
|
||||
$query = ORM::for_table('tbl_bandwidth')->left_outer_join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type', 'PPPOE')->where_like('tbl_plans.name_plan', '%' . $name . '%');
|
||||
$d = Paginator::findMany($query, ['name' => $name]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_bandwidth')->left_outer_join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type', 'PPPOE');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
run_hook('view_list_ppoe'); #HOOK
|
||||
$ui->display('pppoe.tpl');
|
||||
break;
|
||||
|
||||
case 'pppoe-add':
|
||||
$ui->assign('_title', Lang::T('PPPOE Plans'));
|
||||
$d = ORM::for_table('tbl_bandwidth')->find_many();
|
||||
$ui->assign('d', $d);
|
||||
$r = ORM::for_table('tbl_routers')->find_many();
|
||||
$ui->assign('r', $r);
|
||||
run_hook('view_add_ppoe'); #HOOK
|
||||
$ui->display('pppoe-add.tpl');
|
||||
break;
|
||||
|
||||
case 'pppoe-edit':
|
||||
$ui->assign('_title', Lang::T('PPPOE Plans'));
|
||||
$id = $routes['2'];
|
||||
$d = ORM::for_table('tbl_plans')->find_one($id);
|
||||
if ($d) {
|
||||
$ui->assign('d', $d);
|
||||
$p = ORM::for_table('tbl_pool')->where('routers', ($d['is_radius']) ? 'radius' : $d['routers'])->find_many();
|
||||
$ui->assign('p', $p);
|
||||
$b = ORM::for_table('tbl_bandwidth')->find_many();
|
||||
$ui->assign('b', $b);
|
||||
$r = [];
|
||||
if ($d['is_radius']) {
|
||||
$r = ORM::for_table('tbl_routers')->find_many();
|
||||
}
|
||||
$ui->assign('r', $r);
|
||||
run_hook('view_edit_ppoe'); #HOOK
|
||||
$ui->display('pppoe-edit.tpl');
|
||||
} else {
|
||||
r2(U . 'services/pppoe', 'e', Lang::T('Account Not Found'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'pppoe-delete':
|
||||
$id = $routes['2'];
|
||||
|
||||
$d = ORM::for_table('tbl_plans')->find_one($id);
|
||||
if ($d) {
|
||||
run_hook('delete_ppoe'); #HOOK
|
||||
if ($d['is_radius']) {
|
||||
Radius::planDelete($d['id']);
|
||||
} else {
|
||||
try {
|
||||
$mikrotik = Mikrotik::info($d['routers']);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::removePpoePlan($client, $d['name_plan']);
|
||||
} catch (Exception $e) {
|
||||
//ignore exception, it means router has already deleted
|
||||
} catch (Throwable $e) {
|
||||
//ignore exception, it means router has already deleted
|
||||
}
|
||||
}
|
||||
$d->delete();
|
||||
|
||||
r2(U . 'services/pppoe', 's', Lang::T('Data Deleted Successfully'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'pppoe-add-post':
|
||||
$name = _post('name_plan');
|
||||
$plan_type = _post('plan_type');
|
||||
$radius = _post('radius');
|
||||
$id_bw = _post('id_bw');
|
||||
$price = _post('price');
|
||||
$validity = _post('validity');
|
||||
$validity_unit = _post('validity_unit');
|
||||
$routers = _post('routers');
|
||||
$pool = _post('pool_name');
|
||||
$pool_expired = _post('pool_expired');
|
||||
$list_expired = _post('list_expired');
|
||||
$enabled = _post('enabled');
|
||||
$prepaid = _post('prepaid');
|
||||
|
||||
|
||||
$msg = '';
|
||||
if (Validator::UnsignedNumber($validity) == false) {
|
||||
$msg .= 'The validity must be a number' . '<br>';
|
||||
}
|
||||
if (Validator::UnsignedNumber($price) == false) {
|
||||
$msg .= 'The price must be a number' . '<br>';
|
||||
}
|
||||
if ($name == '' or $id_bw == '' or $price == '' or $validity == '' or $pool == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
if (empty($radius)) {
|
||||
if ($routers == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_plans')->where('name_plan', $name)->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('Name Plan Already Exist') . '<br>';
|
||||
}
|
||||
run_hook('add_ppoe'); #HOOK
|
||||
if ($msg == '') {
|
||||
$b = ORM::for_table('tbl_bandwidth')->where('id', $id_bw)->find_one();
|
||||
if ($b['rate_down_unit'] == 'Kbps') {
|
||||
$unitdown = 'K';
|
||||
$raddown = '000';
|
||||
} else {
|
||||
$unitdown = 'M';
|
||||
$raddown = '000000';
|
||||
}
|
||||
if ($b['rate_up_unit'] == 'Kbps') {
|
||||
$unitup = 'K';
|
||||
$radup = '000';
|
||||
} else {
|
||||
$unitup = 'M';
|
||||
$radup = '000000';
|
||||
}
|
||||
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
|
||||
$radiusRate = $b['rate_up'] . $radup . '/' . $b['rate_down'] . $raddown . '/' . $b['burst'];
|
||||
$rate = trim($rate . " " . $b['burst']);
|
||||
$d = ORM::for_table('tbl_plans')->create();
|
||||
$d->type = 'PPPOE';
|
||||
$d->name_plan = $name;
|
||||
$d->id_bw = $id_bw;
|
||||
$d->price = $price;
|
||||
$d->plan_type = $plan_type;
|
||||
$d->validity = $validity;
|
||||
$d->validity_unit = $validity_unit;
|
||||
$d->pool = $pool;
|
||||
if (!empty($radius)) {
|
||||
$d->is_radius = 1;
|
||||
$d->routers = '';
|
||||
} else {
|
||||
$d->is_radius = 0;
|
||||
$d->routers = $routers;
|
||||
}
|
||||
$d->pool_expired = $pool_expired;
|
||||
$d->list_expired = $list_expired;
|
||||
$d->enabled = $enabled;
|
||||
$d->prepaid = $prepaid;
|
||||
$d->save();
|
||||
$plan_id = $d->id();
|
||||
|
||||
if ($d['is_radius']) {
|
||||
Radius::planUpSert($plan_id, $radiusRate, $pool);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($routers);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::addPpoePlan($client, $name, $pool, $rate);
|
||||
if (!empty($pool_expired)) {
|
||||
Mikrotik::setPpoePlan($client, 'EXPIRED NUXBILL ' . $pool_expired, $pool_expired, '512K/512K');
|
||||
}
|
||||
}
|
||||
|
||||
r2(U . 'services/pppoe', 's', Lang::T('Data Created Successfully'));
|
||||
} else {
|
||||
r2(U . 'services/pppoe-add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit-pppoe-post':
|
||||
$id = _post('id');
|
||||
$plan_type = _post('plan_type');
|
||||
$name = _post('name_plan');
|
||||
$id_bw = _post('id_bw');
|
||||
$price = _post('price');
|
||||
$validity = _post('validity');
|
||||
$validity_unit = _post('validity_unit');
|
||||
$routers = _post('routers');
|
||||
$pool = _post('pool_name');
|
||||
$pool_expired = _post('pool_expired');
|
||||
$list_expired = _post('list_expired');
|
||||
$enabled = _post('enabled');
|
||||
$prepaid = _post('prepaid');
|
||||
|
||||
$msg = '';
|
||||
if (Validator::UnsignedNumber($validity) == false) {
|
||||
$msg .= 'The validity must be a number' . '<br>';
|
||||
}
|
||||
if (Validator::UnsignedNumber($price) == false) {
|
||||
$msg .= 'The price must be a number' . '<br>';
|
||||
}
|
||||
if ($name == '' or $id_bw == '' or $price == '' or $validity == '' or $pool == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_plans')->where('id', $id)->find_one();
|
||||
if ($d) {
|
||||
} else {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
run_hook('edit_ppoe'); #HOOK
|
||||
if ($msg == '') {
|
||||
$b = ORM::for_table('tbl_bandwidth')->where('id', $id_bw)->find_one();
|
||||
if ($b['rate_down_unit'] == 'Kbps') {
|
||||
$unitdown = 'K';
|
||||
$raddown = '000';
|
||||
} else {
|
||||
$unitdown = 'M';
|
||||
$raddown = '000000';
|
||||
}
|
||||
if ($b['rate_up_unit'] == 'Kbps') {
|
||||
$unitup = 'K';
|
||||
$radup = '000';
|
||||
} else {
|
||||
$unitup = 'M';
|
||||
$radup = '000000';
|
||||
}
|
||||
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
|
||||
$radiusRate = $b['rate_up'] . $radup . '/' . $b['rate_down'] . $raddown . '/' . $b['burst'];
|
||||
$rate = trim($rate . " " . $b['burst']);
|
||||
|
||||
if ($d['is_radius']) {
|
||||
Radius::planUpSert($id, $radiusRate, $pool);
|
||||
} else {
|
||||
$mikrotik = Mikrotik::info($routers);
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
Mikrotik::setPpoePlan($client, $name, $pool, $rate);
|
||||
if (!empty($pool_expired)) {
|
||||
Mikrotik::setPpoePlan($client, 'EXPIRED NUXBILL ' . $pool_expired, $pool_expired, '512K/512K');
|
||||
}
|
||||
}
|
||||
$d->name_plan = $name;
|
||||
$d->id_bw = $id_bw;
|
||||
$d->price = $price;
|
||||
$d->plan_type = $plan_type;
|
||||
$d->validity = $validity;
|
||||
$d->validity_unit = $validity_unit;
|
||||
$d->routers = $routers;
|
||||
$d->pool = $pool;
|
||||
$d->pool_expired = $pool_expired;
|
||||
$d->list_expired = $list_expired;
|
||||
$d->enabled = $enabled;
|
||||
$d->prepaid = $prepaid;
|
||||
$d->save();
|
||||
|
||||
r2(U . 'services/pppoe', 's', Lang::T('Data Updated Successfully'));
|
||||
} else {
|
||||
r2(U . 'services/pppoe-edit/' . $id, 'e', $msg);
|
||||
}
|
||||
break;
|
||||
case 'balance':
|
||||
$ui->assign('_title', Lang::T('Balance Plans'));
|
||||
$name = _post('name');
|
||||
if ($name != '') {
|
||||
$query = ORM::for_table('tbl_plans')->where('tbl_plans.type', 'Balance')->where_like('tbl_plans.name_plan', '%' . $name . '%');
|
||||
$d = Paginator::findMany($query, ['name' => $name]);
|
||||
} else {
|
||||
$query = ORM::for_table('tbl_plans')->where('tbl_plans.type', 'Balance');
|
||||
$d = Paginator::findMany($query);
|
||||
}
|
||||
|
||||
$ui->assign('d', $d);
|
||||
run_hook('view_list_balance'); #HOOK
|
||||
$ui->display('balance.tpl');
|
||||
break;
|
||||
case 'balance-add':
|
||||
$ui->assign('_title', Lang::T('Balance Plans'));
|
||||
run_hook('view_add_balance'); #HOOK
|
||||
$ui->display('balance-add.tpl');
|
||||
break;
|
||||
case 'balance-edit':
|
||||
$ui->assign('_title', Lang::T('Balance Plans'));
|
||||
$id = $routes['2'];
|
||||
$d = ORM::for_table('tbl_plans')->find_one($id);
|
||||
$ui->assign('d', $d);
|
||||
run_hook('view_edit_balance'); #HOOK
|
||||
$ui->display('balance-edit.tpl');
|
||||
break;
|
||||
case 'balance-delete':
|
||||
$id = $routes['2'];
|
||||
|
||||
$d = ORM::for_table('tbl_plans')->find_one($id);
|
||||
if ($d) {
|
||||
run_hook('delete_balance'); #HOOK
|
||||
$d->delete();
|
||||
r2(U . 'services/balance', 's', Lang::T('Data Deleted Successfully'));
|
||||
}
|
||||
break;
|
||||
case 'balance-edit-post':
|
||||
$id = _post('id');
|
||||
$name = _post('name');
|
||||
$price = _post('price');
|
||||
$enabled = _post('enabled');
|
||||
$prepaid = _post('prepaid');
|
||||
|
||||
$msg = '';
|
||||
if (Validator::UnsignedNumber($price) == false) {
|
||||
$msg .= 'The price must be a number' . '<br>';
|
||||
}
|
||||
if ($name == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_plans')->where('id', $id)->find_one();
|
||||
if ($d) {
|
||||
} else {
|
||||
$msg .= Lang::T('Data Not Found') . '<br>';
|
||||
}
|
||||
run_hook('edit_ppoe'); #HOOK
|
||||
if ($msg == '') {
|
||||
$d->name_plan = $name;
|
||||
$d->price = $price;
|
||||
$d->enabled = $enabled;
|
||||
$d->prepaid = 'yes';
|
||||
$d->save();
|
||||
|
||||
r2(U . 'services/balance', 's', Lang::T('Data Updated Successfully'));
|
||||
} else {
|
||||
r2(U . 'services/balance-edit/' . $id, 'e', $msg);
|
||||
}
|
||||
break;
|
||||
case 'balance-add-post':
|
||||
$name = _post('name');
|
||||
$price = _post('price');
|
||||
$enabled = _post('enabled');
|
||||
|
||||
$msg = '';
|
||||
if (Validator::UnsignedNumber($price) == false) {
|
||||
$msg .= 'The price must be a number' . '<br>';
|
||||
}
|
||||
if ($name == '') {
|
||||
$msg .= Lang::T('All field is required') . '<br>';
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_plans')->where('name_plan', $name)->find_one();
|
||||
if ($d) {
|
||||
$msg .= Lang::T('Name Plan Already Exist') . '<br>';
|
||||
}
|
||||
run_hook('add_ppoe'); #HOOK
|
||||
if ($msg == '') {
|
||||
$d = ORM::for_table('tbl_plans')->create();
|
||||
$d->type = 'Balance';
|
||||
$d->name_plan = $name;
|
||||
$d->id_bw = 0;
|
||||
$d->price = $price;
|
||||
$d->validity = 0;
|
||||
$d->validity_unit = 'Months';
|
||||
$d->routers = '';
|
||||
$d->pool = '';
|
||||
$d->enabled = $enabled;
|
||||
$d->prepaid = 'yes';
|
||||
$d->save();
|
||||
|
||||
r2(U . 'services/balance', 's', Lang::T('Data Created Successfully'));
|
||||
} else {
|
||||
r2(U . 'services/balance-add', 'e', $msg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
1013
system/controllers/settings.php
Normal file
1013
system/controllers/settings.php
Normal file
File diff suppressed because it is too large
Load Diff
26
system/controllers/sms.php
Normal file
26
system/controllers/sms.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Messaging System
|
||||
**/
|
||||
|
||||
// Include necessary files and initialize
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('SMS'));
|
||||
$ui->assign('_system_menu', 'sms');
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
// Fetch balance from the database
|
||||
$balance_row = ORM::for_table('balance')->find_one();
|
||||
$balance = $balance_row ? $balance_row->amount : 0;
|
||||
|
||||
// Set balance in template
|
||||
$ui->assign('balance', $balance);
|
||||
|
||||
// Display the SMS rate
|
||||
$sms_rate = 0.40; // Set SMS rate
|
||||
$ui->assign('sms_rate', $sms_rate);
|
||||
|
||||
// Display the template
|
||||
$ui->display('sms.tpl');
|
||||
}
|
||||
?>
|
||||
50
system/controllers/update_balance.php
Normal file
50
system/controllers/update_balance.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
// update_balance.php
|
||||
|
||||
// Include necessary files and initialize
|
||||
// Include your ORM or database connection logic here
|
||||
// For demonstration purposes, let's assume you're using a simple database connection
|
||||
|
||||
// Connect to the database (replace with your actual database connection code)
|
||||
$db_host = 'localhost';
|
||||
$db_name = 'byte';
|
||||
$db_user = 'root';
|
||||
$db_pass = 'password';
|
||||
|
||||
$response = array(); // Initialize response array
|
||||
|
||||
try {
|
||||
$db = new PDO("mysql:host={$db_host};dbname={$db_name}", $db_user, $db_pass);
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
// Get the paid amount from the POST request
|
||||
$amount = isset($_POST['amount']) ? $_POST['amount'] : 0;
|
||||
|
||||
// Update the balance in the database
|
||||
$stmt = $db->prepare("UPDATE balance SET sms_balance = sms_balance + :amount");
|
||||
$stmt->bindParam(':amount', $amount, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
|
||||
// Check if the update was successful
|
||||
$rowCount = $stmt->rowCount();
|
||||
if ($rowCount > 0) {
|
||||
// Success message
|
||||
$response['status'] = 'success';
|
||||
$response['message'] = "Balance updated successfully. Paid amount: {$amount}";
|
||||
} else {
|
||||
// Error message if no rows were affected
|
||||
$response['status'] = 'error';
|
||||
$response['message'] = "Failed to update balance.";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// Display any database errors
|
||||
$response['status'] = 'error';
|
||||
$response['message'] = "Error: " . $e->getMessage();
|
||||
}
|
||||
|
||||
// Close the database connection
|
||||
$db = null;
|
||||
|
||||
// Return JSON response
|
||||
echo json_encode($response);
|
||||
?>
|
||||
70
system/controllers/verify_payment.php
Normal file
70
system/controllers/verify_payment.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* Verify Paystack Payment
|
||||
**/
|
||||
|
||||
// Include necessary files and initialize
|
||||
_admin();
|
||||
$ui->assign('_title', Lang::T('Payment Verification'));
|
||||
$ui->assign('_system_menu', 'sms');
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (isset($_GET['reference'])) {
|
||||
$reference = $_GET['reference'];
|
||||
|
||||
$paystack_secret_key = 'sk_live_96e0fc93ffc118bef55d81438d569f9f704d1199'; // Replace with your Paystack secret key
|
||||
|
||||
$curl = curl_init();
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://api.paystack.co/transaction/verify/" . rawurlencode($reference),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"authorization: Bearer $paystack_secret_key",
|
||||
"content-type: application/json",
|
||||
"cache-control: no-cache"
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$err = curl_error($curl);
|
||||
curl_close($curl);
|
||||
|
||||
if ($err) {
|
||||
r2(U . 'sms.php', 'e', Lang::T('Curl returned error: ' . $err));
|
||||
} else {
|
||||
$tranx = json_decode($response, true);
|
||||
|
||||
if (!$tranx['status']) {
|
||||
r2(U . 'sms.php', 'e', Lang::T('API returned error: ' . $tranx['message']));
|
||||
} else {
|
||||
if ($tranx['data']['status'] == 'success') {
|
||||
// Payment was successful
|
||||
$email = $tranx['data']['customer']['email'];
|
||||
$amount = $tranx['data']['amount'] / 100; // Amount in KES
|
||||
$phone = $_POST['phone']; // Retrieve phone number from session or form data
|
||||
|
||||
// Update balance in the database
|
||||
$balance_row = ORM::for_table('balance')->find_one();
|
||||
$balance_row->amount += $amount;
|
||||
$balance_row->save();
|
||||
|
||||
// Send SMS notification
|
||||
$message = "Your SMS top-up of " . $amount . " KES was successful. Thank you!";
|
||||
$url = "https://api.netguru.co.ke/bytewave.php?message=" . urlencode($message) . "&phone=" . urlencode($phone) . "&senderid=Lineserve&api=60|wMNX8WcxrIuRAal5LseGW8ixwGlMljl4nIrMQpN5";
|
||||
|
||||
$smsResponse = file_get_contents($url);
|
||||
|
||||
if ($smsResponse) {
|
||||
r2(U . 'sms.php', 's', Lang::T('Payment Successful! Your SMS balance has been updated successfully!'));
|
||||
} else {
|
||||
r2(U . 'sms.php', 'e', Lang::T('Failed to send SMS. Please try again later.'));
|
||||
}
|
||||
} else {
|
||||
r2(U . 'sms.php', 'e', Lang::T('Payment verification failed.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r2(U . 'sms.php', 'e', Lang::T('No reference supplied.'));
|
||||
}
|
||||
?>
|
||||
67
system/controllers/voucher.php
Normal file
67
system/controllers/voucher.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
_auth();
|
||||
$ui->assign('_title', Lang::T('Voucher'));
|
||||
$ui->assign('_system_menu', 'voucher');
|
||||
|
||||
$action = $routes['1'];
|
||||
$user = User::_info();
|
||||
$ui->assign('_user', $user);
|
||||
|
||||
require_once 'system/autoload/PEAR2/Autoload.php';
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'activation':
|
||||
run_hook('view_activate_voucher'); #HOOK
|
||||
$ui->display('user-activation.tpl');
|
||||
break;
|
||||
|
||||
case 'activation-post':
|
||||
$code = _post('code');
|
||||
$v1 = ORM::for_table('tbl_voucher')->where('code', $code)->where('status', 0)->find_one();
|
||||
run_hook('customer_activate_voucher'); #HOOK
|
||||
if ($v1) {
|
||||
if (Package::rechargeUser($user['id'], $v1['routers'], $v1['id_plan'], "Voucher", $code)) {
|
||||
$v1->status = "1";
|
||||
$v1->user = $user['username'];
|
||||
$v1->save();
|
||||
r2(U . "voucher/list-activated", 's', Lang::T('Activation Vouchers Successfully'));
|
||||
} else {
|
||||
r2(U . 'voucher/activation', 'e', "Failed to refill account");
|
||||
}
|
||||
} else {
|
||||
r2(U . 'voucher/activation', 'e', Lang::T('Voucher Not Valid'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'list-activated':
|
||||
$ui->assign('_system_menu', 'list-activated');
|
||||
$query = ORM::for_table('tbl_transactions')->where('username', $user['username'])->order_by_desc('id');
|
||||
$d = Paginator::findMany($query);
|
||||
|
||||
$ui->assign('d', $d);
|
||||
run_hook('customer_view_activation_list'); #HOOK
|
||||
$ui->display('user-activation-list.tpl');
|
||||
|
||||
break;
|
||||
case 'invoice':
|
||||
$id = $routes[2];
|
||||
if(empty($id)){
|
||||
$in = ORM::for_table('tbl_transactions')->where('username', $user['username'])->order_by_desc('id')->find_one();
|
||||
}else{
|
||||
$in = ORM::for_table('tbl_transactions')->where('username', $user['username'])->where('id', $id)->find_one();
|
||||
}
|
||||
if($in){
|
||||
Package::createInvoice($in);
|
||||
$ui->display('invoice-customer.tpl');
|
||||
}else{
|
||||
r2(U . 'voucher/list-activated', 'e', Lang::T('Not Found'));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$ui->display('a404.tpl');
|
||||
}
|
||||
21
system/create_cron_logs_table.sql
Normal file
21
system/create_cron_logs_table.sql
Normal file
@@ -0,0 +1,21 @@
|
||||
-- Create cron logs table for tracking cron job execution
|
||||
CREATE TABLE IF NOT EXISTS `tbl_cron_logs` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`cron_type` varchar(50) NOT NULL DEFAULT 'main' COMMENT 'Type of cron job (main, reminder, etc.)',
|
||||
`started_at` datetime NOT NULL COMMENT 'When cron job started',
|
||||
`finished_at` datetime DEFAULT NULL COMMENT 'When cron job finished',
|
||||
`status` enum('running','completed','failed') NOT NULL DEFAULT 'running' COMMENT 'Cron job status',
|
||||
`expired_users_found` int(11) NOT NULL DEFAULT 0 COMMENT 'Number of expired users found',
|
||||
`expired_users_processed` int(11) NOT NULL DEFAULT 0 COMMENT 'Number of expired users processed',
|
||||
`notifications_sent` int(11) NOT NULL DEFAULT 0 COMMENT 'Number of notifications sent',
|
||||
`auto_renewals_attempted` int(11) NOT NULL DEFAULT 0 COMMENT 'Number of auto-renewals attempted',
|
||||
`auto_renewals_successful` int(11) NOT NULL DEFAULT 0 COMMENT 'Number of successful auto-renewals',
|
||||
`error_message` text DEFAULT NULL COMMENT 'Error message if cron failed',
|
||||
`execution_time` decimal(10,3) DEFAULT NULL COMMENT 'Execution time in seconds',
|
||||
`memory_usage` varchar(20) DEFAULT NULL COMMENT 'Memory usage at completion',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_cron_type` (`cron_type`),
|
||||
KEY `idx_started_at` (`started_at`),
|
||||
KEY `idx_status` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Cron job execution logs';
|
||||
24
system/create_monitoring_table.sql
Normal file
24
system/create_monitoring_table.sql
Normal file
@@ -0,0 +1,24 @@
|
||||
-- Create router monitoring table
|
||||
CREATE TABLE IF NOT EXISTS `tbl_router_monitoring` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`router_id` int(11) NOT NULL,
|
||||
`timestamp` datetime NOT NULL,
|
||||
`ping_status` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`api_status` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`uptime` varchar(50) DEFAULT NULL,
|
||||
`free_memory` bigint(20) DEFAULT NULL,
|
||||
`total_memory` bigint(20) DEFAULT NULL,
|
||||
`cpu_load` int(11) DEFAULT NULL,
|
||||
`temperature` varchar(20) DEFAULT NULL,
|
||||
`voltage` varchar(20) DEFAULT NULL,
|
||||
`error` text DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `router_id` (`router_id`),
|
||||
KEY `timestamp` (`timestamp`),
|
||||
KEY `router_timestamp` (`router_id`, `timestamp`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Add monitoring columns to router table if they don't exist
|
||||
ALTER TABLE `tbl_routers`
|
||||
ADD COLUMN IF NOT EXISTS `last_error` text DEFAULT NULL,
|
||||
ADD COLUMN IF NOT EXISTS `offline_since` datetime DEFAULT NULL;
|
||||
268
system/cron.php
Normal file
268
system/cron.php
Normal file
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
include dirname(__DIR__) . "/init.php";
|
||||
$isCli = true;
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
$isCli = false;
|
||||
echo "<pre>";
|
||||
}
|
||||
|
||||
// Start cron logging
|
||||
$logId = CronLog::start('main');
|
||||
$expiredUsersFound = 0;
|
||||
$expiredUsersProcessed = 0;
|
||||
$notificationsSent = 0;
|
||||
$autoRenewalsAttempted = 0;
|
||||
$autoRenewalsSuccessful = 0;
|
||||
|
||||
echo "PHP Time\t" . date('Y-m-d H:i:s') . "\n";
|
||||
$res = ORM::raw_execute('SELECT NOW() AS WAKTU;');
|
||||
$statement = ORM::get_last_statement();
|
||||
$rows = array();
|
||||
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
|
||||
echo "MYSQL Time\t" . $row['WAKTU'] . "\n";
|
||||
}
|
||||
|
||||
$_c = $config;
|
||||
|
||||
$textExpired = Lang::getNotifText('expired');
|
||||
|
||||
// Retrieve routers that are online
|
||||
$onlineRouters = ORM::for_table('tbl_routers')->where('status', 'online')->find_many();
|
||||
|
||||
// Convert the ORM result set to a regular array
|
||||
$onlineRoutersArray = $onlineRouters->as_array();
|
||||
|
||||
// Extract router names using array_map
|
||||
$onlineRouterNames = array_map(function($router) {
|
||||
return $router['name'];
|
||||
}, $onlineRoutersArray);
|
||||
|
||||
// If no online routers are found, skip processing user recharges
|
||||
if (empty($onlineRouterNames)) {
|
||||
echo "No online routers found. Skipping user recharge processing.\n";
|
||||
exit; // Skip further processing
|
||||
}
|
||||
|
||||
echo "Found " . count($onlineRouters) . " online routers.\n";
|
||||
|
||||
// First, handle suspended users that have reached their suspension time
|
||||
$suspendedUsers = ORM::for_table('tbl_user_recharges')->where('status', 'suspended')->where_lte('expiration', date("Y-m-d"))
|
||||
->where_in('routers', $onlineRouterNames)
|
||||
->find_many();
|
||||
|
||||
echo "Found " . count($suspendedUsers) . " suspended user(s) to process.\n";
|
||||
|
||||
foreach ($suspendedUsers as $susUser) {
|
||||
$date_now = strtotime(date("Y-m-d H:i:s"));
|
||||
$expiration = strtotime($susUser['expiration'] . ' ' . $susUser['time']);
|
||||
|
||||
if ($date_now >= $expiration) {
|
||||
echo "Suspending user: " . (($isCli) ? $susUser['username'] : Lang::maskText($susUser['username'])) . "\n";
|
||||
|
||||
$u = ORM::for_table('tbl_user_recharges')->where('id', $susUser['id'])->find_one();
|
||||
$c = ORM::for_table('tbl_customers')->where('id', $susUser['customer_id'])->find_one();
|
||||
$m = ORM::for_table('tbl_routers')->where('name', $susUser['routers'])->find_one();
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
|
||||
|
||||
if ($p['is_radius']) {
|
||||
if (empty($p['pool_expired'])) {
|
||||
print_r(Radius::customerDeactivate($c['username']));
|
||||
} else {
|
||||
Radius::upsertCustomerAttr($c['username'], 'Framed-Pool', $p['pool_expired'], ':=');
|
||||
print_r(Radius::disconnectCustomer($c['username']));
|
||||
}
|
||||
} else {
|
||||
$client = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
if ($susUser['type'] == 'Hotspot') {
|
||||
if (!empty($p['pool_expired'])) {
|
||||
Mikrotik::setHotspotUserPlan($client, $c['username'], 'EXPIRED NUXBILL ' . $p['pool_expired']);
|
||||
} else {
|
||||
Mikrotik::removeHotspotUser($client, $c['username']);
|
||||
}
|
||||
Mikrotik::removeHotspotActiveUser($client, $c['username']);
|
||||
} else if ($susUser['type'] == 'PPPOE') {
|
||||
if (!empty($p['pool_expired'])) {
|
||||
Mikrotik::setPpoeUserPlan($client, $c['username'], 'EXPIRED NUXBILL ' . $p['pool_expired']);
|
||||
} else {
|
||||
Mikrotik::removePpoeUser($client, $c['username']);
|
||||
}
|
||||
Mikrotik::removePpoeActive($client, $c['username']);
|
||||
}
|
||||
}
|
||||
|
||||
// Update status to 'off' (suspended)
|
||||
$u->status = 'off';
|
||||
$u->save();
|
||||
|
||||
echo "User " . $susUser['username'] . " has been suspended.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve user recharges with status 'on' and expiration less than or equal to today
|
||||
$d = ORM::for_table('tbl_user_recharges')->where('status', 'on')->where_lte('expiration', date("Y-m-d"))
|
||||
->where_in('routers', $onlineRouterNames) // Filter by routers that are online
|
||||
->find_many();
|
||||
|
||||
echo "Found " . count($d) . " user(s) on online routers.\n";
|
||||
|
||||
run_hook('cronjob'); #HOOK
|
||||
|
||||
foreach ($d as $ds) {
|
||||
if ($ds['type'] == 'Hotspot') { # HOTSPOT
|
||||
$date_now = strtotime(date("Y-m-d H:i:s"));
|
||||
$expiration = strtotime($ds['expiration'] . ' ' . $ds['time']);
|
||||
echo $ds['expiration'] . " : " . (($isCli) ? $ds['username'] : Lang::maskText($ds['username']));
|
||||
if ($date_now >= $expiration) {
|
||||
echo " : EXPIRED \r\n";
|
||||
$expiredUsersFound++;
|
||||
$u = ORM::for_table('tbl_user_recharges')->where('id', $ds['id'])->find_one();
|
||||
$c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
|
||||
$m = Mikrotik::info($ds['routers']);
|
||||
|
||||
// Now no need to check the router status again because the query already filters offline routers
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
|
||||
if ($p['is_radius']) {
|
||||
if (empty($p['pool_expired'])) {
|
||||
print_r(Radius::customerDeactivate($c['username']));
|
||||
} else {
|
||||
Radius::upsertCustomerAttr($c['username'], 'Framed-Pool', $p['pool_expired'], ':=');
|
||||
print_r(Radius::disconnectCustomer($c['username']));
|
||||
}
|
||||
} else {
|
||||
$client = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
if (!empty($p['pool_expired'])) {
|
||||
Mikrotik::setHotspotUserPackage($client, $c['username'], 'EXPIRED NUXBILL ' . $p['pool_expired']);
|
||||
} else {
|
||||
Mikrotik::removeHotspotUser($client, $c['username']);
|
||||
}
|
||||
Mikrotik::removeHotspotActiveUser($client, $c['username']);
|
||||
}
|
||||
echo Message::sendPackageNotification($c, $u['namebp'], $p['price'], 'expired', $config['user_notification_expired'], $p['type']) . "\n";
|
||||
$notificationsSent++;
|
||||
|
||||
// Update database user with status off
|
||||
$u->status = 'off';
|
||||
$u->save();
|
||||
$expiredUsersProcessed++;
|
||||
|
||||
// Autoreneewal from deposit
|
||||
if ($config['enable_balance'] == 'yes' && $c['auto_renewal']) {
|
||||
$autoRenewalsAttempted++;
|
||||
list($bills, $add_cost) = User::getBills($ds['customer_id']);
|
||||
if ($add_cost > 0) {
|
||||
if (!empty($add_cost)) {
|
||||
$p['price'] += $add_cost;
|
||||
}
|
||||
}
|
||||
if ($p && $p['enabled'] && $c['balance'] >= $p['price']) {
|
||||
if (Package::rechargeUser($ds['customer_id'], $ds['routers'], $p['id'], 'Customer', 'Balance')) {
|
||||
// if success, then get the balance
|
||||
Balance::min($ds['customer_id'], $p['price']);
|
||||
$autoRenewalsSuccessful++;
|
||||
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
||||
echo "auto renewall Success\n";
|
||||
} else {
|
||||
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
||||
echo "auto renewall Failed\n";
|
||||
Message::sendTelegram("FAILED RENEWAL #cron\n\n#u$c[username] #buy #Hotspot \n" . $p['name_plan'] .
|
||||
"\nRouter: " . $p['routers'] .
|
||||
"\nPrice: " . $p['price']);
|
||||
}
|
||||
} else {
|
||||
echo "no renewall | plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
||||
}
|
||||
} else {
|
||||
echo "no renewall | balance $config[enable_balance] auto_renewal $c[auto_renewal]\n";
|
||||
}
|
||||
} else
|
||||
echo " : ACTIVE \r\n";
|
||||
} else { # PPPOE
|
||||
$date_now = strtotime(date("Y-m-d H:i:s"));
|
||||
$expiration = strtotime($ds['expiration'] . ' ' . $ds['time']);
|
||||
echo $ds['expiration'] . " : " . (($isCli) ? $ds['username'] : Lang::maskText($ds['username']));
|
||||
if ($date_now >= $expiration) {
|
||||
echo " : EXPIRED \r\n";
|
||||
$expiredUsersFound++;
|
||||
$u = ORM::for_table('tbl_user_recharges')->where('id', $ds['id'])->find_one();
|
||||
$c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
|
||||
$m = ORM::for_table('tbl_routers')->where('name', $ds['routers'])->find_one();
|
||||
|
||||
// No need to check router status here, as we've already filtered offline routers
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
|
||||
if ($p['is_radius']) {
|
||||
if (empty($p['pool_expired'])) {
|
||||
print_r(Radius::customerDeactivate($c['username']));
|
||||
} else {
|
||||
Radius::upsertCustomerAttr($c['username'], 'Framed-Pool', $p['pool_expired'], ':=');
|
||||
print_r(Radius::disconnectCustomer($c['username']));
|
||||
}
|
||||
} else {
|
||||
$client = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
|
||||
if (!empty($p['pool_expired'])) {
|
||||
Mikrotik::setPpoeUserPlan($client, $c['username'], 'EXPIRED NUXBILL ' . $p['pool_expired']);
|
||||
} else {
|
||||
Mikrotik::removePpoeUser($client, $c['username']);
|
||||
}
|
||||
Mikrotik::removePpoeActive($client, $c['username']);
|
||||
}
|
||||
echo Message::sendPackageNotification($c, $u['namebp'], $p['price'], 'expired', $config['user_notification_expired'], $p['type']) . "\n";
|
||||
$notificationsSent++;
|
||||
|
||||
// Update database user with status off
|
||||
$u->status = 'off';
|
||||
$u->save();
|
||||
$expiredUsersProcessed++;
|
||||
|
||||
// Autoreneewal from deposit
|
||||
if ($config['enable_balance'] == 'yes' && $c['auto_renewal']) {
|
||||
$autoRenewalsAttempted++;
|
||||
list($bills, $add_cost) = User::getBills($ds['customer_id']);
|
||||
if ($add_cost > 0) {
|
||||
if (!empty($add_cost)) {
|
||||
$p['price'] += $add_cost;
|
||||
}
|
||||
}
|
||||
if ($p && $p['enabled'] && $c['balance'] >= $p['price']) {
|
||||
if (Package::rechargeUser($ds['customer_id'], $ds['routers'], $p['id'], 'Customer', 'Balance')) {
|
||||
// if success, then get the balance
|
||||
Balance::min($ds['customer_id'], $p['price']);
|
||||
$autoRenewalsSuccessful++;
|
||||
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
||||
echo "auto renewall Success\n";
|
||||
} else {
|
||||
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
||||
echo "auto renewall Failed\n";
|
||||
Message::sendTelegram("FAILED RENEWAL #cron\n\n#u$c[username] #buy #PPPOE \n" . $p['name_plan'] .
|
||||
"\nRouter: " . $p['routers'] .
|
||||
"\nPrice: " . $p['price']);
|
||||
}
|
||||
} else {
|
||||
echo "no renewall | plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
||||
}
|
||||
} else {
|
||||
echo "no renewall | balance $config[enable_balance] auto_renewal $c[auto_renewal]\n";
|
||||
}
|
||||
} else
|
||||
echo " : ACTIVE \r\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Complete cron logging
|
||||
echo "\n=== CRON JOB SUMMARY ===\n";
|
||||
echo "Expired users found: $expiredUsersFound\n";
|
||||
echo "Expired users processed: $expiredUsersProcessed\n";
|
||||
echo "Notifications sent: $notificationsSent\n";
|
||||
echo "Auto-renewals attempted: $autoRenewalsAttempted\n";
|
||||
echo "Auto-renewals successful: $autoRenewalsSuccessful\n";
|
||||
|
||||
// Update final statistics and complete logging
|
||||
CronLog::complete([
|
||||
'expired_users_found' => $expiredUsersFound,
|
||||
'expired_users_processed' => $expiredUsersProcessed,
|
||||
'notifications_sent' => $notificationsSent,
|
||||
'auto_renewals_attempted' => $autoRenewalsAttempted,
|
||||
'auto_renewals_successful' => $autoRenewalsSuccessful
|
||||
]);
|
||||
|
||||
echo "Cron job completed successfully!\n";
|
||||
77
system/cron_reminder.php
Normal file
77
system/cron_reminder.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* This file for reminding user about expiration
|
||||
* Example to run every at 7:00 in the morning
|
||||
* 0 7 * * * /usr/bin/php /var/www/system/cron_reminder.php
|
||||
**/
|
||||
|
||||
include dirname(__DIR__) . "/init.php";
|
||||
|
||||
// Start reminder cron logging
|
||||
$logId = CronLog::start('reminder');
|
||||
$remindersSent = 0;
|
||||
|
||||
$isCli = true;
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
$isCli = false;
|
||||
echo "<pre>";
|
||||
}
|
||||
|
||||
$d = ORM::for_table('tbl_user_recharges')->where('status', 'on')->find_many();
|
||||
|
||||
run_hook('cronjob_reminder'); #HOOK
|
||||
|
||||
|
||||
echo "PHP Time\t" . date('Y-m-d H:i:s') . "\n";
|
||||
$res = ORM::raw_execute('SELECT NOW() AS WAKTU;');
|
||||
$statement = ORM::get_last_statement();
|
||||
$rows = array();
|
||||
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
|
||||
echo "MYSQL Time\t" . $row['WAKTU'] . "\n";
|
||||
}
|
||||
|
||||
|
||||
$day7 = date('Y-m-d', strtotime("+7 day"));
|
||||
$day3 = date('Y-m-d', strtotime("+3 day"));
|
||||
$day1 = date('Y-m-d', strtotime("+1 day"));
|
||||
print_r([$day1, $day3, $day7]);
|
||||
foreach ($d as $ds) {
|
||||
if (in_array($ds['expiration'], [$day1, $day3, $day7])) {
|
||||
$u = ORM::for_table('tbl_user_recharges')->where('id', $ds['id'])->find_one();
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
|
||||
$c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
|
||||
if ($p['validity_unit'] == 'Period') {
|
||||
// Postpaid price from field
|
||||
$add_inv = User::getAttribute("Invoice", $ds['customer_id']);
|
||||
if (empty ($add_inv) or $add_inv == 0) {
|
||||
$price = $p['price'];
|
||||
} else {
|
||||
$price = $add_inv;
|
||||
}
|
||||
} else {
|
||||
$price = $p['price'];
|
||||
}
|
||||
if ($ds['expiration'] == $day7) {
|
||||
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_7_day'), $config['user_notification_reminder'], $p['type']) . "\n";
|
||||
$remindersSent++;
|
||||
} else if ($ds['expiration'] == $day3) {
|
||||
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_3_day'), $config['user_notification_reminder'], $p['type']) . "\n";
|
||||
$remindersSent++;
|
||||
} else if ($ds['expiration'] == $day1) {
|
||||
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_1_day'), $config['user_notification_reminder'], $p['type']) . "\n";
|
||||
$remindersSent++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Complete reminder cron logging
|
||||
echo "\n=== REMINDER CRON SUMMARY ===\n";
|
||||
echo "Reminders sent: $remindersSent\n";
|
||||
|
||||
CronLog::complete([
|
||||
'notifications_sent' => $remindersSent
|
||||
]);
|
||||
|
||||
echo "Reminder cron job completed successfully!\n";
|
||||
166
system/error_log
Normal file
166
system/error_log
Normal file
@@ -0,0 +1,166 @@
|
||||
[05-Jul-2024 03:20:03 UTC] Alloworigins called
|
||||
[05-Jul-2024 03:20:03 UTC] Type parameter missing
|
||||
[05-Jul-2024 03:25:02 UTC] Alloworigins called
|
||||
[05-Jul-2024 03:25:02 UTC] Type parameter missing
|
||||
[05-Jul-2024 11:36:03 Africa/Nairobi] PHP Fatal error: Uncaught PEAR2\Net\Transmitter\SocketException: stream_socket_client(): Unable to connect to tcp://us.labkom.us:6529 (Connection timed out) in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/Stream.php(145): PEAR2\Net\Transmitter\TcpClient->createException('stream_socket_c...', 0)
|
||||
#1 [internal function]: PEAR2\Net\Transmitter\Stream->handleError(2, 'stream_socket_c...', '/home/codevibe/...', 159)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(159): stream_socket_client('tcp://us.labkom...', 0, '', '60', 4, Resource id #10)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#5 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#6 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#7 {main}
|
||||
|
||||
Next PEAR2\Net\Transmitter\SocketException: Failed to connect with socket. in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(178): PEAR2\Net\Transmitter\TcpClient->createException('Failed to conne...', 8, Object(PEAR2\Net\Transmitter\SocketException))
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#5 {main}
|
||||
|
||||
Next PEAR2\Net\RouterOS\SocketException: Error connecting to RouterOS in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php:169
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#3 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php on line 169
|
||||
[05-Jul-2024 11:40:04 Africa/Nairobi] PHP Fatal error: Uncaught PEAR2\Net\Transmitter\SocketException: stream_socket_client(): Unable to connect to tcp://us.labkom.us:6529 (Network is unreachable) in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/Stream.php(145): PEAR2\Net\Transmitter\TcpClient->createException('stream_socket_c...', 0)
|
||||
#1 [internal function]: PEAR2\Net\Transmitter\Stream->handleError(2, 'stream_socket_c...', '/home/codevibe/...', 159)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(159): stream_socket_client('tcp://us.labkom...', 0, '', '60', 4, Resource id #10)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#5 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#6 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#7 {main}
|
||||
|
||||
Next PEAR2\Net\Transmitter\SocketException: Failed to connect with socket. in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(178): PEAR2\Net\Transmitter\TcpClient->createException('Failed to conne...', 8, Object(PEAR2\Net\Transmitter\SocketException))
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#5 {main}
|
||||
|
||||
Next PEAR2\Net\RouterOS\SocketException: Error connecting to RouterOS in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php:169
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#3 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php on line 169
|
||||
[05-Jul-2024 11:46:02 Africa/Nairobi] PHP Fatal error: Uncaught PEAR2\Net\Transmitter\SocketException: stream_socket_client(): Unable to connect to tcp://us.labkom.us:6529 (Connection timed out) in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/Stream.php(145): PEAR2\Net\Transmitter\TcpClient->createException('stream_socket_c...', 0)
|
||||
#1 [internal function]: PEAR2\Net\Transmitter\Stream->handleError(2, 'stream_socket_c...', '/home/codevibe/...', 159)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(159): stream_socket_client('tcp://us.labkom...', 0, '', '60', 4, Resource id #10)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#5 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#6 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#7 {main}
|
||||
|
||||
Next PEAR2\Net\Transmitter\SocketException: Failed to connect with socket. in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(178): PEAR2\Net\Transmitter\TcpClient->createException('Failed to conne...', 8, Object(PEAR2\Net\Transmitter\SocketException))
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#5 {main}
|
||||
|
||||
Next PEAR2\Net\RouterOS\SocketException: Error connecting to RouterOS in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php:169
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#3 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php on line 169
|
||||
[05-Jul-2024 11:51:03 Africa/Nairobi] PHP Fatal error: Uncaught PEAR2\Net\Transmitter\SocketException: stream_socket_client(): Unable to connect to tcp://us.labkom.us:6529 (Connection timed out) in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/Stream.php(145): PEAR2\Net\Transmitter\TcpClient->createException('stream_socket_c...', 0)
|
||||
#1 [internal function]: PEAR2\Net\Transmitter\Stream->handleError(2, 'stream_socket_c...', '/home/codevibe/...', 159)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(159): stream_socket_client('tcp://us.labkom...', 0, '', '60', 4, Resource id #10)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#5 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#6 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#7 {main}
|
||||
|
||||
Next PEAR2\Net\Transmitter\SocketException: Failed to connect with socket. in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(178): PEAR2\Net\Transmitter\TcpClient->createException('Failed to conne...', 8, Object(PEAR2\Net\Transmitter\SocketException))
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#5 {main}
|
||||
|
||||
Next PEAR2\Net\RouterOS\SocketException: Error connecting to RouterOS in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php:169
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#3 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php on line 169
|
||||
[06-Jul-2024 23:26:04 Africa/Nairobi] PHP Fatal error: Uncaught PEAR2\Net\Transmitter\SocketException: stream_socket_client(): Unable to connect to tcp://us.labkom.us:6529 (Connection timed out) in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/Stream.php(145): PEAR2\Net\Transmitter\TcpClient->createException('stream_socket_c...', 0)
|
||||
#1 [internal function]: PEAR2\Net\Transmitter\Stream->handleError(2, 'stream_socket_c...', '/home/codevibe/...', 159)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(159): stream_socket_client('tcp://us.labkom...', 0, '', '60', 4, Resource id #10)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#5 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#6 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#7 {main}
|
||||
|
||||
Next PEAR2\Net\Transmitter\SocketException: Failed to connect with socket. in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(178): PEAR2\Net\Transmitter\TcpClient->createException('Failed to conne...', 8, Object(PEAR2\Net\Transmitter\SocketException))
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#5 {main}
|
||||
|
||||
Next PEAR2\Net\RouterOS\SocketException: Error connecting to RouterOS in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php:169
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#3 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php on line 169
|
||||
[06-Jul-2024 23:31:04 Africa/Nairobi] PHP Fatal error: Uncaught PEAR2\Net\Transmitter\SocketException: stream_socket_client(): Unable to connect to tcp://us.labkom.us:6529 (Connection timed out) in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/Stream.php(145): PEAR2\Net\Transmitter\TcpClient->createException('stream_socket_c...', 0)
|
||||
#1 [internal function]: PEAR2\Net\Transmitter\Stream->handleError(2, 'stream_socket_c...', '/home/codevibe/...', 159)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(159): stream_socket_client('tcp://us.labkom...', 0, '', '60', 4, Resource id #10)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#5 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#6 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#7 {main}
|
||||
|
||||
Next PEAR2\Net\Transmitter\SocketException: Failed to connect with socket. in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php:225
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/Transmitter/TcpClient.php(178): PEAR2\Net\Transmitter\TcpClient->createException('Failed to conne...', 8, Object(PEAR2\Net\Transmitter\SocketException))
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php(160): PEAR2\Net\Transmitter\TcpClient->__construct('us.labkom.us', '6529', false, '60', 'admin/11', '', Resource id #10)
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#3 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#4 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#5 {main}
|
||||
|
||||
Next PEAR2\Net\RouterOS\SocketException: Error connecting to RouterOS in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php:169
|
||||
Stack trace:
|
||||
#0 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Client.php(162): PEAR2\Net\RouterOS\Communicator->__construct('us.labkom.us', '6529', false, NULL, 'admin/11', '', NULL)
|
||||
#1 /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/Mikrotik.php(24): PEAR2\Net\RouterOS\Client->__construct('us.labkom.us', 'admin', '11', '6529')
|
||||
#2 /home/codevibe/kejos.codevibeisp.co.ke/system/cron.php(45): Mikrotik::getClient('us.labkom.us:65...', 'admin', '11')
|
||||
#3 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/autoload/PEAR2/Net/RouterOS/Communicator.php on line 169
|
||||
58
system/get_resources.php
Normal file
58
system/get_resources.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
|
||||
* by https://t.me/ibnux
|
||||
**/
|
||||
|
||||
require_once 'system/autoload/PEAR2/Autoload.php';
|
||||
|
||||
function mikrotik_get_resources($routerId)
|
||||
{
|
||||
$mikrotik = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routerId);
|
||||
|
||||
if (!$mikrotik) {
|
||||
// Handle case where router is not found
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
|
||||
|
||||
$health = $client->sendSync(new RouterOS\Request('/system health print'));
|
||||
$res = $client->sendSync(new RouterOS\Request('/system resource print'));
|
||||
|
||||
$resourceData = $res->getAllOfType(RouterOS\Response::TYPE_DATA)[0];
|
||||
$uptime = $resourceData->getProperty('uptime');
|
||||
$freeMemory = $resourceData->getProperty('free-memory');
|
||||
$totalMemory = $resourceData->getProperty('total-memory');
|
||||
$cpuLoad = $resourceData->getProperty('cpu-load');
|
||||
|
||||
return [
|
||||
'uptime' => $uptime,
|
||||
'freeMemory' => mikrotik_formatSize($freeMemory),
|
||||
'totalMemory' => mikrotik_formatSize($totalMemory),
|
||||
'cpuLoad' => $cpuLoad . '%'
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
// Handle exceptions, e.g., connection errors
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to round the value and append the appropriate unit
|
||||
function mikrotik_formatSize($size)
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB'];
|
||||
$unitIndex = 0;
|
||||
while ($size >= 1024 && $unitIndex < count($units) - 1) {
|
||||
$size /= 1024;
|
||||
$unitIndex++;
|
||||
}
|
||||
return round($size, 2) . ' ' . $units[$unitIndex];
|
||||
}
|
||||
|
||||
if (isset($_GET['router_id'])) {
|
||||
$routerId = $_GET['router_id'];
|
||||
$resources = mikrotik_get_resources($routerId);
|
||||
echo json_encode($resources);
|
||||
}
|
||||
8
system/index.html
Normal file
8
system/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
||||
38
system/install_cron_logs.php
Normal file
38
system/install_cron_logs.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Install Cron Logs Table
|
||||
* Run this script to create the cron logs table
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__) . '/init.php';
|
||||
|
||||
echo "Installing Cron Logs Table...\n";
|
||||
|
||||
// Read the SQL file
|
||||
$sqlFile = __DIR__ . '/create_cron_logs_table.sql';
|
||||
if (!file_exists($sqlFile)) {
|
||||
echo "Error: SQL file not found: $sqlFile\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$sql = file_get_contents($sqlFile);
|
||||
|
||||
// Execute the SQL
|
||||
try {
|
||||
ORM::raw_execute($sql);
|
||||
echo "✓ Cron logs table created successfully!\n";
|
||||
|
||||
// Test the table
|
||||
$test = ORM::for_table('tbl_cron_logs')->count();
|
||||
echo "✓ Table test successful - found $test records\n";
|
||||
|
||||
echo "\nCron logs system is now ready!\n";
|
||||
echo "You can view logs at: " . APP_URL . "/settings/cron-logs\n";
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "✗ Error creating table: " . $e->getMessage() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "\nInstallation completed successfully!\n";
|
||||
59
system/lang/country.json
Normal file
59
system/lang/country.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"english": "en",
|
||||
"indonesia": "id",
|
||||
"spanish": "es",
|
||||
"turkish": "tr",
|
||||
"amharic": "am",
|
||||
"arabic": "ar",
|
||||
"basque": "eu",
|
||||
"bengali": "bn",
|
||||
"english-uk": "en-gb",
|
||||
"portuguese-brazil": "pt-br",
|
||||
"bulgarian": "bg",
|
||||
"catalan": "ca",
|
||||
"cherokee": "chr",
|
||||
"croatian": "hr",
|
||||
"czech": "cs",
|
||||
"danish": "da",
|
||||
"dutch": "nl",
|
||||
"estonian": "et",
|
||||
"filipino": "fil",
|
||||
"finnish": "fi",
|
||||
"french": "fr",
|
||||
"german": "de",
|
||||
"greek": "el",
|
||||
"gujarati": "gu",
|
||||
"hebrew": "iw",
|
||||
"hindi": "hi",
|
||||
"hungarian": "hu",
|
||||
"iran": "ir",
|
||||
"icelandic": "is",
|
||||
"italian": "it",
|
||||
"japanese": "ja",
|
||||
"kannada": "kn",
|
||||
"korean": "ko",
|
||||
"latvian": "lv",
|
||||
"lithuanian": "lt",
|
||||
"malay": "ms",
|
||||
"malayalam": "ml",
|
||||
"marathi": "mr",
|
||||
"norwegian": "no",
|
||||
"polish": "pl",
|
||||
"portuguese (portugal)": "pt-pt",
|
||||
"romanian": "ro",
|
||||
"russian": "ru",
|
||||
"serbian": "sr",
|
||||
"chinese (prc)": "zh-cn",
|
||||
"slovak": "sk",
|
||||
"slovenian": "sl",
|
||||
"swahili": "sw",
|
||||
"swedish": "sv",
|
||||
"tamil": "ta",
|
||||
"telugu": "te",
|
||||
"thai": "th",
|
||||
"chinese-taiwan": "zh-tw",
|
||||
"urdu": "ur",
|
||||
"ukrainian": "uk",
|
||||
"vietnamese": "vi",
|
||||
"welsh": "cy"
|
||||
}
|
||||
955
system/lang/english.json
Normal file
955
system/lang/english.json
Normal file
@@ -0,0 +1,955 @@
|
||||
{
|
||||
"Log_in": "Login",
|
||||
"Register": "Register",
|
||||
"Announcement": "Announcement",
|
||||
"Registration_Info": "Registration Info",
|
||||
"Voucher_not_found__please_buy_voucher_befor_register": "Voucher not found, please buy voucher befor register",
|
||||
"Register_Success__You_can_login_now": "Register Success! You can login now",
|
||||
"Log_in_to_Member_Panel": "Log in to Member Panel",
|
||||
"Register_as_Member": "Register as Member",
|
||||
"Enter_Admin_Area": "Enter Admin Area",
|
||||
"PHPNuxBill": "PHPNuxBill",
|
||||
"Username": "Username",
|
||||
"Password": "Password",
|
||||
"Passwords_does_not_match": "Passwords does not match",
|
||||
"Account_already_axist": "Account already axist",
|
||||
"Manage": "Manage",
|
||||
"Submit": "Submit",
|
||||
"Save_Changes": "Save Changes",
|
||||
"Cancel": "Cancel",
|
||||
"Edit": "Edit",
|
||||
"Delete": "Delete",
|
||||
"Welcome": "Welcome",
|
||||
"Data_Created_Successfully": "Data Created Successfully",
|
||||
"Data_Updated_Successfully": "Data Updated Successfully",
|
||||
"Data_Deleted_Successfully": "Data Deleted Successfully",
|
||||
"Static_Pages": "Static Pages",
|
||||
"Failed_to_save_page__make_sure_i_can_write_to_folder_pages___i_chmod_664_pages___html_i_": "Failed to save page, make sure i can write to folder pages, <i>chmod 664 pages\/*.html<i>",
|
||||
"Saving_page_success": "Saving page success",
|
||||
"Sometimes_you_need_to_refresh_3_times_until_content_change": "Sometimes you need to refresh 3 times until content change",
|
||||
"Dashboard": "Dashboard",
|
||||
"Search_Customers___": "Search Customers...",
|
||||
"My_Account": "My Account",
|
||||
"My_Profile": "My Profile",
|
||||
"Settings": "Settings",
|
||||
"Edit_Profile": "Edit Profile",
|
||||
"Change_Password": "Change Password",
|
||||
"Logout": "Logout",
|
||||
"Services": "Services",
|
||||
"Bandwidth_Plans": "Bandwidth Plans",
|
||||
"Bandwidth_Name": "Bandwidth Name",
|
||||
"New_Bandwidth": "New Bandwidth",
|
||||
"Edit_Bandwidth": "Edit Bandwidth",
|
||||
"Add_New_Bandwidth": "Add New Bandwidth",
|
||||
"Rate_Download": "Rate Download",
|
||||
"Rate_Upload": "Rate Upload",
|
||||
"Name_Bandwidth_Already_Exist": "Name Bandwidth Already Exist",
|
||||
"Hotspot_Plans": "Hotspot Plans",
|
||||
"PPPOE_Plans": "PPPOE Plans",
|
||||
"Plan_Name": "Plan Name",
|
||||
"New_Service_Plan": "New Service Plan",
|
||||
"Add_Service_Plan": "Add Service Plan",
|
||||
"Edit_Service_Plan": "Edit Service Plan",
|
||||
"Name_Plan_Already_Exist": "Name Plan Already Exist",
|
||||
"Plan_Type": "Plan Type",
|
||||
"Plan_Price": "Plan Price",
|
||||
"Limit_Type": "Limit Type",
|
||||
"Unlimited": "Unlimited",
|
||||
"Limited": "Limited",
|
||||
"Time_Limit": "Time Limit",
|
||||
"Data_Limit": "Data Limit",
|
||||
"Both_Limit": "Both Limit",
|
||||
"Plan_Validity": "Plan Validity",
|
||||
"Select_Bandwidth": "Select Bandwidth",
|
||||
"Shared_Users": "Shared Users",
|
||||
"Choose_User_Type_Sales_to_disable_access_to_Settings": "Choose User Type Sales to disable access to Settings",
|
||||
"Current_Password": "Current Password",
|
||||
"New_Password": "New Password",
|
||||
"Administrator": "Administrator",
|
||||
"Sales": "Sales",
|
||||
"Member": "Member",
|
||||
"Confirm_New_Password": "Confirm New Password",
|
||||
"Confirm_Password": "Confirm Password",
|
||||
"Full_Name": "Full Name",
|
||||
"User_Type": "User Type",
|
||||
"Address": "Address",
|
||||
"Created_On": "Created On",
|
||||
"Expires_On": "Expires On",
|
||||
"Phone_Number": "Phone Number",
|
||||
"User_deleted_Successfully": "User deleted Successfully",
|
||||
"Full_Administrator": "Full Administrator",
|
||||
"Keep_Blank_to_do_not_change_Password": "Keep Blank to do not change Password",
|
||||
"Keep_it_blank_if_you_do_not_want_to_show_currency_code": "Keep it blank if you do not want to show currency code",
|
||||
"Theme_Style": "Theme Style",
|
||||
"Theme_Color": "Theme Color",
|
||||
"Default_Language": "Default Language",
|
||||
"Network": "Network",
|
||||
"Routers": "Routers",
|
||||
"IP_Pool": "IP Pool",
|
||||
"New_Router": "New Router",
|
||||
"Add_Router": "Add Router",
|
||||
"Edit_Router": "Edit Router",
|
||||
"Router_Name": "Router Name",
|
||||
"IP_Address": "IP Address",
|
||||
"Router_Secret": "Router Secret",
|
||||
"Description": "Description",
|
||||
"IP_Router_Already_Exist": "IP Router Already Exist",
|
||||
"Name_Pool": "Name Pool",
|
||||
"Range_IP": "Range IP",
|
||||
"New_Pool": "New Pool",
|
||||
"Add_Pool": "Add Pool",
|
||||
"Edit_Pool": "Edit Pool",
|
||||
"Pool_Name_Already_Exist": "Pool Name Already Exist",
|
||||
"Refill_Account": "Refill Account",
|
||||
"Recharge_Account": "Recharge Account",
|
||||
"Select_Account": "Select Account",
|
||||
"Service_Plan": "Service Plan",
|
||||
"Recharge": "Recharge",
|
||||
"Method": "Method",
|
||||
"Account_Created_Successfully": "Account Created Successfully",
|
||||
"Database_Status": "Database Status",
|
||||
"Total_Database_Size": "Total Database Size",
|
||||
"Download_Database_Backup": "Download Database Backup",
|
||||
"Table_Name": "Table Name",
|
||||
"Rows": "Rows",
|
||||
"Size": "Size",
|
||||
"Customer": "Customer",
|
||||
"Add_New_Contact": "Add New Contact",
|
||||
"Edit_Contact": "Edit Contact",
|
||||
"List_Contact": "List Contact",
|
||||
"Manage_Contact": "Manage Contact",
|
||||
"Reports": "Reports",
|
||||
"Daily_Reports": "Daily Reports",
|
||||
"Period_Reports": "Period Reports",
|
||||
"All_Transactions": "All Transactions",
|
||||
"Total_Income": "Total Income",
|
||||
"All_Transactions_at_Date": "All Transactions at Date",
|
||||
"Export_for_Print": "Export for Print",
|
||||
"Print": "Print",
|
||||
"Export_to_PDF": "Export to PDF",
|
||||
"Click_Here_to_Print": "Click Here to Print",
|
||||
"You_can_use_html_tag": "You can use html tag",
|
||||
"Date_Format": "Date Format",
|
||||
"Income_Today": "Income Today",
|
||||
"Income_This_Month": "Income This Month",
|
||||
"Users_Active": "Users Active",
|
||||
"Total_Users": "Total Users",
|
||||
"Users": "Users",
|
||||
"Edit_User": "Edit User",
|
||||
"Last_Login": "Last Login",
|
||||
"Administrator_Users": "Administrator Users",
|
||||
"Manage_Administrator": "Manage Administrator",
|
||||
"Add_New_Administrator": "Add New Administrator",
|
||||
"Localisation": "Localisation",
|
||||
"Backup_Restore": "Backup\/Restore",
|
||||
"General_Settings": "General Settings",
|
||||
"Date": "Date",
|
||||
"Login_Successful": "Login Successful",
|
||||
"Failed_Login": "Failed Login",
|
||||
"Settings_Saved_Successfully": "Settings Saved Successfully",
|
||||
"User_Updated_Successfully": "User Updated Successfully",
|
||||
"User_Expired__Today": "User Expired, Today",
|
||||
"Activity_Log": "Activity Log",
|
||||
"View_Reports": "View Reports",
|
||||
"View_All": "View All",
|
||||
"Number_of_Vouchers": "Number of Vouchers",
|
||||
"Length_Code": "Length Code",
|
||||
"Code_Voucher": "Code Voucher",
|
||||
"Voucher": "Voucher",
|
||||
"Hotspot_Voucher": "Hotspot Voucher",
|
||||
"Status_Voucher": "Status Voucher",
|
||||
"Add_Vouchers": "Add Vouchers",
|
||||
"Create_Vouchers_Successfully": "Create Vouchers Successfully",
|
||||
"Generate": "Generate",
|
||||
"Print_side_by_side__it_will_easy_to_cut": "Print side by side, it will easy to cut",
|
||||
"From_Date": "From Date",
|
||||
"To_Date": "To Date",
|
||||
"New_Service": "New Service",
|
||||
"Type": "Type",
|
||||
"Finish": "Finish",
|
||||
"Application_Name__Company_Name": "Application Name\/ Company Name",
|
||||
"This_Name_will_be_shown_on_the_Title": "This Name will be shown on the Title",
|
||||
"Next": "Next",
|
||||
"Last": "Last",
|
||||
"Timezone": "Timezone",
|
||||
"Decimal_Point": "Decimal Point",
|
||||
"Thousands_Separator": "Thousands Separator",
|
||||
"Currency_Code": "Currency Code",
|
||||
"Order_Voucher": "Order Voucher",
|
||||
"Voucher_Activation": "Voucher Activation",
|
||||
"List_Activated_Voucher": "List Activated Voucher",
|
||||
"Enter_voucher_code_here": "Enter voucher code here",
|
||||
"Private_Message": "Private Message",
|
||||
"Inbox": "Inbox",
|
||||
"Outbox": "Outbox",
|
||||
"Compose": "Compose",
|
||||
"Send_to": "Send to",
|
||||
"Title": "Title",
|
||||
"Message": "Message",
|
||||
"Your_Account_Information": "Your Account Information",
|
||||
"Welcome_to_the_Panel_Members_page__on_this_page_you_can_": "Welcome to the Panel Members page, on this page you can:",
|
||||
"Invalid_Username_or_Password": "Invalid Username or Password",
|
||||
"You_do_not_have_permission_to_access_this_page": "You do not have permission to access this page",
|
||||
"Incorrect_Current_Password": "Incorrect Current Password",
|
||||
"Password_changed_successfully__Please_login_again": "Password changed successfully, Please login again",
|
||||
"All_field_is_required": "All field is required",
|
||||
"Voucher_Not_Valid": "Voucher Not Valid",
|
||||
"Activation_Vouchers_Successfully": "Activation Vouchers Successfully",
|
||||
"Data_Not_Found": "Data Not Found",
|
||||
"Search_by_Username": "Search by Username",
|
||||
"Search_by_Name": "Search by Name",
|
||||
"Search_by_Code_Voucher": "Search by Code Voucher",
|
||||
"Search": "Search",
|
||||
"Select_a_customer": "Select a customer",
|
||||
"Select_Routers": "Select Routers",
|
||||
"Select_Plans": "Select Plans",
|
||||
"Select_Pool": "Select Pool",
|
||||
"Hrs": "Hrs",
|
||||
"Mins": "Mins",
|
||||
"Days": "Days",
|
||||
"Months": "Months",
|
||||
"Add_Language": "Add Language",
|
||||
"Language_Name": "Language Name",
|
||||
"Folder_Name": "Folder Name",
|
||||
"Translator": "Translator",
|
||||
"Language_Name_Already_Exist": "Language Name Already Exist",
|
||||
"Payment_Gateway": "Payment Gateway",
|
||||
"Community": "Community",
|
||||
"1_user_can_be_used_for_many_devices_": "1 user can be used for many devices?",
|
||||
"Cannot_be_change_after_saved": "Cannot be change after saved",
|
||||
"Explain_Coverage_of_router": "Explain Coverage of router",
|
||||
"Name_of_Area_that_router_operated": "Name of Area that router operated",
|
||||
"Payment_Notification_URL__Recurring_Notification_URL__Pay_Account_Notification_URL": "Payment Notification URL, Recurring Notification URL, Pay Account Notification URL",
|
||||
"Finish_Redirect_URL__Unfinish_Redirect_URL__Error_Redirect_URL": "Finish Redirect URL, Unfinish Redirect URL, Error Redirect URL",
|
||||
"Status": "Status",
|
||||
"Plan_Not_found": "Plan Not found",
|
||||
"Failed_to_create_transaction_": "Failed to create transaction.",
|
||||
"Seller_has_not_yet_setup_Xendit_payment_gateway": "Seller has not yet setup Xendit payment gateway",
|
||||
"Admin_has_not_yet_setup_Xendit_payment_gateway__please_tell_admin": "Admin has not yet setup Xendit payment gateway, please tell admin",
|
||||
"You_already_have_unpaid_transaction__cancel_it_or_pay_it_": "You already have unpaid transaction, cancel it or pay it.",
|
||||
"Transaction_Not_found": "Transaction Not found",
|
||||
"Cancel_it_": "Cancel it?",
|
||||
"expired": "expired",
|
||||
"Check_for_Payment": "Check for Payment",
|
||||
"Transaction_still_unpaid_": "Transaction still unpaid.",
|
||||
"Paid_Date": "Paid Date",
|
||||
"Transaction_has_been_paid_": "Transaction has been paid.",
|
||||
"PAID": "PAID",
|
||||
"CANCELED": "CANCELED",
|
||||
"UNPAID": "UNPAID",
|
||||
"PAY_NOW": "PAY NOW",
|
||||
"Buy_Hotspot_Plan": "Buy Hotspot Plan",
|
||||
"Buy_PPOE_Plan": "Buy PPOE Plan",
|
||||
"Package": "Package",
|
||||
"Order_Internet_Package": "Order Internet Package",
|
||||
"Unknown_Command_": "Unknown Command.",
|
||||
"Checking_payment": "Checking payment",
|
||||
"Create_Transaction_Success": "Create Transaction Success",
|
||||
"You_have_unpaid_transaction": "You have unpaid transaction",
|
||||
"TripayPayment_Channel": "TripayPayment Channel",
|
||||
"Payment_Channel": "Payment Channel",
|
||||
"Payment_check_failed_": "Payment check failed.",
|
||||
"Order_Package": "Order Package",
|
||||
"Transactions": "Transactions",
|
||||
"Payments": "Payments",
|
||||
"History": "History",
|
||||
"Order_History": "Order History",
|
||||
"Gateway": "Gateway",
|
||||
"Date_Done": "Date Done",
|
||||
"Unpaid_Order": "Unpaid Order",
|
||||
"Payment_Gateway_Not_Found": "Payment Gateway Not Found",
|
||||
"Payment_Gateway_saved_successfully": "Payment Gateway saved successfully",
|
||||
"ORDER": "ORDER",
|
||||
"Package_History": "Package History",
|
||||
"Buy_History": "Buy History",
|
||||
"Activation_History": "Activation History",
|
||||
"Buy_Package": "Buy Package",
|
||||
"Email": "Email",
|
||||
"Company_Footer": "Company Footer",
|
||||
"Will_show_below_user_pages": "Will show below user pages",
|
||||
"Request_OTP": "Request OTP",
|
||||
"Verification_Code": "Verification Code",
|
||||
"SMS_Verification_Code": "SMS Verification Code",
|
||||
"Please_enter_your_email_address": "Please enter your email address",
|
||||
"Failed_to_create_Paypal_transaction_": "Failed to create Paypal transaction.",
|
||||
"Plugin": "Plugin",
|
||||
"Plugin_Manager": "Plugin Manager",
|
||||
"User_Notification": "User Notification",
|
||||
"Messages_Settings": "Messages Settings",
|
||||
"Expired_Notification": "Expired Notification",
|
||||
"User_will_get_notification_when_package_expired": "User will get notification when package expired",
|
||||
"Expired_Notification_Message": "Expired Notification Message",
|
||||
"Payment_Notification": "Payment Notification",
|
||||
"User_will_get_invoice_notification_when_buy_package_or_package_refilled": "User will get invoice notification when buy package or package refilled, and welcome notification when account is created",
|
||||
"Current_IP": "Current IP",
|
||||
"Current_MAC": "Current MAC",
|
||||
"Login_Status": "Login Status",
|
||||
"Login_Request_successfully": "Login Request successfully",
|
||||
"Logout_Request_successfully": "Logout Request successfully",
|
||||
"Disconnect_Internet_": "Disconnect Internet?",
|
||||
"Not_Online__Login_now_": "Not Online, Login now?",
|
||||
"You_are_Online__Logout_": "You are Online, Logout?",
|
||||
"Connect_to_Internet_": "Connect to Internet?",
|
||||
"Your_account_not_connected_to_internet": "Your account not connected to internet",
|
||||
"Failed_to_create_transaction__": "Failed to create transaction. ",
|
||||
"Failed_to_check_status_transaction__": "Failed to check status transaction. ",
|
||||
"Disable_Voucher": "Disable Voucher",
|
||||
"Balance": "Balance",
|
||||
"Balance_System": "Balance System",
|
||||
"Enable_System": "Enable System",
|
||||
"Allow_Transfer": "Allow Transfer",
|
||||
"Telegram_Notification": "Telegram Notification",
|
||||
"SMS_OTP_Registration": "SMS OTP Registration",
|
||||
"Whatsapp_Notification": "Whatsapp Notification",
|
||||
"Tawk_to_Chat_Widget": "Tawk.to Chat Widget",
|
||||
"Invoice": "Invoice",
|
||||
"Country_Code_Phone": "Country Code Phone",
|
||||
"Voucher_activation_menu_will_be_hidden": "Voucher activation menu will be hidden",
|
||||
"Customer_can_deposit_money_to_buy_voucher": "Customer can deposit money to buy voucher",
|
||||
"Allow_balance_transfer_between_customers": "Allow balance transfer between customers",
|
||||
"Reminder_Notification": "Reminder Notification",
|
||||
"Reminder_Notification_Message": "Reminder Notification Message",
|
||||
"Registration_Notification": "Registration Notification",
|
||||
"User_will_get_welcome_notification_when_account_is_created": "User will get welcome notification when account is created",
|
||||
"User_Registration_Message": "User Registration Message",
|
||||
"Registration_Messages": "Registration Messages",
|
||||
"Expiration___Reminder_Messages": "Expiration & Reminder Messages",
|
||||
"Payment___Invoice_Messages": "Payment & Invoice Messages",
|
||||
"Reminder_7_days": "Reminder 7 days",
|
||||
"Reminder_3_days": "Reminder 3 days",
|
||||
"Reminder_1_day": "Reminder 1 day",
|
||||
"PPPOE_Password": "PPPOE Password",
|
||||
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_user_password": "User Cannot change this, only admin. if it Empty it will use user password",
|
||||
"Invoice_Balance_Message": "Invoice Balance Message",
|
||||
"Invoice_Notification_Payment": "Invoice Notification Payment",
|
||||
"Balance_Notification_Payment": "Balance Notification Payment",
|
||||
"Balance_Plans": "Balance Plans",
|
||||
"Buy_Balance": "Buy Balance",
|
||||
"Price": "Price",
|
||||
"Validity": "Validity",
|
||||
"Disable_auto_renewal_": "Disable auto renewal?",
|
||||
"Auto_Renewal_On": "Auto Renewal On",
|
||||
"Enable_auto_renewal_": "Enable auto renewal?",
|
||||
"Auto_Renewal_Off": "Auto Renewal Off",
|
||||
"Refill_Balance": "Refill Balance",
|
||||
"Invoice_Footer": "Invoice Footer",
|
||||
"Pay_With_Balance": "Pay With Balance",
|
||||
"Pay_this_with_Balance__your_active_package_will_be_overwrite": "Pay this with Balance? your active package will be overwrite",
|
||||
"Success_to_buy_package": "Success to buy package",
|
||||
"Auto_Renewal": "Auto Renewal",
|
||||
"View": "View",
|
||||
"Back": "Back",
|
||||
"Active": "Active",
|
||||
"Transfer_Balance": "Transfer Balance",
|
||||
"Send_your_balance_": "Send your balance?",
|
||||
"Send": "Send",
|
||||
"Cannot_send_to_yourself": "Cannot send to yourself",
|
||||
"Sending_balance_success": "Sending balance success",
|
||||
"From": "From",
|
||||
"To": "To",
|
||||
"insufficient_balance": "insufficient balance",
|
||||
"Send_Balance": "Send Balance",
|
||||
"Received_Balance": "Received Balance",
|
||||
"Minimum_Balance_Transfer": "Minimum Balance Transfer",
|
||||
"Minimum_Transfer": "Minimum Transfer",
|
||||
"Company_Logo": "Company Logo",
|
||||
"Expired_IP_Pool": "Expired IP Pool",
|
||||
"Proxy": "Proxy",
|
||||
"Proxy_Server": "Proxy Server",
|
||||
"Proxy_Server_Login": "Proxy Server Login",
|
||||
"Hotspot_Plan": "Hotspot Plan",
|
||||
"PPPOE_Plan": "PPPOE Plan",
|
||||
"UNKNOWN": "UNKNOWN",
|
||||
"Are_You_Sure_": "Are You Sure?",
|
||||
"Success_to_send_package": "Success to send package",
|
||||
"Target_has_active_plan__different_with_current_plant_": "Target has active plan, different with current plant.",
|
||||
"Recharge_a_friend": "Recharge a friend",
|
||||
"Buy_for_friend": "Buy for friend",
|
||||
"Buy_this_for_friend_account_": "Buy this for friend account?",
|
||||
"Review_package_before_recharge": "Review package before recharge",
|
||||
"Activate": "Activate",
|
||||
"Deactivate": "Deactivate",
|
||||
"Sync": "Sync",
|
||||
"Failed_to_create_PaymeTrust_transaction_": "Failed to create PaymeTrust transaction.",
|
||||
"Location": "Location",
|
||||
"Radius_Plans": "Radius Plans",
|
||||
"Change_title_in_user_Plan_order": "Change title in user Plan order",
|
||||
"Logs": "Logs",
|
||||
"Voucher_Format": "Voucher Format",
|
||||
"Resend_To_Customer": "Resend To Customer",
|
||||
"Your_friend_do_not_have_active_package": "Your friend do not have active package",
|
||||
"Service_Type": "Service Type",
|
||||
"Others": "Others",
|
||||
"PPPoE": "PPPoE",
|
||||
"Hotspot": "Hotspot",
|
||||
"Disable_Registration": "Disable Registration",
|
||||
"Customer_just_Login_with_Phone_number_and_Voucher_Code__Voucher_will_be_password": "Customer just Login with Phone number and Voucher Code, Voucher will be password",
|
||||
"Login___Activate_Voucher": "Login \/ Activate Voucher",
|
||||
"After_Customer_activate_voucher_or_login__customer_will_be_redirected_to_this_url": "After Customer activate voucher or login, customer will be redirected to this url",
|
||||
"Voucher_Prefix": "Voucher Prefix",
|
||||
"Voucher_activation_success__now_you_can_login": "Voucher activation success, now you can login",
|
||||
"Buy_this__your_active_package_will_be_overwritten": "Buy this? your active package will be overwritten",
|
||||
"Pay_this_with_Balance__your_active_package_will_be_overwritten": "Pay this with Balance? your active package will be overwritten",
|
||||
"Buy_this__your_active_package_will_be_overwrite": "Buy this? your active package will be overwrite",
|
||||
"Monthly_Registered_Customers": "Monthly Registered Customers",
|
||||
"Total_Monthly_Sales": "Total Monthly Sales",
|
||||
"Active_Users": "Active Users",
|
||||
"All_Users_Insights": "All Users Insights",
|
||||
"SuperAdmin": "Super Admin",
|
||||
"Radius": "Radius",
|
||||
"Radius_NAS": "Radius NAS",
|
||||
"Translation": "Translation",
|
||||
"Translation_saved_Successfully": "Translation saved Successfully",
|
||||
"Language_Editor": "Language Editor",
|
||||
"year": "year",
|
||||
"month": "month",
|
||||
"week": "week",
|
||||
"day": "day",
|
||||
"hour": "hour",
|
||||
"minute": "minute",
|
||||
"second": "second",
|
||||
"Attributes": "Attributes",
|
||||
"Profile": "Profile",
|
||||
"Phone": "Phone",
|
||||
"City": "City",
|
||||
"Sub_District": "Sub District",
|
||||
"Ward": "Ward",
|
||||
"Credentials": "Credentials",
|
||||
"Agent": "Agent",
|
||||
"This_Token_will_act_as_SuperAdmin_Admin": "This Token will act as SuperAdmin\/Admin",
|
||||
"Login": "Login",
|
||||
"Expired_Action": "Expired Action",
|
||||
"Expired_Address_List_Name": "Expired Address List Name",
|
||||
"Address_List": "Address List",
|
||||
"Optional": "Optional",
|
||||
"Generated_By": "Generated By",
|
||||
"Admin": "Admin",
|
||||
"Password_should_be_minimum_6_characters": "Password should be minimum 6 characters",
|
||||
"Add_User": "Add User",
|
||||
"Send_Notification": "Send Notification",
|
||||
"Code": "Code",
|
||||
"Send_To_Customer": "Send To Customer",
|
||||
"Prev": "Prev",
|
||||
"Voucher_Not_Found": "Voucher Not Found",
|
||||
"Miscellaneous": "Miscellaneous",
|
||||
"OTP_Required": "OTP Required",
|
||||
"Change": "Change",
|
||||
"Change_Phone_Number": "Change Phone Number",
|
||||
"Current_Number": "Current Number",
|
||||
"New_Number": "New Number",
|
||||
"Input_your_phone_number": "Input your phone number",
|
||||
"OTP": "OTP",
|
||||
"Enter_OTP_that_was_sent_to_your_phone": "Enter OTP that was sent to your phone",
|
||||
"Update": "Update",
|
||||
"OTP_is_required_when_user_want_to_change_phone_number": "OTP is required when user want to change phone number",
|
||||
"Rate": "Rate",
|
||||
"Burst": "Burst",
|
||||
"Editing_Bandwidth_will_not_automatically_update_the_plan__you_need_to_edit_the_plan_then_save_again": "Editing Bandwidth will not automatically update the plan, you need to edit the plan then save again",
|
||||
"OTP_Method": "OTP Method",
|
||||
"SMS": "SMS",
|
||||
"WhatsApp": "WhatsApp",
|
||||
"SMS_and_WhatsApp": "SMS and WhatsApp",
|
||||
"The_method_which_OTP_will_be_sent_to_user": "The method which OTP will be sent to user",
|
||||
"Report_Viewer": "Report Viewer",
|
||||
"Super_Administrator": "Super Administrator",
|
||||
"Send_To": "Send To",
|
||||
"Resend": "Resend",
|
||||
"Alert": "Alert",
|
||||
"success": "success",
|
||||
"Click_Here": "Click Here",
|
||||
"danger": "danger",
|
||||
"Logout_Successful": "Logout Successful",
|
||||
"warning": "warning",
|
||||
"Users_Announcement": "Users Announcement",
|
||||
"Customer_Announcement": "Customer Announcement",
|
||||
"1_Period___1_Month__Expires_the_20th_of_each_month": "1 Period = 1 Month, Expires the 20th of each month",
|
||||
"Period": "Period",
|
||||
"Add": "Add",
|
||||
"Select_Payment_Gateway": "Select Payment Gateway",
|
||||
"Available_Payment_Gateway": "Available Payment Gateway",
|
||||
"Pay_Now": "Pay Now",
|
||||
"Please_select_Payment_Gateway": "Please select Payment Gateway",
|
||||
"Payment_Gateway_Deleted": "Payment Gateway Deleted",
|
||||
"Payment_Gateway_not_set__please_set_it_in_Settings": "Payment Gateway not set, please set it in Settings",
|
||||
"Failed_to_create_Transaction__": "Failed to create Transaction..",
|
||||
"Show_To_Customer": "Type",
|
||||
"Using": "Using",
|
||||
"Default": "Default",
|
||||
"Customer_Balance": "Customer Balance",
|
||||
"Vouchers": "Vouchers",
|
||||
"Refill_Customer": "Refill Customer",
|
||||
"Recharge_Customer": "Recharge Customer",
|
||||
"Plans": "Plans",
|
||||
"PPPOE": "PPPOE",
|
||||
"Bandwidth": "Bandwidth",
|
||||
"Customers": "Customers",
|
||||
"Actives": "Actives",
|
||||
"Name": "Name",
|
||||
"Confirm": "Confirm",
|
||||
"Plan": "Plan",
|
||||
"Total": "Total",
|
||||
"Current_Cycle": "Current Cycle",
|
||||
"Additional_Cost": "Additional Cost",
|
||||
"Remaining": "Remaining",
|
||||
"Not_Found": "Not Found",
|
||||
"Cash": "Cash",
|
||||
"Payment_not_found": "Payment not found",
|
||||
"If_your_friend_have_Additional_Cost__you_will_pay_for_that_too": "If your friend have Additional Cost, you will pay for that too",
|
||||
"Cache_cleared_successfully_": "Cache cleared successfully!",
|
||||
"Paid": "Paid",
|
||||
"Send_Message": "Send Message",
|
||||
"Send_Personal_Message": "Send Personal Message",
|
||||
"Send_Via": "Send Via",
|
||||
"Compose_your_message___": "Compose your message...",
|
||||
"Use_placeholders_": "Use placeholders:",
|
||||
"Customer_Name": "Customer Name",
|
||||
"Customer_Username": "Customer Username",
|
||||
"Customer_Phone": "Customer Phone",
|
||||
"Your_Company_Name": "Your Company Name",
|
||||
"Message_Sent_Successfully": "Message Sent Successfully",
|
||||
"Send_Bulk_Message": "Send Bulk Message",
|
||||
"Group": "Group",
|
||||
"All_Customers": "All Customers",
|
||||
"New_Customers": "New Customers",
|
||||
"Expired_Customers": "Expired Customers",
|
||||
"Active_Customers": "Active Customers",
|
||||
"Map": "Map",
|
||||
"Customer_Location": "Customer Location",
|
||||
"Account_Type": "Account Type",
|
||||
"Coordinates": "Coordinates",
|
||||
"Latitude_and_Longitude_coordinates_for_map_must_be_separate_with_comma____": "Latitude and Longitude coordinates for map must be separate with comma ","",
|
||||
"Customer_Geo_Location_Information": "Customer Geo Location Information",
|
||||
"List": "List",
|
||||
"Lists": "Lists",
|
||||
"Single_Customer": "Single Customer",
|
||||
"Bulk_Customers": "Bulk Customers",
|
||||
"Message_per_time": "Message per time",
|
||||
"5_Messages": "5 Messages",
|
||||
"10_Messages": "10 Messages",
|
||||
"15_Messages": "15 Messages",
|
||||
"20_Messages": "20 Messages",
|
||||
"30_Messages": "30 Messages",
|
||||
"40_Messages": "40 Messages",
|
||||
"50_Messages": "50 Messages",
|
||||
"60_Messages": "60 Messages",
|
||||
"Use_20_and_above_if_you_are_sending_to_all_customers_to_avoid_server_time_out": "Use 20 and above if you are sending to all customers to avoid server time out",
|
||||
"Delay": "Delay",
|
||||
"No_Delay": "No Delay",
|
||||
"5_Seconds": "5 Seconds",
|
||||
"10_Seconds": "10 Seconds",
|
||||
"15_Seconds": "15 Seconds",
|
||||
"20_Seconds": "20 Seconds",
|
||||
"Use_at_least_5_secs_if_you_are_sending_to_all_customers_to_avoid_being_banned_by_your_message_provider": "Use at least 5 secs if you are sending to all customers to avoid being banned by your message provider",
|
||||
"Testing__if_checked_no_real_message_is_sent_": "Testing [if checked no real message is sent]",
|
||||
"All_fields_are_required": "All fields are required",
|
||||
"Personal": "Personal",
|
||||
"Email_Notification": "Email Notification",
|
||||
"Router_Name___Location": "Router Name \/ Location",
|
||||
"Plan_Category": "Plan Category",
|
||||
"ID": "ID",
|
||||
"Internet_Plan": "Internet Plan",
|
||||
"Privacy_Policy": "Privacy Policy",
|
||||
"Terms_and_Conditions": "Terms and Conditions",
|
||||
"Contact": "Contact",
|
||||
"will_be_replaced_with_Customer_Name": "will be replaced with Customer Name",
|
||||
"will_be_replaced_with_Customer_username": "will be replaced with Customer username",
|
||||
"will_be_replaced_with_Package_name": "will be replaced with Package name",
|
||||
"will_be_replaced_with_Package_price": "will be replaced with Package price",
|
||||
"additional_bills_for_customers": "additional bills for customers",
|
||||
"will_be_replaced_with_Expiration_date": "will be replaced with Expiration date",
|
||||
"Your_Company_Name_at_Settings": "Your Company Name at Settings",
|
||||
"Your_Company_Address_at_Settings": "Your Company Address at Settings",
|
||||
"Your_Company_Phone_at_Settings": "Your Company Phone at Settings",
|
||||
"Invoice_number": "Invoice number",
|
||||
"Date_invoice_created": "Date invoice created",
|
||||
"Payment_gateway_user_paid_from": "Payment gateway user paid from",
|
||||
"Payment_channel_user_paid_from": "Payment channel user paid from",
|
||||
"is_Hotspot_or_PPPOE": "is Hotspot or PPPOE",
|
||||
"Internet_Package": "Internet Package",
|
||||
"Internet_Package_Prices": "Internet Package Prices",
|
||||
"Receiver_name": "Receiver name",
|
||||
"Username_internet": "Username internet",
|
||||
"User_password": "User password",
|
||||
"Expired_datetime": "Expired datetime",
|
||||
"For_Notes_by_admin": "For Notes by admin",
|
||||
"Transaction_datetime": "Transaction datetime",
|
||||
"Balance_Before": "Balance Before",
|
||||
"Balance_After": "Balance After",
|
||||
"how_much_balance_have_been_send": "how much balance have been send",
|
||||
"Current_Balance": "Current Balance",
|
||||
"Sender_name": "Sender name",
|
||||
"how_much_balance_have_been_received": "how much balance have been received",
|
||||
"Extend_Postpaid_Expiration": "Extend Postpaid Expiration",
|
||||
"Allow_Extend": "Allow Extend",
|
||||
"Extend_Days": "Extend Days",
|
||||
"Confirmation_Message": "Confirmation Message",
|
||||
"You_are_already_logged_in": "You are already logged in",
|
||||
"Extend": "Extend",
|
||||
"Created___Expired": "Created \/ Expired",
|
||||
"Bank_Transfer": "Bank Transfer",
|
||||
"Recharge_Using": "Recharge Using",
|
||||
"ago": "ago",
|
||||
"Disabled": "Disabled",
|
||||
"Banned": "Banned",
|
||||
"Customer_cannot_login_again": "Customer cannot login again",
|
||||
"Customer_can_login_but_cannot_buy_internet_plan__Admin_cannot_recharge_customer": "Customer can login but cannot buy internet plan, Admin cannot recharge customer",
|
||||
"Don_t_forget_to_deactivate_all_active_plan_too": "Don't forget to deactivate all active plan too",
|
||||
"Ascending": "Ascending",
|
||||
"Descending": "Descending",
|
||||
"Created_Date": "Created Date",
|
||||
"Inactive": "Inactive",
|
||||
"Suspended": "Suspended",
|
||||
"Query": "Query",
|
||||
"Notes": "Notes",
|
||||
"This_account_status": "This account status",
|
||||
"Maintenance_Mode": "Maintenance Mode",
|
||||
"Maintenance_Mode_Settings": "Maintenance Mode Settings",
|
||||
"Status_": "Status:",
|
||||
"End_Date_": "End Date:",
|
||||
"Save": "Save",
|
||||
"Site_is_temporarily_unavailable_": "Site is temporarily unavailable.",
|
||||
"Scheduled_maintenance_is_currently_in_progress__Please_check_back_soon_": "Scheduled maintenance is currently in progress. Please check back soon.",
|
||||
"We_apologize_for_any_inconvenience_": "We apologize for any inconvenience.",
|
||||
"The": "The",
|
||||
"Team": "Team",
|
||||
"Extend_Package_Expiry": "Extend Package Expiry",
|
||||
"No": "No",
|
||||
"Yes": "Yes",
|
||||
"If_user_buy_same_internet_plan__expiry_date_will_extend": "If user buy same internet plan, expiry date will extend",
|
||||
"Tax_System": "Tax System",
|
||||
"Enable_Tax_System": "Enable Tax System",
|
||||
"Tax_will_be_calculated_in_Internet_Plan_Price": "Tax will be calculated in Internet Plan Price",
|
||||
"Tax_Rate": "Tax Rate",
|
||||
"0_5_": "0.5%",
|
||||
"1_": "1%",
|
||||
"1_5_": "1.5%",
|
||||
"2_": "2%",
|
||||
"5_": "5%",
|
||||
"10_": "10%",
|
||||
"Custom": "Custom",
|
||||
"Tax_Rates_in_percentage": "Tax Rates in percentage",
|
||||
"Custom_Tax_Rate": "Custom Tax Rate",
|
||||
"Enter_Custom_Tax_Rate": "Enter Custom Tax Rate",
|
||||
"Enter_the_custom_tax_rate__e_g___3_75_for_3_75__": "Enter the custom tax rate (e.g., 3.75 for 3.75%)",
|
||||
"Additional_Information": "Additional Information",
|
||||
"City_of_Resident": "City of Resident",
|
||||
"District": "District",
|
||||
"State": "State",
|
||||
"State_of_Resident": "State of Resident",
|
||||
"Zip": "Zip",
|
||||
"Zip_Code": "Zip Code",
|
||||
"M_Pesa": "M-Pesa",
|
||||
"sales": "sales",
|
||||
"Create_Transaction_Success__Please_click_pay_now_to_process_payment": "Create Transaction Success, Please click pay now to process payment",
|
||||
"Failed_to_send_message": "Failed to send message",
|
||||
"": "",
|
||||
"just_now": "just now",
|
||||
"Backup_Database": "Backup Database",
|
||||
"Backup_not_found_": "Backup not found.",
|
||||
"Add_Customer": "Add Customer",
|
||||
"Clients": "Clients",
|
||||
"All_Clients": "All Clients",
|
||||
"New_Client": "New Client",
|
||||
"Active_Clients": "Active Clients",
|
||||
"Refill_Client": "Refill Client",
|
||||
"Recharge_Client": "Recharge Client",
|
||||
"Top_Up_Wallet": "Top-Up Wallet",
|
||||
"_Internet_Packages": "Internet Packages",
|
||||
"Communications": "Communications",
|
||||
"Single_Client": "Single Client",
|
||||
"Bulk_Clients": "Bulk Clients",
|
||||
"Sent_Messages": "Sent Messages",
|
||||
"Recharge_SMS": "Recharge SMS",
|
||||
"User_Alerts": "User Alerts",
|
||||
"Payment_Method": "Payment Method",
|
||||
"System_Logs": "System Logs",
|
||||
"Mpesa_Settings": "Mpesa Settings",
|
||||
"SMS_Balance": "SMS Balance",
|
||||
"View_Balance": "View Balance",
|
||||
"Force_Logout_": "Force Logout:",
|
||||
"Account_Not_Found": "Account Not Found",
|
||||
"Enter_Username": "Enter Username",
|
||||
"Enter_Full_Name": "Enter Full Name",
|
||||
"Enter_Email": "Enter Email",
|
||||
"Enter_Phone_Number": "Enter Phone Number",
|
||||
"Enter_Password": "Enter Password",
|
||||
"Enter_PPPOE_Password": "Enter PPPOE Password",
|
||||
"Enter_Address": "Enter Address",
|
||||
"Business": "Business",
|
||||
"Messages": "Messages",
|
||||
"Manage_Messages": "Manage Messages",
|
||||
"Actions": "Actions",
|
||||
"Download": "Download",
|
||||
"Uptime": "Uptime",
|
||||
"Memory_Used_Total": "Memory Used\/Total",
|
||||
"CPU_Load": "CPU Load",
|
||||
"Free_Memory": "Free Memory",
|
||||
"Online_Users": "Online Users",
|
||||
"Hotspot_Users": "Hotspot Users",
|
||||
"New_Hotspot_User": "New Hotspot User",
|
||||
"Server": "Server",
|
||||
"MAC_Address": "MAC Address",
|
||||
"Session_Time": "Session Time",
|
||||
"Rx_Bytes": "Rx Bytes",
|
||||
"Tx_Bytes": "Tx Bytes",
|
||||
"Online_Clients": "Online Clients",
|
||||
"Upload": "Upload",
|
||||
"Action": "Action",
|
||||
"Backup_File": "Backup File",
|
||||
"Are_you_Sure_you_want_to_Restore_this_Database_": "Are you Sure you want to Restore this Database?",
|
||||
"Restore": "Restore",
|
||||
"Are_you_Sure_you_want_to_Delete_this_Database_": "Are you Sure you want to Delete this Database?",
|
||||
"Hotspot_Client": "Hotspot Client",
|
||||
"Pppoe_Clients": "Pppoe Clients",
|
||||
"Reboot": "Reboot",
|
||||
"Close": "Close",
|
||||
"Today_s_Sales": "Today's Sales",
|
||||
"Active_Accounts": "Active Accounts",
|
||||
"Expired_Accounts": "Expired Accounts",
|
||||
"Account": "Account",
|
||||
"Enter_Account_Number": "Enter Account Number",
|
||||
"Online_Hotspot_Users": "Online Hotspot Users",
|
||||
"View_Hotspot_Users": "View Hotspot Users",
|
||||
"Online_PPPoE_Users": "Online PPPoE Users",
|
||||
"View_PPPoE_Users": "View PPPoE Users",
|
||||
"Data_Usage": "Data Usage",
|
||||
"View_Data_Usage": "View Data Usage",
|
||||
"Amount": "Amount",
|
||||
"Unused": "Unused",
|
||||
"Used": "Used",
|
||||
"Quotes": "Quotes",
|
||||
"Recent_Orders": "Recent Orders",
|
||||
"Order_ID": "Order ID",
|
||||
"Add_Client": "Add Client",
|
||||
"Clients_Map": "Clients Map",
|
||||
"Admin_Users": "Admin Users",
|
||||
"Payment_Setup": "Payment Setup",
|
||||
"Add_New_Client": "Add New Client",
|
||||
"Acc_Type": "Acc Type",
|
||||
"Area": "Area",
|
||||
"Service": "Service",
|
||||
"Monthly_Customers": "Monthly Customers",
|
||||
"This_Token_will_act_as_SuperAdmin_Admin_": "This Token will act as SuperAdmin\/Admin.",
|
||||
"Expired_Users": "Expired Users",
|
||||
"Fullname": "Fullname",
|
||||
"Router": "Router",
|
||||
"IT709": "IT709",
|
||||
"IT794": "IT794",
|
||||
"IT889": "IT889",
|
||||
"Temperature": "Temperature",
|
||||
"Voltage": "Voltage",
|
||||
"Wireless_Status": "Wireless Status",
|
||||
"Interface_Status": "Interface Status",
|
||||
"Hotspot_Online_Users": "Hotspot Online Users",
|
||||
"PPPoE_Online_Users": "PPPoE Online Users",
|
||||
"Traffic_Monitor": "Traffic Monitor",
|
||||
"Interface_Name": "Interface Name",
|
||||
"Tx__bytes_Out_": "Tx (bytes Out)",
|
||||
"Rx__bytes_In_": "Rx (bytes In)",
|
||||
"Total_Usage": "Total Usage",
|
||||
"Mac_Address": "Mac Address",
|
||||
"Session_Time_Left": "Session Time Left",
|
||||
"Upload__RX_": "Upload (RX)",
|
||||
"Download__TX_": "Download (TX)",
|
||||
"Caller_ID": "Caller ID",
|
||||
"Interface": "Interface",
|
||||
"Last_Ip": "Last Ip",
|
||||
"Last_Activity": "Last Activity",
|
||||
"Signal_Strength": "Signal Strength",
|
||||
"Tx___Rx_CCQ": "Tx \/ Rx CCQ",
|
||||
"Rx_Rate": "Rx Rate",
|
||||
"Tx_Rate": "Tx Rate",
|
||||
"Interace": "Interace",
|
||||
"TX": "TX",
|
||||
"RX": "RX",
|
||||
"Date_Time": "Date\/Time",
|
||||
"Topic": "Topic",
|
||||
"Info": "Info",
|
||||
"Success": "Success",
|
||||
"Warning": "Warning",
|
||||
"Error": "Error",
|
||||
"IT983": "IT983",
|
||||
"moha": "moss",
|
||||
"Username_should_be_between_3_to_45_characters": "Username should be between 3 to 45 characters",
|
||||
"Full_Name_should_be_between_3_to_45_characters": "Full Name should be between 3 to 45 characters",
|
||||
"Mpesa_C2B_Settings__Offline_Payment_": "Mpesa C2B Settings [Offline Payment]",
|
||||
"M_Pesa_C2B_Payment_Gateway": "M-Pesa C2B Payment Gateway",
|
||||
"M_Pesa_C2B_Environment": "M-Pesa C2B Environment",
|
||||
"SandBox_or_Testing": "SandBox or Testing",
|
||||
"Live_or_Production": "Live or Production",
|
||||
"Sandbox": "Sandbox",
|
||||
"is_for_testing_purpose__please_switch_to": "is for testing purpose, please switch to",
|
||||
"Live": "Live",
|
||||
"in_production_": "in production.",
|
||||
"Accept_Insufficient_Fee": "Accept Insufficient Fee",
|
||||
"You_haven_t_registered_your_validation_and_verification_URLs__Please_register_URLs_by_clicking_": "You haven't registered your validation and verification URLs, Please register URLs by clicking",
|
||||
"Payment_History": "Payment History",
|
||||
"Transaction_Type": "Transaction Type",
|
||||
"Transaction_Time": "Transaction Time",
|
||||
"Amount_Paid": "Amount Paid",
|
||||
"Package_Name": "Package Name",
|
||||
"Package_Price": "Package Price",
|
||||
"Bill_Ref_Number": "Bill Ref Number",
|
||||
"Company_Balance": "Company Balance",
|
||||
"_Cleared_the_system_cache_": "Cleared the system cache",
|
||||
"taxip": "taxi",
|
||||
"lessa": "lessa",
|
||||
"brian": "brian",
|
||||
"hussein": "hussein",
|
||||
"lucy": "lucy",
|
||||
"karis": "cook",
|
||||
"taslim": "surrender",
|
||||
"sofia": "sofia",
|
||||
"cynthia": "cynthia",
|
||||
"husein": "Hussein",
|
||||
"shad": "shad",
|
||||
"aliali": "face",
|
||||
"briton": "briton",
|
||||
"729632854": "729632854",
|
||||
"oscar": "oscar",
|
||||
"Refresh": "Refresh",
|
||||
"Status_Filter": "Status Filter",
|
||||
"All_Status": "All Status",
|
||||
"Filter_by_Status": "Filter by Status",
|
||||
"Showing": "Showing",
|
||||
"customers": "customers",
|
||||
"Enter_the_number_of_vouchers_to_generate__1_1000_": "Enter the number of vouchers to generate (1-1000)",
|
||||
"Choose_the_case_format_for_voucher_codes": "Choose the case format for voucher codes",
|
||||
"Optional_prefix_for_voucher_codes__e_g___NUX__": "Optional prefix for voucher codes (e.g., NUX-)",
|
||||
"Length_of_the_voucher_code__6_20_characters_": "Length of the voucher code (6-20 characters)",
|
||||
"Generate_Vouchers": "Generate Vouchers",
|
||||
"Please_select_a_type": "Please select a type",
|
||||
"Please_select_a_router": "Please select a router",
|
||||
"Please_select_a_plan": "Please select a plan",
|
||||
"Number_of_vouchers_must_be_between_1_and_1000": "Number of vouchers must be between 1 and 1000",
|
||||
"Select_a_router_first__then_choose_the_service_type": "Select a router first, then choose the service type",
|
||||
"How_it_works": "How it works",
|
||||
"Select_the_service_type__Hotspot_PPPoE___then_choose_a_router__and_finally_select_from_available_internet_plans_to_generate_vouchers_": "Select the service type (Hotspot\/PPPoE), then choose a router, and finally select from available internet plans to generate vouchers.",
|
||||
"Select_service_type_first_to_load_available_routers": "Select service type first to load available routers",
|
||||
"Select_a_router_and_type_first_to_load_available_plans": "Select a router and type first to load available plans",
|
||||
"Start_typing_to_search_for_customers": "Start typing to search for customers",
|
||||
"Select_a_balance_plan_for_the_customer": "Select a balance plan for the customer",
|
||||
"Payment": "Payment",
|
||||
"Support": "Support",
|
||||
"BankStkPush_Configuration": "BankStkPush Configuration",
|
||||
"Manage_Banks": "Manage Banks",
|
||||
"Select_Bank_Account": "Select Bank Account",
|
||||
"DEFAULT": "DEFAULT",
|
||||
"Only_banks_with_STK_Push_support_are_shown": "Only banks with STK Push support are shown",
|
||||
"Selected_Bank_Details": "Selected Bank Details",
|
||||
"Bank_Name": "Bank Name",
|
||||
"Account_Number": "Account Number",
|
||||
"Account_Name": "Account Name",
|
||||
"After_applying_these_changes__funds_will_be_sent_to_the_selected_bank_account__Please_ensure_the_bank_details_are_correct_": "After applying these changes, funds will be sent to the selected bank account. Please ensure the bank details are correct.",
|
||||
"Save_Configuration": "Save Configuration",
|
||||
"Add_New_Bank": "Add New Bank",
|
||||
"Please_select_a_bank_account": "Please select a bank account",
|
||||
"Bank_Management": "Bank Management",
|
||||
"Search_banks___": "Search banks...",
|
||||
"STK_Push": "STK Push",
|
||||
"All": "All",
|
||||
"Supported": "Supported",
|
||||
"Not_Supported": "Not Supported",
|
||||
"Filter": "Filter",
|
||||
"Reset": "Reset",
|
||||
"Bank_Code": "Bank Code",
|
||||
"Paybill": "Paybill",
|
||||
"Deactivate_this_bank_": "Deactivate this bank?",
|
||||
"Are_you_sure_you_want_to_delete_this_bank_": "Are you sure you want to delete this bank?",
|
||||
"Set_as_Default": "Set as Default",
|
||||
"Set_this_bank_as_default_for_STK_Push_": "Set this bank as default for STK Push?",
|
||||
"Enter_bank_name": "Enter bank name",
|
||||
"e_g___Safaricom_M_Pesa__Equity_Bank__KCB_Bank": "e.g., Safaricom M-Pesa, Equity Bank, KCB Bank",
|
||||
"Enter_account_number": "Enter account number",
|
||||
"The_account_number_for_STK_Push_payments": "The account number for STK Push payments",
|
||||
"Enter_account_holder_name": "Enter account holder name",
|
||||
"Name_of_the_account_holder": "Name of the account holder",
|
||||
"Enter_bank_code": "Enter bank code",
|
||||
"e_g___MPESA__EQTY__KCB": "e.g., MESA, EQTY, KCB",
|
||||
"Paybill_Number": "Paybill Number",
|
||||
"Enter_paybill_number": "Enter paybill number",
|
||||
"M_Pesa_paybill_number__if_applicable_": "M-Pesa paybill number (if applicable)",
|
||||
"STK_Push_Support": "STK Push Support",
|
||||
"This_bank_supports_STK_Push_payments": "This bank supports STK Push payments",
|
||||
"Only_banks_with_STK_Push_support_can_be_used_for_BankStkPush_payments": "Only banks with STK Push support can be used for BankStkPush payments",
|
||||
"Only_active_banks_can_be_used_for_payments": "Only active banks can be used for payments",
|
||||
"Set_as_default_bank_for_STK_Push": "Set as default bank for STK Push",
|
||||
"This_will_make_this_bank_the_default_selection_for_BankStkPush_configuration": "This will make this bank the default selection for BankStkPush configuration",
|
||||
"Save_Bank": "Save Bank",
|
||||
"Back_to_List": "Back to List",
|
||||
"Please_fill_in_all_required_fields": "Please fill in all required fields",
|
||||
"Account_number_already_exists": "Account number already exists",
|
||||
"This_bank_is_set_as_default__Disabling_STK_Push_support_will_also_remove_it_as_default__Continue_": "This bank is set as default. Disabling STK Push support will also remove it as default. Continue?",
|
||||
"This_bank_is_set_as_default__Deactivating_it_will_also_remove_it_as_default__Continue_": "This bank is set as default. Deactivating it will also remove it as default. Continue?",
|
||||
"Edit_Bank": "Edit Bank",
|
||||
"Update_Bank": "Update Bank",
|
||||
"Bank_Updated_Successfully": "Bank Updated Successfully",
|
||||
"Bank_Deleted_Successfully": "Bank Deleted Successfully",
|
||||
"BankStkPush_configuration_updated_successfully": "BankStkPush configuration updated successfully",
|
||||
"Manage_bank_accounts_and_STK_Push_configurations": "Manage bank accounts and STK Push configurations",
|
||||
"Configure_STK_Push": "Configure STK Push",
|
||||
"Bank_Accounts": "Bank Accounts",
|
||||
"Banks": "Banks",
|
||||
"Search___Filter": "Search & Filter",
|
||||
"Search_by_bank_name__account_number___": "Search by bank name, account number...",
|
||||
"Default_Bank": "Default Bank",
|
||||
"Delete_Bank": "Delete Bank",
|
||||
"Not_Set": "Not Set",
|
||||
"Suspend": "Suspend",
|
||||
"Suspend_Customer": "Suspend Customer",
|
||||
"Create_a_new_bank_account_for_STK_Push_payments": "Create a new bank account for STK Push payments",
|
||||
"Bank_Information": "Bank Information",
|
||||
"Basic_Information": "Basic Information",
|
||||
"Settings___Configuration": "Settings & Configuration",
|
||||
"Suspension_Countdown": "Suspension Countdown",
|
||||
"Suspension_Date": "Suspension Date",
|
||||
"Suspension_Time": "Suspension Time",
|
||||
"The_customer_will_be_suspended_at_the_specified_date_and_time_": "The customer will be suspended at the specified date and time.",
|
||||
"Postpaid_Recharge_for_the_first_time_use": "Postpaid Recharge for the first time use",
|
||||
"Time_Left": "Time Left",
|
||||
"Client_Management": "Client Management",
|
||||
"Service_Management": "Service Management",
|
||||
"Communication": "Communication",
|
||||
"Financial___Reports": "Financial & Reports",
|
||||
"System_Administration": "System Administration",
|
||||
"Select_Plan": "Select Plan",
|
||||
"Complete": "Complete",
|
||||
"Customer_Information": "Customer Information",
|
||||
"Plan_Information": "Plan Information",
|
||||
"Total_Cost": "Total Cost",
|
||||
"Processing": "Processing",
|
||||
"Processing_Recharge": "Processing Recharge",
|
||||
"Please_wait_while_we_process_your_recharge_request": "Please wait while we process your recharge request",
|
||||
"Recharge_Successful": "Recharge Successful",
|
||||
"The_customer_has_been_recharged_successfully": "The customer has been recharged successfully",
|
||||
"Invoice_Details": "Invoice Details",
|
||||
"Invoice_Number": "Invoice Number",
|
||||
"Transaction_Date": "Transaction Date",
|
||||
"Print_Invoice": "Print Invoice",
|
||||
"Share_via_WhatsApp": "Share via WhatsApp",
|
||||
"Recharge_Failed": "Recharge Failed",
|
||||
"Previous": "Previous",
|
||||
"User_will_get_invoice_notification_when_buy_package_or_package_refilled__and_welcome_notification_when_account_is_created": "User will get invoice notification when buy package or package refilled, and welcome notification when account is created",
|
||||
"Test_Registration_SMS": "Test Registration SMS",
|
||||
"Use_this_to_test_the_registration_SMS_message_with_custom_details": "Use this to test the registration SMS message with custom details",
|
||||
"Test_Phone_Number": "Test Phone Number",
|
||||
"Test_Username": "Test Username",
|
||||
"Test_Password": "Test Password",
|
||||
"Test_Service_Type": "Test Service Type",
|
||||
"Test_Full_Name": "Test Full Name",
|
||||
"Send_Test_SMS": "Send Test SMS",
|
||||
"Application_Name_Company_Name": "Application Name\/Company Name",
|
||||
"FreeRadius": "FreeRadius",
|
||||
"Notification_Settings": "Notification Settings",
|
||||
"Additional_Settings": "Additional Settings",
|
||||
"Expired_Notification_Messages": "Expired Notification Messages",
|
||||
"PPPoE_Users": "PPPoE Users",
|
||||
"Reminder_7_Days_Messages": "Reminder 7 Days Messages",
|
||||
"Reminder_3_Days_Messages": "Reminder 3 Days Messages",
|
||||
"Reminder_1_Day_Messages": "Reminder 1 Day Messages",
|
||||
"Hotspot_Portal_URL": "Hotspot Portal URL",
|
||||
"URL_for_hotspot_user_portal__used_in_notifications_": "URL for hotspot user portal (used in notifications)",
|
||||
"Cron_Logs": "Cron Logs",
|
||||
"Cron_Jobs": "Cron Jobs",
|
||||
"Cron_Job_Logs": "Cron Job Logs"
|
||||
}
|
||||
8
system/lang/index.html
Normal file
8
system/lang/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Directory access is forbidden.</p>
|
||||
</body>
|
||||
</html>
|
||||
480
system/lang/indonesia.json
Normal file
480
system/lang/indonesia.json
Normal file
@@ -0,0 +1,480 @@
|
||||
{
|
||||
"Log_in": "Masuk",
|
||||
"Register": "Daftar",
|
||||
"Announcement": "Pemberitahuan",
|
||||
"Registration_Info": "Info Pendaftaran",
|
||||
"Voucher_not_found__please_buy_voucher_befor_register": "Voucher tidak ditemukan, silakan beli voucher sebelum mendaftar",
|
||||
"Register_Success__You_can_login_now": "Daftar Sukses! Anda dapat masuk sekarang",
|
||||
"Log_in_to_Member_Panel": "Masuk ke Panel Anggota",
|
||||
"Register_as_Member": "Daftar sebagai Anggota",
|
||||
"Enter_Admin_Area": "Masuk ke Admin Panel",
|
||||
"PHPNuxBill": "PHPNuxBill",
|
||||
"Username": "Nama Pengguna",
|
||||
"Password": "Kata Sandi",
|
||||
"Passwords_does_not_match": "Kata sandi tidak cocok",
|
||||
"Account_already_axist": "Akun telah ada",
|
||||
"Manage": "Mengelola",
|
||||
"Submit": "Kirim",
|
||||
"Save_Changes": "Simpan Perubahan",
|
||||
"Cancel": "Batal",
|
||||
"Edit": "Sunting",
|
||||
"Delete": "Hapus",
|
||||
"Welcome": "Selamat Datang",
|
||||
"Data_Created_Successfully": "Data Berhasil Dibuat",
|
||||
"Data_Updated_Successfully": "Data Berhasil Diperbarui",
|
||||
"Data_Deleted_Successfully": "Data Berhasil Dihapus",
|
||||
"Static_Pages": "Halaman Statis",
|
||||
"Failed_to_save_page__make_sure_i_can_write_to_folder_pages___i_chmod_664_pages___html_i_": "Gagal menyimpan halaman, pastikan diperbolehkan menulis file di folder pages, <i>chmod 664 pages\/*.html<i>",
|
||||
"Saving_page_success": "Menyimpan halaman berhasil",
|
||||
"Sometimes_you_need_to_refresh_3_times_until_content_change": "Terkadang Anda perlu menyegarkan 3 kali hingga konten berubah",
|
||||
"Dashboard": "Dasbor",
|
||||
"Search_Customers___": "Cari Member...",
|
||||
"My_Account": "Akun Saya",
|
||||
"My_Profile": "Profil Saya",
|
||||
"Settings": "Pengaturan",
|
||||
"Edit_Profile": "Sunting Profil",
|
||||
"Change_Password": "Ganti kata sandi",
|
||||
"Logout": "Keluar",
|
||||
"Services": "Layanan",
|
||||
"Bandwidth_Plans": "Paket Bandwidth",
|
||||
"Bandwidth_Name": "Nama Bandwidth",
|
||||
"New_Bandwidth": "Bandwidth Baru",
|
||||
"Edit_Bandwidth": "Sunting Bandwidth",
|
||||
"Add_New_Bandwidth": "Tambahkan Bandwidth Baru",
|
||||
"Rate_Download": "Nilai Unduhan",
|
||||
"Rate_Upload": "Nilai Unggahan",
|
||||
"Name_Bandwidth_Already_Exist": "Nama Bandwidth sudah ada",
|
||||
"Hotspot_Plans": "Paket Hotspot",
|
||||
"PPPOE_Plans": "Paket PPPoE",
|
||||
"Plan_Name": "Nama Paket",
|
||||
"New_Service_Plan": "Paket Layanan Baru",
|
||||
"Add_Service_Plan": "Tambah Paket Layanan",
|
||||
"Edit_Service_Plan": "Sunting Paket Layanan",
|
||||
"Name_Plan_Already_Exist": "Nama Paket sudah ada",
|
||||
"Plan_Type": "Jenis Paket",
|
||||
"Plan_Price": "Harga Paket",
|
||||
"Limit_Type": "Tipe Batas",
|
||||
"Unlimited": "Tak Terbatas",
|
||||
"Limited": "Terbatas",
|
||||
"Time_Limit": "Batas waktu",
|
||||
"Data_Limit": "Batas Data",
|
||||
"Both_Limit": "Membatasi keduanya",
|
||||
"Plan_Validity": "Waktu Paket",
|
||||
"Select_Bandwidth": "Pilih Bandwidth",
|
||||
"Shared_Users": "Berbagi Pelanggan",
|
||||
"Choose_User_Type_Sales_to_disable_access_to_Settings": "Pilih Sales untuk menonaktifkan akses ke Pengaturan",
|
||||
"Current_Password": "Kata sandi saat ini",
|
||||
"New_Password": "Kata sandi baru",
|
||||
"Administrator": "Administrator",
|
||||
"Sales": "Sales",
|
||||
"Member": "Anggota",
|
||||
"Confirm_New_Password": "Konfirmasi sandi baru",
|
||||
"Confirm_Password": "Konfirmasi sandi",
|
||||
"Full_Name": "Nama Lengkap",
|
||||
"User_Type": "Tipe Pelanggan",
|
||||
"Address": "Alamat",
|
||||
"Created_On": "Dibuat pada",
|
||||
"Expires_On": "Kedaluwarsa pada",
|
||||
"Phone_Number": "Nomor telepon",
|
||||
"User_deleted_Successfully": "Pelanggan berhasil dihapus",
|
||||
"Full_Administrator": "Administrator Penuh",
|
||||
"Keep_Blank_to_do_not_change_Password": "Biarkan kosong apabila tidak ingin mengubah kata sandi",
|
||||
"Keep_it_blank_if_you_do_not_want_to_show_currency_code": "Kosongkan jika Anda tidak ingin menampilkan kode mata uang",
|
||||
"Theme_Style": "Gaya Tema",
|
||||
"Theme_Color": "Warna Tema",
|
||||
"Default_Language": "Bahasa Bawaan",
|
||||
"Network": "Jaringan",
|
||||
"Routers": "Router",
|
||||
"IP_Pool": "IP Pool",
|
||||
"New_Router": "Router baru",
|
||||
"Add_Router": "Tambahkan Router",
|
||||
"Edit_Router": "Sunting Router",
|
||||
"Router_Name": "Nama Router",
|
||||
"IP_Address": "Alamat IP",
|
||||
"Router_Secret": "Password Router",
|
||||
"Description": "Deskrispi",
|
||||
"IP_Router_Already_Exist": "IP Router sudah ada",
|
||||
"Name_Pool": "Nama Pool",
|
||||
"Range_IP": "Rentang IP",
|
||||
"New_Pool": "Pool baru",
|
||||
"Add_Pool": "Tambahkan Pool",
|
||||
"Edit_Pool": "Sunting Pool",
|
||||
"Pool_Name_Already_Exist": "Nama Pool sudah ada",
|
||||
"Refill_Account": "Isi Ulang Akun",
|
||||
"Recharge_Account": "Isi Ulang Akun",
|
||||
"Select_Account": "Pilih Akun",
|
||||
"Service_Plan": "Paket Layanan",
|
||||
"Recharge": "Isi Ulang",
|
||||
"Method": "Metode",
|
||||
"Account_Created_Successfully": "Akun berhasil dibuat",
|
||||
"Database_Status": "Status Database",
|
||||
"Total_Database_Size": "Ukuran total database",
|
||||
"Download_Database_Backup": "Unduh cadangan database",
|
||||
"Table_Name": "Nama Tabel",
|
||||
"Rows": "Baris",
|
||||
"Size": "Ukuran",
|
||||
"Customer": "Pelanggan",
|
||||
"Add_New_Contact": "Tambahkan Kontak Baru",
|
||||
"Edit_Contact": "Sunting Kontak",
|
||||
"List_Contact": "Daftar Kontak",
|
||||
"Manage_Contact": "Kelola Kontak",
|
||||
"Reports": "Laporan",
|
||||
"Daily_Reports": "Laporan Harian",
|
||||
"Period_Reports": "Laporan Periode",
|
||||
"All_Transactions": "Semua Transaksi",
|
||||
"Total_Income": "Jumlah Pemasukan",
|
||||
"All_Transactions_at_Date": "Semua transaksi pada ganggal",
|
||||
"Export_for_Print": "Ekspor untuk cetak",
|
||||
"Print": "Cetak",
|
||||
"Export_to_PDF": "Ekspor ke PDF",
|
||||
"Click_Here_to_Print": "Klik Disini untuk mencetak",
|
||||
"You_can_use_html_tag": "Anda dapat menggunakan tag HTML",
|
||||
"Date_Format": "Format tanggal",
|
||||
"Income_Today": "Pendapatan hari ini",
|
||||
"Income_This_Month": "Penghasilan bulan ini",
|
||||
"Users_Active": "Pelanggan Aktif",
|
||||
"Total_Users": "Total Pelanggan",
|
||||
"Users": "Pelanggan",
|
||||
"Edit_User": "Sunting Pelanggan",
|
||||
"Last_Login": "Terakhir Masuk",
|
||||
"Administrator_Users": "Pengguna Administrator",
|
||||
"Manage_Administrator": "Kelola Administrator",
|
||||
"Add_New_Administrator": "Tambahkan Administrator Baru",
|
||||
"Localisation": "Lokalisasi",
|
||||
"Backup_Restore": "Cadangkan\/Pulihkan",
|
||||
"General_Settings": "Pengaturan Umum",
|
||||
"Date": "Tanggal",
|
||||
"Login_Successful": "Berhasil Masuk",
|
||||
"Failed_Login": "Gagal Masuk",
|
||||
"Settings_Saved_Successfully": "Pengaturan Berhasil Disimpan",
|
||||
"User_Updated_Successfully": "Pengguna Berhasil Diperbarui",
|
||||
"User_Expired__Today": "Pengguna Kedaluwarsa, Hari Ini",
|
||||
"Activity_Log": "Log Aktivitas",
|
||||
"View_Reports": "Lihat Laporan",
|
||||
"View_All": "Lihat semua",
|
||||
"Number_of_Vouchers": "Jumlah Voucher",
|
||||
"Length_Code": "Panjang Kode",
|
||||
"Code_Voucher": "Kode Voucher",
|
||||
"Voucher": "Voucher",
|
||||
"Hotspot_Voucher": "Voucher Hotspot",
|
||||
"Status_Voucher": "Voucher Status",
|
||||
"Add_Vouchers": "Tambah Voucher",
|
||||
"Create_Vouchers_Successfully": "Buat Voucher Berhasil",
|
||||
"Generate": "Menghasilkan",
|
||||
"Print_side_by_side__it_will_easy_to_cut": "Info Cetak",
|
||||
"From_Date": "Dari tanggal",
|
||||
"To_Date": "Hingga saat ini",
|
||||
"New_Service": "Layanan Baru",
|
||||
"Type": "Jenis",
|
||||
"Finish": "Menyelesaikan",
|
||||
"Application_Name__Company_Name": "Nama Aplikasi\/ Nama Perusahaan",
|
||||
"This_Name_will_be_shown_on_the_Title": "Nama ini akan ditampilkan pada Judul",
|
||||
"Next": "Berikutnya",
|
||||
"Last": "Terakhir",
|
||||
"Timezone": "Zona waktu",
|
||||
"Decimal_Point": "Titik Desimal",
|
||||
"Thousands_Separator": "Pemisah Ribuan",
|
||||
"Currency_Code": "Kode Mata Uang",
|
||||
"Order_Voucher": "Pesan Voucher",
|
||||
"Voucher_Activation": "Aktivasi Voucher",
|
||||
"List_Activated_Voucher": "Daftar Voucher yang diaktifkan",
|
||||
"Enter_voucher_code_here": "Masukkan kode voucher di sini",
|
||||
"Private_Message": "Pesan Pribadi",
|
||||
"Inbox": "Kotak Masuk",
|
||||
"Outbox": "Kotak Keluar",
|
||||
"Compose": "Menyusun",
|
||||
"Send_to": "Kirim ke",
|
||||
"Title": "Judul",
|
||||
"Message": "Pesan",
|
||||
"Your_Account_Information": "Informasi Akun Anda",
|
||||
"Welcome_to_the_Panel_Members_page__on_this_page_you_can_": "Selamat datang di halaman Anggota Panel, di halaman ini Anda dapat:",
|
||||
"Invalid_Username_or_Password": "Nama pengguna atau kata sandi salah",
|
||||
"You_do_not_have_permission_to_access_this_page": "Anda tidak memiliki izin untuk mengakses halaman ini",
|
||||
"Incorrect_Current_Password": "Kata sandi saat ini salah",
|
||||
"Password_changed_successfully__Please_login_again": "Kata sandi berhasil diubah, silakan login kembali",
|
||||
"All_field_is_required": "Semua bidang wajib diisi",
|
||||
"Voucher_Not_Valid": "Voucher tidak berlaku",
|
||||
"Activation_Vouchers_Successfully": "Aktivasi Voucher Berhasil",
|
||||
"Data_Not_Found": "Data tidak ditemukan",
|
||||
"Search_by_Username": "Cari berdasarkan nama pengguna",
|
||||
"Search_by_Name": "Cari berdasarkan nama",
|
||||
"Search_by_Code_Voucher": "Cari berdasarkan kode voucher",
|
||||
"Search": "Mencari",
|
||||
"Select_a_customer": "Pilih pelanggan",
|
||||
"Select_Routers": "Pilih Router",
|
||||
"Select_Plans": "Pilih Paket",
|
||||
"Select_Pool": "Pilih Pool",
|
||||
"Hrs": "Jam",
|
||||
"Mins": "Menit",
|
||||
"Days": "Hari",
|
||||
"Months": "Bulan",
|
||||
"Add_Language": "Tambahkan Bahasa",
|
||||
"Language_Name": "Nama Bahasa",
|
||||
"Folder_Name": "Nama Folder",
|
||||
"Translator": "Penerjemah",
|
||||
"Language_Name_Already_Exist": "Nama Bahasa sudah ada",
|
||||
"Payment_Gateway": "Gerbang Pembayaran",
|
||||
"Community": "Komunitas",
|
||||
"1_user_can_be_used_for_many_devices_": "1 pengguna bisa digunakan untuk banyak perangkat?",
|
||||
"Cannot_be_change_after_saved": "Tidak dapat diubah setelah disimpan",
|
||||
"Explain_Coverage_of_router": "Jelaskan cakupan router",
|
||||
"Name_of_Area_that_router_operated": "Nama area tempat router dioperasikan",
|
||||
"Payment_Notification_URL__Recurring_Notification_URL__Pay_Account_Notification_URL": "URL notifikasi pembayaran, URL notifikasi berulang, URL notifikasi akun bayar",
|
||||
"Finish_Redirect_URL__Unfinish_Redirect_URL__Error_Redirect_URL": "Selesaikan URL pengalihan, selesaikan URL pengalihan, URL pengalihan kesalahan",
|
||||
"Status": "Status",
|
||||
"Plan_Not_found": "Paket tidak ditemukan",
|
||||
"Failed_to_create_transaction_": "Gagal membuat transaksi.",
|
||||
"Seller_has_not_yet_setup_Xendit_payment_gateway": "Penjual belum menyiapkan gateway pembayaran Xendit",
|
||||
"Admin_has_not_yet_setup_Xendit_payment_gateway__please_tell_admin": "Admin belum menyiapkan gerbang pembayaran Xendit, mohon beritahu Admin",
|
||||
"Buy_this__your_active_package_will_be_overwrite": "Beli ini? Paket aktif Anda akan ditimpa",
|
||||
"You_already_have_unpaid_transaction__cancel_it_or_pay_it_": "Anda sudah memiliki transaksi yang belum dibayar, batalkan atau bayar.",
|
||||
"Transaction_Not_found": "Transaksi tidak ditemukan",
|
||||
"Cancel_it_": "Batalkan itu?",
|
||||
"expired": "Kedaluwarsa",
|
||||
"Check_for_Payment": "Periksa Pembayaran",
|
||||
"Transaction_still_unpaid_": "Transaksi masih belum dibayar.",
|
||||
"Paid_Date": "Tanggal Pembayaran",
|
||||
"Transaction_has_been_paid_": "Transaksi telah dibayar.",
|
||||
"PAID": "DIBAYAR",
|
||||
"CANCELED": "DIBATALKAN",
|
||||
"UNPAID": "BELUM DIBAYAR",
|
||||
"PAY_NOW": "BAYAR SEKARANG",
|
||||
"Buy_Hotspot_Plan": "Beli Paket Hotspot",
|
||||
"Buy_PPOE_Plan": "Beli Paket PPPoE",
|
||||
"Package": "Paket",
|
||||
"Order_Internet_Package": "Pesan Paket Internet",
|
||||
"Unknown_Command_": "Perintah tidak diketahui.",
|
||||
"Checking_payment": "Memeriksa pembayaran",
|
||||
"Create_Transaction_Success": "Transaksi berhasil dibuat",
|
||||
"You_have_unpaid_transaction": "Anda memiliki transaksi yang belum dibayar",
|
||||
"TripayPayment_Channel": "Saluran Pembayaran Tripay",
|
||||
"Payment_Channel": "Saluran Pembayaran",
|
||||
"Payment_check_failed_": "Pemeriksaan pembayaran gagal.",
|
||||
"Order_Package": "Pesan Paket",
|
||||
"Transactions": "Transaksi",
|
||||
"Payments": "Pembayaran",
|
||||
"History": "Riwayat",
|
||||
"Order_History": "Riwayat Pesanan",
|
||||
"Gateway": "Gerbang",
|
||||
"Date_Done": "Tanggal Selesai",
|
||||
"Unpaid_Order": "Pesanan Belum Dibayar",
|
||||
"Payment_Gateway_Not_Found": "Gerbang Pembayaran tidak ditemukan",
|
||||
"Payment_Gateway_saved_successfully": "Gerbang Pembayaran berhasil disimpan",
|
||||
"ORDER": "MEMESAN",
|
||||
"Package_History": "Riwayat Paket",
|
||||
"Buy_History": "Riwayat Beli",
|
||||
"Activation_History": "Riwayat Aktivasi",
|
||||
"Buy_Package": "Beli Paket",
|
||||
"Email": "Email",
|
||||
"Company_Footer": "Catatan Kaki Perusahaan",
|
||||
"Will_show_below_user_pages": "Akan ditampilkan dibawah halaman pengguna",
|
||||
"Request_OTP": "Minta OTP",
|
||||
"Verification_Code": "Kode Verifikasi",
|
||||
"SMS_Verification_Code": "Kode Verifikasi SMS",
|
||||
"Please_enter_your_email_address": "Silakan masukkan alamat email Anda",
|
||||
"Failed_to_create_Paypal_transaction_": "Gagal membuat transaksi Paypal.",
|
||||
"Plugin": "Plugin",
|
||||
"Plugin_Manager": "Manajer Plugin",
|
||||
"User_Notification": "Pemberitahuan Pelanggan",
|
||||
"Expired_Notification": "Pemberitahuan Kedaluwarsa",
|
||||
"User_will_get_notification_when_package_expired": "Pengguna akan mendapat notifikasi ketika paket sudah habis masa berlakunya",
|
||||
"Expired_Notification_Message": "Pesan Pemberitahuan Kedaluwarsa",
|
||||
"Payment_Notification": "Notifikasi Pembayaran",
|
||||
"User_will_get_invoice_notification_when_buy_package_or_package_refilled": "Pengguna akan mendapatkan notifikasi invoice saat membeli paket atau isi ulang paket",
|
||||
"Current_IP": "IP saat ini",
|
||||
"Current_MAC": "MAC saat ini",
|
||||
"Login_Status": "Status Masuk",
|
||||
"Login_Request_successfully": "Permintaan masuk berhasil",
|
||||
"Logout_Request_successfully": "Permintaan keluar berhasil",
|
||||
"Disconnect_Internet_": "Putuskan sambungan internet?",
|
||||
"Not_Online__Login_now_": "Tidak, masuk sekarang?",
|
||||
"You_are_Online__Logout_": "Kamu sedang aktif, ingin keluar?",
|
||||
"Connect_to_Internet_": "Hubungkan ke Internet?",
|
||||
"Your_account_not_connected_to_internet": "Akun Anda tidak terhubung ke internet",
|
||||
"Failed_to_create_transaction__": "Gagal membuat transaksi. ",
|
||||
"Failed_to_check_status_transaction__": "Gagal memeriksa status transaksi.",
|
||||
"Disable_Voucher": "Nonaktifkan Voucher",
|
||||
"Balance": "Saldo",
|
||||
"Balance_System": "Saldo Sistem",
|
||||
"Enable_System": "Aktifkan Sistem",
|
||||
"Allow_Transfer": "Izinkan Transfer",
|
||||
"Telegram_Notification": "Pemberitahuan Telegram",
|
||||
"SMS_OTP_Registration": "Pendaftaran SMS OTP",
|
||||
"Whatsapp_Notification": "Pemberitahuan WhatsApp",
|
||||
"Tawk_to_Chat_Widget": "Widget Obrolan Tawk.to",
|
||||
"Invoice": "Faktur",
|
||||
"Country_Code_Phone": "Kode Negara Telepon",
|
||||
"Voucher_activation_menu_will_be_hidden": "Menu aktivasi voucher akan disembunyikan",
|
||||
"Customer_can_deposit_money_to_buy_voucher": "Pelanggan dapat menyetor uang untuk membeli voucher",
|
||||
"Allow_balance_transfer_between_customers": "Izinkan transfer saldo antar pelanggan",
|
||||
"Reminder_Notification": "Pemberitahuan Pengingat",
|
||||
"Reminder_Notification_Message": "Pesan Pemberitahuan Pengingat",
|
||||
"Reminder_7_days": "Pengingat 7 hari",
|
||||
"Reminder_3_days": "Pengingat 3 hari",
|
||||
"Reminder_1_day": "Pengingat 1 hari",
|
||||
"PPPOE_Password": "Kata sandi PPPoE",
|
||||
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_user_password": "Pelanggan tidak dapat mengubah ini, hanya Admin. Jika kosong maka akan menggunakan kata sandi pelanggan",
|
||||
"Invoice_Balance_Message": "Faktur Pesan Saldo",
|
||||
"Invoice_Notification_Payment": "Faktur Pemberitahuan Pembayaran",
|
||||
"Balance_Notification_Payment": "Saldo Pemberitahuan Pembayaran",
|
||||
"Balance_Plans": "Paket Saldo",
|
||||
"Buy_Balance": "Beli Saldo",
|
||||
"Price": "Harga",
|
||||
"Validity": "Waktu",
|
||||
"Disable_auto_renewal_": "Nonaktifkan perpanjangan otomatis?",
|
||||
"Auto_Renewal_On": "Perpanjangan otomatis aktif",
|
||||
"Enable_auto_renewal_": "Aktifkan perpanjangan otomatis?",
|
||||
"Auto_Renewal_Off": "Perpanjangan otomatis mati",
|
||||
"Refill_Balance": "Isi Ulang Saldo",
|
||||
"Invoice_Footer": "Catatan Kaki Faktur",
|
||||
"Pay_With_Balance": "Bayar dengan Saldo",
|
||||
"Pay_this_with_Balance__your_active_package_will_be_overwrite": "Bayar ini dengan Saldo? Paket aktif Anda akan ditimpa",
|
||||
"Success_to_buy_package": "Berhasil membeli paket",
|
||||
"Auto_Renewal": "Perpanjangan otomatis",
|
||||
"View": "Melihat",
|
||||
"Back": "Kembali",
|
||||
"Active": "Aktif",
|
||||
"Transfer_Balance": "Kirim saldo",
|
||||
"Send_your_balance_": "Kirim saldo Anda?",
|
||||
"Send": "Kirim",
|
||||
"Cannot_send_to_yourself": "Tidak dapat mengirim ke diri Anda sendiri",
|
||||
"Sending_balance_success": "Berhasil mengirim saldo",
|
||||
"From": "Dari",
|
||||
"To": "Ke",
|
||||
"insufficient_balance": "Saldo tidak mencukupi",
|
||||
"Send_Balance": "Kirim saldo",
|
||||
"Received_Balance": "Saldo yang diterima",
|
||||
"Minimum_Balance_Transfer": "Minimal transfer saldo",
|
||||
"Minimum_Transfer": "Minimal Transfer",
|
||||
"Company_Logo": "Logo Perusahaan",
|
||||
"Expired_IP_Pool": "IP Pool Kedaluwarsa",
|
||||
"Proxy": "Proksi",
|
||||
"Proxy_Server": "Server Proksi",
|
||||
"Proxy_Server_Login": "Masuk Server Proksi",
|
||||
"Hotspot_Plan": "Paket Hotspot",
|
||||
"PPPOE_Plan": "Paket PPPoE",
|
||||
"UNKNOWN": "TIDAK DIKENAL",
|
||||
"Are_You_Sure_": "Apa kamu yakin?",
|
||||
"Success_to_send_package": "Berhasil mengirim paket",
|
||||
"Target_has_active_plan__different_with_current_plant_": "Target mempunyai paket aktif, berbeda dengan paket saat ini.",
|
||||
"Recharge_a_friend": "Isi ulang teman",
|
||||
"Buy_for_friend": "Beli untuk teman",
|
||||
"Buy_this_for_friend_account_": "Beli ini untuk akun teman?",
|
||||
"Review_package_before_recharge": "Tinjau paket sebelum mengisi ulang",
|
||||
"Activate": "Mengaktifkan paket",
|
||||
"Deactivate": "Menonaktifkan paket",
|
||||
"Sync": "Sinkronisasi",
|
||||
"Failed_to_create_PaymeTrust_transaction_": "Gagal membuat transaksi PaymeTrust.",
|
||||
"Location": "Lokasi",
|
||||
"Radius_Plans": "Paket Radius",
|
||||
"Change_title_in_user_Plan_order": "Ubah Judul dalam urutan paket pelanggan",
|
||||
"Logs": "Log",
|
||||
"Voucher_Format": "Format Voucher",
|
||||
"Resend_To_Customer": "Kirim Ulang Ke Pelanggan",
|
||||
"Service_Type": "Jenis Layanan",
|
||||
"Others": "Lainnya",
|
||||
"PPPoE": "PPPoE",
|
||||
"Hotspot": "Hotspot",
|
||||
"Monthly_Registered_Customers": "Pendaftaran Pelanggan perbulan",
|
||||
"Total_Monthly_Sales": "Total penjualan perbulan",
|
||||
"Active_Users": "Pelanggan Aktif",
|
||||
"SuperAdmin": "Super Admin",
|
||||
"Lists": "Daftar",
|
||||
"Vouchers": "Voucher",
|
||||
"Refill_Customer": "Isi Ulang Pelanggan",
|
||||
"Recharge_Customer": "Isi Ulang Pelanggan",
|
||||
"Plans": "Paket",
|
||||
"PPPOE": "PPPOE",
|
||||
"Bandwidth": "Bandwidth",
|
||||
"Send_Message": "Mengirim pesan",
|
||||
"Single_Customer": "Pelanggan Tunggal",
|
||||
"Bulk_Customers": "Pelanggan Massal",
|
||||
"Radius": "Radius",
|
||||
"Radius_NAS": "Radius NAS",
|
||||
"Customer_Announcement": "Pengumuman Pelanggan",
|
||||
"Language_Editor": "Editor Bahasa",
|
||||
"Plan_Category": "Kategori Paket",
|
||||
"ID": "ID",
|
||||
"Prev": "Sebelumnya",
|
||||
"Internet_Plan": "Paket Internet",
|
||||
"Generated_By": "Dihasilkan oleh",
|
||||
"All_Users_Insights": "Semua Wawasan Pengguna",
|
||||
"year": "Tahun",
|
||||
"month": "Bulan",
|
||||
"week": "Pekan",
|
||||
"day": "Hari",
|
||||
"hour": "Jam",
|
||||
"minute": "Menit",
|
||||
"second": "Kedua",
|
||||
"Account_Type": "Jenis akun",
|
||||
"Contact": "Kontak",
|
||||
"Paid": "Dibayar",
|
||||
"Personal": "Pribadi",
|
||||
"Coordinates": "Koordinat",
|
||||
"Confirm": "Mengonfirmasi",
|
||||
"Name": "Nama",
|
||||
"Plan": "Paket",
|
||||
"Using": "Menggunakan",
|
||||
"Total": "Total",
|
||||
"Additional_Cost": "Biaya tambahan",
|
||||
"Resend": "Kirim ulang",
|
||||
"Login": "Masuk",
|
||||
"success": "Sukses",
|
||||
"Click_Here": "Klik disini",
|
||||
"Your_friend_do_not_have_active_package": "Teman Anda tidak memiliki paket aktif",
|
||||
"If_your_friend_have_Additional_Cost__you_will_pay_for_that_too": "Jika teman Anda memiliki biaya tambahan, Anda juga akan membayarnya",
|
||||
"Select_Payment_Gateway": "Pilih Gerbang Pembayaran",
|
||||
"Available_Payment_Gateway": "Gerbang Pembayaran yang tersedia",
|
||||
"Pay_Now": "Bayar sekarang",
|
||||
"Notes": "Catatan",
|
||||
"will_be_replaced_with_Customer_Name": "akan diganti dengan nama Pelanggan",
|
||||
"will_be_replaced_with_Customer_username": "akan diganti dengan nama pengguna Pelanggan",
|
||||
"will_be_replaced_with_Package_name": "akan diganti dengan nama paket",
|
||||
"will_be_replaced_with_Package_price": "akan diganti dengan harga Paket",
|
||||
"will_be_replaced_with_Expiration_date": "akan diganti dengan tanggal kedaluwarsa",
|
||||
"additional_bills_for_customers": "tagihan tambahan untuk pelanggan",
|
||||
"Your_Company_Name_at_Settings": "Nama Perusahaan Anda di pengaturan",
|
||||
"Your_Company_Address_at_Settings": "Alamat Perusahaan Anda di pengaturan",
|
||||
"Your_Company_Phone_at_Settings": "Telepon Perusahaan Anda di pengaturan",
|
||||
"Invoice_number": "Nomor faktur",
|
||||
"Date_invoice_created": "Tanggal faktur dibuat",
|
||||
"Payment_gateway_user_paid_from": "Pengguna gateway pembayaran membayar dari",
|
||||
"Payment_channel_user_paid_from": "Pengguna saluran pembayaran membayar dari",
|
||||
"is_Hotspot_or_PPPOE": "adalah Hotspot atau PPPOE",
|
||||
"Internet_Package": "Paket internet",
|
||||
"Internet_Package_Prices": "Harga paket internet",
|
||||
"Receiver_name": "Nama penerima",
|
||||
"Username_internet": "Nama pengguna internet",
|
||||
"User_password": "Kata sandi pengguna",
|
||||
"Transaction_datetime": "Tanggal waktu transaksi",
|
||||
"Balance_Before": "Saldo sebelumnya",
|
||||
"Balance_After": "Saldo setelahnya",
|
||||
"For_Notes_by_admin": "Untuk catatan oleh Admin",
|
||||
"how_much_balance_have_been_send": "berapa banyak saldo yang telah dikirim",
|
||||
"Current_Balance": "Saldo saat ini",
|
||||
"Sender_name": "Nama pengirim",
|
||||
"Customer_Balance": "Saldo Pelanggan",
|
||||
"Privacy_Policy": "Kebijakan Privasi",
|
||||
"Terms_and_Conditions": "Syarat dan Ketentuan",
|
||||
"Disable_Registration": "Nonaktifkan Pendaftaran",
|
||||
"Customer_just_Login_with_Phone_number_and_Voucher_Code__Voucher_will_be_password": "Pelanggan cukup masuk dengan nomor telepon dan kode voucher, Voucher akan menjadi kata sandi",
|
||||
"After_Customer_activate_voucher_or_login__customer_will_be_redirected_to_this_url": "Setelah Pelanggan mengaktifkan voucher atau masuk, Pelanggan akan diarahkan ke URL ini",
|
||||
"Extend_Postpaid_Expiration": "Perpanjang masa kedaluwarsa pascabayar",
|
||||
"Allow_Extend": "Izinkan Perpanjang",
|
||||
"Extend_Days": "Perpanjang Hari",
|
||||
"Confirmation_Message": "Pesan konfirmasi",
|
||||
"Email_Notification": "Pemberitahuan Email",
|
||||
"This_Token_will_act_as_SuperAdmin_Admin": "Token ini akan bertindak sebagai SuperAdmin\/Admin",
|
||||
"Miscellaneous": "Aneka ragam",
|
||||
"OTP_Required": "Diperlukan OTP",
|
||||
"OTP_is_required_when_user_want_to_change_phone_number": "OTP diperlukan ketika pengguna ingin mengganti nomor telepon",
|
||||
"OTP_Method": "Metode OTP",
|
||||
"SMS": "SMS",
|
||||
"WhatsApp": "WhatsApp",
|
||||
"SMS_and_WhatsApp": "SMS dan WhatsApp",
|
||||
"The_method_which_OTP_will_be_sent_to_user": "Metode OTP yang akan dikirimkan ke pengguna",
|
||||
"Disabled": "Dinonaktifkan",
|
||||
"Banned": "Dicekal",
|
||||
"Inactive": "Tidak Aktif",
|
||||
"Suspended": "Disuspend"
|
||||
}
|
||||
377
system/lang/spanish.json
Normal file
377
system/lang/spanish.json
Normal file
@@ -0,0 +1,377 @@
|
||||
{
|
||||
"Log_in": "Entrar",
|
||||
"Register": "Registro",
|
||||
"Announcement": "Anuncio",
|
||||
"Registration_Info": "Informaci\u00f3n de registro",
|
||||
"Voucher_not_found__please_buy_voucher_befor_register": "Cup\u00f3n no encontrado, compre el cup\u00f3n antes de registrarse",
|
||||
"Register_Success__You_can_login_now": "\u00a1Registro exitoso! Puedes iniciar sesi\u00f3n ahora",
|
||||
"Log_in_to_Member_Panel": "Log in to Member Panel",
|
||||
"Register_as_Member": "Reg\u00edstrese como miembro",
|
||||
"Enter_Admin_Area": "Panel de administraci\u00f3n",
|
||||
"PHPNuxBill": "DIGITAL-RED",
|
||||
"Username": "Usuario",
|
||||
"Password": "Contrase\u00f1a",
|
||||
"Passwords_does_not_match": "Las contrase\u00f1as no coinciden",
|
||||
"Account_already_axist": "La cuenta ya existe",
|
||||
"Manage": "Administrar",
|
||||
"Submit": "Enviar",
|
||||
"Save_Changes": "Guardar cambios",
|
||||
"Cancel": "Cancelar",
|
||||
"Edit": "Editar",
|
||||
"Delete": "Eliminar",
|
||||
"Welcome": "Bienvenido",
|
||||
"Data_Created_Successfully": "Datos creados con \u00e9xito",
|
||||
"Data_Updated_Successfully": "Datos actualizados con \u00e9xito",
|
||||
"Data_Deleted_Successfully": "Datos eliminados con \u00e9xito",
|
||||
"Static_Pages": "P\u00e1ginas est\u00e1ticas",
|
||||
"Failed_to_save_page__make_sure_i_can_write_to_folder_pages___i_chmod_664_pages___html_i_": "No se pudo guardar la p\u00e1gina, aseg\u00farese de que pueda escribir en las p\u00e1ginas de la carpeta, <i>chmod 664 pages\/*.html<i>",
|
||||
"Saving_page_success": "Guardando el \u00e9xito de la p\u00e1gina",
|
||||
"Sometimes_you_need_to_refresh_3_times_until_content_change": "A veces es necesario actualizar 3 veces hasta que cambie el contenido",
|
||||
"Dashboard": "Dashboard",
|
||||
"Search_Customers___": "Buscar clientes...",
|
||||
"My_Account": "Mi cuenta",
|
||||
"My_Profile": "Mi perfil",
|
||||
"Settings": "Ajustes",
|
||||
"Edit_Profile": "Editar perfil",
|
||||
"Change_Password": "Cambia la contrase\u00f1a",
|
||||
"Logout": "Cerrar sesi\u00f3n",
|
||||
"Services": "Servicios",
|
||||
"Bandwidth_Plans": "Planes de velocidad",
|
||||
"Bandwidth_Name": "Nombre de ancho de banda",
|
||||
"New_Bandwidth": "Nuevo ancho de banda",
|
||||
"Edit_Bandwidth": "Editar ancho de banda",
|
||||
"Add_New_Bandwidth": "Agregar nuevo ancho de banda",
|
||||
"Rate_Download": "Tarifa Descarga",
|
||||
"Rate_Upload": "Tasa de subida",
|
||||
"Name_Bandwidth_Already_Exist": "El ancho de banda del nombre ya existe",
|
||||
"Hotspot_Plans": "Planes de Hotspot",
|
||||
"PPPOE_Plans": "Planes PPPOE",
|
||||
"Plan_Name": "Nombre",
|
||||
"New_Service_Plan": "Nuevo plan de servicio",
|
||||
"Add_Service_Plan": "Agregar plan de servicio",
|
||||
"Edit_Service_Plan": "Editar plan de servicio",
|
||||
"Name_Plan_Already_Exist": "Nombre El plan ya existe",
|
||||
"Plan_Type": "Tipo de plan",
|
||||
"Plan_Price": "Precio del plan",
|
||||
"Limit_Type": "Tipo de l\u00edmite",
|
||||
"Unlimited": "Ilimitado",
|
||||
"Limited": "Limitado",
|
||||
"Time_Limit": "L\u00edmite de tiempo",
|
||||
"Data_Limit": "L\u00edmite de datos",
|
||||
"Both_Limit": "Ambos L\u00edmite",
|
||||
"Plan_Validity": "Validez del Plan",
|
||||
"Select_Bandwidth": "Seleccionar ancho de banda",
|
||||
"Shared_Users": "Usuarios compartidos",
|
||||
"Choose_User_Type_Sales_to_disable_access_to_Settings": "Elija Ventas de tipo de usuario para deshabilitar el acceso a la Configuraci\u00f3n",
|
||||
"Current_Password": "Contrase\u00f1a actual",
|
||||
"New_Password": "Nueva contrase\u00f1a",
|
||||
"Administrator": "Administrador",
|
||||
"Sales": "Ventas",
|
||||
"Member": "Usuario",
|
||||
"Confirm_New_Password": "Confirmar nueva contrase\u00f1a",
|
||||
"Confirm_Password": "Confirmar contrase\u00f1a",
|
||||
"Full_Name": "Nombre completo",
|
||||
"User_Type": "Tipo de usuario",
|
||||
"Address": "Direcci\u00f3n",
|
||||
"Created_On": "Creado en",
|
||||
"Expires_On": "Expira el",
|
||||
"Phone_Number": "N\u00famero de tel\u00e9fono",
|
||||
"User_deleted_Successfully": "Usuario eliminado con \u00e9xito",
|
||||
"Full_Administrator": "Administrador completo",
|
||||
"Keep_Blank_to_do_not_change_Password": "Mantener en blanco para no cambiar la contrase\u00f1a",
|
||||
"Keep_it_blank_if_you_do_not_want_to_show_currency_code": "Mant\u00e9ngalo en blanco si no desea mostrar el c\u00f3digo de moneda",
|
||||
"Theme_Style": "Estilo de tema",
|
||||
"Theme_Color": "Color del tema",
|
||||
"Default_Language": "Idioma predeterminado",
|
||||
"Network": "Red",
|
||||
"Routers": "Routers",
|
||||
"IP_Pool": "IP Pool",
|
||||
"New_Router": "Nuevo Router",
|
||||
"Add_Router": "Agregar Router",
|
||||
"Edit_Router": "Editar Router",
|
||||
"Router_Name": "Nombre del Router",
|
||||
"IP_Address": "Direccion IP",
|
||||
"Router_Secret": "Contrase\u00f1a Router",
|
||||
"Description": "Descripcion",
|
||||
"IP_Router_Already_Exist": "El enrutador IP ya existe",
|
||||
"Name_Pool": "Nombre del Pool",
|
||||
"Range_IP": "Rango de IP",
|
||||
"New_Pool": "Nuevo Pool",
|
||||
"Add_Pool": "Agregar Pool",
|
||||
"Edit_Pool": "Editar Pool",
|
||||
"Pool_Name_Already_Exist": "Nombre del Pool ya existe",
|
||||
"Refill_Account": "Recargar Ficha",
|
||||
"Recharge_Account": "Recargar Cuenta",
|
||||
"Select_Account": "Seleccionar cuenta",
|
||||
"Service_Plan": "Plan de servicio",
|
||||
"Recharge": "Recargar",
|
||||
"Method": "M\u00e9todo",
|
||||
"Account_Created_Successfully": "Cuenta creada con \u00e9xito",
|
||||
"Database_Status": "Estado de la base de datos",
|
||||
"Total_Database_Size": "Tama\u00f1o total de la base de datos",
|
||||
"Download_Database_Backup": "Descargar copia de seguridad de la base de datos",
|
||||
"Table_Name": "Nombre de la tabla",
|
||||
"Rows": "Filas",
|
||||
"Size": "Tama\u00f1o",
|
||||
"Customer": "Clientes",
|
||||
"Add_New_Contact": "A\u00f1adir nuevo contacto",
|
||||
"Edit_Contact": "Editar contacto",
|
||||
"List_Contact": "Lista de contactos",
|
||||
"Manage_Contact": "Administrar contacto",
|
||||
"Reports": "Reportes",
|
||||
"Daily_Reports": "Reportes diarios",
|
||||
"Period_Reports": "Informes del per\u00edodo",
|
||||
"All_Transactions": "Todas las transacciones",
|
||||
"Total_Income": "Ingresos totales",
|
||||
"All_Transactions_at_Date": "Todas las transacciones en la fecha",
|
||||
"Export_for_Print": "Exportar para imprimir",
|
||||
"Print": "Impresi\u00f3n",
|
||||
"Export_to_PDF": "Exportar a PDF",
|
||||
"Click_Here_to_Print": "Haga clic aqu\u00ed para imprimir",
|
||||
"You_can_use_html_tag": "Puedes usar la etiqueta html",
|
||||
"Date_Format": "Formato de fecha",
|
||||
"Income_Today": "Ingresos hoy",
|
||||
"Income_This_Month": "Ingresos este mes",
|
||||
"Users_Active": "Usuarios activos",
|
||||
"Total_Users": "Total de usuarios",
|
||||
"Users": "Usuarios",
|
||||
"Edit_User": "Editar usuario",
|
||||
"Last_Login": "\u00daltimo acceso",
|
||||
"Administrator_Users": "Usuarios administradores",
|
||||
"Manage_Administrator": "Administrar administrador",
|
||||
"Add_New_Administrator": "Agregar nuevo administrador",
|
||||
"Localisation": "Localizaci\u00f3n",
|
||||
"Backup_Restore": "Copia de seguridad\/restauracion",
|
||||
"General_Settings": "Configuraci\u00f3n general",
|
||||
"Date": "Fecha",
|
||||
"Login_Successful": "Inicio de sesi\u00f3n exitoso",
|
||||
"Failed_Login": "Inicio de sesi\u00f3n fallido",
|
||||
"Settings_Saved_Successfully": "Configuraci\u00f3n guardada con \u00e9xito",
|
||||
"User_Updated_Successfully": "Usuario actualizado con \u00e9xito",
|
||||
"User_Expired__Today": "Usuario vencido, hoy",
|
||||
"Activity_Log": "Registro de actividades",
|
||||
"View_Reports": "Ver los informes",
|
||||
"View_All": "Ver todo",
|
||||
"Number_of_Vouchers": "N\u00famero de Fichas",
|
||||
"Length_Code": "Longitud de codigo",
|
||||
"Code_Voucher": "Cup\u00f3n de Ficha",
|
||||
"Voucher": "Ficha",
|
||||
"Hotspot_Voucher": "Ficha Hotspot",
|
||||
"Status_Voucher": "Estatado de Ficha",
|
||||
"Add_Vouchers": "Agregar Ficha",
|
||||
"Create_Vouchers_Successfully": "Crear Ficha con \u00e9xito",
|
||||
"Generate": "Generar",
|
||||
"Print_side_by_side__it_will_easy_to_cut": "Imprimir uno al lado del otro, ser\u00e1 f\u00e1cil de cortar",
|
||||
"From_Date": "Partir de la fecha",
|
||||
"To_Date": "Hasta la fecha",
|
||||
"New_Service": "Nuevo servicio",
|
||||
"Type": "Tipo",
|
||||
"Finish": "Finalizar",
|
||||
"Application_Name__Company_Name": "Nombre de la aplicaci\u00f3n\/ Nombre de la empresa",
|
||||
"This_Name_will_be_shown_on_the_Title": "Este nombre se mostrar\u00e1 en el t\u00edtulo",
|
||||
"Next": "Siguiente",
|
||||
"Last": "Atras",
|
||||
"Timezone": "Zona horaria",
|
||||
"Decimal_Point": "Punto decimal",
|
||||
"Thousands_Separator": "Separador de miles",
|
||||
"Currency_Code": "C\u00f3digo de moneda",
|
||||
"Order_Voucher": "Comprobante de pedido",
|
||||
"Voucher_Activation": "Activaci\u00f3n de Fichas",
|
||||
"List_Activated_Voucher": "Lista de Fichas activados",
|
||||
"Enter_voucher_code_here": "Ingrese el c\u00f3digo de la Ficha aqu\u00ed",
|
||||
"Private_Message": "Mensaje privado",
|
||||
"Inbox": "Bandeja de entrada",
|
||||
"Outbox": "Bandeja de salida",
|
||||
"Compose": "Componer",
|
||||
"Send_to": "Enviar a",
|
||||
"Title": "T\u00edtulo",
|
||||
"Message": "Mensaje",
|
||||
"Your_Account_Information": "Informaci\u00f3n de su cuenta",
|
||||
"Welcome_to_the_Panel_Members_page__on_this_page_you_can_": "Bienvenido a la p\u00e1gina de Miembros del Panel, en esta p\u00e1gina puede:",
|
||||
"Invalid_Username_or_Password": "Usuario o contrase\u00f1a invalido",
|
||||
"You_do_not_have_permission_to_access_this_page": "Usted no tiene permiso para acceder a esta p\u00e1gina",
|
||||
"Incorrect_Current_Password": "IContrase\u00f1a actual incorrecta",
|
||||
"Password_changed_successfully__Please_login_again": "Contrase\u00f1a cambiada con \u00e9xito, por favor inicie sesi\u00f3n de nuevo",
|
||||
"All_field_is_required": "Todo el campo es requerido",
|
||||
"Voucher_Not_Valid": "Ficha no v\u00e1lida",
|
||||
"Activation_Vouchers_Successfully": "Fichas de activaci\u00f3n con \u00e9xito",
|
||||
"Data_Not_Found": "Datos no encontrados",
|
||||
"Search_by_Username": "Buscar por nombre de usuario",
|
||||
"Search_by_Name": "Buscar por nombre",
|
||||
"Search_by_Code_Voucher": "B\u00fasqueda por c\u00f3digo de Ficha",
|
||||
"Search": "B\u00fasqueda",
|
||||
"Select_a_customer": "Seleccione un cliente",
|
||||
"Select_Routers": "Seleccionar enrutadores",
|
||||
"Select_Plans": "Seleccionar planes",
|
||||
"Select_Pool": "Seleccionar Pool",
|
||||
"Hrs": "Hrs",
|
||||
"Mins": "Mins",
|
||||
"Days": "Dias",
|
||||
"Months": "Meses",
|
||||
"Add_Language": "Agregar idioma",
|
||||
"Language_Name": "Nombre del lenguaje",
|
||||
"Folder_Name": "Nombre de la carpeta",
|
||||
"Translator": "Traducir",
|
||||
"Language_Name_Already_Exist": "El nombre del idioma ya existe",
|
||||
"Payment_Gateway": "Payment Gateway",
|
||||
"Community": "Community",
|
||||
"1_user_can_be_used_for_many_devices_": "1 user can be used for many devices?",
|
||||
"Cannot_be_change_after_saved": "Cannot be change after saved",
|
||||
"Explain_Coverage_of_router": "Jelaskan Cakupan wilayah hotspot",
|
||||
"Name_of_Area_that_router_operated": "Nama Lokasi\/Wilayah Router beroperasi",
|
||||
"Payment_Notification_URL__Recurring_Notification_URL__Pay_Account_Notification_URL": "Payment Notification URL, Recurring Notification URL, Pay Account Notification URL",
|
||||
"Finish_Redirect_URL__Unfinish_Redirect_URL__Error_Redirect_URL": "Finish Redirect URL, Unfinish Redirect URL, Error Redirect URL",
|
||||
"Status": "Status",
|
||||
"Plan_Not_found": "Plan Not found",
|
||||
"Failed_to_create_transaction_": "Failed to create transaction.",
|
||||
"Seller_has_not_yet_setup_Xendit_payment_gateway": "Seller has not yet setup Xendit payment gateway",
|
||||
"Admin_has_not_yet_setup_Xendit_payment_gateway__please_tell_admin": "Admin has not yet setup Xendit payment gateway, please tell admin",
|
||||
"Buy_this__your_active_package_will_be_overwrite": "Buy this? your active package will be overwrite",
|
||||
"You_already_have_unpaid_transaction__cancel_it_or_pay_it_": "You already have unpaid transaction, cancel it or pay it.",
|
||||
"Transaction_Not_found": "Transaction Not found",
|
||||
"Cancel_it_": "Cancel it?",
|
||||
"expired": "expired",
|
||||
"Check_for_Payment": "Check for Payment",
|
||||
"Transaction_still_unpaid_": "Transaction still unpaid.",
|
||||
"Paid_Date": "Paid Date",
|
||||
"Transaction_has_been_paid_": "Transaction has been paid.",
|
||||
"PAID": "PAID",
|
||||
"CANCELED": "CANCELED",
|
||||
"UNPAID": "UNPAID",
|
||||
"PAY_NOW": "PAY NOW",
|
||||
"Buy_Hotspot_Plan": "Buy Hotspot Plan",
|
||||
"Buy_PPOE_Plan": "Buy PPOE Plan",
|
||||
"Package": "Package",
|
||||
"Order_Internet_Package": "Order Internet Package",
|
||||
"Unknown_Command_": "Unknown Command.",
|
||||
"Checking_payment": "Checking payment",
|
||||
"Create_Transaction_Success": "Create Transaction Success",
|
||||
"You_have_unpaid_transaction": "You have unpaid transaction",
|
||||
"TripayPayment_Channel": "TripayPayment Channel",
|
||||
"Payment_Channel": "Payment Channel",
|
||||
"Payment_check_failed_": "Payment check failed.",
|
||||
"Order_Package": "Order Package",
|
||||
"Transactions": "Transactions",
|
||||
"Payments": "Payments",
|
||||
"History": "History",
|
||||
"Order_History": "Order History",
|
||||
"Gateway": "Gateway",
|
||||
"Date_Done": "Date Done",
|
||||
"Unpaid_Order": "Unpaid Order",
|
||||
"Payment_Gateway_Not_Found": "Payment Gateway Not Found",
|
||||
"Payment_Gateway_saved_successfully": "Payment Gateway saved successfully",
|
||||
"ORDER": "ORDER",
|
||||
"Package_History": "Package History",
|
||||
"Buy_History": "Buy History",
|
||||
"Activation_History": "Activation History",
|
||||
"Buy_Package": "Buy Package",
|
||||
"Email": "Email",
|
||||
"Company_Footer": "Company Footer",
|
||||
"Will_show_below_user_pages": "Will show below user pages",
|
||||
"Request_OTP": "Request OTP",
|
||||
"Verification_Code": "Verification Code",
|
||||
"SMS_Verification_Code": "SMS Verification Code",
|
||||
"Please_enter_your_email_address": "Please enter your email address",
|
||||
"Failed_to_create_Paypal_transaction_": "Failed to create Paypal transaction.",
|
||||
"Plugin": "Plugin",
|
||||
"Plugin_Manager": "Plugin Manager",
|
||||
"User_Notification": "User Notification",
|
||||
"Expired_Notification": "Expired Notification",
|
||||
"User_will_get_notification_when_package_expired": "User will get notification when package expired",
|
||||
"Expired_Notification_Message": "Expired Notification Message",
|
||||
"Payment_Notification": "Payment Notification",
|
||||
"User_will_get_invoice_notification_when_buy_package_or_package_refilled": "User will get invoice notification when buy package or package refilled",
|
||||
"Current_IP": "Current IP",
|
||||
"Current_MAC": "Current MAC",
|
||||
"Login_Status": "Login Status",
|
||||
"Login_Request_successfully": "Login Request successfully",
|
||||
"Logout_Request_successfully": "Logout Request successfully",
|
||||
"Disconnect_Internet_": "Disconnect Internet?",
|
||||
"Not_Online__Login_now_": "Not Online, Login now?",
|
||||
"You_are_Online__Logout_": "You are Online, Logout?",
|
||||
"Connect_to_Internet_": "Connect to Internet?",
|
||||
"Your_account_not_connected_to_internet": "Your account not connected to internet",
|
||||
"Balance": "Balance",
|
||||
"Balance_System": "Balance System",
|
||||
"Enable_System": "Enable System",
|
||||
"Allow_Transfer": "Allow Transfer",
|
||||
"Telegram_Notification": "Telegram Notification",
|
||||
"SMS_OTP_Registration": "SMS OTP Registration",
|
||||
"Whatsapp_Notification": "Whatsapp Notification",
|
||||
"Tawk_to_Chat_Widget": "Tawk.to Chat Widget",
|
||||
"Invoice": "Invoice",
|
||||
"Country_Code_Phone": "Country Code Phone",
|
||||
"Voucher_activation_menu_will_be_hidden": "Voucher activation menu will be hidden",
|
||||
"Customer_can_deposit_money_to_buy_voucher": "Customer can deposit money to buy voucher",
|
||||
"Allow_balance_transfer_between_customers": "Allow balance transfer between customers",
|
||||
"Refill_Balance": "Refill Balance",
|
||||
"Balance_Plans": "Balance Plans",
|
||||
"Failed_to_create_transaction__": "Failed to create transaction. ",
|
||||
"Failed_to_check_status_transaction__": "Failed to check status transaction. ",
|
||||
"Disable_Voucher": "Disable Voucher",
|
||||
"Reminder_Notification": "Reminder Notification",
|
||||
"Reminder_Notification_Message": "Reminder Notification Message",
|
||||
"Reminder_7_days": "Reminder 7 days",
|
||||
"Reminder_3_days": "Reminder 3 days",
|
||||
"Reminder_1_day": "Reminder 1 day",
|
||||
"PPPOE_Password": "PPPOE Password",
|
||||
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_user_password": "User Cannot change this, only admin. if it Empty it will use user password",
|
||||
"Invoice_Balance_Message": "Invoice Balance Message",
|
||||
"Invoice_Notification_Payment": "Invoice Notification Payment",
|
||||
"Balance_Notification_Payment": "Balance Notification Payment",
|
||||
"Buy_Balance": "Buy Balance",
|
||||
"Price": "Price",
|
||||
"Validity": "Validity",
|
||||
"Disable_auto_renewal_": "Disable auto renewal?",
|
||||
"Auto_Renewal_On": "Auto Renewal On",
|
||||
"Enable_auto_renewal_": "Enable auto renewal?",
|
||||
"Auto_Renewal_Off": "Auto Renewal Off",
|
||||
"Invoice_Footer": "Invoice Footer",
|
||||
"Pay_With_Balance": "Pay With Balance",
|
||||
"Pay_this_with_Balance__your_active_package_will_be_overwrite": "Pay this with Balance? your active package will be overwrite",
|
||||
"Success_to_buy_package": "Success to buy package",
|
||||
"Auto_Renewal": "Auto Renewal",
|
||||
"View": "View",
|
||||
"Back": "Back",
|
||||
"Active": "Active",
|
||||
"Transfer_Balance": "Transfer Balance",
|
||||
"Send_your_balance_": "Send your balance?",
|
||||
"Send": "Send",
|
||||
"Cannot_send_to_yourself": "Cannot send to yourself",
|
||||
"Sending_balance_success": "Sending balance success",
|
||||
"From": "From",
|
||||
"To": "To",
|
||||
"insufficient_balance": "insufficient balance",
|
||||
"Send_Balance": "Send Balance",
|
||||
"Received_Balance": "Received Balance",
|
||||
"Minimum_Balance_Transfer": "Minimum Balance Transfer",
|
||||
"Minimum_Transfer": "Minimum Transfer",
|
||||
"Company_Logo": "Company Logo",
|
||||
"Expired_IP_Pool": "Expired IP Pool",
|
||||
"Proxy": "Proxy",
|
||||
"Proxy_Server": "Proxy Server",
|
||||
"Proxy_Server_Login": "Proxy Server Login",
|
||||
"Hotspot_Plan": "Hotspot Plan",
|
||||
"PPPOE_Plan": "PPPOE Plan",
|
||||
"UNKNOWN": "UNKNOWN",
|
||||
"Are_You_Sure_": "Are You Sure?",
|
||||
"Success_to_send_package": "Success to send package",
|
||||
"Target_has_active_plan__different_with_current_plant_": "Target has active plan, different with current plant.",
|
||||
"Recharge_a_friend": "Recharge a friend",
|
||||
"Buy_for_friend": "Buy for friend",
|
||||
"Buy_this_for_friend_account_": "Buy this for friend account?",
|
||||
"Review_package_before_recharge": "Review package before recharge",
|
||||
"Activate": "Activate",
|
||||
"Deactivate": "Deactivate",
|
||||
"Sync": "Sync",
|
||||
"Failed_to_create_PaymeTrust_transaction_": "Failed to create PaymeTrust transaction.",
|
||||
"Location": "Location",
|
||||
"Voucher_Format": "Voucher Format",
|
||||
"Service_Type": "Service Type",
|
||||
"Others": "Others",
|
||||
"PPPoE": "PPPoE",
|
||||
"Hotspot": "Hotspot",
|
||||
"Monthly_Registered_Customers": "Monthly Registered Customers",
|
||||
"Total_Monthly_Sales": "Total Monthly Sales",
|
||||
"Active_Users": "Active Users"
|
||||
}
|
||||
3
system/lang/swahili.json
Normal file
3
system/lang/swahili.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Settings_Saved_Successfully": "Mipangilio Imehifadhiwa Imefaulu"
|
||||
}
|
||||
372
system/lang/turkish.json
Normal file
372
system/lang/turkish.json
Normal file
@@ -0,0 +1,372 @@
|
||||
{
|
||||
"Log_in": "Oturum a\u00e7",
|
||||
"Register": "Kay\u0131t olmak",
|
||||
"Announcement": "Duyuru",
|
||||
"Registration_Info": "Kay\u0131t Bilgisi",
|
||||
"Voucher_not_found__please_buy_voucher_befor_register": "Kupon bulunamad\u0131, l\u00fctfen kay\u0131t olun ve kay\u0131t olun",
|
||||
"Register_Success__You_can_login_now": "Kay\u0131t Ba\u015far\u0131l\u0131! \u015eimdi giri\u015f yapabilirsiniz ",
|
||||
"Log_in_to_Member_Panel": "\u00dcye Paneli'ne giri\u015f yap\u0131n",
|
||||
"Register_as_Member": "\u00dcye olarak kay\u0131t ol",
|
||||
"Enter_Admin_Area": "Y\u00f6netici Paneli Giri\u015fi",
|
||||
"PHPNuxBill": "PHPNuxBill",
|
||||
"Username": "Kullan\u0131c\u0131 ad\u0131",
|
||||
"Password": "Parola",
|
||||
"Passwords_does_not_match": "Parolalar e\u015fle\u015fmiyor",
|
||||
"Account_already_axist": "Hesap zaten aksanl\u0131",
|
||||
"Manage": "Y\u00f6net",
|
||||
"Submit": "G\u00f6nder",
|
||||
"Save_Changes": "De\u011fi\u015fiklikleri Kaydet",
|
||||
"Cancel": "\u0130ptal etmek",
|
||||
"Edit": "D\u00fczenle",
|
||||
"Delete": "Sil",
|
||||
"Welcome": "Ho\u015fgeldiniz",
|
||||
"Data_Created_Successfully": "Veriler Ba\u015far\u0131yla Olu\u015fturuldu ",
|
||||
"Data_Updated_Successfully": "Veriler Ba\u015far\u0131yla G\u00fcncellendi",
|
||||
"Data_Deleted_Successfully": "Veri Ba\u015far\u0131yla Silindi",
|
||||
"Dashboard": "Dashboard",
|
||||
"Search_Customers___": "M\u00fc\u015fteri Ara ...",
|
||||
"My_Account": "Hesab\u0131m",
|
||||
"My_Profile": "Benim profilim",
|
||||
"Settings": "Ayarlar",
|
||||
"Edit_Profile": "Profili D\u00fczenle",
|
||||
"Change_Password": "\u015eifre de\u011fi\u015ftir",
|
||||
"Logout": "\u00c7\u0131k\u0131\u015f Yap",
|
||||
"Services": "Hizmetler",
|
||||
"Bandwidth_Plans": "Bant Geni\u015fli\u011fi Planlar\u0131",
|
||||
"Bandwidth_Name": "Bant Geni\u015fli\u011fi Ad\u0131",
|
||||
"New_Bandwidth": "Yeni Bant Geni\u015fli\u011fi",
|
||||
"Edit_Bandwidth": "Bant Geni\u015fli\u011fini D\u00fczenle",
|
||||
"Add_New_Bandwidth": "Yeni Bant Geni\u015fli\u011fi Ekle",
|
||||
"Rate_Download": "\u0130ndirme Oran\u0131",
|
||||
"Rate_Upload": "\u00dccret Y\u00fckleme",
|
||||
"Name_Bandwidth_Already_Exist": "Bandwidth Ad\u0131 Zaten Var",
|
||||
"Hotspot_Plans": "Hotspot Planlar\u0131",
|
||||
"PPPOE_Plans": "PPPOE Planlar\u0131",
|
||||
"Plan_Name": "Plan Ad\u0131",
|
||||
"New_Service_Plan": "Yeni Hizmet Plan\u0131",
|
||||
"Add_Service_Plan": "Hizmet Plan\u0131 Ekle",
|
||||
"Edit_Service_Plan": "Hizmet Plan\u0131n\u0131 D\u00fczenle",
|
||||
"Name_Plan_Already_Exist": "Plan\u0131 \u0130smi Zaten Var",
|
||||
"Plan_Type": "Plan T\u00fcr\u00fc",
|
||||
"Plan_Price": "Plan Fiyat\u0131",
|
||||
"Limit_Type": "S\u0131n\u0131r Tipi",
|
||||
"Unlimited": "S\u0131n\u0131rs\u0131z",
|
||||
"Limited": "S\u0131n\u0131rl\u0131",
|
||||
"Time_Limit": "Zaman s\u0131n\u0131r\u0131",
|
||||
"Data_Limit": "Veri S\u0131n\u0131r\u0131",
|
||||
"Both_Limit": "Her \u0130ki S\u0131n\u0131r",
|
||||
"Plan_Validity": "Plan Ge\u00e7erlili\u011fi",
|
||||
"Select_Bandwidth": "Bant Geni\u015fli\u011fini Se\u00e7",
|
||||
"Shared_Users": "Payla\u015f\u0131lan Kullan\u0131c\u0131lar",
|
||||
"Choose_User_Type_Sales_to_disable_access_to_Settings": "Ayarlar'a eri\u015fimi devre d\u0131\u015f\u0131 b\u0131rakmak i\u00e7in Kullan\u0131c\u0131 T\u00fcr\u00fc Sat\u0131\u015flar\u0131n\u0131 Se\u00e7",
|
||||
"Current_Password": "\u015eimdiki \u015eifre",
|
||||
"New_Password": "Yeni \u015eifre",
|
||||
"Administrator": "Y\u00f6netici",
|
||||
"Sales": "Sat\u0131\u015f",
|
||||
"Member": "\u00dcye",
|
||||
"Confirm_New_Password": "Yeni \u015fifreyi onayla",
|
||||
"Confirm_Password": "\u015eifreyi Onayla",
|
||||
"Full_Name": "Ad Soyad",
|
||||
"User_Type": "Kullan\u0131c\u0131 tipi",
|
||||
"Address": "Adres",
|
||||
"Created_On": "Olu\u015fturuldu",
|
||||
"Expires_On": "Tarihinde sona eriyor",
|
||||
"Phone_Number": "Telefon numaras\u0131",
|
||||
"User_deleted_Successfully": "Kullan\u0131c\u0131 Ba\u015far\u0131yla Silindi",
|
||||
"Full_Administrator": "Tam Y\u00f6netici",
|
||||
"Keep_Blank_to_do_not_change_Password": "\u015eifreyi de\u011fi\u015ftirmemek i\u00e7in bo\u015f tutun",
|
||||
"Keep_it_blank_if_you_do_not_want_to_show_currency_code": "Para birimi kodunu g\u00f6stermek istemiyorsan\u0131z bo\u015f b\u0131rak\u0131n",
|
||||
"Theme_Style": "Tema Stili",
|
||||
"Theme_Color": "Tema Rengi",
|
||||
"Default_Language": "Varsay\u0131lan dil",
|
||||
"Network": "A\u011f",
|
||||
"Routers": "Y\u00f6nlendiriciler",
|
||||
"IP_Pool": "IP Havuzu",
|
||||
"New_Router": "Yeni Y\u00f6nlendirici",
|
||||
"Add_Router": "Router ekle",
|
||||
"Edit_Router": "Y\u00f6nlendiriciyi D\u00fczenle",
|
||||
"Router_Name": "Y\u00f6nlendirici Ad\u0131",
|
||||
"IP_Address": "IP adresi",
|
||||
"Router_Secret": "Y\u00f6nlendirici S\u0131rr\u0131",
|
||||
"Description": "A\u00e7\u0131klama",
|
||||
"IP_Router_Already_Exist": "IP Router Zaten Var",
|
||||
"Name_Pool": "\u0130sim Havuzu",
|
||||
"Range_IP": "Aral\u0131k \u0130P",
|
||||
"New_Pool": "Yeni Havuz",
|
||||
"Add_Pool": "Havuz ekle",
|
||||
"Edit_Pool": "Havuzu D\u00fczenle",
|
||||
"Pool_Name_Already_Exist": "Havuz Ad\u0131 \u200b\u200bZaten Var",
|
||||
"Refill_Account": "Hesab\u0131 Yenile",
|
||||
"Recharge_Account": "Hesab\u0131 Yeniden \u015earj Et",
|
||||
"Select_Account": "Hesap Se\u00e7",
|
||||
"Service_Plan": "Servis plan\u0131",
|
||||
"Recharge": "\u015earj",
|
||||
"Method": "Y\u00f6ntem",
|
||||
"Account_Created_Successfully": "Hesap ba\u015far\u0131yla olu\u015fturuldu",
|
||||
"Database_Status": "Veritaban\u0131 Durumu",
|
||||
"Total_Database_Size": "Toplam Veritaban\u0131 Boyutu",
|
||||
"Download_Database_Backup": "Veritaban\u0131 Yedekleme \u0130ndir",
|
||||
"Table_Name": "Tablo ismi",
|
||||
"Rows": "Sat\u0131r",
|
||||
"Size": "Boyut",
|
||||
"Customer": "M\u00fc\u015fteri",
|
||||
"Add_New_Contact": "Yeni \u0130leti\u015fim Ekle",
|
||||
"Edit_Contact": "\u0130leti\u015fim D\u00fczenle",
|
||||
"List_Contact": "Liste \u0130rtibat",
|
||||
"Manage_Contact": "Ki\u015fiyi Y\u00f6netin",
|
||||
"Reports": "Raporlar",
|
||||
"Daily_Reports": "G\u00fcnl\u00fck raporlar",
|
||||
"Period_Reports": "D\u00f6nem Raporlar\u0131",
|
||||
"All_Transactions": "T\u00fcm \u0130\u015flemler",
|
||||
"Total_Income": "Toplam gelir",
|
||||
"All_Transactions_at_Date": "Tarihte T\u00fcm \u0130\u015flemler",
|
||||
"Export_for_Print": "Bask\u0131 i\u00e7in \u0130hracat",
|
||||
"Print": "Bask\u0131",
|
||||
"Export_to_PDF": "PDF'ye Aktar",
|
||||
"Click_Here_to_Print": "Yazd\u0131rmak i\u00e7in Buraya T\u0131klay\u0131n",
|
||||
"You_can_use_html_tag": "Html etiketini kullanabilirsiniz",
|
||||
"Date_Format": "Tarih format\u0131",
|
||||
"Income_Today": "Gelir Bug\u00fcn",
|
||||
"Income_This_Month": "Bu Ay Gelir",
|
||||
"Users_Active": "Kullan\u0131c\u0131lar Aktif",
|
||||
"Total_Users": "Toplam Kullan\u0131c\u0131",
|
||||
"Users": "Kullan\u0131c\u0131lar",
|
||||
"Edit_User": "Kullan\u0131c\u0131y\u0131 d\u00fczenle",
|
||||
"Last_Login": "Son giri\u015f",
|
||||
"Administrator_Users": "Y\u00f6netici Kullan\u0131c\u0131lar\u0131",
|
||||
"Manage_Administrator": "Y\u00f6netici Y\u00f6net",
|
||||
"Add_New_Administrator": "Yeni Y\u00f6netici Ekleyin",
|
||||
"Localisation": "Lokalizasyon",
|
||||
"Backup_Restore": "Yedekleme \/ Geri",
|
||||
"General_Settings": "Genel Ayarlar",
|
||||
"Date": "Tarih",
|
||||
"Login_Successful": "Giri\u015f ba\u015far\u0131l\u0131",
|
||||
"Failed_Login": "Ba\u015far\u0131s\u0131z oturum a\u00e7ma",
|
||||
"Settings_Saved_Successfully": "Ayarlar ba\u015far\u0131yla kaydedildi",
|
||||
"User_Updated_Successfully": "Kullan\u0131c\u0131 Ba\u015far\u0131yla G\u00fcncellendi",
|
||||
"User_Expired__Today": "Kullan\u0131c\u0131 S\u00fcresi Doldu, Bug\u00fcn",
|
||||
"Activity_Log": "Etkinlik G\u00fcnl\u00fc\u011f\u00fc",
|
||||
"View_Reports": "Raporlar\u0131 G\u00f6r\u00fcnt\u00fcle",
|
||||
"View_All": "Hepsini g\u00f6r",
|
||||
"Number_of_Vouchers": "Kuponlar\u0131n Say\u0131s\u0131",
|
||||
"Length_Code": "Uzunluk Kodu",
|
||||
"Code_Voucher": "Kod Makbuzu",
|
||||
"Voucher": "Fi\u015f",
|
||||
"Hotspot_Voucher": "Hotspot Kuponu",
|
||||
"Status_Voucher": "Durum Makbuzu",
|
||||
"Add_Vouchers": "Kupon Ekle",
|
||||
"Create_Vouchers_Successfully": "Kuponlar\u0131 ba\u015far\u0131yla olu\u015ftur",
|
||||
"Generate": "Genel",
|
||||
"Print_side_by_side__it_will_easy_to_cut": "Yanyana yazd\u0131r\u0131rsan\u0131z, kesmesi daha kolay olacakt\u0131r.",
|
||||
"From_Date": "\u0130tibaren",
|
||||
"To_Date": "Bug\u00fcne kadar",
|
||||
"New_Service": "Yeni Servis",
|
||||
"Type": "T\u00fcr",
|
||||
"Finish": "Biti\u015f",
|
||||
"Application_Name__Company_Name": "Uygulama Ad\u0131 \/ \u015eirket Ad\u0131",
|
||||
"This_Name_will_be_shown_on_the_Title": "Bu \u0130sim Ba\u015fl\u0131kta g\u00f6sterilecek",
|
||||
"Next": "Sonraki",
|
||||
"Last": "Son",
|
||||
"Timezone": "Saat dilimi",
|
||||
"Decimal_Point": "Ondal\u0131k nokta",
|
||||
"Thousands_Separator": "Bin Ay\u0131r\u0131c\u0131",
|
||||
"Currency_Code": "Para Birimi Kodu",
|
||||
"Order_Voucher": "Sipari\u015f Makbuzu",
|
||||
"Voucher_Activation": "Kupon Aktivasyonu",
|
||||
"List_Activated_Voucher": "Aktif Fi\u015f Listesi",
|
||||
"Enter_voucher_code_here": "Kupon kodunu buraya girin",
|
||||
"Private_Message": "\u00d6zel mesaj",
|
||||
"Inbox": "Gelen kutusu",
|
||||
"Outbox": "Giden",
|
||||
"Compose": "Olu\u015fturma",
|
||||
"Send_to": "G\u00f6nderildi",
|
||||
"Title": "Ba\u015fl\u0131k",
|
||||
"Message": "Mesaj",
|
||||
"Your_Account_Information": "Hesap Bilgileriniz",
|
||||
"Invalid_Username_or_Password": "Ge\u00e7ersiz kullan\u0131c\u0131 ad\u0131 veya \u015fifre",
|
||||
"You_do_not_have_permission_to_access_this_page": "Bu sayfaya eri\u015fim izniniz yok",
|
||||
"Incorrect_Current_Password": "Yanl\u0131\u015f Ge\u00e7erli \u015eifre",
|
||||
"Password_changed_successfully__Please_login_again": "\u015eifre ba\u015far\u0131yla de\u011fi\u015ftirildi, L\u00fctfen tekrar giri\u015f yap\u0131n",
|
||||
"All_field_is_required": "T\u00fcm alan gerekli",
|
||||
"Voucher_Not_Valid": "Kupon Ge\u00e7erli De\u011fil",
|
||||
"Activation_Vouchers_Successfully": "Aktivasyon Kuponlar\u0131 Ba\u015far\u0131yla",
|
||||
"Data_Not_Found": "Veri bulunamad\u0131",
|
||||
"Search_by_Username": "Kullan\u0131c\u0131 Ad\u0131na G\u00f6re Ara",
|
||||
"Search_by_Name": "\u0130sme G\u00f6re Ara",
|
||||
"Search_by_Code_Voucher": "Kod Makbuzuna G\u00f6re Ara",
|
||||
"Search": "Arama",
|
||||
"Select_a_customer": "Bir m\u00fc\u015fteri se\u00e7in",
|
||||
"Select_Routers": "Router'lar\u0131 Se\u00e7",
|
||||
"Select_Plans": "Planlar\u0131 Se\u00e7",
|
||||
"Select_Pool": "Havuz Se\u00e7",
|
||||
"Hrs": "Saat",
|
||||
"Mins": "Dk",
|
||||
"Days": "G\u00fcn",
|
||||
"Months": "Ay",
|
||||
"Add_Language": "Dil Ekle",
|
||||
"Language_Name": "Dil ad\u0131",
|
||||
"Folder_Name": "Klas\u00f6r ad\u0131",
|
||||
"Translator": "\u00c7evirmen",
|
||||
"Language_Name_Already_Exist": "Dil Ad\u0131 Zaten Var",
|
||||
"Payment_Gateway": "Payment Gateway",
|
||||
"Community": "Community",
|
||||
"1_user_can_be_used_for_many_devices_": "1 user can be used for many devices?",
|
||||
"Cannot_be_change_after_saved": "Cannot be change after saved",
|
||||
"Explain_Coverage_of_router": "Jelaskan Cakupan wilayah hotspot",
|
||||
"Name_of_Area_that_router_operated": "Nama Lokasi\/Wilayah Router beroperasi",
|
||||
"Payment_Notification_URL__Recurring_Notification_URL__Pay_Account_Notification_URL": "Payment Notification URL, Recurring Notification URL, Pay Account Notification URL",
|
||||
"Finish_Redirect_URL__Unfinish_Redirect_URL__Error_Redirect_URL": "Finish Redirect URL, Unfinish Redirect URL, Error Redirect URL",
|
||||
"Status": "Status",
|
||||
"Plan_Not_found": "Plan Not found",
|
||||
"Failed_to_create_transaction_": "Failed to create transaction.",
|
||||
"Seller_has_not_yet_setup_Xendit_payment_gateway": "Seller has not yet setup Xendit payment gateway",
|
||||
"Admin_has_not_yet_setup_Xendit_payment_gateway__please_tell_admin": "Admin has not yet setup Xendit payment gateway, please tell admin",
|
||||
"Buy_this__your_active_package_will_be_overwrite": "Buy this? your active package will be overwrite",
|
||||
"You_already_have_unpaid_transaction__cancel_it_or_pay_it_": "You already have unpaid transaction, cancel it or pay it.",
|
||||
"Transaction_Not_found": "Transaction Not found",
|
||||
"Cancel_it_": "Cancel it?",
|
||||
"expired": "expired",
|
||||
"Check_for_Payment": "Check for Payment",
|
||||
"Transaction_still_unpaid_": "Transaction still unpaid.",
|
||||
"Paid_Date": "Paid Date",
|
||||
"Transaction_has_been_paid_": "Transaction has been paid.",
|
||||
"PAID": "PAID",
|
||||
"CANCELED": "CANCELED",
|
||||
"UNPAID": "UNPAID",
|
||||
"PAY_NOW": "PAY NOW",
|
||||
"Buy_Hotspot_Plan": "Buy Hotspot Plan",
|
||||
"Buy_PPOE_Plan": "Buy PPOE Plan",
|
||||
"Package": "Package",
|
||||
"Order_Internet_Package": "Order Internet Package",
|
||||
"Unknown_Command_": "Unknown Command.",
|
||||
"Checking_payment": "Checking payment",
|
||||
"Create_Transaction_Success": "Create Transaction Success",
|
||||
"You_have_unpaid_transaction": "You have unpaid transaction",
|
||||
"TripayPayment_Channel": "TripayPayment Channel",
|
||||
"Payment_Channel": "Payment Channel",
|
||||
"Payment_check_failed_": "Payment check failed.",
|
||||
"Order_Package": "Order Package",
|
||||
"Transactions": "Transactions",
|
||||
"Payments": "Payments",
|
||||
"History": "History",
|
||||
"Order_History": "Order History",
|
||||
"Gateway": "Gateway",
|
||||
"Date_Done": "Date Done",
|
||||
"Unpaid_Order": "Unpaid Order",
|
||||
"Payment_Gateway_Not_Found": "Payment Gateway Not Found",
|
||||
"Payment_Gateway_saved_successfully": "Payment Gateway saved successfully",
|
||||
"ORDER": "ORDER",
|
||||
"Package_History": "Package History",
|
||||
"Buy_History": "Buy History",
|
||||
"Activation_History": "Activation History",
|
||||
"Buy_Package": "Buy Package",
|
||||
"Email": "Email",
|
||||
"Company_Footer": "Company Footer",
|
||||
"Will_show_below_user_pages": "Will show below user pages",
|
||||
"Request_OTP": "Request OTP",
|
||||
"Verification_Code": "Verification Code",
|
||||
"SMS_Verification_Code": "SMS Verification Code",
|
||||
"Please_enter_your_email_address": "Please enter your email address",
|
||||
"Failed_to_create_Paypal_transaction_": "Failed to create Paypal transaction.",
|
||||
"Plugin": "Plugin",
|
||||
"Plugin_Manager": "Plugin Manager",
|
||||
"User_Notification": "User Notification",
|
||||
"Expired_Notification": "Expired Notification",
|
||||
"User_will_get_notification_when_package_expired": "User will get notification when package expired",
|
||||
"Expired_Notification_Message": "Expired Notification Message",
|
||||
"Payment_Notification": "Payment Notification",
|
||||
"User_will_get_invoice_notification_when_buy_package_or_package_refilled": "User will get invoice notification when buy package or package refilled",
|
||||
"Current_IP": "Current IP",
|
||||
"Current_MAC": "Current MAC",
|
||||
"Login_Status": "Login Status",
|
||||
"Login_Request_successfully": "Login Request successfully",
|
||||
"Logout_Request_successfully": "Logout Request successfully",
|
||||
"Disconnect_Internet_": "Disconnect Internet?",
|
||||
"Not_Online__Login_now_": "Not Online, Login now?",
|
||||
"You_are_Online__Logout_": "You are Online, Logout?",
|
||||
"Connect_to_Internet_": "Connect to Internet?",
|
||||
"Your_account_not_connected_to_internet": "Your account not connected to internet",
|
||||
"Balance": "Balance",
|
||||
"Balance_System": "Balance System",
|
||||
"Enable_System": "Enable System",
|
||||
"Allow_Transfer": "Allow Transfer",
|
||||
"Telegram_Notification": "Telegram Notification",
|
||||
"SMS_OTP_Registration": "SMS OTP Registration",
|
||||
"Whatsapp_Notification": "Whatsapp Notification",
|
||||
"Tawk_to_Chat_Widget": "Tawk.to Chat Widget",
|
||||
"Invoice": "Invoice",
|
||||
"Country_Code_Phone": "Country Code Phone",
|
||||
"Voucher_activation_menu_will_be_hidden": "Voucher activation menu will be hidden",
|
||||
"Customer_can_deposit_money_to_buy_voucher": "Customer can deposit money to buy voucher",
|
||||
"Allow_balance_transfer_between_customers": "Allow balance transfer between customers",
|
||||
"Failed_to_create_transaction__": "Failed to create transaction. ",
|
||||
"Failed_to_check_status_transaction__": "Failed to check status transaction. ",
|
||||
"Disable_Voucher": "Disable Voucher",
|
||||
"Reminder_Notification": "Reminder Notification",
|
||||
"Reminder_Notification_Message": "Reminder Notification Message",
|
||||
"Reminder_7_days": "Reminder 7 days",
|
||||
"Reminder_3_days": "Reminder 3 days",
|
||||
"Reminder_1_day": "Reminder 1 day",
|
||||
"PPPOE_Password": "PPPOE Password",
|
||||
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_user_password": "User Cannot change this, only admin. if it Empty it will use user password",
|
||||
"Invoice_Balance_Message": "Invoice Balance Message",
|
||||
"Invoice_Notification_Payment": "Invoice Notification Payment",
|
||||
"Balance_Notification_Payment": "Balance Notification Payment",
|
||||
"Balance_Plans": "Balance Plans",
|
||||
"Buy_Balance": "Buy Balance",
|
||||
"Price": "Price",
|
||||
"Validity": "Validity",
|
||||
"Disable_auto_renewal_": "Disable auto renewal?",
|
||||
"Auto_Renewal_On": "Auto Renewal On",
|
||||
"Enable_auto_renewal_": "Enable auto renewal?",
|
||||
"Auto_Renewal_Off": "Auto Renewal Off",
|
||||
"Refill_Balance": "Refill Balance",
|
||||
"Invoice_Footer": "Invoice Footer",
|
||||
"Pay_With_Balance": "Pay With Balance",
|
||||
"Pay_this_with_Balance__your_active_package_will_be_overwrite": "Pay this with Balance? your active package will be overwrite",
|
||||
"Success_to_buy_package": "Success to buy package",
|
||||
"Auto_Renewal": "Auto Renewal",
|
||||
"View": "View",
|
||||
"Back": "Back",
|
||||
"Active": "Active",
|
||||
"Transfer_Balance": "Transfer Balance",
|
||||
"Send_your_balance_": "Send your balance?",
|
||||
"Send": "Send",
|
||||
"Cannot_send_to_yourself": "Cannot send to yourself",
|
||||
"Sending_balance_success": "Sending balance success",
|
||||
"From": "From",
|
||||
"To": "To",
|
||||
"insufficient_balance": "insufficient balance",
|
||||
"Send_Balance": "Send Balance",
|
||||
"Received_Balance": "Received Balance",
|
||||
"Minimum_Balance_Transfer": "Minimum Balance Transfer",
|
||||
"Minimum_Transfer": "Minimum Transfer",
|
||||
"Company_Logo": "Company Logo",
|
||||
"Expired_IP_Pool": "Expired IP Pool",
|
||||
"Proxy": "Proxy",
|
||||
"Proxy_Server": "Proxy Server",
|
||||
"Proxy_Server_Login": "Proxy Server Login",
|
||||
"Hotspot_Plan": "Hotspot Plan",
|
||||
"PPPOE_Plan": "PPPOE Plan",
|
||||
"UNKNOWN": "UNKNOWN",
|
||||
"Are_You_Sure_": "Are You Sure?",
|
||||
"Success_to_send_package": "Success to send package",
|
||||
"Target_has_active_plan__different_with_current_plant_": "Target has active plan, different with current plant.",
|
||||
"Recharge_a_friend": "Recharge a friend",
|
||||
"Buy_for_friend": "Buy for friend",
|
||||
"Buy_this_for_friend_account_": "Buy this for friend account?",
|
||||
"Review_package_before_recharge": "Review package before recharge",
|
||||
"Activate": "Activate",
|
||||
"Deactivate": "Deactivate",
|
||||
"Sync": "Sync",
|
||||
"Failed_to_create_PaymeTrust_transaction_": "Failed to create PaymeTrust transaction.",
|
||||
"Location": "Location",
|
||||
"Voucher_Format": "Voucher Format",
|
||||
"Service_Type": "Service Type",
|
||||
"Others": "Others",
|
||||
"PPPoE": "PPPoE",
|
||||
"Hotspot": "Hotspot",
|
||||
"Monthly_Registered_Customers": "Monthly Registered Customers",
|
||||
"Total_Monthly_Sales": "Total Monthly Sales",
|
||||
"Active_Users": "Active Users"
|
||||
}
|
||||
40
system/mail/Exception.php
Normal file
40
system/mail/Exception.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHPMailer Exception class.
|
||||
* PHP Version 5.5.
|
||||
*
|
||||
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
|
||||
*
|
||||
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
|
||||
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
|
||||
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
|
||||
* @author Brent R. Matzelle (original founder)
|
||||
* @copyright 2012 - 2020 Marcus Bointon
|
||||
* @copyright 2010 - 2012 Jim Jagielski
|
||||
* @copyright 2004 - 2009 Andy Prevost
|
||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
||||
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
namespace PHPMailer\PHPMailer;
|
||||
|
||||
/**
|
||||
* PHPMailer exception handler.
|
||||
*
|
||||
* @author Marcus Bointon <phpmailer@synchromedia.co.uk>
|
||||
*/
|
||||
class Exception extends \Exception
|
||||
{
|
||||
/**
|
||||
* Prettify error message output.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function errorMessage()
|
||||
{
|
||||
return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n";
|
||||
}
|
||||
}
|
||||
5252
system/mail/PHPMailer.php
Normal file
5252
system/mail/PHPMailer.php
Normal file
File diff suppressed because it is too large
Load Diff
1497
system/mail/SMTP.php
Normal file
1497
system/mail/SMTP.php
Normal file
File diff suppressed because it is too large
Load Diff
112
system/migrate_notifications.php
Normal file
112
system/migrate_notifications.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Migration script for plan-type-specific notification templates
|
||||
* This script converts existing notification templates to the new plan-type-specific structure
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__) . '/init.php';
|
||||
|
||||
// Check if running from command line or web
|
||||
$isCli = php_sapi_name() === 'cli';
|
||||
|
||||
if (!$isCli) {
|
||||
_admin(); // Check admin permissions for web access
|
||||
}
|
||||
|
||||
echo "Starting notification template migration...\n";
|
||||
|
||||
$notifications_file = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'notifications.json';
|
||||
$default_file = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'notifications.default.json';
|
||||
|
||||
// Load existing notifications
|
||||
$existing_notifications = [];
|
||||
if (file_exists($notifications_file)) {
|
||||
$existing_notifications = json_decode(file_get_contents($notifications_file), true);
|
||||
echo "Found existing notifications.json\n";
|
||||
} else {
|
||||
echo "No existing notifications.json found, using default templates\n";
|
||||
$existing_notifications = json_decode(file_get_contents($default_file), true);
|
||||
}
|
||||
|
||||
// Load new default structure
|
||||
$new_default = json_decode(file_get_contents($default_file), true);
|
||||
|
||||
// Plan-specific keys that need migration
|
||||
$plan_specific_keys = ['expired', 'reminder_7_day', 'reminder_3_day', 'reminder_1_day'];
|
||||
|
||||
$migrated_notifications = [];
|
||||
|
||||
// Migrate plan-specific templates
|
||||
foreach ($plan_specific_keys as $key) {
|
||||
if (isset($existing_notifications[$key])) {
|
||||
$existing_value = $existing_notifications[$key];
|
||||
|
||||
// Check if it's already in new format (array)
|
||||
if (is_array($existing_value)) {
|
||||
echo "Template '$key' is already in new format, keeping as is\n";
|
||||
$migrated_notifications[$key] = $existing_value;
|
||||
} else {
|
||||
// Convert old format to new format
|
||||
echo "Converting template '$key' to plan-type-specific format\n";
|
||||
|
||||
// Use the old template as default for both plan types
|
||||
$migrated_notifications[$key] = [
|
||||
'hotspot' => $existing_value,
|
||||
'pppoe' => $existing_value,
|
||||
'default' => $existing_value
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// Use new default structure
|
||||
echo "Using default template for '$key'\n";
|
||||
$migrated_notifications[$key] = $new_default[$key];
|
||||
}
|
||||
}
|
||||
|
||||
// Copy other notification types as-is
|
||||
$other_keys = ['balance_send', 'balance_received', 'invoice_paid', 'invoice_balance', 'user_registration'];
|
||||
foreach ($other_keys as $key) {
|
||||
if (isset($existing_notifications[$key])) {
|
||||
$migrated_notifications[$key] = $existing_notifications[$key];
|
||||
} else {
|
||||
$migrated_notifications[$key] = $new_default[$key];
|
||||
}
|
||||
}
|
||||
|
||||
// Create backup of original file
|
||||
if (file_exists($notifications_file)) {
|
||||
$backup_file = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'notifications.backup.' . date('Y-m-d_H-i-s') . '.json';
|
||||
copy($notifications_file, $backup_file);
|
||||
echo "Created backup: $backup_file\n";
|
||||
}
|
||||
|
||||
// Save migrated notifications
|
||||
$result = file_put_contents($notifications_file, json_encode($migrated_notifications, JSON_PRETTY_PRINT));
|
||||
|
||||
if ($result !== false) {
|
||||
echo "Migration completed successfully!\n";
|
||||
echo "New notification structure saved to: $notifications_file\n";
|
||||
echo "\nMigration Summary:\n";
|
||||
echo "- Converted " . count($plan_specific_keys) . " plan-specific templates\n";
|
||||
echo "- Preserved " . count($other_keys) . " other notification types\n";
|
||||
echo "- Backup created for safety\n";
|
||||
echo "\nYou can now customize plan-type-specific templates in the admin panel.\n";
|
||||
} else {
|
||||
echo "Error: Failed to save migrated notifications\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Test the new structure
|
||||
echo "\nTesting new notification structure...\n";
|
||||
$test_notifications = json_decode(file_get_contents($notifications_file), true);
|
||||
|
||||
foreach ($plan_specific_keys as $key) {
|
||||
if (isset($test_notifications[$key]) && is_array($test_notifications[$key])) {
|
||||
echo "✓ Template '$key' is properly structured\n";
|
||||
} else {
|
||||
echo "✗ Template '$key' has issues\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\nMigration completed!\n";
|
||||
2773
system/orm.php
Normal file
2773
system/orm.php
Normal file
File diff suppressed because it is too large
Load Diff
BIN
system/plugin/.DS_Store
vendored
Normal file
BIN
system/plugin/.DS_Store
vendored
Normal file
Binary file not shown.
2
system/plugin/.gitattributes
vendored
Normal file
2
system/plugin/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
420
system/plugin/CreateHotspotUser.php
Normal file
420
system/plugin/CreateHotspotUser.php
Normal file
@@ -0,0 +1,420 @@
|
||||
<?php
|
||||
function Alloworigins()
|
||||
{
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
exit;
|
||||
}
|
||||
$requestUri = $_SERVER['REQUEST_URI'];
|
||||
$queryString = parse_url($requestUri, PHP_URL_QUERY);
|
||||
$type = null;
|
||||
if ($queryString) {
|
||||
parse_str($queryString, $queryParameters);
|
||||
if (isset($queryParameters['type'])) {
|
||||
$type = $queryParameters['type'];
|
||||
if ($type === "grant") {
|
||||
CreateHostspotUser();
|
||||
exit;
|
||||
} elseif ($type === "verify") {
|
||||
VerifyHotspot();
|
||||
exit;
|
||||
} elseif ($type === "reconnect") {
|
||||
ReconnectUser();
|
||||
exit;
|
||||
} elseif ($type === "voucher") {
|
||||
ReconnectVoucher();
|
||||
exit;
|
||||
} else {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'The parameter is not present in the URL.']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ReconnectVoucher() {
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$rawData = file_get_contents('php://input');
|
||||
$postData = json_decode($rawData, true);
|
||||
|
||||
if (!isset($postData['voucher_code'], $postData['account_id'])) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'Missing accountId or voucherCode field']);
|
||||
return;
|
||||
}
|
||||
|
||||
$accountId = $postData['account_id'];
|
||||
$voucherCode = $postData['voucher_code'];
|
||||
|
||||
$voucher = ORM::for_table('tbl_voucher')
|
||||
->where('code', $voucherCode)
|
||||
->where('status', '0')
|
||||
->find_one();
|
||||
|
||||
if (!$voucher) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'Resultcode' => '1',
|
||||
'voucher' => 'Not Found',
|
||||
'message' => 'Invalid Voucher code'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($voucher['status'] == '1') {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'Resultcode' => '3',
|
||||
'voucher' => 'Used',
|
||||
'message' => 'Voucher code is already used'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$planId = $voucher['id_plan'];
|
||||
$routername = $voucher['routers'];
|
||||
|
||||
$router = ORM::for_table('tbl_routers')
|
||||
->where('name', $routername)
|
||||
->find_one();
|
||||
|
||||
if (!$router) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'Router not found'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$routerId = $router['id'];
|
||||
|
||||
if (!ORM::for_table('tbl_plans')->where('id', $planId)->count() || !ORM::for_table('tbl_routers')->where('id', $routerId)->count()) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'Unable to process your request, please refresh the page'
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$user = ORM::for_table('tbl_customers')->where('username', $accountId)->find_one();
|
||||
if (!$user) {
|
||||
// Create a new user if not exists
|
||||
$user = ORM::for_table('tbl_customers')->create();
|
||||
$user->username = $accountId;
|
||||
$user->password = '1234';
|
||||
$user->fullname = $accountId;
|
||||
$user->email = $accountId . '@gmail.com';
|
||||
$user->phonenumber = $accountId;
|
||||
$user->pppoe_password = '1234';
|
||||
$user->address = '';
|
||||
$user->service_type = 'Hotspot';
|
||||
}
|
||||
|
||||
$user->router_id = $routerId;
|
||||
$user->save();
|
||||
|
||||
// Update the voucher with the user ID
|
||||
$voucher->user = $user->id;
|
||||
$voucher->status = '1'; // Mark as used
|
||||
$voucher->save();
|
||||
|
||||
if (Package::rechargeUser($user->id, $routername, $planId, 'Voucher', $voucherCode)) {
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'Resultcode' => '2',
|
||||
'voucher' => 'activated',
|
||||
'message' => 'Voucher code has been activated',
|
||||
'username' => $user->username
|
||||
]);
|
||||
} else {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'Failed to recharge user package'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function ReconnectUser()
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
$rawData = file_get_contents('php://input');
|
||||
$postData = json_decode($rawData, true);
|
||||
if (!$postData) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'Invalid JSON DATA']);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!isset($postData['mpesa_code'])) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'missing required fields']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$mpesaCode = $postData['mpesa_code'];
|
||||
|
||||
// Query the payment gateway table
|
||||
$payment = ORM::for_table('tbl_payment_gateway')
|
||||
->where('gateway_trx_id', $mpesaCode)
|
||||
->find_one();
|
||||
|
||||
if (!$payment) {
|
||||
$data = array(['status' => 'error', "Resultcode" => "1", 'user' => "Not Found", 'message' => 'Invalid Mpesa Transaction code']);
|
||||
echo json_encode($data);
|
||||
exit();
|
||||
}
|
||||
|
||||
$username = $payment['username'];
|
||||
|
||||
// Query the user recharges table
|
||||
$recharge = ORM::for_table('tbl_user_recharges')
|
||||
->where('username', $username)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
if ($recharge) {
|
||||
$status = $recharge['status'];
|
||||
if ($status == 'on') {
|
||||
$data = array(
|
||||
"Resultcode" => "2",
|
||||
"user" => "Active User",
|
||||
"username" => $username,
|
||||
"tyhK" => "1234", // Replace with the actual password or token
|
||||
"Message" => "We have verified your transaction under the Mpesa Transaction $mpesaCode. Please don't leave this page as we are redirecting you.",
|
||||
"Status" => "success"
|
||||
);
|
||||
} elseif ($status == "off") {
|
||||
$data = array(
|
||||
"Resultcode" => "3",
|
||||
"user" => "Expired User",
|
||||
"Message" => "We have verified your transaction under the Mpesa Transaction $mpesaCode. But your Package is already Expired. Please buy a new Package.",
|
||||
"Status" => "danger"
|
||||
);
|
||||
} else {
|
||||
$data = array(
|
||||
"Message" => "Unexpected status value",
|
||||
"Status" => "error"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$data = array(
|
||||
"Message" => "Recharge information not found",
|
||||
"Status" => "error"
|
||||
);
|
||||
}
|
||||
|
||||
echo json_encode($data);
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
function VerifyHotspot() {
|
||||
header('Content-Type: application/json');
|
||||
$rawData = file_get_contents('php://input');
|
||||
$postData = json_decode($rawData, true);
|
||||
|
||||
if (!$postData) {
|
||||
echo json_encode(['Resultcode' => 'error', 'Message' => 'Invalid JSON data']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($postData['account_id'])) {
|
||||
echo json_encode(['Resultcode' => 'error', 'Message' => 'Missing required fields']);
|
||||
return;
|
||||
}
|
||||
|
||||
$accountId = $postData['account_id'];
|
||||
$user = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $accountId)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
|
||||
if ($user) {
|
||||
$status = $user->status;
|
||||
$mpesacode = $user->gateway_trx_id;
|
||||
$res = $user->pg_paid_response;
|
||||
|
||||
if ($status == 2 && !empty($mpesacode)) {
|
||||
echo json_encode([
|
||||
"Resultcode" => "3",
|
||||
"Message" => "We have received your transaction under the Mpesa Transaction $mpesacode. Please do not leave this page as we are redirecting you.",
|
||||
"Status" => "success"
|
||||
]);
|
||||
} elseif ($res == "Not enough balance") {
|
||||
echo json_encode([
|
||||
"Resultcode" => "2",
|
||||
"Message" => "Insufficient Balance for the transaction",
|
||||
"Status" => "danger"
|
||||
]);
|
||||
} elseif ($res == "Wrong Mpesa pin") {
|
||||
echo json_encode([
|
||||
"Resultcode" => "2",
|
||||
"Message" => "You entered Wrong Mpesa pin, please resubmit",
|
||||
"Status" => "danger"
|
||||
]);
|
||||
} elseif ($status == 4) {
|
||||
echo json_encode([
|
||||
"Resultcode" => "2",
|
||||
"Message" => "You cancelled the transaction, you can enter phone number again to activate",
|
||||
"Status" => "info"
|
||||
]);
|
||||
} elseif (empty($mpesacode)) {
|
||||
echo json_encode([
|
||||
"Resultcode" => "1",
|
||||
"Message" => "A payment pop up has been sent to your phone. Please enter PIN to continue (Please do not leave or reload the page until redirected).",
|
||||
"Status" => "primary"
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode([
|
||||
"Resultcode" => "error",
|
||||
"Message" => "User not found"
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function CreateHostspotUser()
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
$rawData = file_get_contents('php://input');
|
||||
$postData = json_decode($rawData, true);
|
||||
if (!$postData) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'Invalid JSON DATA' . $postData . ' n tes ']);
|
||||
} else {
|
||||
$phone = $postData['phone_number'];
|
||||
$planId = $postData['plan_id'];
|
||||
$routerId = $postData['router_id'];
|
||||
$accountId = $postData['account_id'];
|
||||
|
||||
|
||||
|
||||
if (!isset( $postData['phone_number'], $postData['plan_id'], $postData['router_id'], $postData['account_id'])) {
|
||||
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'missing required fields' . $postData, 'phone' => $phone, 'planId' => $planId, 'routerId' => $routerId, 'accountId' => $accountId]);
|
||||
} else {
|
||||
$phone = (substr($phone, 0, 1) == '+') ? str_replace('+', '', $phone) : $phone;
|
||||
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^0/', '254', $phone) : $phone;
|
||||
$phone = (substr($phone, 0, 1) == '7') ? preg_replace('/^7/', '2547', $phone) : $phone; //cater for phone number prefix 2547XXXX
|
||||
$phone = (substr($phone, 0, 1) == '1') ? preg_replace('/^1/', '2541', $phone) : $phone; //cater for phone number prefix 2541XXXX
|
||||
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^01/', '2541', $phone) : $phone;
|
||||
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^07/', '2547', $phone) : $phone;
|
||||
if (strlen($phone) !== 12) {
|
||||
echo json_encode(['status' => 'error', 'code' => 1, 'message' => 'Phone number ' . $phone . ' is invalid. Please confirm.']);
|
||||
}
|
||||
if (strlen($phone) == 12 && !empty($planId) && !empty($routerId)) {
|
||||
$PlanExist = ORM::for_table('tbl_plans')->where('id', $planId)->count() > 0;
|
||||
$RouterExist = ORM::for_table('tbl_routers')->where('id', $routerId)->count() > 0;
|
||||
if (!$PlanExist || !$RouterExist)
|
||||
echo json_encode(["status" => "error", "message" => "Unable to process your request, please refresh the page."]);
|
||||
}
|
||||
$Userexist = ORM::for_table('tbl_customers')->where('username', $accountId)->find_one();
|
||||
if ($Userexist) {
|
||||
$Userexist->router_id = $routerId;
|
||||
$Userexist->save();
|
||||
InitiateStkpush($phone, $planId, $accountId, $routerId);
|
||||
} else {
|
||||
try {
|
||||
$defpass = '1234';
|
||||
$defaddr = 'netXtreme';
|
||||
$defmail = $phone . '@gmail.com';
|
||||
$createUser = ORM::for_table('tbl_customers')->create();
|
||||
$createUser->username = $accountId;
|
||||
$createUser->password = $defpass;
|
||||
$createUser->fullname = $phone;
|
||||
$createUser->router_id = $routerId;
|
||||
$createUser->phonenumber = $phone;
|
||||
$createUser->pppoe_password = $defpass;
|
||||
$createUser->address = $defaddr;
|
||||
$createUser->email = $defmail;
|
||||
$createUser->service_type = 'Hotspot';
|
||||
if ($createUser->save()) {
|
||||
InitiateStkpush($phone, $planId, $accountId, $routerId);
|
||||
} else {
|
||||
echo json_encode(["status" => "error", "message" => "There was a system error when registering user, please contact support."]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(["status" => "error", "message" => "Error creating user: " . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function InitiateStkpush($phone, $planId, $accountId, $routerId)
|
||||
{
|
||||
$gateway = ORM::for_table('tbl_appconfig')
|
||||
->where('setting', 'payment_gateway')
|
||||
->find_one();
|
||||
$gateway = ($gateway) ? $gateway->value : null;
|
||||
if ($gateway == "MpesatillStk") {
|
||||
$url = U . "plugin/initiatetillstk";
|
||||
} elseif ($gateway == "BankStkPush") {
|
||||
$url = U . "plugin/initiatebankstk";
|
||||
} elseif ($gateway == "mpesa") {
|
||||
$url = U . "plugin/initiatempesa";
|
||||
} else {
|
||||
$url = null; // or handle the default case appropriately
|
||||
}
|
||||
$Planname = ORM::for_table('tbl_plans')
|
||||
->where('id', $planId)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
$Findrouter = ORM::for_table('tbl_routers')
|
||||
->where('id', $routerId)
|
||||
->order_by_desc('id')
|
||||
->find_one();
|
||||
$rname = $Findrouter->name;
|
||||
$price = $Planname->price;
|
||||
$Planname = $Planname->name_plan;
|
||||
$Checkorders = ORM::for_table('tbl_payment_gateway')
|
||||
->where('username', $accountId)
|
||||
->where('status', 1)
|
||||
->order_by_desc('id')
|
||||
->find_many();
|
||||
if ($Checkorders) {
|
||||
foreach ($Checkorders as $Dorder) {
|
||||
$Dorder->delete();
|
||||
}
|
||||
}
|
||||
try {
|
||||
$d = ORM::for_table('tbl_payment_gateway')->create();
|
||||
$d->username = $accountId;
|
||||
$d->gateway = $gateway;
|
||||
$d->plan_id = $planId;
|
||||
$d->plan_name = $Planname;
|
||||
$d->routers_id = $routerId;
|
||||
$d->routers = $rname;
|
||||
$d->price = $price;
|
||||
$d->payment_method = $gateway;
|
||||
$d->payment_channel = $gateway;
|
||||
$d->created_date = date('Y-m-d H:i:s');
|
||||
$d->paid_date = date('Y-m-d H:i:s');
|
||||
$d->expired_date = date('Y-m-d H:i:s');
|
||||
$d->pg_url_payment = $url;
|
||||
$d->status = 1;
|
||||
$d->save();
|
||||
} catch (Exception $e) {
|
||||
error_log('Error saving payment gateway record: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
SendSTKcred($phone, $url, $accountId);
|
||||
}
|
||||
|
||||
function SendSTKcred($phone, $url, $accountId )
|
||||
{
|
||||
$link = $url;
|
||||
$fields = array(
|
||||
'username' => $accountId,
|
||||
'phone' => $phone,
|
||||
'channel' => 'Yes',
|
||||
);
|
||||
$postvars = http_build_query($fields);
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $link);
|
||||
curl_setopt($ch, CURLOPT_POST, count($fields));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postvars);
|
||||
$result = curl_exec($ch);
|
||||
}
|
||||
|
||||
Alloworigins();
|
||||
636
system/plugin/c2b.php
Normal file
636
system/plugin/c2b.php
Normal file
@@ -0,0 +1,636 @@
|
||||
<?php
|
||||
|
||||
register_menu("Mpesa C2B Settings", true, "c2b_settings", 'SETTINGS', '', '', "");
|
||||
register_menu("Mpesa Transactions", true, "c2b_overview", 'AFTER_REPORTS', 'fa fa-paypal', '', "");
|
||||
|
||||
try {
|
||||
$db = ORM::get_db();
|
||||
$tableCheckQuery = "CREATE TABLE IF NOT EXISTS tbl_mpesa_transactions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
TransID VARCHAR(255) NOT NULL,
|
||||
TransactionType VARCHAR(255) NOT NULL,
|
||||
TransTime VARCHAR(255) NOT NULL,
|
||||
TransAmount DECIMAL(10, 2) NOT NULL,
|
||||
BusinessShortCode VARCHAR(255) NOT NULL,
|
||||
BillRefNumber VARCHAR(255) NOT NULL,
|
||||
OrgAccountBalance DECIMAL(10, 2) NOT NULL,
|
||||
MSISDN VARCHAR(255) NOT NULL,
|
||||
FirstName VARCHAR(255) NOT NULL,
|
||||
CustomerID VARCHAR(255) NOT NULL,
|
||||
PackageName VARCHAR(255) NOT NULL,
|
||||
PackagePrice VARCHAR(255) NOT NULL,
|
||||
TransactionStatus VARCHAR(255) NOT NULL,
|
||||
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
)";
|
||||
$db->exec($tableCheckQuery);
|
||||
} catch (PDOException $e) {
|
||||
echo "Error creating the table: " . $e->getMessage();
|
||||
} catch (Exception $e) {
|
||||
echo "An unexpected error occurred: " . $e->getMessage();
|
||||
}
|
||||
|
||||
function c2b_overview()
|
||||
{
|
||||
global $ui, $config;
|
||||
_admin();
|
||||
$ui->assign('_title', 'Mpesa C2B Payment Overview');
|
||||
$ui->assign('_system_menu', '');
|
||||
$admin = Admin::_info();
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
// Check user type for access
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
exit;
|
||||
}
|
||||
|
||||
$query = ORM::for_table('tbl_mpesa_transactions')->order_by_desc('TransTime');
|
||||
$payments = $query->find_many();
|
||||
|
||||
if (
|
||||
(empty($config['mpesa_c2b_consumer_key']) || empty($config['mpesa_c2b_consumer_secret']) || empty($config['mpesa_c2b_business_code']))
|
||||
&& !$config['c2b_registered']
|
||||
) {
|
||||
$ui->assign('message', '<em>' . Lang::T("You haven't registered your validation and verification URLs. Please register URLs by clicking ") . ' <a href="' . APP_URL . '/index.php?_route=plugin/c2b_settings"> Register URL </a>' . '</em>');
|
||||
}
|
||||
$ui->assign('payments', $payments);
|
||||
$ui->assign('xheader', '<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.5/css/jquery.dataTables.css">');
|
||||
$ui->display('c2b_overview.tpl');
|
||||
}
|
||||
|
||||
function c2b_settings()
|
||||
{
|
||||
global $ui, $admin, $config;
|
||||
$ui->assign('_title', Lang::T("Mpesa C2B Settings [Offline Payment]"));
|
||||
$ui->assign('_system_menu', 'settings');
|
||||
$admin = Admin::_info();
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
if (_post('save') == 'save') {
|
||||
$mpesa_c2b_consumer_key = _post('mpesa_c2b_consumer_key');
|
||||
$mpesa_c2b_consumer_secret = _post('mpesa_c2b_consumer_secret');
|
||||
$mpesa_c2b_business_code = _post('mpesa_c2b_business_code');
|
||||
$mpesa_c2b_env = _post('mpesa_c2b_env');
|
||||
$mpesa_c2b_api = _post('mpesa_c2b_api');
|
||||
$mpesa_c2b_low_fee = _post('mpesa_c2b_low_fee') ? 1 : 0;
|
||||
$mpesa_c2b_bill_ref = _post('mpesa_c2b_bill_ref');
|
||||
|
||||
$errors = [];
|
||||
if (empty($mpesa_c2b_consumer_key)) {
|
||||
$errors[] = Lang::T('Mpesa C2B Consumer Key is required.');
|
||||
}
|
||||
if (empty($mpesa_c2b_consumer_secret)) {
|
||||
$errors[] = Lang::T('Mpesa C2B Consumer Secret is required.');
|
||||
}
|
||||
if (empty($mpesa_c2b_business_code)) {
|
||||
$errors[] = Lang::T('Mpesa C2B Business Code is required.');
|
||||
}
|
||||
if (empty($mpesa_c2b_env)) {
|
||||
$errors[] = Lang::T('Mpesa C2B Environment is required.');
|
||||
}
|
||||
if (empty($mpesa_c2b_api)) {
|
||||
$errors[] = Lang::T('Mpesa C2B API URL is required.');
|
||||
}
|
||||
|
||||
if (empty($mpesa_c2b_bill_ref)) {
|
||||
$errors[] = Lang::T('Mpesa Bill Ref Number Type is required.');
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$ui->assign('message', implode('<br>', $errors));
|
||||
$ui->display('c2b_settings.tpl');
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = [
|
||||
'mpesa_c2b_consumer_key' => $mpesa_c2b_consumer_key,
|
||||
'mpesa_c2b_consumer_secret' => $mpesa_c2b_consumer_secret,
|
||||
'mpesa_c2b_business_code' => $mpesa_c2b_business_code,
|
||||
'mpesa_c2b_env' => $mpesa_c2b_env,
|
||||
'mpesa_c2b_api' => $mpesa_c2b_api,
|
||||
'mpesa_c2b_low_fee' => $mpesa_c2b_low_fee,
|
||||
'mpesa_c2b_bill_ref' => $mpesa_c2b_bill_ref,
|
||||
];
|
||||
|
||||
// Update or insert settings in the database
|
||||
foreach ($settings as $key => $value) {
|
||||
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
|
||||
if ($d) {
|
||||
$d->value = $value;
|
||||
$d->save();
|
||||
} else {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = $key;
|
||||
$d->value = $value;
|
||||
$d->save();
|
||||
}
|
||||
}
|
||||
|
||||
if ($admin) {
|
||||
_log('[' . $admin['username'] . ']: ' . Lang::T('Settings Saved Successfully'));
|
||||
}
|
||||
r2(U . 'plugin/c2b_settings', 's', Lang::T('Settings Saved Successfully'));
|
||||
}
|
||||
|
||||
if (!empty($config['mpesa_c2b_consumer_key'] && $config['mpesa_c2b_consumer_secret'] && $config['mpesa_c2b_business_code']) && !$config['c2b_registered']) {
|
||||
$ui->assign('message', '<em>' . Lang::T("You haven't registered your validation and verification URLs, Please register URLs by clicking ") . ' <a href="' . APP_URL . '/index.php?_route=plugin/c2b_settings"> Register URL </a>' . '</em>');
|
||||
}
|
||||
$ui->assign('_c', $config);
|
||||
$ui->assign('companyName', $config['CompanyName']);
|
||||
$ui->display('c2b_settings.tpl');
|
||||
}
|
||||
|
||||
function c2b_generateAccessToken()
|
||||
{
|
||||
global $config;
|
||||
$mpesa_c2b_env = $config['mpesa_c2b_env'] ?? null;
|
||||
$mpesa_c2b_consumer_key = $config['mpesa_c2b_consumer_key'] ?? null;
|
||||
$mpesa_c2b_consumer_secret = $config['mpesa_c2b_consumer_secret'] ?? null;
|
||||
$access_token_url = match ($mpesa_c2b_env) {
|
||||
"live" => 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
|
||||
"sandbox" => 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
|
||||
};
|
||||
$headers = ['Content-Type:application/json; charset=utf8'];
|
||||
$curl = curl_init($access_token_url);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
||||
curl_setopt($curl, CURLOPT_HEADER, FALSE);
|
||||
curl_setopt($curl, CURLOPT_USERPWD, "$mpesa_c2b_consumer_key:$mpesa_c2b_consumer_secret");
|
||||
$result = curl_exec($curl);
|
||||
$result = json_decode($result);
|
||||
if (isset($result->access_token)) {
|
||||
return $result->access_token;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function c2b_registerUrl()
|
||||
{
|
||||
global $config;
|
||||
if (
|
||||
(empty($config['mpesa_c2b_consumer_key']) || empty($config['mpesa_c2b_consumer_secret']) || empty($config['mpesa_c2b_business_code']))
|
||||
&& !$config['c2b_registered']
|
||||
) {
|
||||
r2(U . 'plugin/c2b_settings', 'e', Lang::T('Please setup your M-Pesa C2B settings first'));
|
||||
exit;
|
||||
}
|
||||
$access_token = c2b_generateAccessToken();
|
||||
switch ($access_token) {
|
||||
case null:
|
||||
r2(U . 'plugin/c2b_settings', 'e', Lang::T('Failed to generate access token'));
|
||||
exit;
|
||||
default:
|
||||
$BusinessShortCode = $config['mpesa_c2b_business_code'] ?? null;
|
||||
$mpesa_c2b_env = $config['mpesa_c2b_env'] ?? null;
|
||||
$confirmationUrl = U . 'plugin/c2b_confirmation';
|
||||
$validationUrl = U . 'plugin/c2b_validation';
|
||||
$mpesa_c2b_api = $config['mpesa_c2b_api'] ?? null;
|
||||
$registerurl = match ($mpesa_c2b_env) {
|
||||
"live" => match ($mpesa_c2b_api) {
|
||||
"v1" => 'https://api.safaricom.co.ke/mpesa/c2b/v1/registerurl',
|
||||
"v2" => 'https://api.safaricom.co.ke/mpesa/c2b/v2/registerurl',
|
||||
},
|
||||
"sandbox" => 'https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl',
|
||||
};
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $registerurl);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type:application/json',
|
||||
"Authorization:Bearer $access_token"
|
||||
]);
|
||||
$data = [
|
||||
'ShortCode' => $BusinessShortCode,
|
||||
'ResponseType' => 'Completed',
|
||||
'ConfirmationURL' => $confirmationUrl,
|
||||
'ValidationURL' => $validationUrl
|
||||
];
|
||||
$data_string = json_encode($data);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
|
||||
$curl_response = curl_exec($curl);
|
||||
$data = json_decode($curl_response);
|
||||
if (isset($data->ResponseCode) && $data->ResponseCode == 0) {
|
||||
try {
|
||||
$d = ORM::for_table('tbl_appconfig')->create();
|
||||
$d->setting = 'c2b_registered';
|
||||
$d->value = '1';
|
||||
$d->save();
|
||||
} catch (Exception $e) {
|
||||
_log("Failed to save M-Pesa C2B URL to database.\n\n" . $e->getMessage());
|
||||
sendTelegram("Failed to save M-Pesa C2B URL to database.\n\n" . $e->getMessage());
|
||||
}
|
||||
sendTelegram("M-Pesa C2B URL registered successfully");
|
||||
r2(U . 'plugin/c2b_settings', 's', "M-Pesa C2B URL registered successfully");
|
||||
} else {
|
||||
$errorMessage = $data->errorMessage;
|
||||
sendTelegram("Resister M-Pesa C2B URL Failed\n\n" . json_encode($curl_response, JSON_PRETTY_PRINT));
|
||||
r2(U . 'plugin/c2b_settings', 'e', "Failed to register M-Pesa C2B URL Error $errorMessage");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function c2b_webhook_log($data)
|
||||
{
|
||||
$logFile = 'pages/mpesa-webhook.html';
|
||||
$logEntry = date('Y-m-d H:i:s') . "<pre>" . htmlspecialchars($data, ENT_QUOTES, 'UTF-8') . "</pre>\n";
|
||||
|
||||
if (file_put_contents($logFile, $logEntry, FILE_APPEND) === false) {
|
||||
sendTelegram("Failed to write to log file: $logFile");
|
||||
}
|
||||
}
|
||||
|
||||
function c2b_isValidSafaricomIP($ip)
|
||||
{
|
||||
$config = c2b_config();
|
||||
$safaricomIPs = [
|
||||
'196.201.214.0/24',
|
||||
'196.201.213.0/24',
|
||||
'196.201.212.0/24',
|
||||
'172.69.79.0/24',
|
||||
'172.69.0.0/24',
|
||||
'0.0.0.0/0',
|
||||
];
|
||||
if ($config['mpesa_c2b_env'] == 'sandbox') {
|
||||
$safaricomIPs[] = '::1';
|
||||
}
|
||||
|
||||
foreach ($safaricomIPs as $range) {
|
||||
if (c2b_ipInRange($ip, $range)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function c2b_ipInRange($ip, $range)
|
||||
{
|
||||
list($subnet, $bits) = explode('/', $range);
|
||||
$ip = ip2long($ip);
|
||||
$subnet = ip2long($subnet);
|
||||
$mask = -1 << (32 - $bits);
|
||||
$subnet &= $mask;
|
||||
return ($ip & $mask) == $subnet;
|
||||
}
|
||||
|
||||
function c2b_confirmation()
|
||||
{
|
||||
|
||||
global $config;
|
||||
header("Content-Type: application/json");
|
||||
|
||||
$clientIP = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if (!c2b_isValidSafaricomIP($clientIP)) {
|
||||
c2b_logAndNotify("Unauthorized request from IP: {$clientIP}");
|
||||
http_response_code(403);
|
||||
echo json_encode(["ResultCode" => 1, "ResultDesc" => "Unauthorized"]);
|
||||
return;
|
||||
}
|
||||
|
||||
$mpesaResponse = file_get_contents('php://input');
|
||||
if ($mpesaResponse === false) {
|
||||
c2b_logAndNotify("Failed to get input stream.");
|
||||
return;
|
||||
}
|
||||
|
||||
c2b_webhook_log('Received webhook request');
|
||||
c2b_webhook_log($mpesaResponse);
|
||||
|
||||
$content = json_decode($mpesaResponse);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
c2b_logAndNotify("Failed to decode JSON response: " . json_last_error_msg());
|
||||
return;
|
||||
}
|
||||
c2b_webhook_log('Decoded JSON data successfully');
|
||||
|
||||
if (!class_exists('Package')) {
|
||||
c2b_logAndNotify("Error: Package class does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($config['mpesa_c2b_bill_ref'])) {
|
||||
switch ($config['mpesa_c2b_bill_ref']) {
|
||||
case 'phone':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('phonenumber', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
case 'username':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('username', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
case 'id':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('id', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
default:
|
||||
$customer = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$customer) {
|
||||
sendTelegram("Validation failed: No account found for BillRefNumber: $content->BillRefNumber");
|
||||
_log("Validation failed: No account found for BillRefNumber: $content->BillRefNumber");
|
||||
echo json_encode(["ResultCode" => "C2B00012", "ResultDesc" => "Invalid Account Number"]);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_log("Configuration error: mpesa_c2b_bill_ref not set.");
|
||||
sendTelegram("Configuration error: mpesa_c2b_bill_ref not set.");
|
||||
}
|
||||
|
||||
|
||||
$bills = c2b_billing($customer->id);
|
||||
if (!$bills) {
|
||||
c2b_logAndNotify("No matching bill found for BillRefNumber: {$content->BillRefNumber}");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($bills as $bill) {
|
||||
c2b_handleBillPayment($content, $customer, $bill);
|
||||
}
|
||||
|
||||
echo json_encode(["ResultCode" => 0, "ResultDesc" => "Accepted"]);
|
||||
}
|
||||
|
||||
|
||||
function c2b_handleBillPayment($content, $customer, $bill)
|
||||
{
|
||||
$amountToPay = $bill['price'];
|
||||
$amountPaid = $content->TransAmount;
|
||||
$channel_mode = "Mpesa C2B - {$content->TransID}";
|
||||
$customerBalance = $customer->balance;
|
||||
$currentBalance = $customerBalance + $amountPaid;
|
||||
$customerID = $customer->id;
|
||||
|
||||
try {
|
||||
$transaction = c2b_storeTransaction($content, $bill['namebp'], $amountToPay, $customerID);
|
||||
} catch (Exception $e) {
|
||||
c2b_handleException("Failed to save transaction", $e);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($currentBalance >= $amountToPay) {
|
||||
$excessAmount = $currentBalance - $amountToPay;
|
||||
try {
|
||||
$result = Package::rechargeUser($customer->id, $bill['routers'], $bill['plan_id'], 'mpesa', $channel_mode);
|
||||
if (!$result) {
|
||||
c2b_logAndNotify("Mpesa Payment Successful, but failed to activate the package for customer {$customer->username}.");
|
||||
} else {
|
||||
if ($excessAmount > 0) {
|
||||
$customer->balance = $excessAmount;
|
||||
$customer->save();
|
||||
} else {
|
||||
$customer->balance = 0;
|
||||
$customer->save();
|
||||
}
|
||||
c2b_sendPaymentSuccessMessage($customer, $amountPaid, $bill['namebp']);
|
||||
$transaction->transactionStatus = 'Completed';
|
||||
$transaction->save();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
c2b_handleException("Error during package activation", $e);
|
||||
}
|
||||
} else {
|
||||
c2b_updateCustomerBalance($customer, $currentBalance, $amountPaid);
|
||||
$neededToActivate = $amountToPay - $currentBalance;
|
||||
c2b_sendBalanceUpdateMessage($customer, $amountPaid, $currentBalance, $neededToActivate);
|
||||
$transaction->transactionStatus = 'Completed';
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function c2b_storeTransaction($content, $packageName, $packagePrice, $customerID)
|
||||
{
|
||||
ORM::get_db()->beginTransaction();
|
||||
try {
|
||||
$transaction = ORM::for_table('tbl_mpesa_transactions')
|
||||
->where('TransID', $content->TransID)
|
||||
->find_one();
|
||||
|
||||
if ($transaction) {
|
||||
// Update existing transaction
|
||||
$transaction->TransactionType = $content->TransactionType;
|
||||
$transaction->TransTime = $content->TransTime;
|
||||
$transaction->TransAmount = $content->TransAmount;
|
||||
$transaction->BusinessShortCode = $content->BusinessShortCode;
|
||||
$transaction->BillRefNumber = $content->BillRefNumber;
|
||||
$transaction->OrgAccountBalance = $content->OrgAccountBalance;
|
||||
$transaction->MSISDN = $content->MSISDN;
|
||||
$transaction->FirstName = $content->FirstName;
|
||||
$transaction->PackageName = $packageName;
|
||||
$transaction->PackagePrice = $packagePrice;
|
||||
$transaction->customerID = $customerID;
|
||||
$transaction->transactionStatus = 'Pending';
|
||||
} else {
|
||||
// Create new transaction
|
||||
$transaction = ORM::for_table('tbl_mpesa_transactions')->create();
|
||||
$transaction->TransID = $content->TransID;
|
||||
$transaction->TransactionType = $content->TransactionType;
|
||||
$transaction->TransTime = $content->TransTime;
|
||||
$transaction->TransAmount = $content->TransAmount;
|
||||
$transaction->BusinessShortCode = $content->BusinessShortCode;
|
||||
$transaction->BillRefNumber = $content->BillRefNumber;
|
||||
$transaction->OrgAccountBalance = $content->OrgAccountBalance;
|
||||
$transaction->MSISDN = $content->MSISDN;
|
||||
$transaction->FirstName = $content->FirstName;
|
||||
$transaction->PackageName = $packageName;
|
||||
$transaction->PackagePrice = $packagePrice;
|
||||
$transaction->customerID = $customerID;
|
||||
$transaction->transactionStatus = 'Pending';
|
||||
}
|
||||
$transaction->save();
|
||||
ORM::get_db()->commit();
|
||||
return $transaction;
|
||||
} catch (Exception $e) {
|
||||
ORM::get_db()->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
function c2b_logAndNotify($message)
|
||||
{
|
||||
_log($message);
|
||||
sendTelegram($message);
|
||||
}
|
||||
|
||||
function c2b_handleException($message, $e)
|
||||
{
|
||||
$fullMessage = "$message: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine();
|
||||
c2b_logAndNotify($fullMessage);
|
||||
}
|
||||
|
||||
function c2b_updateCustomerBalance($customer, $newBalance, $amountPaid)
|
||||
{
|
||||
try {
|
||||
$customer->balance = $newBalance;
|
||||
$customer->save();
|
||||
c2b_logAndNotify("Payment of KES {$amountPaid} has been added to the balance of customer {$customer->username}.");
|
||||
} catch (Exception $e) {
|
||||
c2b_handleException("Failed to update customer balance", $e);
|
||||
}
|
||||
}
|
||||
|
||||
function c2b_sendPaymentSuccessMessage($customer, $amountPaid, $packageName)
|
||||
{
|
||||
$config = c2b_config();
|
||||
$message = "Dear {$customer->fullname}, your payment of KES {$amountPaid} has been received and your plan {$packageName} has been successfully activated. Thank you for choosing {$config['CompanyName']}.";
|
||||
c2b_sendNotification($customer, $message);
|
||||
}
|
||||
|
||||
function c2b_sendBalanceUpdateMessage($customer, $amountPaid, $currentBalance, $neededToActivate)
|
||||
{
|
||||
$config = c2b_config();
|
||||
$message = "Dear {$customer->fullname}, your payment of KES {$amountPaid} has been received and added to your account balance. Your current balance is KES {$currentBalance}.";
|
||||
if ($neededToActivate > 0) {
|
||||
$message .= " To activate your package, you need to add KES {$neededToActivate} more to your account.";
|
||||
} else {
|
||||
$message .= " Your current balance is sufficient to activate your package.";
|
||||
}
|
||||
$message .= "\n" . $config['CompanyName'];
|
||||
c2b_sendNotification($customer, $message);
|
||||
}
|
||||
|
||||
function c2b_sendNotification($customer, $message)
|
||||
{
|
||||
try {
|
||||
Message::sendSMS($customer->phonenumber, $message);
|
||||
Message::sendWhatsapp($customer->phonenumber, $message);
|
||||
} catch (Exception $e) {
|
||||
c2b_handleException("Failed to send SMS/WhatsApp message", $e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function c2b_validation()
|
||||
{
|
||||
header("Content-Type: application/json");
|
||||
$mpesaResponse = file_get_contents('php://input');
|
||||
|
||||
$config = c2b_config();
|
||||
$content = json_decode($mpesaResponse);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
sendTelegram("Failed to decode JSON response.");
|
||||
_log("Failed to decode JSON response.");
|
||||
echo json_encode(["ResultCode" => "C2B00016", "ResultDesc" => "Invalid JSON format"]);
|
||||
return;
|
||||
}
|
||||
|
||||
$BillRefNumber = $content->BillRefNumber;
|
||||
$TransAmount = $content->TransAmount;
|
||||
|
||||
if (isset($config['mpesa_c2b_bill_ref'])) {
|
||||
switch ($config['mpesa_c2b_bill_ref']) {
|
||||
case 'phone':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('phonenumber', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
case 'username':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('username', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
case 'id':
|
||||
$customer = ORM::for_table('tbl_customers')
|
||||
->where('id', $content->BillRefNumber)
|
||||
->find_one();
|
||||
break;
|
||||
|
||||
default:
|
||||
$customer = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$customer) {
|
||||
sendTelegram("Validation failed: No account found for BillRefNumber: $BillRefNumber");
|
||||
_log("Validation failed: No account found for BillRefNumber: $BillRefNumber");
|
||||
echo json_encode(["ResultCode" => "C2B00012", "ResultDesc" => "Invalid Account Number"]);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_log("Configuration error: mpesa_c2b_bill_ref not set.");
|
||||
sendTelegram("Configuration error: mpesa_c2b_bill_ref not set.");
|
||||
}
|
||||
|
||||
|
||||
$bills = c2b_billing($customer->id);
|
||||
|
||||
if (!$bills) {
|
||||
sendTelegram("Validation failed: No bill found for BillRefNumber: $BillRefNumber");
|
||||
_log("Validation failed: No bill found for BillRefNumber: $BillRefNumber");
|
||||
echo json_encode(["ResultCode" => "C2B00012", "ResultDesc" => "Invalid Bill Reference"]);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($bills as $bill) {
|
||||
}
|
||||
|
||||
$billAmount = $bill['price'];
|
||||
if (!$config['mpesa_c2b_low_fee']) {
|
||||
if ($TransAmount < $billAmount) {
|
||||
sendTelegram("Validation failed: Insufficient amount. Transferred: $TransAmount, Required: $billAmount");
|
||||
_log("Validation failed: Insufficient amount. Transferred: $TransAmount, Required: $billAmount");
|
||||
echo json_encode(["ResultCode" => "C2B00013", "ResultDesc" => "Invalid or Insufficient Amount"]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sendTelegram("Validation successful for BillRefNumber: $BillRefNumber with amount: $TransAmount");
|
||||
_log("Validation successful for BillRefNumber: $BillRefNumber with amount: $TransAmount");
|
||||
echo json_encode(["ResultCode" => 0, "ResultDesc" => "Accepted"]);
|
||||
}
|
||||
|
||||
function c2b_billing($id)
|
||||
{
|
||||
$d = ORM::for_table('tbl_user_recharges')
|
||||
->selects([
|
||||
'customer_id',
|
||||
'username',
|
||||
'plan_id',
|
||||
'namebp',
|
||||
'recharged_on',
|
||||
'recharged_time',
|
||||
'expiration',
|
||||
'time',
|
||||
'status',
|
||||
'method',
|
||||
'plan_type',
|
||||
['tbl_user_recharges.routers', 'routers'],
|
||||
['tbl_user_recharges.type', 'type'],
|
||||
'admin_id',
|
||||
'prepaid'
|
||||
])
|
||||
->select('tbl_plans.price', 'price')
|
||||
->left_outer_join('tbl_plans', array('tbl_plans.id', '=', 'tbl_user_recharges.plan_id'))
|
||||
->where('customer_id', $id)
|
||||
->find_many();
|
||||
return $d;
|
||||
}
|
||||
|
||||
function c2b_config()
|
||||
{
|
||||
$result = ORM::for_table('tbl_appconfig')->find_many();
|
||||
foreach ($result as $value) {
|
||||
$config[$value['setting']] = $value['value'];
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
48
system/plugin/clear_cache.php
Normal file
48
system/plugin/clear_cache.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
register_menu("Clear System Cache", true, "clear_cache", 'SETTINGS', '');
|
||||
|
||||
function clear_cache()
|
||||
{
|
||||
global $ui;
|
||||
_admin();
|
||||
$ui->assign('_title', 'Clear Cache');
|
||||
$ui->assign('_system_menu', 'settings');
|
||||
$admin = Admin::_info();
|
||||
$ui->assign('_admin', $admin);
|
||||
|
||||
// Check user type for access
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
exit;
|
||||
}
|
||||
|
||||
$compiledCacheDir = 'ui/compiled';
|
||||
$templateCacheDir = 'system/cache';
|
||||
|
||||
try {
|
||||
// Clear the compiled cache
|
||||
$files = scandir($compiledCacheDir);
|
||||
foreach ($files as $file) {
|
||||
if ($file !== '.' && $file !== '..' && is_file($compiledCacheDir . '/' . $file)) {
|
||||
unlink($compiledCacheDir . '/' . $file);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the template cache
|
||||
$templateCacheFiles = glob($templateCacheDir . '/*.{json,temp}', GLOB_BRACE);
|
||||
foreach ($templateCacheFiles as $file) {
|
||||
if (is_file($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache cleared successfully
|
||||
_log('[' . ($admin['fullname'] ?? 'Unknown Admin') . ']: ' . Lang::T(' Cleared the system cache '), $admin['user_type']);
|
||||
r2(U . 'dashboard', 's', Lang::T("Cache cleared successfully!"));
|
||||
} catch (Exception $e) {
|
||||
// Error occurred while clearing the cache
|
||||
_log('[' . ($admin['fullname'] ?? 'Unknown Admin') . ']: ' . Lang::T(' Error occurred while clearing the cache: ' . $e->getMessage()), $admin['user_type']);
|
||||
r2(U . 'dashboard', 'e', Lang::T("Error occurred while clearing the cache: ") . $e->getMessage());
|
||||
}
|
||||
}
|
||||
907
system/plugin/download.php
Normal file
907
system/plugin/download.php
Normal file
@@ -0,0 +1,907 @@
|
||||
<?php
|
||||
include '../../config.php';
|
||||
$mysqli = new mysqli($db_host, $db_user, $db_password, $db_name);
|
||||
|
||||
if ($mysqli->connect_error) {
|
||||
die("Connection failed: " . $mysqli->connect_error);
|
||||
}
|
||||
|
||||
// Function to get a setting value
|
||||
function getSettingValue($mysqli, $setting) {
|
||||
$query = $mysqli->prepare("SELECT value FROM tbl_appconfig WHERE setting = ?");
|
||||
$query->bind_param("s", $setting);
|
||||
$query->execute();
|
||||
$result = $query->get_result();
|
||||
if ($row = $result->fetch_assoc()) {
|
||||
return $row['value'];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Fetch hotspot title and description from tbl_appconfig
|
||||
$hotspotTitle = getSettingValue($mysqli, 'hotspot_title');
|
||||
$description = getSettingValue($mysqli, 'description');
|
||||
$phone = getSettingValue($mysqli, 'phone');
|
||||
$company = getSettingValue($mysqli, 'CompanyName');
|
||||
|
||||
// Fetch router name and router ID from tbl_appconfig
|
||||
$routerName = getSettingValue($mysqli, 'router_name');
|
||||
$routerId = getSettingValue($mysqli, 'router_id');
|
||||
|
||||
// Fetch available plans
|
||||
|
||||
$planQuery = "SELECT id, type, name_plan, price, validity, validity_unit FROM tbl_plans WHERE routers = ? AND type = 'Hotspot'";
|
||||
$planStmt = $mysqli->prepare($planQuery);
|
||||
$planStmt->bind_param("s", $routerName);
|
||||
$planStmt->execute();
|
||||
$planResult = $planStmt->get_result();
|
||||
|
||||
|
||||
$htmlContent = "";
|
||||
$htmlContent .= "<!DOCTYPE html>\n";
|
||||
$htmlContent .= "<html lang=\"en\">\n";
|
||||
$htmlContent .= "<head>\n";
|
||||
$htmlContent .= " <meta charset=\"UTF-8\">\n";
|
||||
$htmlContent .= " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n";
|
||||
$htmlContent .= " <title>$company</title>\n";
|
||||
$htmlContent .= " <script src=\"https://cdn.tailwindcss.com\"></script>\n";
|
||||
$htmlContent .= " <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\">\n";
|
||||
$htmlContent .= " <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/glider-js@1.7.7/glider.min.css\" />\n";
|
||||
$htmlContent .= " <script src=\"https://cdn.jsdelivr.net/npm/glider-js@1.7.7/glider.min.js\"></script>\n";
|
||||
$htmlContent .= " <link rel=\"preconnect\" href=\"https://cdn.jsdelivr.net\">\n";
|
||||
$htmlContent .= " <link rel=\"preconnect\" href=\"https://cdnjs.cloudflare.com\" crossorigin>\n";
|
||||
$htmlContent .= " <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap\">\n";
|
||||
$htmlContent .= " <style>\n";
|
||||
$htmlContent .= " :root {\n";
|
||||
$htmlContent .= " --primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n";
|
||||
$htmlContent .= " --secondary-gradient: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);\n";
|
||||
$htmlContent .= " --success-gradient: linear-gradient(135deg, #10b981 0%, #059669 100%);\n";
|
||||
$htmlContent .= " --card-bg: rgba(255, 255, 255, 0.95);\n";
|
||||
$htmlContent .= " --glass-bg: rgba(255, 255, 255, 0.1);\n";
|
||||
$htmlContent .= " --backdrop-blur: blur(10px);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .glass-card {\n";
|
||||
$htmlContent .= " background: var(--card-bg);\n";
|
||||
$htmlContent .= " backdrop-filter: var(--backdrop-blur);\n";
|
||||
$htmlContent .= " border: 1px solid rgba(255, 255, 255, 0.2);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .gradient-text {\n";
|
||||
$htmlContent .= " background: var(--primary-gradient);\n";
|
||||
$htmlContent .= " -webkit-background-clip: text;\n";
|
||||
$htmlContent .= " -webkit-text-fill-color: transparent;\n";
|
||||
$htmlContent .= " background-clip: text;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .floating-label {\n";
|
||||
$htmlContent .= " position: relative;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .floating-label input:focus + label,\n";
|
||||
$htmlContent .= " .floating-label input:not(:placeholder-shown) + label {\n";
|
||||
$htmlContent .= " transform: translateY(-1.5rem) scale(0.85);\n";
|
||||
$htmlContent .= " color: #1e40af;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .floating-label label {\n";
|
||||
$htmlContent .= " position: absolute;\n";
|
||||
$htmlContent .= " left: 0.75rem;\n";
|
||||
$htmlContent .= " top: 0.75rem;\n";
|
||||
$htmlContent .= " transition: all 0.2s ease-in-out;\n";
|
||||
$htmlContent .= " pointer-events: none;\n";
|
||||
$htmlContent .= " color: #6b7280;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .card-hover {\n";
|
||||
$htmlContent .= " transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .card-hover:hover {\n";
|
||||
$htmlContent .= " transform: translateY(-4px);\n";
|
||||
$htmlContent .= " box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .btn-modern {\n";
|
||||
$htmlContent .= " background: var(--primary-gradient);\n";
|
||||
$htmlContent .= " transition: all 0.3s ease;\n";
|
||||
$htmlContent .= " position: relative;\n";
|
||||
$htmlContent .= " overflow: hidden;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .btn-modern::before {\n";
|
||||
$htmlContent .= " content: '';\n";
|
||||
$htmlContent .= " position: absolute;\n";
|
||||
$htmlContent .= " top: 0;\n";
|
||||
$htmlContent .= " left: -100%;\n";
|
||||
$htmlContent .= " width: 100%;\n";
|
||||
$htmlContent .= " height: 100%;\n";
|
||||
$htmlContent .= " background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);\n";
|
||||
$htmlContent .= " transition: left 0.5s;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .btn-modern:hover::before {\n";
|
||||
$htmlContent .= " left: 100%;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .btn-modern:hover {\n";
|
||||
$htmlContent .= " transform: translateY(-2px);\n";
|
||||
$htmlContent .= " box-shadow: 0 10px 20px rgba(30, 64, 175, 0.4);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " .pulse-animation {\n";
|
||||
$htmlContent .= " animation: pulse 2s infinite;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " @keyframes pulse {\n";
|
||||
$htmlContent .= " 0%, 100% { opacity: 1; }\n";
|
||||
$htmlContent .= " 50% { opacity: 0.7; }\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " </style>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "</head>\n";
|
||||
$htmlContent .= "<body class=\"font-sans antialiased text-gray-900 bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 min-h-screen font-inter\">\n";
|
||||
$htmlContent .= " <!-- Hero Section -->\n";
|
||||
$htmlContent .= " <div class=\"relative overflow-hidden\" style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\">\n";
|
||||
$htmlContent .= " <!-- Background Pattern -->\n";
|
||||
$htmlContent .= " <div class=\"absolute inset-0 bg-gradient-to-br from-slate-600/20 via-gray-600/20 to-slate-700/20\"></div>\n";
|
||||
$htmlContent .= " <div class=\"absolute inset-0 bg-[url('data:image/svg+xml,%3Csvg width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" xmlns=\"http://www.w3.org/2000/svg\"%3E%3Cg fill=\"none\" fill-rule=\"evenodd\"%3E%3Cg fill=\"%23ffffff\" fill-opacity=\"0.05\"%3E%3Ccircle cx=\"30\" cy=\"30\" r=\"2\"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E')] opacity-30\"></div>\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " <div class=\"relative mx-auto max-w-screen-2xl px-4 py-4 md:px-6\">\n";
|
||||
$htmlContent .= " <div class=\"glass-card relative mx-auto max-w-2xl rounded-2xl p-4 shadow-xl backdrop-blur-xl\">\n";
|
||||
$htmlContent .= " <div class=\"text-center space-y-3\">\n";
|
||||
$htmlContent .= " <div class=\"space-y-1\">\n";
|
||||
$htmlContent .= " <h1 class=\"text-2xl md:text-3xl font-black text-gray-800 mb-1\">$company</h1>\n";
|
||||
$htmlContent .= " <div class=\"w-16 h-0.5 bg-gray-600 mx-auto rounded-full\"></div>\n";
|
||||
$htmlContent .= " <p class=\"text-lg font-semibold text-gray-700\">HOTSPOT LOGIN</p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " <div class=\"bg-white/90 rounded-lg p-2 mb-1 shadow-sm\">\n";
|
||||
$htmlContent .= " <p class=\"text-xs text-gray-700 italic leading-relaxed\">\n";
|
||||
$htmlContent .= " Empowering businesses with reliable WiFi solutions and seamless connectivity across Kenya.\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " <div class=\"bg-white/90 rounded-xl p-3 backdrop-blur-sm shadow-sm\">\n";
|
||||
$htmlContent .= " <h3 class=\"text-sm font-bold text-gray-800 mb-2\">How to Connect</h3>\n";
|
||||
$htmlContent .= " <div class=\"grid grid-cols-2 gap-2 text-gray-700\">\n";
|
||||
$htmlContent .= " <div class=\"flex items-center space-x-2\">\n";
|
||||
$htmlContent .= " <span class=\"flex-shrink-0 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-xs font-bold\">1</span>\n";
|
||||
$htmlContent .= " <span class=\"text-xs\">Click on your preferred package</span>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"flex items-center space-x-2\">\n";
|
||||
$htmlContent .= " <span class=\"flex-shrink-0 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-xs font-bold\">2</span>\n";
|
||||
$htmlContent .= " <span class=\"text-xs\">Enter Mpesa No.</span>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"flex items-center space-x-2\">\n";
|
||||
$htmlContent .= " <span class=\"flex-shrink-0 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-xs font-bold\">3</span>\n";
|
||||
$htmlContent .= " <span class=\"text-xs\">Enter pin</span>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"flex items-center space-x-2\">\n";
|
||||
$htmlContent .= " <span class=\"flex-shrink-0 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-xs font-bold\">4</span>\n";
|
||||
$htmlContent .= " <span class=\"text-xs\">Wait to be connected</span>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " <div class=\"bg-white/90 rounded-lg p-2 border border-gray-200/50 shadow-sm\">\n";
|
||||
$htmlContent .= " <p class=\"text-sm font-medium text-gray-700\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-phone-alt text-blue-500 mr-2\"></i>\n";
|
||||
$htmlContent .= " For any enquiries contact: <a href=\"tel:$phone\" class=\"font-bold text-blue-600 hover:text-blue-800 transition-colors duration-200\">$phone</a>\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " <!-- Voucher Section -->\n";
|
||||
$htmlContent .= " <div class=\"py-3 sm:py-4 lg:py-5\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-screen-2xl px-4 md:px-6\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-md\">\n";
|
||||
$htmlContent .= " <div class=\"glass-card rounded-xl p-4 shadow-lg backdrop-blur-xl\">\n";
|
||||
$htmlContent .= " <div class=\"text-center space-y-2\">\n";
|
||||
$htmlContent .= " <div class=\"w-12 h-12 bg-blue-500 rounded-full flex items-center justify-center mx-auto\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-ticket-alt text-white text-lg\"></i>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <h3 class=\"text-lg font-bold text-gray-800\">Have a Voucher Code?</h3>\n";
|
||||
$htmlContent .= " <p class=\"text-sm text-gray-600\">Redeem your voucher for instant access</p>\n";
|
||||
$htmlContent .= " <button type=\"button\" class=\"w-full flex items-center justify-center gap-3 rounded-xl bg-gradient-to-r from-purple-500 to-pink-500 px-8 py-4 text-center text-sm font-semibold text-white shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 focus:outline-none focus:ring-4 focus:ring-purple-300 md:text-base\" onclick=\"redeemVoucher()\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-gift text-lg\"></i>\n";
|
||||
$htmlContent .= " Redeem Voucher\n";
|
||||
$htmlContent .= " </button>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " <!-- Packages Section -->\n";
|
||||
$htmlContent .= " <div class=\"py-3 sm:py-4 lg:py-5\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-screen-2xl px-4 md:px-6\">\n";
|
||||
$htmlContent .= " <div class=\"text-center mb-4\">\n";
|
||||
$htmlContent .= " <h2 class=\"text-xl font-bold text-gray-800 mb-1\">Choose Your Package</h2>\n";
|
||||
$htmlContent .= " <p class=\"text-sm text-gray-600\">Select the perfect plan for your internet needs</p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-6xl grid grid-cols-2 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4 gap-3\" id=\"cards-container\">\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
|
||||
$htmlContent .= " <!-- Reconnection & Login Section -->\n";
|
||||
$htmlContent .= " <div class=\"py-3 sm:py-4 lg:py-5\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-screen-2xl px-4 md:px-6\">\n";
|
||||
$htmlContent .= " <div class=\"max-w-2xl mx-auto space-y-4\">\n";
|
||||
$htmlContent .= " <!-- Reconnection Card -->\n";
|
||||
$htmlContent .= " <div class=\"glass-card rounded-xl p-4 shadow-lg backdrop-blur-xl\">\n";
|
||||
$htmlContent .= " <div class=\"text-center mb-3\">\n";
|
||||
$htmlContent .= " <div class=\"w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center mx-auto mb-2\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-redo text-white text-sm\"></i>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <h3 class=\"text-lg font-bold text-gray-800 mb-1\">Reconnect with M-Pesa Code</h3>\n";
|
||||
$htmlContent .= " <p class=\"text-sm text-gray-600\">Enter your M-Pesa transaction code to reconnect</p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"floating-label mb-3\">\n";
|
||||
$htmlContent .= " <input type=\"text\" id=\"mpesaCodeInput\" name=\"mpesa_code\" placeholder=\"\" class=\"w-full rounded-lg border-2 border-gray-200 bg-white/80 px-3 py-2 text-gray-800 outline-none ring-2 ring-transparent transition-all duration-200 focus:border-blue-500 focus:ring-blue-100 focus:bg-white text-sm\" />\n";
|
||||
$htmlContent .= " <label for=\"mpesaCodeInput\" class=\"text-gray-500 font-medium text-sm\">Mpesa Code or Mpesa Message</label>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <button id=\"reconnectBtn\" class=\"btn-modern w-full flex items-center justify-center gap-2 rounded-lg px-4 py-2 text-white font-semibold transition-all duration-300 text-sm\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-wifi text-lg\"></i>\n";
|
||||
$htmlContent .= " Reconnect Now\n";
|
||||
$htmlContent .= " </button>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " <!-- Login Card -->\n";
|
||||
$htmlContent .= " <div class=\"glass-card rounded-xl p-4 shadow-lg backdrop-blur-xl\">\n";
|
||||
$htmlContent .= " <div class=\"text-center mb-6\">\n";
|
||||
$htmlContent .= " <div class=\"w-16 h-16 bg-green-500 rounded-full flex items-center justify-center mx-auto mb-4\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-user-check text-white text-2xl\"></i>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <h3 class=\"text-2xl font-bold text-gray-800 mb-2\">Already Have an Active Package?</h3>\n";
|
||||
$htmlContent .= " <p class=\"text-gray-600\">Login with your account details</p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <form id=\"loginForm\" class=\"space-y-6\" name=\"login\" action=\"$(link-login-only)\" method=\"post\" $(if chap-id)onSubmit=\"return doLogin()\" $(endif)>\n";
|
||||
$htmlContent .= " <input type=\"hidden\" name=\"dst\" value=\"$(link-orig)\" />\n";
|
||||
$htmlContent .= " <input type=\"hidden\" name=\"popup\" value=\"true\" />\n";
|
||||
$htmlContent .= " <div class=\"floating-label\">\n";
|
||||
$htmlContent .= " <input id=\"usernameInput\" name=\"username\" type=\"text\" value=\"\" placeholder=\"\" class=\"w-full rounded-xl border-2 border-gray-200 bg-white/80 px-4 py-4 text-gray-800 outline-none ring-2 ring-transparent transition-all duration-200 focus:border-green-500 focus:ring-green-100 focus:bg-white\" />\n";
|
||||
$htmlContent .= " <label for=\"username\" class=\"text-gray-500 font-medium\">Username or Account Number</label>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <input type=\"hidden\" name=\"password\" value=\"1234\">\n";
|
||||
$htmlContent .= " <button id=\"submitBtn\" class=\"btn-modern w-full flex items-center justify-center gap-3 rounded-xl px-6 py-4 text-white font-semibold transition-all duration-300\" type=\"button\" onclick=\"submitLogin()\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-sign-in-alt text-lg\"></i>\n";
|
||||
$htmlContent .= " Connect Now\n";
|
||||
$htmlContent .= " </button>\n";
|
||||
$htmlContent .= " </form>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <!-- Footer -->\n";
|
||||
$htmlContent .= " <div class=\"py-4 sm:py-6\">\n";
|
||||
$htmlContent .= " <div class=\"mx-auto max-w-screen-2xl px-4 md:px-6\">\n";
|
||||
$htmlContent .= " <div class=\"glass-card rounded-xl p-4 text-center backdrop-blur-xl\">\n";
|
||||
$htmlContent .= " <div class=\"flex items-center justify-center space-x-2 mb-4\">\n";
|
||||
$htmlContent .= " <div class=\"w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-wifi text-white text-sm\"></i>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <span class=\"text-lg font-bold text-gray-800\">Powered by NestICT</span>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <p class=\"text-sm text-gray-600 mb-4\">\n";
|
||||
$htmlContent .= " © " . date("Y") . " Billing System\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " <div class=\"flex flex-col sm:flex-row items-center justify-center space-y-2 sm:space-y-0 sm:space-x-6 mb-4\">\n";
|
||||
$htmlContent .= " <a href=\"tel:+254705042522\" class=\"inline-flex items-center text-blue-600 hover:text-blue-800 transition-colors duration-200\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-phone text-blue-500 mr-2\"></i>\n";
|
||||
$htmlContent .= " <span>+254705042522</span>\n";
|
||||
$htmlContent .= " </a>\n";
|
||||
$htmlContent .= " <a href=\"mailto:sales@nestict.net\" class=\"inline-flex items-center text-blue-600 hover:text-blue-800 transition-colors duration-200\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-envelope text-blue-500 mr-2\"></i>\n";
|
||||
$htmlContent .= " <span>sales@nestict.net</span>\n";
|
||||
$htmlContent .= " </a>\n";
|
||||
$htmlContent .= " <a href=\"https://wa.me/254705042522\" target=\"_blank\" class=\"inline-flex items-center text-blue-600 hover:text-blue-800 transition-colors duration-200\">\n";
|
||||
$htmlContent .= " <i class=\"fab fa-whatsapp text-blue-500 mr-2\"></i>\n";
|
||||
$htmlContent .= " <span>WhatsApp</span>\n";
|
||||
$htmlContent .= " </a>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <a href=\"https://www.nestict.africa/\" target=\"_blank\" class=\"inline-flex items-center text-blue-600 hover:text-blue-800 transition-colors duration-200\">\n";
|
||||
$htmlContent .= " <span class=\"mr-2\">Visit NestICT</span>\n";
|
||||
$htmlContent .= " <i class=\"fas fa-external-link-alt text-xs\"></i>\n";
|
||||
$htmlContent .= " </a>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= "</body>\n";
|
||||
|
||||
// Add the closing script section as well, if necessary
|
||||
$htmlContent .= "<script>\n";
|
||||
// Add any required JavaScript here
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "function fetchData() {\n";
|
||||
$htmlContent .= " let domain = '" . APP_URL . "/';\n";
|
||||
$htmlContent .= " let siteUrl = domain + \"/index.php?_route=plugin/hotspot_plan\";\n";
|
||||
$htmlContent .= " let request = new XMLHttpRequest();\n";
|
||||
$htmlContent .= " const routerName = encodeURIComponent(\"$routerName\");\n";
|
||||
$htmlContent .= " const dataparams = `routername=\${routerName}`;\n";
|
||||
$htmlContent .= " request.open(\"POST\", siteUrl, true);\n";
|
||||
$htmlContent .= " request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\n";
|
||||
$htmlContent .= " request.onload = () => {\n";
|
||||
$htmlContent .= " if (request.readyState === XMLHttpRequest.DONE) {\n";
|
||||
$htmlContent .= " if (request.status === 200) {\n";
|
||||
$htmlContent .= " let fetchedData = JSON.parse(request.responseText);\n";
|
||||
$htmlContent .= " populateCards(fetchedData);\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.log(`Error \${request.status}: \${request.statusText}`);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " };\n";
|
||||
$htmlContent .= " request.onerror = () => {\n";
|
||||
$htmlContent .= " console.error(\"Network error\");\n";
|
||||
$htmlContent .= " };\n";
|
||||
$htmlContent .= " request.send(dataparams);\n";
|
||||
$htmlContent .= "}\n";
|
||||
|
||||
$htmlContent .= "function populateCards(data) {\n";
|
||||
$htmlContent .= " var cardsContainer = document.getElementById('cards-container');\n";
|
||||
$htmlContent .= " cardsContainer.innerHTML = ''; // Clear existing content\n";
|
||||
|
||||
$htmlContent .= " // Sort the plans by price in ascending order\n";
|
||||
$htmlContent .= " data.data.forEach(router => {\n";
|
||||
$htmlContent .= " // Sort hotspot plans by price\n";
|
||||
$htmlContent .= " router.plans_hotspot.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));\n";
|
||||
|
||||
$htmlContent .= " router.plans_hotspot.forEach(item => {\n";
|
||||
$htmlContent .= " var cardDiv = document.createElement('div');\n";
|
||||
$htmlContent .= " cardDiv.className = 'glass-card card-hover rounded-2xl shadow-xl backdrop-blur-xl overflow-hidden flex flex-col mx-auto w-full max-w-xs group';\n";
|
||||
$htmlContent .= " cardDiv.innerHTML = `\n";
|
||||
$htmlContent .= " <div class=\"relative bg-gradient-to-r from-blue-500 to-blue-600 text-white p-3\">\n";
|
||||
$htmlContent .= " <div class=\"absolute top-3 right-3 w-3 h-3 bg-white/30 rounded-full\"></div>\n";
|
||||
$htmlContent .= " <h2 class=\"text-base font-bold text-center mb-2\" style=\"font-size: clamp(0.875rem, 2vw, 1.125rem); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\">\n";
|
||||
$htmlContent .= " \${item.planname}\n";
|
||||
$htmlContent .= " </h2>\n";
|
||||
$htmlContent .= " <div class=\"w-12 h-0.5 bg-white/50 rounded-full mx-auto\"></div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"p-3 flex-grow flex flex-col justify-between\">\n";
|
||||
$htmlContent .= " <div class=\"text-center mb-3\">\n";
|
||||
$htmlContent .= " <div class=\"mb-2\">\n";
|
||||
$htmlContent .= " <span class=\"text-2xl font-black text-gray-800\">\${item.currency}</span>\n";
|
||||
$htmlContent .= " <span class=\"text-3xl font-black text-blue-600\">\${item.price}</span>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <div class=\"bg-gradient-to-r from-slate-50 to-gray-100 rounded-lg p-2\">\n";
|
||||
$htmlContent .= " <p class=\"text-sm font-semibold text-gray-700 flex items-center justify-center\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-clock text-blue-500 mr-2\"></i>\n";
|
||||
$htmlContent .= " Valid for \${item.validity} \${item.timelimit}\n";
|
||||
$htmlContent .= " </p>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " </div>\n";
|
||||
$htmlContent .= " <button class=\"w-full bg-gradient-to-r from-gray-800 to-gray-900 text-white hover:from-gray-900 hover:to-black font-semibold py-3 px-4 rounded-xl transition-all duration-200 transform hover:-translate-y-0.5 shadow-lg hover:shadow-xl flex items-center justify-center gap-2 text-sm\"\n";
|
||||
$htmlContent .= " onclick=\"handlePhoneNumberSubmission('\${item.planId}', '\${item.routerId}', '\${item.price}'); return false;\"\n";
|
||||
$htmlContent .= " data-plan-id=\"\${item.planId}\"\n";
|
||||
$htmlContent .= " data-router-id=\"\${item.routerId}\">\n";
|
||||
$htmlContent .= " <i class=\"fas fa-shopping-cart text-base\"></i>\n";
|
||||
$htmlContent .= " <span>Buy Now</span>\n";
|
||||
$htmlContent .= " </button>\n";
|
||||
$htmlContent .= " `;\n";
|
||||
$htmlContent .= " cardsContainer.appendChild(cardDiv);\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "}\n";
|
||||
$htmlContent .= "fetchData();\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script src=\"https://cdn.jsdelivr.net/npm/sweetalert2@11\"></script>\n";
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= " function formatPhoneNumber(phoneNumber) {\n";
|
||||
$htmlContent .= " if (phoneNumber.startsWith('+')) {\n";
|
||||
$htmlContent .= " phoneNumber = phoneNumber.substring(1);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " if (phoneNumber.startsWith('0')) {\n";
|
||||
$htmlContent .= " phoneNumber = '254' + phoneNumber.substring(1);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " if (phoneNumber.match(/^(7|1)/)) {\n";
|
||||
$htmlContent .= " phoneNumber = '254' + phoneNumber;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " return phoneNumber;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " function setCookie(name, value, days) {\n";
|
||||
$htmlContent .= " var expires = \"\";\n";
|
||||
$htmlContent .= " if (days) {\n";
|
||||
$htmlContent .= " var date = new Date();\n";
|
||||
$htmlContent .= " date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));\n";
|
||||
$htmlContent .= " expires = \"; expires=\" + date.toUTCString();\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " document.cookie = name + \"=\" + (value || \"\") + expires + \"; path=/\";\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " function getCookie(name) {\n";
|
||||
$htmlContent .= " var nameEQ = name + \"=\";\n";
|
||||
$htmlContent .= " var ca = document.cookie.split(';');\n";
|
||||
$htmlContent .= " for (var i = 0; i < ca.length; i++) {\n";
|
||||
$htmlContent .= " var c = ca[i];\n";
|
||||
$htmlContent .= " while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n";
|
||||
$htmlContent .= " if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " return null;\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " function generateAccountId() {\n";
|
||||
$htmlContent .= " return 'ACC' + Math.floor(10000 + Math.random() * 90000); // Generate a random number between 10000 and 99999\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= "\n";
|
||||
|
||||
$htmlContent .= "var loginTimeout; // Variable to store the timeout ID\n";
|
||||
$htmlContent .= "function handlePhoneNumberSubmission(planId, routerId, price) {\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " var msg = \"You are about to pay Kes: \" + price + \". Enter phone number below and click pay now to initialize payment\";\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " const regexp = /\\\${([^{}]+)}/g;\n";
|
||||
$htmlContent .= " let result = msg.replace(regexp, function(ignore, key) {\n";
|
||||
$htmlContent .= " return eval(key);\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " swal.fire({\n";
|
||||
$htmlContent .= " title: 'Enter Your Mpesa Number',\n";
|
||||
$htmlContent .= " input: 'number',\n";
|
||||
$htmlContent .= " inputAttributes: {\n";
|
||||
$htmlContent .= " required: 'true'\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " inputValidator: function(value) {\n";
|
||||
$htmlContent .= " if (value === '') {\n";
|
||||
$htmlContent .= " return 'You need to write your phonenumber!';\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " text: result,\n";
|
||||
$htmlContent .= " showCancelButton: true,\n";
|
||||
$htmlContent .= " confirmButtonColor: '#3085d6',\n";
|
||||
$htmlContent .= " cancelButtonColor: '#d33',\n";
|
||||
$htmlContent .= " confirmButtonText: 'Pay Now',\n";
|
||||
$htmlContent .= " showLoaderOnConfirm: true,\n";
|
||||
$htmlContent .= " preConfirm: (phoneNumber) => {\n";
|
||||
$htmlContent .= " var formattedPhoneNumber = formatPhoneNumber(phoneNumber);\n";
|
||||
$htmlContent .= " var accountId = getCookie('accountId');\n";
|
||||
$htmlContent .= " if (!accountId) {\n";
|
||||
$htmlContent .= " accountId = generateAccountId(); // Generate a new account ID\n";
|
||||
$htmlContent .= " setCookie('accountId', accountId, 7); // Set account ID as a cookie\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " document.getElementById('usernameInput').value = accountId; // Use account ID as the new username\n";
|
||||
$htmlContent .= " console.log(\"Phone number for autofill:\", formattedPhoneNumber);\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= " return fetch('" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=grant', {\n";
|
||||
$htmlContent .= " method: 'POST',\n";
|
||||
$htmlContent .= " headers: {'Content-Type': 'application/json'},\n";
|
||||
$htmlContent .= " body: JSON.stringify({phone_number: formattedPhoneNumber, plan_id: planId, router_id: routerId, account_id: accountId}),\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(response => {\n";
|
||||
$htmlContent .= " if (!response.ok) throw new Error('Network response was not ok');\n";
|
||||
$htmlContent .= " return response.json();\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(data => {\n";
|
||||
$htmlContent .= " if (data.status === 'error') throw new Error(data.message);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Processing..',\n";
|
||||
$htmlContent .= " html: `A payment request has been sent to your phone. Please wait while we process your payment.`,\n";
|
||||
$htmlContent .= " showConfirmButton: false,\n";
|
||||
$htmlContent .= " allowOutsideClick: false,\n";
|
||||
$htmlContent .= " didOpen: () => {\n";
|
||||
$htmlContent .= " Swal.showLoading();\n";
|
||||
$htmlContent .= " checkPaymentStatus(formattedPhoneNumber);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " return formattedPhoneNumber;\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .catch(error => {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Oops...',\n";
|
||||
$htmlContent .= " text: error.message,\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " allowOutsideClick: () => !Swal.isLoading()\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "}\n";
|
||||
$htmlContent .= "\n";
|
||||
$htmlContent .= "function checkPaymentStatus(phoneNumber) {\n";
|
||||
$htmlContent .= " let checkInterval = setInterval(() => {\n";
|
||||
$htmlContent .= " $.ajax({\n";
|
||||
$htmlContent .= " url: '" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=verify',\n";
|
||||
$htmlContent .= " method: 'POST',\n";
|
||||
$htmlContent .= " data: JSON.stringify({account_id: document.getElementById('usernameInput').value}),\n";
|
||||
$htmlContent .= " contentType: 'application/json',\n";
|
||||
$htmlContent .= " dataType: 'json',\n";
|
||||
$htmlContent .= " success: function(data) {\n";
|
||||
$htmlContent .= " console.log('Raw Response:', data); // Debugging\n";
|
||||
$htmlContent .= " if (data.Resultcode === '3') { // Success\n";
|
||||
$htmlContent .= " clearInterval(checkInterval);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'success',\n";
|
||||
$htmlContent .= " title: 'Payment Successful',\n";
|
||||
$htmlContent .= " text: data.Message,\n";
|
||||
$htmlContent .= " showConfirmButton: false\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " if (loginTimeout) {\n";
|
||||
$htmlContent .= " clearTimeout(loginTimeout);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " loginTimeout = setTimeout(function() {\n";
|
||||
$htmlContent .= " document.getElementById('loginForm').submit();\n";
|
||||
$htmlContent .= " }, 2000);\n";
|
||||
$htmlContent .= " } else if (data.Resultcode === '2') { // Error\n";
|
||||
$htmlContent .= " clearInterval(checkInterval);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Payment Issue',\n";
|
||||
$htmlContent .= " text: data.Message,\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " } else if (data.Resultcode === '1') { // Primary\n";
|
||||
$htmlContent .= " // Continue checking\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " error: function(xhr, textStatus, errorThrown) {\n";
|
||||
$htmlContent .= " console.log('Error: ' + errorThrown);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }, 2000);\n";
|
||||
$htmlContent .= "\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= " setTimeout(() => {\n";
|
||||
$htmlContent .= " clearInterval(checkInterval);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'warning',\n";
|
||||
$htmlContent .= " title: 'Timeout',\n";
|
||||
$htmlContent .= " text: 'Payment verification timed out. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }, 600000); // Stop checking after 60 seconds\n";
|
||||
$htmlContent .= "}\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "</script>\n";
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "var loginTimeout; // Variable to store the timeout ID\n";
|
||||
$htmlContent .= "function redeemVoucher() {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Redeem Voucher',\n";
|
||||
$htmlContent .= " input: 'text',\n";
|
||||
$htmlContent .= " inputPlaceholder: 'Enter voucher code',\n";
|
||||
$htmlContent .= " inputValidator: function(value) {\n";
|
||||
$htmlContent .= " if (!value) {\n";
|
||||
$htmlContent .= " return 'You need to enter a voucher code!';\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " confirmButtonColor: '#3085d6',\n";
|
||||
$htmlContent .= " cancelButtonColor: '#d33',\n";
|
||||
$htmlContent .= " confirmButtonText: 'Redeem',\n";
|
||||
$htmlContent .= " showLoaderOnConfirm: true,\n";
|
||||
$htmlContent .= " preConfirm: (voucherCode) => {\n";
|
||||
$htmlContent .= " var accountId = voucherCode;\n";
|
||||
$htmlContent .= " if (!accountId) {\n";
|
||||
$htmlContent .= " accountId = voucherCode;\n";
|
||||
$htmlContent .= " setCookie('accountId', accountId, 7);\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " return fetch('" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=voucher', {\n";
|
||||
$htmlContent .= " method: 'POST',\n";
|
||||
$htmlContent .= " headers: {'Content-Type': 'application/json'},\n";
|
||||
$htmlContent .= " body: JSON.stringify({voucher_code: voucherCode, account_id: accountId}),\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(response => {\n";
|
||||
$htmlContent .= " if (!response.ok) throw new Error('Network response was not ok');\n";
|
||||
$htmlContent .= " return response.json();\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(data => {\n";
|
||||
$htmlContent .= " if (data.status === 'error') throw new Error(data.message);\n";
|
||||
$htmlContent .= " return data;\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " },\n";
|
||||
$htmlContent .= " allowOutsideClick: () => !Swal.isLoading()\n";
|
||||
$htmlContent .= " }).then((result) => {\n";
|
||||
$htmlContent .= " if (result.isConfirmed) {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'success',\n";
|
||||
$htmlContent .= " title: 'Voucher Redeemed',\n";
|
||||
$htmlContent .= " text: result.value.message,\n";
|
||||
$htmlContent .= " showConfirmButton: false,\n";
|
||||
$htmlContent .= " allowOutsideClick: false,\n";
|
||||
$htmlContent .= " didOpen: () => {\n";
|
||||
$htmlContent .= " Swal.showLoading();\n";
|
||||
$htmlContent .= " var username = result.value.username;\n";
|
||||
$htmlContent .= " console.log('Received username from server:', username);\n";
|
||||
$htmlContent .= " var usernameInput = document.querySelector('input[name=\"username\"]');\n";
|
||||
$htmlContent .= " if (usernameInput) {\n";
|
||||
$htmlContent .= " console.log('Found username input element.');\n";
|
||||
$htmlContent .= " usernameInput.value = username;\n";
|
||||
$htmlContent .= " loginTimeout = setTimeout(function() {\n";
|
||||
$htmlContent .= " var loginForm = document.getElementById('loginForm');\n";
|
||||
$htmlContent .= " if (loginForm) {\n";
|
||||
$htmlContent .= " loginForm.submit();\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.error('Login form not found.');\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Login form not found. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }, 2000);\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.error('Username input element not found.');\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Username input not found. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }).catch(error => {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Oops...',\n";
|
||||
$htmlContent .= " text: error.message,\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "}\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "var loginTimeout; // Variable to store the timeout ID\n";
|
||||
$htmlContent .= "document.addEventListener('DOMContentLoaded', function() {\n";
|
||||
$htmlContent .= " document.getElementById('reconnectBtn').addEventListener('click', function() {\n";
|
||||
$htmlContent .= " var mpesaCode = document.getElementById('mpesaCodeInput').value;\n";
|
||||
$htmlContent .= " var firstWord = mpesaCode.split(' ')[0]; // Get the first word in the MPESA code\n";
|
||||
$htmlContent .= " fetch('" . APP_URL . "/index.php?_route=plugin/CreateHotspotuser&type=reconnect', {\n";
|
||||
$htmlContent .= " method: 'POST',\n";
|
||||
$htmlContent .= " headers: {'Content-Type': 'application/json'},\n";
|
||||
$htmlContent .= " body: JSON.stringify({mpesa_code: firstWord}),\n"; // Sending only the first word of the MPESA code\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .then(response => response.json())\n";
|
||||
$htmlContent .= " .then(data => {\n";
|
||||
$htmlContent .= " if (data.Status === 'success') {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " icon: 'success',\n";
|
||||
$htmlContent .= " title: 'Reconnection Successful',\n";
|
||||
$htmlContent .= " text: data.Message,\n";
|
||||
$htmlContent .= " showConfirmButton: false,\n";
|
||||
$htmlContent .= " allowOutsideClick: false,\n";
|
||||
$htmlContent .= " didOpen: () => {\n";
|
||||
$htmlContent .= " Swal.showLoading();\n";
|
||||
$htmlContent .= " var username = data.username; // Replace with actual JSON field name\n";
|
||||
$htmlContent .= " console.log('Received username from server:', username);\n";
|
||||
$htmlContent .= " var usernameInput = document.querySelector('input[name=\"username\"]');\n";
|
||||
$htmlContent .= " if (usernameInput) {\n";
|
||||
$htmlContent .= " console.log('Found username input element.');\n";
|
||||
$htmlContent .= " usernameInput.value = username;\n";
|
||||
$htmlContent .= " loginTimeout = setTimeout(function() {\n";
|
||||
$htmlContent .= " var loginForm = document.getElementById('loginForm');\n";
|
||||
$htmlContent .= " if (loginForm) {\n";
|
||||
$htmlContent .= " loginForm.submit();\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.error('Login form not found.');\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Login form not found. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }, 2000);\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " console.error('Username input element not found.');\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Username input not found. Please try again.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " } else {\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Reconnection Failed',\n";
|
||||
$htmlContent .= " text: data.Message,\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " }\n";
|
||||
$htmlContent .= " })\n";
|
||||
$htmlContent .= " .catch(error => {\n";
|
||||
$htmlContent .= " console.error('Error:', error);\n";
|
||||
$htmlContent .= " Swal.fire({\n";
|
||||
$htmlContent .= " title: 'Error',\n";
|
||||
$htmlContent .= " text: 'Failed to reconnect. Please try again later.',\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "});\n";
|
||||
$htmlContent .= "</script>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js\"></script>\n";
|
||||
$htmlContent .= "<script>\n";
|
||||
$htmlContent .= "document.addEventListener('DOMContentLoaded', function() {\n";
|
||||
$htmlContent .= " // Ensure the button is correctly targeted by its ID.\n";
|
||||
$htmlContent .= " var submitBtn = document.getElementById('submitBtn');\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " // Add a click event listener to the \"Login Now\" button.\n";
|
||||
$htmlContent .= " submitBtn.addEventListener('click', function(event) {\n";
|
||||
$htmlContent .= " event.preventDefault(); // Prevent the default button action.\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " // Optional: Log to console for debugging purposes.\n";
|
||||
$htmlContent .= " console.log(\"Login Now button clicked.\");\n";
|
||||
$htmlContent .= " \n";
|
||||
$htmlContent .= " // Direct form submission, bypassing the doLogin function for simplicity.\n";
|
||||
$htmlContent .= " var form = document.getElementById('loginForm');\n";
|
||||
$htmlContent .= " form.submit(); // Submit the form directly.\n";
|
||||
$htmlContent .= " });\n";
|
||||
$htmlContent .= "});\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$htmlContent .= "</script>\n";
|
||||
$htmlContent .= "</html>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$planStmt->close();
|
||||
$mysqli->close();
|
||||
// Check if the download parameter is set
|
||||
if (isset($_GET['download']) && $_GET['download'] == '1') {
|
||||
// Prepare the HTML content for download
|
||||
// ... build your HTML content ...
|
||||
|
||||
// Specify the filename for the download
|
||||
$filename = "login.html";
|
||||
|
||||
// Send headers to force download
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename='.basename($filename));
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: ' . strlen($htmlContent));
|
||||
|
||||
// Output the content
|
||||
echo $htmlContent;
|
||||
|
||||
// Prevent any further output
|
||||
exit;
|
||||
}
|
||||
|
||||
// Regular page content goes here
|
||||
// ... HTML and PHP code to display the page ...
|
||||
|
||||
|
||||
12
system/plugin/error_log
Normal file
12
system/plugin/error_log
Normal file
@@ -0,0 +1,12 @@
|
||||
[06-Jul-2024 15:05:25 UTC] PHP Fatal error: Uncaught Error: Undefined constant "request" in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php:154
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php on line 154
|
||||
[06-Jul-2024 15:05:28 UTC] PHP Fatal error: Uncaught Error: Undefined constant "request" in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php:154
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php on line 154
|
||||
[06-Jul-2024 17:35:47 UTC] PHP Fatal error: Uncaught Error: Undefined constant "request" in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php:154
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in /home/codevibe/kejos.codevibeisp.co.ke/system/plugin/download.php on line 154
|
||||
79
system/plugin/hotspot_plan.php
Normal file
79
system/plugin/hotspot_plan.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
// Assuming you have ORM or database access configured correctly
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['routername'])) {
|
||||
// Example of fetching data (simplified)
|
||||
$routerName = $_POST['routername'];
|
||||
|
||||
// Fetch routers and plans from database (replace with your actual ORM or database queries)
|
||||
$routers = ORM::for_table('tbl_routers')->find_many();
|
||||
$plans_hotspot = ORM::for_table('tbl_plans')->where('type', 'Hotspot')->find_many();
|
||||
|
||||
// Fetch bandwidth limits for all plans
|
||||
$bandwidth_limits = ORM::for_table('tbl_bandwidth')->find_many();
|
||||
$bandwidth_map = [];
|
||||
foreach ($bandwidth_limits as $limit) {
|
||||
$bandwidth_map[$limit['plan_id']] = [
|
||||
'downlimit' => $limit['rate_down'],
|
||||
'uplimit' => $limit['rate_up'],
|
||||
];
|
||||
}
|
||||
|
||||
// Fetch currency from tbl_appconfig using the correct column names
|
||||
$currency_config = ORM::for_table('tbl_appconfig')->where('setting', 'currency_code')->find_one();
|
||||
$currency = $currency_config ? $currency_config->value : 'Ksh'; // Default to 'Ksh' if not found
|
||||
|
||||
// Initialize empty data array to store router-specific plans
|
||||
$data = [];
|
||||
|
||||
// Process each router to filter and collect hotspot plans
|
||||
foreach ($routers as $router) {
|
||||
if ($router['name'] === $routerName) { // Check if router name matches POSTed routername
|
||||
$routerData = [
|
||||
'name' => $router['name'],
|
||||
'router_id' => $router['id'],
|
||||
'description' => $router['description'],
|
||||
'plans_hotspot' => [],
|
||||
];
|
||||
|
||||
// Filter and collect hotspot plans associated with the router
|
||||
foreach ($plans_hotspot as $plan) {
|
||||
if ($router['name'] == $plan['routers']) {
|
||||
$plan_id = $plan['id'];
|
||||
$bandwidth_data = isset($bandwidth_map[$plan_id]) ? $bandwidth_map[$plan_id] : [];
|
||||
|
||||
// Construct payment link using $_url
|
||||
$paymentlink = "https://codevibeisp.co.ke/index.php?_route=plugin/hotspot_pay&routerName={$router['name']}&planId={$plan['id']}&routerId={$router['id']}";
|
||||
|
||||
// Prepare plan data to be sent in JSON response
|
||||
$routerData['plans_hotspot'][] = [
|
||||
'plantype' => $plan['type'],
|
||||
'planname' => $plan['name_plan'],
|
||||
'currency' => $currency,
|
||||
'price' => $plan['price'],
|
||||
'validity' => $plan['validity'],
|
||||
'device' => $plan['shared_users'],
|
||||
'datalimit' => $plan['data_limit'],
|
||||
'timelimit' => $plan['validity_unit'] ?? null,
|
||||
'downlimit' => $bandwidth_data['downlimit'] ?? null,
|
||||
'uplimit' => $bandwidth_data['uplimit'] ?? null,
|
||||
'paymentlink' => $paymentlink,
|
||||
'planId' => $plan['id'],
|
||||
'routerName' => $router['name'],
|
||||
'routerId' => $router['id']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Add router data to $data array
|
||||
$data[] = $routerData;
|
||||
}
|
||||
}
|
||||
|
||||
// Respond with JSON data
|
||||
// header('Content-Type: application/json');
|
||||
// header('Access-Control-Allow-Origin: *'); // Adjust this based on your CORS requirements
|
||||
echo json_encode(['data' => $data], JSON_PRETTY_PRINT);
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user