diff --git a/.gitignore b/.gitignore
index 5a9167ba..c14fa910 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,4 +48,7 @@ system/devices/**
!system/devices/MikrotikPppoe.php
!system/devices/MikrotikHotspot.php
/.vs
-docker-compose.yml
\ No newline at end of file
+docker-compose.yml
+docs/**
+!docs/*.html
+!docs/*.md
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c484c8c7..20662636 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
# CHANGELOG
+## 2024.7.15
+
+- Radius Rest API
+- Getting Started Documentation
+- Only Show new update just once
+
## 2024.6.21
- Add filter result in voucher and internet plan
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 00000000..777780f8
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,3298 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+PHPNuxBill — a Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This TiddlyWiki contains the following tiddlers:
+
+$:/config/NewTiddler/Tags
+
+$:/config/PageControlButtons/Visibility/$:/core/ui/Buttons/close-all
+
+$:/config/PageControlButtons/Visibility/$:/core/ui/Buttons/home
+
+$:/config/PageControlButtons/Visibility/$:/core/ui/Buttons/new-tiddler
+
+$:/config/PageControlButtons/Visibility/$:/core/ui/Buttons/save-wiki
+
+$:/config/PageControlButtons/Visibility/$:/core/ui/Buttons/tag-manager
+
+$:/core
+
+$:/DefaultTiddlers
+
+$:/isEncrypted
+
+$:/language/DefaultNewTiddlerTitle
+
+$:/layout
+
+$:/palette
+
+$:/SiteSubtitle
+
+$:/SiteTitle
+
+$:/state/add-plugin-info/$:/temp/ServerConnection/https://tiddlywiki.com/library/v5.3.5/index.html/$:/temp/RemoteAssetInfo/https://tiddlywiki.com/library/v5.3.5/index.html/$:/plugins/tiddlywiki/comments
+
+$:/state/add-plugin-info/$:/temp/ServerConnection/https://tiddlywiki.com/library/v5.3.5/index.html/$:/temp/RemoteAssetInfo/https://tiddlywiki.com/library/v5.3.5/index.html/$:/plugins/tiddlywiki/menubar
+
+$:/state/add-plugin-info/$:/temp/ServerConnection/https://tiddlywiki.com/library/v5.3.5/index.html/$:/temp/RemoteAssetInfo/https://tiddlywiki.com/library/v5.3.5/index.html/$:/plugins/tiddlywiki/railroad
+
+$:/state/add-plugin-info/$:/temp/ServerConnection/https://tiddlywiki.com/library/v5.3.5/index.html/$:/temp/RemoteAssetInfo/https://tiddlywiki.com/library/v5.3.5/index.html/$:/themes/tiddlywiki/centralised
+
+$:/state/add-plugin-info/$:/temp/ServerConnection/https://tiddlywiki.com/library/v5.3.5/index.html/$:/temp/RemoteAssetInfo/https://tiddlywiki.com/library/v5.3.5/index.html/$:/themes/tiddlywiki/readonly
+
+$:/state/http-requests
+
+$:/state/image-picker/system--1059320382
+
+$:/state/plugin-info-2003269606-$:/themes/tiddlywiki/readonly
+
+$:/state/showeditpreview
+
+$:/state/sidebar
+
+$:/state/tab--1963855381
+
+$:/state/tab--2112689675
+
+$:/state/tab--86143343
+
+$:/state/tab--959111941
+
+$:/state/tab-1188684346
+
+$:/state/tab-1749438307
+
+$:/state/tab-2065006209
+
+$:/state/tab/moresidebar-1850697562
+
+$:/state/tab/sidebar--595412856
+
+$:/state/tabs/controlpanel/toolbars-1345989671
+
+$:/status/RequireReloadDueToPluginChange
+
+$:/status/UserName
+
+$:/StoryList
+
+$:/theme
+
+$:/themes/tiddlywiki/readonly
+
+$:/themes/tiddlywiki/snowwhite
+
+$:/themes/tiddlywiki/vanilla
+
+$:/themes/tiddlywiki/vanilla/options/sidebarlayout
+
+$:/themes/tiddlywiki/vanilla/options/stickytitles
+
+$:/view
+
+Add Mikrotik
+
+Add NAS
+
+Additional Billing
+
+Create Bandwidth Plan
+
+Create Hotspot Plan
+
+Create PPPOE Plan
+
+GettingStarted
+
+Hotspot System
+
+Integrated Radius System
+
+Internet Voucher
+
+Mikrotik Login Template
+
+Payment Gateway
+
+PPPOE System
+
+Radius System with Mysql
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/index.php b/index.php
index 2e483a60..f3b2dadb 100644
--- a/index.php
+++ b/index.php
@@ -18,6 +18,14 @@ if(isset($_GET['nux-router']) && !empty($_GET['nux-router'])){
$_SESSION['nux-router'] = $_GET['nux-router'];
}
+//get chap id and chap challenge
+if(isset($_GET['nux-key']) && !empty($_GET['nux-key'])){
+ $_SESSION['nux-key'] = $_GET['nux-key'];
+}
+//get mikrotik hostname
+if(isset($_GET['nux-hostname']) && !empty($_GET['nux-hostname'])){
+ $_SESSION['nux-hostname'] = $_GET['nux-hostname'];
+}
require_once 'system/vendor/autoload.php';
require_once 'system/boot.php';
App::_run();
diff --git a/install/phpnuxbill.sql b/install/phpnuxbill.sql
index dcc48863..d3074192 100644
--- a/install/phpnuxbill.sql
+++ b/install/phpnuxbill.sql
@@ -205,6 +205,34 @@ CREATE TABLE `tbl_voucher` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+DROP TABLE IF EXISTS `rad_acct`;
+CREATE TABLE `rad_acct` (
+ `id` bigint NOT NULL,
+ `acctsessionid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
+ `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
+ `realm` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
+ `nasid` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
+ `nasipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
+ `nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+ `nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+ `framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
+ `acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+ `macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+ALTER TABLE `rad_acct`
+ ADD PRIMARY KEY (`id`),
+ ADD KEY `username` (`username`),
+ ADD KEY `framedipaddress` (`framedipaddress`),
+ ADD KEY `acctsessionid` (`acctsessionid`),
+ ADD KEY `nasipaddress` (`nasipaddress`);
+
+
+ALTER TABLE `rad_acct`
+ MODIFY `id` bigint NOT NULL AUTO_INCREMENT;
+
ALTER TABLE `tbl_appconfig`
ADD PRIMARY KEY (`id`);
diff --git a/radius.php b/radius.php
new file mode 100644
index 00000000..e3662b56
--- /dev/null
+++ b/radius.php
@@ -0,0 +1,262 @@
+ $_SERVER,
+// 'get' => $_GET,
+// 'post' => $_POST,
+// 'time' => time()
+// ]));
+// }
+
+try {
+ switch ($action) {
+ case 'authenticate':
+ $username = _req('username');
+ $password = _req('password');
+ if (empty($username) || empty($password)) {
+ show_radius_result([
+ "control:Auth-Type" => "Reject",
+ "reply:Reply-Message" => 'Login invalid'
+ ], 401);
+ }
+ if ($username == $password) {
+ $d = ORM::for_table('tbl_voucher')->where('code', $username)->find_one();
+ } else {
+ $d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
+ if ($d['password'] != $password) {
+ if ($d['pppoe_password'] != $password) {
+ unset($d);
+ }
+ }
+ }
+ if ($d) {
+ header("HTTP/1.1 204 No Content");
+ die();
+ } else {
+ show_radius_result([
+ "control:Auth-Type" => "Reject",
+ "reply:Reply-Message" => 'Login invalid......'
+ ], 401);
+ }
+ break;
+ case 'authorize':
+ $username = _req('username');
+ $password = _req('password');
+ $isVoucher = ($username == $password);
+ if (empty($username) || empty($password)) {
+ show_radius_result([
+ "control:Auth-Type" => "Reject",
+ "reply:Reply-Message" => 'Login invalid......'
+ ], 401);
+ }
+ $tur = ORM::for_table('tbl_user_recharges')->where('username', $username)->find_one();
+ if ($tur) {
+ if (!$isVoucher) {
+ $d = ORM::for_table('tbl_customers')->select('password')->where('username', $username)->find_one();
+ if ($d['password'] != $password) {
+ if ($d['pppoe_password'] != $password) {
+ show_radius_result(['Reply-Message' => 'Username or Password is wrong'], 401);
+ }
+ }
+ }
+ process_radiust_rest($tur, $code);
+ } else {
+ if ($isVoucher) {
+ $v = ORM::for_table('tbl_voucher')->where('code', $username)->where('routers', 'radius')->find_one();
+ if ($v) {
+ if ($v['status'] == 0) {
+ if (Package::rechargeUser(0, $v['routers'], $v['id_plan'], "Voucher", $username)) {
+ $v->status = "1";
+ $v->save();
+ $tur = ORM::for_table('tbl_user_recharges')->where('username', $username)->find_one();
+ if ($tur) {
+ process_radiust_rest($tur, $code);
+ } else {
+ show_radius_result(['Reply-Message' => 'Voucher activation failed'], 401);
+ }
+ } else {
+ show_radius_result(['Reply-Message' => 'Voucher activation failed.'], 401);
+ }
+ } else {
+ show_radius_result(['Reply-Message' => 'Voucher Expired...'], 401);
+ }
+ } else {
+ show_radius_result(['Reply-Message' => 'Voucher Expired..'], 401);
+ }
+ } else {
+ show_radius_result(['Reply-Message' => 'Internet Plan Expired..'], 401);
+ }
+ }
+ break;
+ case 'accounting':
+ $username = _req('username');
+ if (empty($username)) {
+ show_radius_result([
+ "control:Auth-Type" => "Reject",
+ "reply:Reply-Message" => 'Username empty'
+ ], 200);
+ die();
+ }
+ header("HTTP/1.1 200 ok");
+ $d = ORM::for_table('rad_acct')
+ ->where('username', $username)
+ ->where('macaddr', _post('macAddr'))
+ ->where('acctstatustype', _post('acctStatusType'))
+ ->findOne();
+ if (!$d) {
+ $d = ORM::for_table('rad_acct')->create();
+ }
+ $d->acctsessionid = _post('acctSessionId');
+ $d->username = $username;
+ $d->realm = _post('realm');
+ $d->nasipaddress = _post('nasip');
+ $d->nasid = _post('nasid');
+ $d->nasportid = _post('nasPortId');
+ $d->nasporttype = _post('nasPortType');
+ $d->framedipaddress = _post('framedIPAddress');
+ $d->acctstatustype = _post('acctStatusType');
+ $d->macaddr = _post('macAddr');
+ $d->dateAdded = date('Y-m-d H:i:s');
+ $d->save();
+ show_radius_result([
+ "control:Auth-Type" => "Accept",
+ "reply:Reply-Message" => 'Saved'
+ ], 200);
+ break;
+ }
+ die();
+} catch (Throwable $e) {
+ Message::sendTelegram(
+ "Sistem Error.\n" .
+ $e->getMessage() . "\n" .
+ $e->getTraceAsString()
+ );
+ show_radius_result(['Reply-Message' => 'Command Failed : ' . $action], 401);
+} catch (Exception $e) {
+ Message::sendTelegram(
+ "Sistem Error.\n" .
+ $e->getMessage() . "\n" .
+ $e->getTraceAsString()
+ );
+ show_radius_result(['Reply-Message' => 'Command Failed : ' . $action], 401);
+}
+show_radius_result(['Reply-Message' => 'Invalid Command : ' . $action], 401);
+
+function process_radiust_rest($tur, $code)
+{
+ global $config;
+ $plan = ORM::for_table('tbl_plans')->where('id', $tur['plan_id'])->find_one();
+ $bw = ORM::for_table("tbl_bandwidth")->find_one($plan['id_bw']);
+ if ($bw['rate_down_unit'] == 'Kbps') {
+ $unitdown = 'K';
+ } else {
+ $unitdown = 'M';
+ }
+ if ($bw['rate_up_unit'] == 'Kbps') {
+ $unitup = 'K';
+ } else {
+ $unitup = 'M';
+ }
+ $rate = $bw['rate_up'] . $unitup . "/" . $bw['rate_down'] . $unitdown;
+ $rates = explode('/', $rate);
+
+ if (!empty(trim($bw['burst']))) {
+ $ratos = $rate . ' ' . $bw['burst'];
+ } else {
+ $ratos = $rates[0] . '/' . $rates[1];
+ }
+
+ $attrs = [];
+ $timeexp = strtotime($tur['expiration'] . ' ' . $tur['time']);
+ $attrs['reply:Reply-Message'] = 'success';
+ $attrs['Simultaneous-Use'] = $plan['shared_users'];
+ $attrs['reply:Mikrotik-Wireless-Comment'] = $plan['name_plan'] . ' | ' . $tur['expiration'] . ' ' . $tur['time'];
+
+ $attrs['reply:Ascend-Data-Rate'] = str_replace('M', '000000', str_replace('K', '000', $rates[1]));
+ $attrs['reply:Ascend-Xmit-Rate'] = str_replace('M', '000000', str_replace('K', '000', $rates[0]));
+ $attrs['reply:Mikrotik-Rate-Limit'] = $ratos;
+ $attrs['reply:WISPr-Bandwidth-Max-Up'] = str_replace('M', '000000', str_replace('K', '000', $rates[0]));
+ $attrs['reply:WISPr-Bandwidth-Max-Down'] = str_replace('M', '000000', str_replace('K', '000', $rates[1]));
+ $attrs['reply:expiration'] = date('d M Y H:i:s', $timeexp);
+ $attrs['reply:WISPr-Session-Terminate-Time'] = date('Y-m-d', $timeexp) . 'T' . date('H:i:sP', $timeexp);
+
+ if ($plan['type'] == 'PPPOE') {
+ $attrs['reply:Framed-Pool'] = $plan['pool'];
+ }
+
+ if ($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;
+ $attrs['reply:Max-All-Session'] = $timelimit;
+ $attrs['reply: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";
+ $attrs['reply:Max-Data'] = $datalimit;
+ $attrs['reply:Mikrotik-Recv-Limit-Gigawords'] = $datalimit;
+ $attrs['reply:Mikrotik-Xmit-Limit-Gigawords'] = $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";
+ $attrs['reply:Max-All-Session'] = $timelimit;
+ $attrs['reply:Max-Data'] = $datalimit;
+ $attrs['reply:Mikrotik-Recv-Limit-Gigawords'] = $datalimit;
+ $attrs['reply:Mikrotik-Xmit-Limit-Gigawords'] = $datalimit;
+ }
+ }
+ $result = array_merge([
+ "control:Auth-Type" => "Accept",
+ "reply" => ["Reply-Message" => ['value' => 'success']]
+ ], $attrs);
+ show_radius_result($result, $code);
+}
+
+function show_radius_result($array, $code = 200)
+{
+ if ($code == 401) {
+ header("HTTP/1.1 401 Unauthorized");
+ } else if ($code == 200) {
+ header("HTTP/1.1 200 OK");
+ } else if ($code == 204) {
+ header("HTTP/1.1 204 No Content");
+ die();
+ }
+ die(json_encode($array));
+ die();
+}
diff --git a/system/autoload/Package.php b/system/autoload/Package.php
index a8acdf1f..a04d5e6d 100644
--- a/system/autoload/Package.php
+++ b/system/autoload/Package.php
@@ -26,16 +26,26 @@ class Package
$time_only = date("H:i:s");
$time = date("H:i:s");
$inv = "";
+ $isVoucher = false;
+ $c = [];
if ($id_customer == '' or $router_name == '' or $plan_id == '') {
return false;
}
+ if(trim($gateway) == 'Voucher' && $id_customer == 0){
+ $isVoucher = true;
+ }
- $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', "");
+ if(!$isVoucher){
+ $c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one();
+ if ($c['status'] != 'Active') {
+ _alert(Lang::T('This account status') . ' : ' . Lang::T($c['status']), 'danger', "");
+ }
+ }else{
+ $c['username'] = $channel;
+ $c['fullname'] = $gateway;
}
$add_cost = 0;
diff --git a/system/autoload/Parsedown.php b/system/autoload/Parsedown.php
new file mode 100644
index 00000000..ae0cbdec
--- /dev/null
+++ b/system/autoload/Parsedown.php
@@ -0,0 +1,1994 @@
+textElements($text);
+
+ # convert to markup
+ $markup = $this->elements($Elements);
+
+ # trim line breaks
+ $markup = trim($markup, "\n");
+
+ return $markup;
+ }
+
+ protected function textElements($text)
+ {
+ # make sure no definitions are set
+ $this->DefinitionData = array();
+
+ # standardize line breaks
+ $text = str_replace(array("\r\n", "\r"), "\n", $text);
+
+ # remove surrounding line breaks
+ $text = trim($text, "\n");
+
+ # split text into lines
+ $lines = explode("\n", $text);
+
+ # iterate through lines to identify blocks
+ return $this->linesElements($lines);
+ }
+
+ #
+ # Setters
+ #
+
+ function setBreaksEnabled($breaksEnabled)
+ {
+ $this->breaksEnabled = $breaksEnabled;
+
+ return $this;
+ }
+
+ protected $breaksEnabled;
+
+ function setMarkupEscaped($markupEscaped)
+ {
+ $this->markupEscaped = $markupEscaped;
+
+ return $this;
+ }
+
+ protected $markupEscaped;
+
+ function setUrlsLinked($urlsLinked)
+ {
+ $this->urlsLinked = $urlsLinked;
+
+ return $this;
+ }
+
+ protected $urlsLinked = true;
+
+ function setSafeMode($safeMode)
+ {
+ $this->safeMode = (bool) $safeMode;
+
+ return $this;
+ }
+
+ protected $safeMode;
+
+ function setStrictMode($strictMode)
+ {
+ $this->strictMode = (bool) $strictMode;
+
+ return $this;
+ }
+
+ protected $strictMode;
+
+ protected $safeLinksWhitelist = array(
+ 'http://',
+ 'https://',
+ 'ftp://',
+ 'ftps://',
+ 'mailto:',
+ 'tel:',
+ 'data:image/png;base64,',
+ 'data:image/gif;base64,',
+ 'data:image/jpeg;base64,',
+ 'irc:',
+ 'ircs:',
+ 'git:',
+ 'ssh:',
+ 'news:',
+ 'steam:',
+ );
+
+ #
+ # Lines
+ #
+
+ protected $BlockTypes = array(
+ '#' => array('Header'),
+ '*' => array('Rule', 'List'),
+ '+' => array('List'),
+ '-' => array('SetextHeader', 'Table', 'Rule', 'List'),
+ '0' => array('List'),
+ '1' => array('List'),
+ '2' => array('List'),
+ '3' => array('List'),
+ '4' => array('List'),
+ '5' => array('List'),
+ '6' => array('List'),
+ '7' => array('List'),
+ '8' => array('List'),
+ '9' => array('List'),
+ ':' => array('Table'),
+ '<' => array('Comment', 'Markup'),
+ '=' => array('SetextHeader'),
+ '>' => array('Quote'),
+ '[' => array('Reference'),
+ '_' => array('Rule'),
+ '`' => array('FencedCode'),
+ '|' => array('Table'),
+ '~' => array('FencedCode'),
+ );
+
+ # ~
+
+ protected $unmarkedBlockTypes = array(
+ 'Code',
+ );
+
+ #
+ # Blocks
+ #
+
+ protected function lines(array $lines)
+ {
+ return $this->elements($this->linesElements($lines));
+ }
+
+ protected function linesElements(array $lines)
+ {
+ $Elements = array();
+ $CurrentBlock = null;
+
+ foreach ($lines as $line)
+ {
+ if (chop($line) === '')
+ {
+ if (isset($CurrentBlock))
+ {
+ $CurrentBlock['interrupted'] = (isset($CurrentBlock['interrupted'])
+ ? $CurrentBlock['interrupted'] + 1 : 1
+ );
+ }
+
+ continue;
+ }
+
+ while (($beforeTab = strstr($line, "\t", true)) !== false)
+ {
+ $shortage = 4 - mb_strlen($beforeTab, 'utf-8') % 4;
+
+ $line = $beforeTab
+ . str_repeat(' ', $shortage)
+ . substr($line, strlen($beforeTab) + 1)
+ ;
+ }
+
+ $indent = strspn($line, ' ');
+
+ $text = $indent > 0 ? substr($line, $indent) : $line;
+
+ # ~
+
+ $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
+
+ # ~
+
+ if (isset($CurrentBlock['continuable']))
+ {
+ $methodName = 'block' . $CurrentBlock['type'] . 'Continue';
+ $Block = $this->$methodName($Line, $CurrentBlock);
+
+ if (isset($Block))
+ {
+ $CurrentBlock = $Block;
+
+ continue;
+ }
+ else
+ {
+ if ($this->isBlockCompletable($CurrentBlock['type']))
+ {
+ $methodName = 'block' . $CurrentBlock['type'] . 'Complete';
+ $CurrentBlock = $this->$methodName($CurrentBlock);
+ }
+ }
+ }
+
+ # ~
+
+ $marker = $text[0];
+
+ # ~
+
+ $blockTypes = $this->unmarkedBlockTypes;
+
+ if (isset($this->BlockTypes[$marker]))
+ {
+ foreach ($this->BlockTypes[$marker] as $blockType)
+ {
+ $blockTypes []= $blockType;
+ }
+ }
+
+ #
+ # ~
+
+ foreach ($blockTypes as $blockType)
+ {
+ $Block = $this->{"block$blockType"}($Line, $CurrentBlock);
+
+ if (isset($Block))
+ {
+ $Block['type'] = $blockType;
+
+ if ( ! isset($Block['identified']))
+ {
+ if (isset($CurrentBlock))
+ {
+ $Elements[] = $this->extractElement($CurrentBlock);
+ }
+
+ $Block['identified'] = true;
+ }
+
+ if ($this->isBlockContinuable($blockType))
+ {
+ $Block['continuable'] = true;
+ }
+
+ $CurrentBlock = $Block;
+
+ continue 2;
+ }
+ }
+
+ # ~
+
+ if (isset($CurrentBlock) and $CurrentBlock['type'] === 'Paragraph')
+ {
+ $Block = $this->paragraphContinue($Line, $CurrentBlock);
+ }
+
+ if (isset($Block))
+ {
+ $CurrentBlock = $Block;
+ }
+ else
+ {
+ if (isset($CurrentBlock))
+ {
+ $Elements[] = $this->extractElement($CurrentBlock);
+ }
+
+ $CurrentBlock = $this->paragraph($Line);
+
+ $CurrentBlock['identified'] = true;
+ }
+ }
+
+ # ~
+
+ if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
+ {
+ $methodName = 'block' . $CurrentBlock['type'] . 'Complete';
+ $CurrentBlock = $this->$methodName($CurrentBlock);
+ }
+
+ # ~
+
+ if (isset($CurrentBlock))
+ {
+ $Elements[] = $this->extractElement($CurrentBlock);
+ }
+
+ # ~
+
+ return $Elements;
+ }
+
+ protected function extractElement(array $Component)
+ {
+ if ( ! isset($Component['element']))
+ {
+ if (isset($Component['markup']))
+ {
+ $Component['element'] = array('rawHtml' => $Component['markup']);
+ }
+ elseif (isset($Component['hidden']))
+ {
+ $Component['element'] = array();
+ }
+ }
+
+ return $Component['element'];
+ }
+
+ protected function isBlockContinuable($Type)
+ {
+ return method_exists($this, 'block' . $Type . 'Continue');
+ }
+
+ protected function isBlockCompletable($Type)
+ {
+ return method_exists($this, 'block' . $Type . 'Complete');
+ }
+
+ #
+ # Code
+
+ protected function blockCode($Line, $Block = null)
+ {
+ if (isset($Block) and $Block['type'] === 'Paragraph' and ! isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if ($Line['indent'] >= 4)
+ {
+ $text = substr($Line['body'], 4);
+
+ $Block = array(
+ 'element' => array(
+ 'name' => 'pre',
+ 'element' => array(
+ 'name' => 'code',
+ 'text' => $text,
+ ),
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function blockCodeContinue($Line, $Block)
+ {
+ if ($Line['indent'] >= 4)
+ {
+ if (isset($Block['interrupted']))
+ {
+ $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']);
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['element']['element']['text'] .= "\n";
+
+ $text = substr($Line['body'], 4);
+
+ $Block['element']['element']['text'] .= $text;
+
+ return $Block;
+ }
+ }
+
+ protected function blockCodeComplete($Block)
+ {
+ return $Block;
+ }
+
+ #
+ # Comment
+
+ protected function blockComment($Line)
+ {
+ if ($this->markupEscaped or $this->safeMode)
+ {
+ return;
+ }
+
+ if (strpos($Line['text'], '') !== false)
+ {
+ $Block['closed'] = true;
+ }
+
+ return $Block;
+ }
+ }
+
+ protected function blockCommentContinue($Line, array $Block)
+ {
+ if (isset($Block['closed']))
+ {
+ return;
+ }
+
+ $Block['element']['rawHtml'] .= "\n" . $Line['body'];
+
+ if (strpos($Line['text'], '-->') !== false)
+ {
+ $Block['closed'] = true;
+ }
+
+ return $Block;
+ }
+
+ #
+ # Fenced Code
+
+ protected function blockFencedCode($Line)
+ {
+ $marker = $Line['text'][0];
+
+ $openerLength = strspn($Line['text'], $marker);
+
+ if ($openerLength < 3)
+ {
+ return;
+ }
+
+ $infostring = trim(substr($Line['text'], $openerLength), "\t ");
+
+ if (strpos($infostring, '`') !== false)
+ {
+ return;
+ }
+
+ $Element = array(
+ 'name' => 'code',
+ 'text' => '',
+ );
+
+ if ($infostring !== '')
+ {
+ /**
+ * https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
+ * Every HTML element may have a class attribute specified.
+ * The attribute, if specified, must have a value that is a set
+ * of space-separated tokens representing the various classes
+ * that the element belongs to.
+ * [...]
+ * The space characters, for the purposes of this specification,
+ * are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab),
+ * U+000A LINE FEED (LF), U+000C FORM FEED (FF), and
+ * U+000D CARRIAGE RETURN (CR).
+ */
+ $language = substr($infostring, 0, strcspn($infostring, " \t\n\f\r"));
+
+ $Element['attributes'] = array('class' => "language-$language");
+ }
+
+ $Block = array(
+ 'char' => $marker,
+ 'openerLength' => $openerLength,
+ 'element' => array(
+ 'name' => 'pre',
+ 'element' => $Element,
+ ),
+ );
+
+ return $Block;
+ }
+
+ protected function blockFencedCodeContinue($Line, $Block)
+ {
+ if (isset($Block['complete']))
+ {
+ return;
+ }
+
+ if (isset($Block['interrupted']))
+ {
+ $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']);
+
+ unset($Block['interrupted']);
+ }
+
+ if (($len = strspn($Line['text'], $Block['char'])) >= $Block['openerLength']
+ and chop(substr($Line['text'], $len), ' ') === ''
+ ) {
+ $Block['element']['element']['text'] = substr($Block['element']['element']['text'], 1);
+
+ $Block['complete'] = true;
+
+ return $Block;
+ }
+
+ $Block['element']['element']['text'] .= "\n" . $Line['body'];
+
+ return $Block;
+ }
+
+ protected function blockFencedCodeComplete($Block)
+ {
+ return $Block;
+ }
+
+ #
+ # Header
+
+ protected function blockHeader($Line)
+ {
+ $level = strspn($Line['text'], '#');
+
+ if ($level > 6)
+ {
+ return;
+ }
+
+ $text = trim($Line['text'], '#');
+
+ if ($this->strictMode and isset($text[0]) and $text[0] !== ' ')
+ {
+ return;
+ }
+
+ $text = trim($text, ' ');
+
+ $Block = array(
+ 'element' => array(
+ 'name' => 'h' . $level,
+ 'handler' => array(
+ 'function' => 'lineElements',
+ 'argument' => $text,
+ 'destination' => 'elements',
+ )
+ ),
+ );
+
+ return $Block;
+ }
+
+ #
+ # List
+
+ protected function blockList($Line, array $CurrentBlock = null)
+ {
+ list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}+[.\)]');
+
+ if (preg_match('/^('.$pattern.'([ ]++|$))(.*+)/', $Line['text'], $matches))
+ {
+ $contentIndent = strlen($matches[2]);
+
+ if ($contentIndent >= 5)
+ {
+ $contentIndent -= 1;
+ $matches[1] = substr($matches[1], 0, -$contentIndent);
+ $matches[3] = str_repeat(' ', $contentIndent) . $matches[3];
+ }
+ elseif ($contentIndent === 0)
+ {
+ $matches[1] .= ' ';
+ }
+
+ $markerWithoutWhitespace = strstr($matches[1], ' ', true);
+
+ $Block = array(
+ 'indent' => $Line['indent'],
+ 'pattern' => $pattern,
+ 'data' => array(
+ 'type' => $name,
+ 'marker' => $matches[1],
+ 'markerType' => ($name === 'ul' ? $markerWithoutWhitespace : substr($markerWithoutWhitespace, -1)),
+ ),
+ 'element' => array(
+ 'name' => $name,
+ 'elements' => array(),
+ ),
+ );
+ $Block['data']['markerTypeRegex'] = preg_quote($Block['data']['markerType'], '/');
+
+ if ($name === 'ol')
+ {
+ $listStart = ltrim(strstr($matches[1], $Block['data']['markerType'], true), '0') ?: '0';
+
+ if ($listStart !== '1')
+ {
+ if (
+ isset($CurrentBlock)
+ and $CurrentBlock['type'] === 'Paragraph'
+ and ! isset($CurrentBlock['interrupted'])
+ ) {
+ return;
+ }
+
+ $Block['element']['attributes'] = array('start' => $listStart);
+ }
+ }
+
+ $Block['li'] = array(
+ 'name' => 'li',
+ 'handler' => array(
+ 'function' => 'li',
+ 'argument' => !empty($matches[3]) ? array($matches[3]) : array(),
+ 'destination' => 'elements'
+ )
+ );
+
+ $Block['element']['elements'] []= & $Block['li'];
+
+ return $Block;
+ }
+ }
+
+ protected function blockListContinue($Line, array $Block)
+ {
+ if (isset($Block['interrupted']) and empty($Block['li']['handler']['argument']))
+ {
+ return null;
+ }
+
+ $requiredIndent = ($Block['indent'] + strlen($Block['data']['marker']));
+
+ if ($Line['indent'] < $requiredIndent
+ and (
+ (
+ $Block['data']['type'] === 'ol'
+ and preg_match('/^[0-9]++'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches)
+ ) or (
+ $Block['data']['type'] === 'ul'
+ and preg_match('/^'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches)
+ )
+ )
+ ) {
+ if (isset($Block['interrupted']))
+ {
+ $Block['li']['handler']['argument'] []= '';
+
+ $Block['loose'] = true;
+
+ unset($Block['interrupted']);
+ }
+
+ unset($Block['li']);
+
+ $text = isset($matches[1]) ? $matches[1] : '';
+
+ $Block['indent'] = $Line['indent'];
+
+ $Block['li'] = array(
+ 'name' => 'li',
+ 'handler' => array(
+ 'function' => 'li',
+ 'argument' => array($text),
+ 'destination' => 'elements'
+ )
+ );
+
+ $Block['element']['elements'] []= & $Block['li'];
+
+ return $Block;
+ }
+ elseif ($Line['indent'] < $requiredIndent and $this->blockList($Line))
+ {
+ return null;
+ }
+
+ if ($Line['text'][0] === '[' and $this->blockReference($Line))
+ {
+ return $Block;
+ }
+
+ if ($Line['indent'] >= $requiredIndent)
+ {
+ if (isset($Block['interrupted']))
+ {
+ $Block['li']['handler']['argument'] []= '';
+
+ $Block['loose'] = true;
+
+ unset($Block['interrupted']);
+ }
+
+ $text = substr($Line['body'], $requiredIndent);
+
+ $Block['li']['handler']['argument'] []= $text;
+
+ return $Block;
+ }
+
+ if ( ! isset($Block['interrupted']))
+ {
+ $text = preg_replace('/^[ ]{0,'.$requiredIndent.'}+/', '', $Line['body']);
+
+ $Block['li']['handler']['argument'] []= $text;
+
+ return $Block;
+ }
+ }
+
+ protected function blockListComplete(array $Block)
+ {
+ if (isset($Block['loose']))
+ {
+ foreach ($Block['element']['elements'] as &$li)
+ {
+ if (end($li['handler']['argument']) !== '')
+ {
+ $li['handler']['argument'] []= '';
+ }
+ }
+ }
+
+ return $Block;
+ }
+
+ #
+ # Quote
+
+ protected function blockQuote($Line)
+ {
+ if (preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches))
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'blockquote',
+ 'handler' => array(
+ 'function' => 'linesElements',
+ 'argument' => (array) $matches[1],
+ 'destination' => 'elements',
+ )
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function blockQuoteContinue($Line, array $Block)
+ {
+ if (isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if ($Line['text'][0] === '>' and preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches))
+ {
+ $Block['element']['handler']['argument'] []= $matches[1];
+
+ return $Block;
+ }
+
+ if ( ! isset($Block['interrupted']))
+ {
+ $Block['element']['handler']['argument'] []= $Line['text'];
+
+ return $Block;
+ }
+ }
+
+ #
+ # Rule
+
+ protected function blockRule($Line)
+ {
+ $marker = $Line['text'][0];
+
+ if (substr_count($Line['text'], $marker) >= 3 and chop($Line['text'], " $marker") === '')
+ {
+ $Block = array(
+ 'element' => array(
+ 'name' => 'hr',
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ #
+ # Setext
+
+ protected function blockSetextHeader($Line, array $Block = null)
+ {
+ if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if ($Line['indent'] < 4 and chop(chop($Line['text'], ' '), $Line['text'][0]) === '')
+ {
+ $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
+
+ return $Block;
+ }
+ }
+
+ #
+ # Markup
+
+ protected function blockMarkup($Line)
+ {
+ if ($this->markupEscaped or $this->safeMode)
+ {
+ return;
+ }
+
+ if (preg_match('/^<[\/]?+(\w*)(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+(\/)?>/', $Line['text'], $matches))
+ {
+ $element = strtolower($matches[1]);
+
+ if (in_array($element, $this->textLevelElements))
+ {
+ return;
+ }
+
+ $Block = array(
+ 'name' => $matches[1],
+ 'element' => array(
+ 'rawHtml' => $Line['text'],
+ 'autobreak' => true,
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
+ protected function blockMarkupContinue($Line, array $Block)
+ {
+ if (isset($Block['closed']) or isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ $Block['element']['rawHtml'] .= "\n" . $Line['body'];
+
+ return $Block;
+ }
+
+ #
+ # Reference
+
+ protected function blockReference($Line)
+ {
+ if (strpos($Line['text'], ']') !== false
+ and preg_match('/^\[(.+?)\]:[ ]*+(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $Line['text'], $matches)
+ ) {
+ $id = strtolower($matches[1]);
+
+ $Data = array(
+ 'url' => $matches[2],
+ 'title' => isset($matches[3]) ? $matches[3] : null,
+ );
+
+ $this->DefinitionData['Reference'][$id] = $Data;
+
+ $Block = array(
+ 'element' => array(),
+ );
+
+ return $Block;
+ }
+ }
+
+ #
+ # Table
+
+ protected function blockTable($Line, array $Block = null)
+ {
+ if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if (
+ strpos($Block['element']['handler']['argument'], '|') === false
+ and strpos($Line['text'], '|') === false
+ and strpos($Line['text'], ':') === false
+ or strpos($Block['element']['handler']['argument'], "\n") !== false
+ ) {
+ return;
+ }
+
+ if (chop($Line['text'], ' -:|') !== '')
+ {
+ return;
+ }
+
+ $alignments = array();
+
+ $divider = $Line['text'];
+
+ $divider = trim($divider);
+ $divider = trim($divider, '|');
+
+ $dividerCells = explode('|', $divider);
+
+ foreach ($dividerCells as $dividerCell)
+ {
+ $dividerCell = trim($dividerCell);
+
+ if ($dividerCell === '')
+ {
+ return;
+ }
+
+ $alignment = null;
+
+ if ($dividerCell[0] === ':')
+ {
+ $alignment = 'left';
+ }
+
+ if (substr($dividerCell, - 1) === ':')
+ {
+ $alignment = $alignment === 'left' ? 'center' : 'right';
+ }
+
+ $alignments []= $alignment;
+ }
+
+ # ~
+
+ $HeaderElements = array();
+
+ $header = $Block['element']['handler']['argument'];
+
+ $header = trim($header);
+ $header = trim($header, '|');
+
+ $headerCells = explode('|', $header);
+
+ if (count($headerCells) !== count($alignments))
+ {
+ return;
+ }
+
+ foreach ($headerCells as $index => $headerCell)
+ {
+ $headerCell = trim($headerCell);
+
+ $HeaderElement = array(
+ 'name' => 'th',
+ 'handler' => array(
+ 'function' => 'lineElements',
+ 'argument' => $headerCell,
+ 'destination' => 'elements',
+ )
+ );
+
+ if (isset($alignments[$index]))
+ {
+ $alignment = $alignments[$index];
+
+ $HeaderElement['attributes'] = array(
+ 'style' => "text-align: $alignment;",
+ );
+ }
+
+ $HeaderElements []= $HeaderElement;
+ }
+
+ # ~
+
+ $Block = array(
+ 'alignments' => $alignments,
+ 'identified' => true,
+ 'element' => array(
+ 'name' => 'table',
+ 'elements' => array(),
+ ),
+ );
+
+ $Block['element']['elements'] []= array(
+ 'name' => 'thead',
+ );
+
+ $Block['element']['elements'] []= array(
+ 'name' => 'tbody',
+ 'elements' => array(),
+ );
+
+ $Block['element']['elements'][0]['elements'] []= array(
+ 'name' => 'tr',
+ 'elements' => $HeaderElements,
+ );
+
+ return $Block;
+ }
+
+ protected function blockTableContinue($Line, array $Block)
+ {
+ if (isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ if (count($Block['alignments']) === 1 or $Line['text'][0] === '|' or strpos($Line['text'], '|'))
+ {
+ $Elements = array();
+
+ $row = $Line['text'];
+
+ $row = trim($row);
+ $row = trim($row, '|');
+
+ preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches);
+
+ $cells = array_slice($matches[0], 0, count($Block['alignments']));
+
+ foreach ($cells as $index => $cell)
+ {
+ $cell = trim($cell);
+
+ $Element = array(
+ 'name' => 'td',
+ 'handler' => array(
+ 'function' => 'lineElements',
+ 'argument' => $cell,
+ 'destination' => 'elements',
+ )
+ );
+
+ if (isset($Block['alignments'][$index]))
+ {
+ $Element['attributes'] = array(
+ 'style' => 'text-align: ' . $Block['alignments'][$index] . ';',
+ );
+ }
+
+ $Elements []= $Element;
+ }
+
+ $Element = array(
+ 'name' => 'tr',
+ 'elements' => $Elements,
+ );
+
+ $Block['element']['elements'][1]['elements'] []= $Element;
+
+ return $Block;
+ }
+ }
+
+ #
+ # ~
+ #
+
+ protected function paragraph($Line)
+ {
+ return array(
+ 'type' => 'Paragraph',
+ 'element' => array(
+ 'name' => 'p',
+ 'handler' => array(
+ 'function' => 'lineElements',
+ 'argument' => $Line['text'],
+ 'destination' => 'elements',
+ ),
+ ),
+ );
+ }
+
+ protected function paragraphContinue($Line, array $Block)
+ {
+ if (isset($Block['interrupted']))
+ {
+ return;
+ }
+
+ $Block['element']['handler']['argument'] .= "\n".$Line['text'];
+
+ return $Block;
+ }
+
+ #
+ # Inline Elements
+ #
+
+ protected $InlineTypes = array(
+ '!' => array('Image'),
+ '&' => array('SpecialCharacter'),
+ '*' => array('Emphasis'),
+ ':' => array('Url'),
+ '<' => array('UrlTag', 'EmailTag', 'Markup'),
+ '[' => array('Link'),
+ '_' => array('Emphasis'),
+ '`' => array('Code'),
+ '~' => array('Strikethrough'),
+ '\\' => array('EscapeSequence'),
+ );
+
+ # ~
+
+ protected $inlineMarkerList = '!*_&[:<`~\\';
+
+ #
+ # ~
+ #
+
+ public function line($text, $nonNestables = array())
+ {
+ return $this->elements($this->lineElements($text, $nonNestables));
+ }
+
+ protected function lineElements($text, $nonNestables = array())
+ {
+ # standardize line breaks
+ $text = str_replace(array("\r\n", "\r"), "\n", $text);
+
+ $Elements = array();
+
+ $nonNestables = (empty($nonNestables)
+ ? array()
+ : array_combine($nonNestables, $nonNestables)
+ );
+
+ # $excerpt is based on the first occurrence of a marker
+
+ while ($excerpt = strpbrk($text, $this->inlineMarkerList))
+ {
+ $marker = $excerpt[0];
+
+ $markerPosition = strlen($text) - strlen($excerpt);
+
+ $Excerpt = array('text' => $excerpt, 'context' => $text);
+
+ foreach ($this->InlineTypes[$marker] as $inlineType)
+ {
+ # check to see if the current inline type is nestable in the current context
+
+ if (isset($nonNestables[$inlineType]))
+ {
+ continue;
+ }
+
+ $Inline = $this->{"inline$inlineType"}($Excerpt);
+
+ if ( ! isset($Inline))
+ {
+ continue;
+ }
+
+ # makes sure that the inline belongs to "our" marker
+
+ if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
+ {
+ continue;
+ }
+
+ # sets a default inline position
+
+ if ( ! isset($Inline['position']))
+ {
+ $Inline['position'] = $markerPosition;
+ }
+
+ # cause the new element to 'inherit' our non nestables
+
+
+ $Inline['element']['nonNestables'] = isset($Inline['element']['nonNestables'])
+ ? array_merge($Inline['element']['nonNestables'], $nonNestables)
+ : $nonNestables
+ ;
+
+ # the text that comes before the inline
+ $unmarkedText = substr($text, 0, $Inline['position']);
+
+ # compile the unmarked text
+ $InlineText = $this->inlineText($unmarkedText);
+ $Elements[] = $InlineText['element'];
+
+ # compile the inline
+ $Elements[] = $this->extractElement($Inline);
+
+ # remove the examined text
+ $text = substr($text, $Inline['position'] + $Inline['extent']);
+
+ continue 2;
+ }
+
+ # the marker does not belong to an inline
+
+ $unmarkedText = substr($text, 0, $markerPosition + 1);
+
+ $InlineText = $this->inlineText($unmarkedText);
+ $Elements[] = $InlineText['element'];
+
+ $text = substr($text, $markerPosition + 1);
+ }
+
+ $InlineText = $this->inlineText($text);
+ $Elements[] = $InlineText['element'];
+
+ foreach ($Elements as &$Element)
+ {
+ if ( ! isset($Element['autobreak']))
+ {
+ $Element['autobreak'] = false;
+ }
+ }
+
+ return $Elements;
+ }
+
+ #
+ # ~
+ #
+
+ protected function inlineText($text)
+ {
+ $Inline = array(
+ 'extent' => strlen($text),
+ 'element' => array(),
+ );
+
+ $Inline['element']['elements'] = self::pregReplaceElements(
+ $this->breaksEnabled ? '/[ ]*+\n/' : '/(?:[ ]*+\\\\|[ ]{2,}+)\n/',
+ array(
+ array('name' => 'br'),
+ array('text' => "\n"),
+ ),
+ $text
+ );
+
+ return $Inline;
+ }
+
+ protected function inlineCode($Excerpt)
+ {
+ $marker = $Excerpt['text'][0];
+
+ if (preg_match('/^(['.$marker.']++)[ ]*+(.+?)[ ]*+(? strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'code',
+ 'text' => $text,
+ ),
+ );
+ }
+ }
+
+ protected function inlineEmailTag($Excerpt)
+ {
+ $hostnameLabel = '[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?';
+
+ $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@'
+ . $hostnameLabel . '(?:\.' . $hostnameLabel . ')*';
+
+ if (strpos($Excerpt['text'], '>') !== false
+ and preg_match("/^<((mailto:)?$commonMarkEmail)>/i", $Excerpt['text'], $matches)
+ ){
+ $url = $matches[1];
+
+ if ( ! isset($matches[2]))
+ {
+ $url = "mailto:$url";
+ }
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $matches[1],
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ ),
+ );
+ }
+ }
+
+ protected function inlineEmphasis($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]))
+ {
+ return;
+ }
+
+ $marker = $Excerpt['text'][0];
+
+ if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
+ {
+ $emphasis = 'strong';
+ }
+ elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
+ {
+ $emphasis = 'em';
+ }
+ else
+ {
+ return;
+ }
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => $emphasis,
+ 'handler' => array(
+ 'function' => 'lineElements',
+ 'argument' => $matches[1],
+ 'destination' => 'elements',
+ )
+ ),
+ );
+ }
+
+ protected function inlineEscapeSequence($Excerpt)
+ {
+ if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
+ {
+ return array(
+ 'element' => array('rawHtml' => $Excerpt['text'][1]),
+ 'extent' => 2,
+ );
+ }
+ }
+
+ protected function inlineImage($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
+ {
+ return;
+ }
+
+ $Excerpt['text']= substr($Excerpt['text'], 1);
+
+ $Link = $this->inlineLink($Excerpt);
+
+ if ($Link === null)
+ {
+ return;
+ }
+
+ $Inline = array(
+ 'extent' => $Link['extent'] + 1,
+ 'element' => array(
+ 'name' => 'img',
+ 'attributes' => array(
+ 'src' => $Link['element']['attributes']['href'],
+ 'alt' => $Link['element']['handler']['argument'],
+ ),
+ 'autobreak' => true,
+ ),
+ );
+
+ $Inline['element']['attributes'] += $Link['element']['attributes'];
+
+ unset($Inline['element']['attributes']['href']);
+
+ return $Inline;
+ }
+
+ protected function inlineLink($Excerpt)
+ {
+ $Element = array(
+ 'name' => 'a',
+ 'handler' => array(
+ 'function' => 'lineElements',
+ 'argument' => null,
+ 'destination' => 'elements',
+ ),
+ 'nonNestables' => array('Url', 'Link'),
+ 'attributes' => array(
+ 'href' => null,
+ 'title' => null,
+ ),
+ );
+
+ $extent = 0;
+
+ $remainder = $Excerpt['text'];
+
+ if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
+ {
+ $Element['handler']['argument'] = $matches[1];
+
+ $extent += strlen($matches[0]);
+
+ $remainder = substr($remainder, $extent);
+ }
+ else
+ {
+ return;
+ }
+
+ if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches))
+ {
+ $Element['attributes']['href'] = $matches[1];
+
+ if (isset($matches[2]))
+ {
+ $Element['attributes']['title'] = substr($matches[2], 1, - 1);
+ }
+
+ $extent += strlen($matches[0]);
+ }
+ else
+ {
+ if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
+ {
+ $definition = strlen($matches[1]) ? $matches[1] : $Element['handler']['argument'];
+ $definition = strtolower($definition);
+
+ $extent += strlen($matches[0]);
+ }
+ else
+ {
+ $definition = strtolower($Element['handler']['argument']);
+ }
+
+ if ( ! isset($this->DefinitionData['Reference'][$definition]))
+ {
+ return;
+ }
+
+ $Definition = $this->DefinitionData['Reference'][$definition];
+
+ $Element['attributes']['href'] = $Definition['url'];
+ $Element['attributes']['title'] = $Definition['title'];
+ }
+
+ return array(
+ 'extent' => $extent,
+ 'element' => $Element,
+ );
+ }
+
+ protected function inlineMarkup($Excerpt)
+ {
+ if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false)
+ {
+ return;
+ }
+
+ if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'element' => array('rawHtml' => $matches[0]),
+ 'extent' => strlen($matches[0]),
+ );
+ }
+
+ if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'element' => array('rawHtml' => $matches[0]),
+ 'extent' => strlen($matches[0]),
+ );
+ }
+
+ if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*+(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+\/?>/s', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'element' => array('rawHtml' => $matches[0]),
+ 'extent' => strlen($matches[0]),
+ );
+ }
+ }
+
+ protected function inlineSpecialCharacter($Excerpt)
+ {
+ if (substr($Excerpt['text'], 1, 1) !== ' ' and strpos($Excerpt['text'], ';') !== false
+ and preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches)
+ ) {
+ return array(
+ 'element' => array('rawHtml' => '&' . $matches[1] . ';'),
+ 'extent' => strlen($matches[0]),
+ );
+ }
+
+ return;
+ }
+
+ protected function inlineStrikethrough($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]))
+ {
+ return;
+ }
+
+ if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'del',
+ 'handler' => array(
+ 'function' => 'lineElements',
+ 'argument' => $matches[1],
+ 'destination' => 'elements',
+ )
+ ),
+ );
+ }
+ }
+
+ protected function inlineUrl($Excerpt)
+ {
+ if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
+ {
+ return;
+ }
+
+ if (strpos($Excerpt['context'], 'http') !== false
+ and preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)
+ ) {
+ $url = $matches[0][0];
+
+ $Inline = array(
+ 'extent' => strlen($matches[0][0]),
+ 'position' => $matches[0][1],
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $url,
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ ),
+ );
+
+ return $Inline;
+ }
+ }
+
+ protected function inlineUrlTag($Excerpt)
+ {
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w++:\/{2}[^ >]++)>/i', $Excerpt['text'], $matches))
+ {
+ $url = $matches[1];
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $url,
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ ),
+ );
+ }
+ }
+
+ # ~
+
+ protected function unmarkedText($text)
+ {
+ $Inline = $this->inlineText($text);
+ return $this->element($Inline['element']);
+ }
+
+ #
+ # Handlers
+ #
+
+ protected function handle(array $Element)
+ {
+ if (isset($Element['handler']))
+ {
+ if (!isset($Element['nonNestables']))
+ {
+ $Element['nonNestables'] = array();
+ }
+
+ if (is_string($Element['handler']))
+ {
+ $function = $Element['handler'];
+ $argument = $Element['text'];
+ unset($Element['text']);
+ $destination = 'rawHtml';
+ }
+ else
+ {
+ $function = $Element['handler']['function'];
+ $argument = $Element['handler']['argument'];
+ $destination = $Element['handler']['destination'];
+ }
+
+ $Element[$destination] = $this->{$function}($argument, $Element['nonNestables']);
+
+ if ($destination === 'handler')
+ {
+ $Element = $this->handle($Element);
+ }
+
+ unset($Element['handler']);
+ }
+
+ return $Element;
+ }
+
+ protected function handleElementRecursive(array $Element)
+ {
+ return $this->elementApplyRecursive(array($this, 'handle'), $Element);
+ }
+
+ protected function handleElementsRecursive(array $Elements)
+ {
+ return $this->elementsApplyRecursive(array($this, 'handle'), $Elements);
+ }
+
+ protected function elementApplyRecursive($closure, array $Element)
+ {
+ $Element = call_user_func($closure, $Element);
+
+ if (isset($Element['elements']))
+ {
+ $Element['elements'] = $this->elementsApplyRecursive($closure, $Element['elements']);
+ }
+ elseif (isset($Element['element']))
+ {
+ $Element['element'] = $this->elementApplyRecursive($closure, $Element['element']);
+ }
+
+ return $Element;
+ }
+
+ protected function elementApplyRecursiveDepthFirst($closure, array $Element)
+ {
+ if (isset($Element['elements']))
+ {
+ $Element['elements'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['elements']);
+ }
+ elseif (isset($Element['element']))
+ {
+ $Element['element'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['element']);
+ }
+
+ $Element = call_user_func($closure, $Element);
+
+ return $Element;
+ }
+
+ protected function elementsApplyRecursive($closure, array $Elements)
+ {
+ foreach ($Elements as &$Element)
+ {
+ $Element = $this->elementApplyRecursive($closure, $Element);
+ }
+
+ return $Elements;
+ }
+
+ protected function elementsApplyRecursiveDepthFirst($closure, array $Elements)
+ {
+ foreach ($Elements as &$Element)
+ {
+ $Element = $this->elementApplyRecursiveDepthFirst($closure, $Element);
+ }
+
+ return $Elements;
+ }
+
+ protected function element(array $Element)
+ {
+ if ($this->safeMode)
+ {
+ $Element = $this->sanitiseElement($Element);
+ }
+
+ # identity map if element has no handler
+ $Element = $this->handle($Element);
+
+ $hasName = isset($Element['name']);
+
+ $markup = '';
+
+ if ($hasName)
+ {
+ $markup .= '<' . $Element['name'];
+
+ if (isset($Element['attributes']))
+ {
+ foreach ($Element['attributes'] as $name => $value)
+ {
+ if ($value === null)
+ {
+ continue;
+ }
+
+ $markup .= " $name=\"".self::escape($value).'"';
+ }
+ }
+ }
+
+ $permitRawHtml = false;
+
+ if (isset($Element['text']))
+ {
+ $text = $Element['text'];
+ }
+ // very strongly consider an alternative if you're writing an
+ // extension
+ elseif (isset($Element['rawHtml']))
+ {
+ $text = $Element['rawHtml'];
+
+ $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode'];
+ $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode;
+ }
+
+ $hasContent = isset($text) || isset($Element['element']) || isset($Element['elements']);
+
+ if ($hasContent)
+ {
+ $markup .= $hasName ? '>' : '';
+
+ if (isset($Element['elements']))
+ {
+ $markup .= $this->elements($Element['elements']);
+ }
+ elseif (isset($Element['element']))
+ {
+ $markup .= $this->element($Element['element']);
+ }
+ else
+ {
+ if (!$permitRawHtml)
+ {
+ $markup .= self::escape($text, true);
+ }
+ else
+ {
+ $markup .= $text;
+ }
+ }
+
+ $markup .= $hasName ? '' . $Element['name'] . '>' : '';
+ }
+ elseif ($hasName)
+ {
+ $markup .= ' />';
+ }
+
+ return $markup;
+ }
+
+ protected function elements(array $Elements)
+ {
+ $markup = '';
+
+ $autoBreak = true;
+
+ foreach ($Elements as $Element)
+ {
+ if (empty($Element))
+ {
+ continue;
+ }
+
+ $autoBreakNext = (isset($Element['autobreak'])
+ ? $Element['autobreak'] : isset($Element['name'])
+ );
+ // (autobreak === false) covers both sides of an element
+ $autoBreak = !$autoBreak ? $autoBreak : $autoBreakNext;
+
+ $markup .= ($autoBreak ? "\n" : '') . $this->element($Element);
+ $autoBreak = $autoBreakNext;
+ }
+
+ $markup .= $autoBreak ? "\n" : '';
+
+ return $markup;
+ }
+
+ # ~
+
+ protected function li($lines)
+ {
+ $Elements = $this->linesElements($lines);
+
+ if ( ! in_array('', $lines)
+ and isset($Elements[0]) and isset($Elements[0]['name'])
+ and $Elements[0]['name'] === 'p'
+ ) {
+ unset($Elements[0]['name']);
+ }
+
+ return $Elements;
+ }
+
+ #
+ # AST Convenience
+ #
+
+ /**
+ * Replace occurrences $regexp with $Elements in $text. Return an array of
+ * elements representing the replacement.
+ */
+ protected static function pregReplaceElements($regexp, $Elements, $text)
+ {
+ $newElements = array();
+
+ while (preg_match($regexp, $text, $matches, PREG_OFFSET_CAPTURE))
+ {
+ $offset = $matches[0][1];
+ $before = substr($text, 0, $offset);
+ $after = substr($text, $offset + strlen($matches[0][0]));
+
+ $newElements[] = array('text' => $before);
+
+ foreach ($Elements as $Element)
+ {
+ $newElements[] = $Element;
+ }
+
+ $text = $after;
+ }
+
+ $newElements[] = array('text' => $text);
+
+ return $newElements;
+ }
+
+ #
+ # Deprecated Methods
+ #
+
+ function parse($text)
+ {
+ $markup = $this->text($text);
+
+ return $markup;
+ }
+
+ protected function sanitiseElement(array $Element)
+ {
+ static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
+ static $safeUrlNameToAtt = array(
+ 'a' => 'href',
+ 'img' => 'src',
+ );
+
+ if ( ! isset($Element['name']))
+ {
+ unset($Element['attributes']);
+ return $Element;
+ }
+
+ if (isset($safeUrlNameToAtt[$Element['name']]))
+ {
+ $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
+ }
+
+ if ( ! empty($Element['attributes']))
+ {
+ foreach ($Element['attributes'] as $att => $val)
+ {
+ # filter out badly parsed attribute
+ if ( ! preg_match($goodAttribute, $att))
+ {
+ unset($Element['attributes'][$att]);
+ }
+ # dump onevent attribute
+ elseif (self::striAtStart($att, 'on'))
+ {
+ unset($Element['attributes'][$att]);
+ }
+ }
+ }
+
+ return $Element;
+ }
+
+ protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
+ {
+ foreach ($this->safeLinksWhitelist as $scheme)
+ {
+ if (self::striAtStart($Element['attributes'][$attribute], $scheme))
+ {
+ return $Element;
+ }
+ }
+
+ $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]);
+
+ return $Element;
+ }
+
+ #
+ # Static Methods
+ #
+
+ protected static function escape($text, $allowQuotes = false)
+ {
+ return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8');
+ }
+
+ protected static function striAtStart($string, $needle)
+ {
+ $len = strlen($needle);
+
+ if ($len > strlen($string))
+ {
+ return false;
+ }
+ else
+ {
+ return strtolower(substr($string, 0, $len)) === strtolower($needle);
+ }
+ }
+
+ static function instance($name = 'default')
+ {
+ if (isset(self::$instances[$name]))
+ {
+ return self::$instances[$name];
+ }
+
+ $instance = new static();
+
+ self::$instances[$name] = $instance;
+
+ return $instance;
+ }
+
+ private static $instances = array();
+
+ #
+ # Fields
+ #
+
+ protected $DefinitionData;
+
+ #
+ # Read-Only
+
+ protected $specialCharacters = array(
+ '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', '~'
+ );
+
+ protected $StrongRegex = array(
+ '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*+[*])+?)[*]{2}(?![*])/s',
+ '_' => '/^__((?:\\\\_|[^_]|_[^_]*+_)+?)__(?!_)/us',
+ );
+
+ protected $EmRegex = array(
+ '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
+ '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
+ );
+
+ protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*+(?:\s*+=\s*+(?:[^"\'=<>`\s]+|"[^"]*+"|\'[^\']*+\'))?+';
+
+ protected $voidElements = array(
+ 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
+ );
+
+ protected $textLevelElements = array(
+ 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
+ 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
+ 'i', 'rp', 'del', 'code', 'strike', 'marquee',
+ 'q', 'rt', 'ins', 'font', 'strong',
+ 's', 'tt', 'kbd', 'mark',
+ 'u', 'xm', 'sub', 'nobr',
+ 'sup', 'ruby',
+ 'var', 'span',
+ 'wbr', 'time',
+ );
+}
diff --git a/system/controllers/home.php b/system/controllers/home.php
index f7b8983d..87641d26 100644
--- a/system/controllers/home.php
+++ b/system/controllers/home.php
@@ -210,7 +210,7 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
}
}
-if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
+if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && $_c['hs_auth_method'] != 'hchap')) {
$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();
@@ -232,6 +232,60 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
}
}
+if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSION['nux-hostname']) && $_c['hs_auth_method'] == 'hchap')) {
+ $apkurl = (((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'onoff')|| $_SERVER['SERVER_PORT'] == 443)?'https':'http').'://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+ $ui->assign('nux_mac', $_SESSION['nux-mac']);
+ $ui->assign('nux_ip', $_SESSION['nux-ip']);
+ $keys = explode('-', $_SESSION['nux-key']);
+ $ui->assign('hostname', $_SESSION['nux-hostname']);
+ $ui->assign('apkurl', $apkurl);
+ $ui->assign('key1', $keys[0]);
+ $ui->assign('key2', $keys[1]);
+ $ui->assign('hchap', $_GET['hchap']);
+ $ui->assign('logged', $_GET['logged']);
+ if ($_app_stage != 'demo') {
+ if ($_GET['mikrotik'] == 'login') {
+ r2(U . 'home&hchap=true', 's', Lang::T('Login Request successfully'));
+ }
+ $getmsg = $_GET['msg'];
+ ///get auth notification from mikrotik
+ if($getmsg == 'Connected') {
+ $msg .= Lang::T($getmsg);
+ r2(U . 'home&logged=1', 's', $msg);
+ } else if($getmsg){
+ $msg .= Lang::T($getmsg);
+ r2(U . 'home', 's', $msg);
+ }
+ }
+ }
+
+if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSION['nux-hostname']) && $_c['hs_auth_method'] == 'hchap')) {
+ $apkurl = (((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'onoff')|| $_SERVER['SERVER_PORT'] == 443)?'https':'http').'://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+ $ui->assign('nux_mac', $_SESSION['nux-mac']);
+ $ui->assign('nux_ip', $_SESSION['nux-ip']);
+ $keys = explode('-', $_SESSION['nux-key']);
+ $ui->assign('hostname', $_SESSION['nux-hostname']);
+ $ui->assign('apkurl', $apkurl);
+ $ui->assign('key1', $keys[0]);
+ $ui->assign('key2', $keys[1]);
+ $ui->assign('hchap', $_GET['hchap']);
+ $ui->assign('logged', $_GET['logged']);
+ if ($_app_stage != 'demo') {
+ if ($_GET['mikrotik'] == 'login') {
+ r2(U . 'home&hchap=true', 's', Lang::T('Login Request successfully'));
+ }
+ $getmsg = $_GET['msg'];
+ ///get auth notification from mikrotik
+ if($getmsg == 'Connected') {
+ $msg .= Lang::T($getmsg);
+ r2(U . 'home&logged=1', 's', $msg);
+ } else if($getmsg){
+ $msg .= Lang::T($getmsg);
+ r2(U . 'home', 's', $msg);
+ }
+ }
+ }
+
$ui->assign('unpaid', ORM::for_table('tbl_payment_gateway')
->where('username', $user['username'])
->where('status', 1)
diff --git a/system/controllers/settings.php b/system/controllers/settings.php
index 4096a8d7..70456892 100644
--- a/system/controllers/settings.php
+++ b/system/controllers/settings.php
@@ -12,6 +12,19 @@ $action = $routes['1'];
$ui->assign('_admin', $admin);
switch ($action) {
+ case 'docs':
+ $d = ORM::for_table('tbl_appconfig')->where('setting', 'docs_clicked')->find_one();
+ if ($d) {
+ $d->value = 'yes';
+ $d->save();
+ } else {
+ $d = ORM::for_table('tbl_appconfig')->create();
+ $d->setting = 'docs_clicked';
+ $d->value = 'yes';
+ $d->save();
+ }
+ r2('./docs');
+ break;
case 'devices':
$files = scandir($DEVICE_PATH);
$devices = [];
@@ -111,9 +124,12 @@ switch ($action) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$company = _post('CompanyName');
+ $custom_tax_rate = filter_var(_post('custom_tax_rate'), FILTER_SANITIZE_SPECIAL_CHARS);
+ if (preg_match('/[^0-9.]/', $custom_tax_rate)) {
+ r2(U . 'settings/app', 'e', 'Special characters are not allowed in tax rate');
+ die();
+ }
run_hook('save_settings'); #HOOK
-
-
if (!empty($_FILES['logo']['name'])) {
if (function_exists('imagecreatetruecolor')) {
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) unlink($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png');
@@ -141,6 +157,9 @@ switch ($action) {
}
// Save all settings including tax system
foreach ($_POST as $key => $value) {
+ $key = filter_var($key, FILTER_SANITIZE_SPECIAL_CHARS);
+ $value = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
+
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
if ($d) {
$d->value = $value;
diff --git a/system/cron_reminder.php b/system/cron_reminder.php
index 85b1a076..437f77f5 100644
--- a/system/cron_reminder.php
+++ b/system/cron_reminder.php
@@ -15,7 +15,7 @@ if (php_sapi_name() !== 'cli') {
echo "";
}
-$d = ORM::for_table('tbl_user_recharges')->where('status', 'on')->find_many();
+$d = ORM::for_table('tbl_user_recharges')->where('status', 'on')->whereNotEqual('customer_id', '0')->find_many();
run_hook('cronjob_reminder'); #HOOK
diff --git a/system/devices/Dummy.php b/system/devices/Dummy.php
index e2c76b88..e4db1519 100644
--- a/system/devices/Dummy.php
+++ b/system/devices/Dummy.php
@@ -11,7 +11,7 @@ class Dummy {
'author' => 'ibnu maksum',
'url' => [
'Github' => 'https://github.com/hotspotbilling/phpnuxbill/',
- 'Telegram' => 'https://t.me/ibnux',
+ 'Telegram' => 'https://t.me/phpnuxbill',
'Donate' => 'https://paypal.me/ibnux'
]
];
diff --git a/system/devices/MikrotikHotspot.php b/system/devices/MikrotikHotspot.php
index 8ebb79b4..b4b44b35 100644
--- a/system/devices/MikrotikHotspot.php
+++ b/system/devices/MikrotikHotspot.php
@@ -22,7 +22,7 @@ class MikrotikHotspot
'author' => 'ibnux',
'url' => [
'Github' => 'https://github.com/hotspotbilling/phpnuxbill/',
- 'Telegram' => 'https://t.me/ibnux',
+ 'Telegram' => 'https://t.me/phpnuxbill',
'Donate' => 'https://paypal.me/ibnux'
]
];
diff --git a/system/devices/MikrotikPppoe.php b/system/devices/MikrotikPppoe.php
index 6c311b3d..9424a5c2 100644
--- a/system/devices/MikrotikPppoe.php
+++ b/system/devices/MikrotikPppoe.php
@@ -21,7 +21,7 @@ class MikrotikPppoe
'author' => 'ibnux',
'url' => [
'Github' => 'https://github.com/hotspotbilling/phpnuxbill/',
- 'Telegram' => 'https://t.me/ibnux',
+ 'Telegram' => 'https://t.me/phpnuxbill',
'Donate' => 'https://paypal.me/ibnux'
]
];
@@ -31,9 +31,23 @@ class MikrotikPppoe
{
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
- $this->removePpoeUser($client, $customer['username']);
- $this->removePpoeActive($client, $customer['username']);
- $this->addPpoeUser($client, $plan, $customer);
+ //check if customer exists
+ $printRequest = new RouterOS\Request('/ppp/secret/print');
+ $printRequest->setQuery(RouterOS\Query::where('name', $customer['username']));
+ $cid = $client->sendSync($printRequest)->getProperty('.id');
+ if (empty($cid)) {
+ //customer not exists, add it
+ $this->addPpoeUser($client, $plan, $customer);
+ }else{
+ $setRequest = new RouterOS\Request('/ppp/secret/set');
+ $setRequest->setArgument('numbers', $cid);
+ $setRequest->setArgument('profile', $plan['name_plan']);
+ $setRequest->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email']);
+ $setRequest->setArgument('password', $customer['password']);
+ $client->sendSync($setRequest);
+ //disconnect then
+ $this->removePpoeActive($client, $customer['username']);
+ }
}
function remove_customer($customer, $plan)
@@ -212,15 +226,6 @@ class MikrotikPppoe
return $client->sendSync($printRequest)->getProperty('.id');
}
-
- function connect_customer($customer, $ip, $mac_address, $router_name)
- {
- }
-
- function disconnect_customer($customer, $router_name)
- {
- }
-
function info($name)
{
return ORM::for_table('tbl_routers')->where('name', $name)->find_one();
diff --git a/system/devices/Radius.php b/system/devices/Radius.php
index af09b6fa..60e4d080 100644
--- a/system/devices/Radius.php
+++ b/system/devices/Radius.php
@@ -20,7 +20,7 @@ class Radius
'author' => 'ibnux',
'url' => [
'Github' => 'https://github.com/hotspotbilling/phpnuxbill/',
- 'Telegram' => 'https://t.me/ibnux',
+ 'Telegram' => 'https://t.me/phpnuxbill',
'Donate' => 'https://paypal.me/ibnux'
]
];
diff --git a/system/devices/RadiusRest.php b/system/devices/RadiusRest.php
new file mode 100644
index 00000000..0dc34eae
--- /dev/null
+++ b/system/devices/RadiusRest.php
@@ -0,0 +1,60 @@
+ 'Radius Rest API',
+ 'description' => 'This devices will handle Radius Connection using Rest API',
+ 'author' => 'ibnu maksum',
+ 'url' => [
+ 'Wiki Tutorial' => 'https://github.com/hotspotbilling/phpnuxbill/wiki/FreeRadius-Rest',
+ 'Telegram' => 'https://t.me/phpnuxbill',
+ 'Donate' => 'https://paypal.me/ibnux'
+ ]
+ ];
+ }
+
+ // Add Customer to Mikrotik/Device
+ function add_customer($customer, $plan)
+ {
+ }
+
+ // Remove Customer to Mikrotik/Device
+ function remove_customer($customer, $plan)
+ {
+ }
+
+ // Add Plan to Mikrotik/Device
+ function add_plan($plan)
+ {
+ }
+
+ // Update Plan to Mikrotik/Device
+ function update_plan($old_name, $plan)
+ {
+ }
+
+ // Remove Plan from Mikrotik/Device
+ function remove_plan($plan)
+ {
+ }
+
+ // check if customer is online
+ function online_customer($customer, $router_name)
+ {
+ }
+
+ // make customer online
+ function connect_customer($customer, $ip, $mac_address, $router_name)
+ {
+ }
+
+ // make customer disconnect
+ function disconnect_customer($customer, $router_name)
+ {
+ }
+
+}
\ No newline at end of file
diff --git a/system/lan/english.json b/system/lan/english.json
index 0a759219..7859fa03 100644
--- a/system/lan/english.json
+++ b/system/lan/english.json
@@ -1,219 +1,655 @@
{
- "Recharge_Account": "Recharge Account",
- "Refill_Account": "Refill Account",
- "SuperAdmin": "SuperAdmin",
- "Change_Password": "Change Password",
- "My_Account": "My Account",
- "Logout": "Logout",
- "Dashboard": "Dashboard",
- "Customer": "Customer",
- "Lists": "Lists",
- "Location": "Location",
- "Services": "Services",
- "Active_Users": "Active Users",
- "Vouchers": "Vouchers",
- "Refill_Customer": "Refill Customer",
- "Recharge_Customer": "Recharge Customer",
- "Refill_Balance": "Refill Balance",
- "Internet_Plan": "Internet Plan",
- "Bandwidth": "Bandwidth",
- "Customer_Balance": "Customer Balance",
- "Reports": "Reports",
- "Daily_Reports": "Daily Reports",
- "Period_Reports": "Period Reports",
- "Activation_History": "Activation History",
- "Send_Message": "Send Message",
- "Single_Customer": "Single Customer",
- "Bulk_Customers": "Bulk Customers",
- "Network": "Network",
- "Routers": "Routers",
- "IP_Pool": "IP Pool",
- "Radius": "Radius",
- "Radius_NAS": "Radius NAS",
- "Static_Pages": "Static Pages",
- "Order_Voucher": "Order Voucher",
- "Voucher": "Voucher",
+ "Log_in": "Login",
+ "Register": "Register",
"Announcement": "Announcement",
- "Customer_Announcement": "Customer Announcement",
"Registration_Info": "Registration Info",
- "Privacy_Policy": "Privacy Policy",
- "Terms_and_Conditions": "Terms and Conditions",
- "Settings": "Settings",
- "General_Settings": "General Settings",
- "Localisation": "Localisation",
- "Maintenance_Mode": "Maintenance Mode",
- "User_Notification": "User Notification",
- "Administrator_Users": "Administrator Users",
- "Backup_Restore": "Backup\/Restore",
- "Payment_Gateway": "Payment Gateway",
- "Plugin_Manager": "Plugin Manager",
- "Logs": "Logs",
- "Community": "Community",
- "Select_Account": "Select Account",
- "Select_a_customer": "Select a customer",
- "Code_Voucher": "Code Voucher",
- "Enter_voucher_code_here": "Enter voucher code here",
- "Recharge": "Recharge",
- "Cancel": "Cancel",
- "Hotspot_Plans": "Hotspot Plans",
- "Search_by_Name": "Search by Name",
- "Search": "Search",
- "New_Service_Plan": "New Service Plan",
- "Expired": "Expired",
- "Name": "Name",
- "Type": "Type",
- "Category": "Category",
- "Price": "Price",
- "Validity": "Validity",
- "Time": "Time",
- "Data": "Data",
- "Device": "Device",
- "Date": "Date",
- "ID": "ID",
+ "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",
- "Prev": "Prev",
- "Next": "Next",
+ "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, chmod 664 pages\/*.html",
+ "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",
- "Plan_Type": "Plan Type",
- "Bandwidth_Plans": "Bandwidth Plans",
- "Plan_Price": "Plan Price",
- "Plan_Validity": "Plan Validity",
- "Status": "Status",
- "Business": "Business",
- "Personal": "Personal",
- "Hap_Lite": "Hap Lite",
- "": "",
+ "New_Service_Plan": "New Service Plan",
"Add_Service_Plan": "Add Service Plan",
- "Cannot_be_change_after_saved": "Cannot be change after saved",
+ "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",
- "Limit_Type": "Limit Type",
"Time_Limit": "Time Limit",
"Data_Limit": "Data Limit",
"Both_Limit": "Both Limit",
- "Hrs": "Hrs",
- "Mins": "Mins",
- "Bandwidth_Name": "Bandwidth Name",
+ "Plan_Validity": "Plan Validity",
"Select_Bandwidth": "Select Bandwidth",
"Shared_Users": "Shared Users",
- "1_Period___1_Month__Expires_the_20th_of_each_month": "1 Period = 1 Month, Expires the 20th of each month",
- "Expired_Date": "Expired Date",
- "Router_Name": "Router Name",
- "Select_Routers": "Select Routers",
- "Save_Changes": "Save Changes",
- "Days": "Days",
- "Months": "Months",
- "Period": "Period",
- "New_Bandwidth": "New Bandwidth",
- "Rate": "Rate",
- "Burst": "Burst",
- "Balance_Plans": "Balance Plans",
- "New_Router": "New Router",
- "IP_Address": "IP Address",
- "Username": "Username",
- "Description": "Description",
- "Edit_Service_Plan": "Edit Service Plan",
- "Expired_Action": "Expired Action",
- "Optional": "Optional",
- "Expired_Internet_Plan": "Expired Internet Plan",
- "When_Expired__customer_will_be_move_to_selected_internet_plan": "When Expired, customer will be move to selected internet plan",
- "Data_Updated_Successfully": "Data Updated Successfully",
- "Plugin_Installer": "Plugin Installer",
- "Plugin": "Plugin",
- "Search_by_Username": "Search by Username",
+ "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",
- "Extend": "Extend",
+ "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",
- "Generated_By": "Generated By",
- "View": "View",
- "Buy_Balance": "Buy Balance",
- "Buy_Package": "Buy Package",
- "Order_History": "Order History",
- "List_Activated_Voucher": "List Activated Voucher",
- "Invoice": "Invoice",
- "Select_Plans": "Select Plans",
+ "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",
- "Company_Logo": "Company Logo",
+ "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",
- "Address": "Address",
- "You_can_use_html_tag": "You can use html tag",
- "Phone_Number": "Phone Number",
- "Invoice_Footer": "Invoice Footer",
- "Recharge_Using": "Recharge Using",
- "Cash": "Cash",
- "Bank_Transfer": "Bank Transfer",
- "Income_reset_date": "Income reset date",
- "Monthly_Registered_Customers": "Monthly Registered Customers",
- "Total_Monthly_Sales": "Total Monthly Sales",
- "All_Users_Insights": "All Users Insights",
- "Activity_Log": "Activity Log",
- "User_Expired__Today": "User Expired, Today",
+ "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",
+ "Failed_to_create_transaction__": "Failed to create transaction. ",
+ "Failed_to_check_status_transaction__": "Failed to check status transaction. ",
"Disable_Voucher": "Disable Voucher",
- "Voucher_activation_menu_will_be_hidden": "Voucher activation menu will be hidden",
- "Voucher_Format": "Voucher Format",
- "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",
- "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",
- "Extend_Postpaid_Expiration": "Extend Postpaid Expiration",
- "Allow_Extend": "Allow Extend",
- "Extend_Days": "Extend Days",
- "Confirmation_Message": "Confirmation Message",
+ "Balance": "Balance",
"Balance_System": "Balance System",
"Enable_System": "Enable System",
- "Customer_can_deposit_money_to_buy_voucher": "Customer can deposit money to buy voucher",
"Allow_Transfer": "Allow Transfer",
- "Allow_balance_transfer_between_customers": "Allow balance transfer between customers",
- "Minimum_Balance_Transfer": "Minimum Balance Transfer",
"Telegram_Notification": "Telegram Notification",
"SMS_OTP_Registration": "SMS OTP Registration",
"Whatsapp_Notification": "Whatsapp Notification",
- "Email_Notification": "Email Notification",
- "Expired_Notification": "Expired Notification",
- "User_will_get_notification_when_package_expired": "User will get notification when package expired",
- "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",
- "Reminder_Notification": "Reminder Notification",
"Tawk_to_Chat_Widget": "Tawk.to Chat Widget",
- "This_Token_will_act_as_SuperAdmin_Admin": "This Token will act as SuperAdmin\/Admin",
+ "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",
+ "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",
- "Yes": "Yes",
"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%)",
- "Timezone": "Timezone",
- "Date_Format": "Date Format",
- "Default_Language": "Default Language",
- "Language_Editor": "Language Editor",
- "Decimal_Point": "Decimal Point",
- "Thousands_Separator": "Thousands Separator",
- "Currency_Code": "Currency Code",
- "Keep_it_blank_if_you_do_not_want_to_show_currency_code": "Keep it blank if you do not want to show currency code",
- "Country_Code_Phone": "Country Code Phone",
- "Change_title_in_user_Plan_order": "Change title in user Plan order"
+ "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",
+ "Local_IP": "Local IP",
+ "Device": "Device",
+ "Expired_Internet_Plan": "Expired Internet Plan",
+ "When_Expired__customer_will_be_move_to_selected_internet_plan": "When Expired, customer will be move to selected internet plan",
+ "Plugin_Installer": "Plugin Installer",
+ "Expired_Date": "Expired Date",
+ "Expired": "Expired",
+ "Time": "Time",
+ "Data": "Data",
+ "Category": "Category",
+ "later": "later",
+ "Package_Details": "Package Details",
+ "Summary": "Summary",
+ "Devices_Not_Found": "Devices Not Found",
+ "Income_reset_date": "Income reset date",
+ "Devices": "Devices",
+ "Documentation": "Documentation",
+ "Hotspot_Auth_Method": "Hotspot Auth Method",
+ "Api": "Api",
+ "Http_Chap": "Http-Chap",
+ "Hotspot_Authentication_Method__Make_sure_you_have_changed_your_hotspot_login_page_": "Hotspot Authentication Method. Make sure you have changed your hotspot login page."
}
\ No newline at end of file
diff --git a/system/lan/indonesia.json b/system/lan/indonesia.json
index 61a5fa43..962c844a 100644
--- a/system/lan/indonesia.json
+++ b/system/lan/indonesia.json
@@ -561,5 +561,11 @@
"Please_wait_1015_seconds_before_sending_another_SMS": "Harap tunggu 1015 detik sebelum mengirim SMS lainnya",
"Phone_number_updated_successfully": "Nomor telepon berhasil diperbarui",
"You_cannot_use_your_current_phone_number": "Anda tidak dapat menggunakan nomor telepon Anda saat ini",
- "Devices": "Perangkat"
+ "Devices": "Perangkat",
+ "Voucher_Prefix": "Awalan Voucher",
+ "This_account_status": "Status akun ini",
+ "Hotspot_Auth_Method": "Metode Otentikasi Hotspot",
+ "Api": "Api",
+ "Http_Chap": "Http-Bab",
+ "Hotspot_Authentication_Method__Make_sure_you_have_changed_your_hotspot_login_page_": "Metode Otentikasi Hotspot. Pastikan Anda telah mengubah halaman login hotspot Anda."
}
\ No newline at end of file
diff --git a/system/updates.json b/system/updates.json
index a0aadee5..0ed14652 100644
--- a/system/updates.json
+++ b/system/updates.json
@@ -120,5 +120,10 @@
"2024.6.21" : [
"ALTER TABLE `tbl_plans` ADD `on_login` TEXT NULL DEFAULT NULL AFTER `device`;",
"ALTER TABLE `tbl_plans` ADD `on_logout` TEXT NULL DEFAULT NULL AFTER `on_login`;"
+ ],
+ "2024.7.6" : [
+ "CREATE TABLE IF NOT EXISTS `rad_acct` ( `id` bigint NOT NULL, `acctsessionid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `realm` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasid` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
+ "ALTER TABLE `rad_acct` ADD PRIMARY KEY (`id`), ADD KEY `username` (`username`), ADD KEY `framedipaddress` (`framedipaddress`), ADD KEY `acctsessionid` (`acctsessionid`), ADD KEY `nasipaddress` (`nasipaddress`);",
+ "ALTER TABLE `rad_acct` MODIFY `id` bigint NOT NULL AUTO_INCREMENT;"
]
}
\ No newline at end of file
diff --git a/ui/ui/app-settings.tpl b/ui/ui/app-settings.tpl
index c3d215a1..11486ca7 100644
--- a/ui/ui/app-settings.tpl
+++ b/ui/ui/app-settings.tpl
@@ -621,6 +621,22 @@
{Lang::T('If user buy same internet plan, expiry date will extend')}
+
diff --git a/ui/ui/hotspot-edit.tpl b/ui/ui/hotspot-edit.tpl
index d5332f1d..75de4635 100644
--- a/ui/ui/hotspot-edit.tpl
+++ b/ui/ui/hotspot-edit.tpl
@@ -248,20 +248,24 @@
-
-
-
on-login / on-up
-
-
+ {if !$d['is_radius']}
+
+
+
on-login / on-up
+
+
+
+
+
+
on-logout / on-down
+
+
+
-
-
on-logout / on-down
-
-
-
-
-
+ {/if}