From 106a93f89ac92a7715ab81bca23dad7f7d160704 Mon Sep 17 00:00:00 2001 From: nestict Date: Sat, 24 May 2025 10:55:05 +0200 Subject: [PATCH 1/2] Upload files to "system/autoload" Signed-off-by: nestict --- system/autoload/Message.php | 212 +++++++++ system/autoload/Meta.php | 118 +++++ system/autoload/Mikrotik.php | 562 +++++++++++++++++++++++ system/autoload/Package.php | 817 ++++++++++++++++++++++++++++++++++ system/autoload/Paginator.php | 366 +++++++++++++++ 5 files changed, 2075 insertions(+) create mode 100644 system/autoload/Message.php create mode 100644 system/autoload/Meta.php create mode 100644 system/autoload/Mikrotik.php create mode 100644 system/autoload/Package.php create mode 100644 system/autoload/Paginator.php diff --git a/system/autoload/Message.php b/system/autoload/Message.php new file mode 100644 index 0000000..4f72dcb --- /dev/null +++ b/system/autoload/Message.php @@ -0,0 +1,212 @@ + 4 && substr($config['sms_url'], 0, 4) != "http") { + if (strlen($txt) > 160) { + $txts = str_split($txt, 160); + try { + $mikrotik = Mikrotik::info($config['sms_url']); + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + foreach ($txts as $txt) { + Mikrotik::sendSMS($client, $phone, $txt); + } + } catch (Exception $e) { + // ignore, add to logs + _log("Failed to send SMS using Mikrotik.\n" . $e->getMessage(), 'SMS', 0); + } + } else { + try { + $mikrotik = Mikrotik::info($config['sms_url']); + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::sendSMS($client, $phone, $txt); + } catch (Exception $e) { + // ignore, add to logs + _log("Failed to send SMS using Mikrotik.\n" . $e->getMessage(), 'SMS', 0); + } + } + } else { + $smsurl = str_replace('[number]', urlencode($phone), $config['sms_url']); + $smsurl = str_replace('[text]', urlencode($txt), $smsurl); + return Http::getData($smsurl); + } + } + } + + public static function sendWhatsapp($phone, $txt) + { + global $config; + if(empty($txt)){ + return ""; + } + run_hook('send_whatsapp'); #HOOK + if (!empty($config['wa_url'])) { + $waurl = str_replace('[number]', urlencode(Lang::phoneFormat($phone)), $config['wa_url']); + $waurl = str_replace('[text]', urlencode($txt), $waurl); + return Http::getData($waurl); + } + } + + public static function sendEmail($to, $subject, $body) + { + global $config; + if(empty($body)){ + return ""; + } + run_hook('send_email'); #HOOK + if (empty($config['smtp_host'])) { + $attr = ""; + if (!empty($config['mail_from'])) { + $attr .= "From: " . $config['mail_from'] . "\r\n"; + } + if (!empty($config['mail_reply_to'])) { + $attr .= "Reply-To: " . $config['mail_reply_to'] . "\r\n"; + } + mail($to, $subject, $body, $attr); + } else { + $mail = new PHPMailer(); + $mail->isSMTP(); + $mail->SMTPDebug = SMTP::DEBUG_SERVER; + $mail->Host = $config['smtp_host']; + $mail->SMTPAuth = true; + $mail->Username = $config['smtp_user']; + $mail->Password = $config['smtp_pass']; + $mail->SMTPSecure = $config['smtp_ssltls']; + $mail->Port = $config['smtp_port']; + if (!empty($config['mail_from'])) { + $mail->setFrom($config['mail_from']); + } + if (!empty($config['mail_reply_to'])) { + $mail->addReplyTo($config['mail_reply_to']); + } + $mail->isHTML(false); + $mail->addAddress($to); + $mail->Subject = $subject; + $mail->Body = $body; + $mail->send(); + } + } + + public static function sendPackageNotification($customer, $package, $price, $message, $via) + { + global $ds; + if(empty($message)){ + return ""; + } + $msg = str_replace('[[name]]', $customer['fullname'], $message); + $msg = str_replace('[[username]]', $customer['username'], $msg); + $msg = str_replace('[[plan]]', $package, $msg); + $msg = str_replace('[[package]]', $package, $msg); + $msg = str_replace('[[price]]', Lang::moneyFormat($price), $msg); + list($bills, $add_cost) = User::getBills($customer['id']); + if($add_cost>0){ + $note = ""; + foreach ($bills as $k => $v) { + $note .= $k . " : " . Lang::moneyFormat($v) . "\n"; + } + $note .= "Total : " . Lang::moneyFormat($add_cost+$price) . "\n"; + $msg = str_replace('[[bills]]', $note, $msg); + }else{ + $msg = str_replace('[[bills]]', '', $msg); + } + if ($ds) { + $msg = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($ds['expiration'], $ds['time']), $msg); + }else{ + $msg = str_replace('[[expired_date]]', "", $msg); + } + if ( + !empty($customer['phonenumber']) && strlen($customer['phonenumber']) > 5 + && !empty($message) && in_array($via, ['sms', 'wa']) + ) { + if ($via == 'sms') { + echo Message::sendSMS($customer['phonenumber'], $msg); + } else if ($via == 'wa') { + echo Message::sendWhatsapp($customer['phonenumber'], $msg); + } + } + return "$via: $msg"; + } + + public static function sendBalanceNotification($phone, $name, $balance, $balance_now, $message, $via) + { + $msg = str_replace('[[name]]', $name, $message); + $msg = str_replace('[[current_balance]]', Lang::moneyFormat($balance_now), $msg); + $msg = str_replace('[[balance]]', Lang::moneyFormat($balance), $msg); + if ( + !empty($phone) && strlen($phone) > 5 + && !empty($message) && in_array($via, ['sms', 'wa']) + ) { + if ($via == 'sms') { + Message::sendSMS($phone, $msg); + } else if ($via == 'wa') { + Message::sendWhatsapp($phone, $msg); + } + } + return "$via: $msg"; + } + + public static function sendInvoice($cust, $trx) + { + global $config; + $textInvoice = Lang::getNotifText('invoice_paid'); + $textInvoice = str_replace('[[company_name]]', $config['CompanyName'], $textInvoice); + $textInvoice = str_replace('[[address]]', $config['address'], $textInvoice); + $textInvoice = str_replace('[[phone]]', $config['phone'], $textInvoice); + $textInvoice = str_replace('[[invoice]]', $trx['invoice'], $textInvoice); + $textInvoice = str_replace('[[date]]', Lang::dateAndTimeFormat($trx['recharged_on'], $trx['recharged_time']), $textInvoice); + if (!empty($trx['note'])) { + $textInvoice = str_replace('[[note]]', $trx['note'], $textInvoice); + } + $gc = explode("-", $trx['method']); + $textInvoice = str_replace('[[payment_gateway]]', trim($gc[0]), $textInvoice); + $textInvoice = str_replace('[[payment_channel]]', trim($gc[1]), $textInvoice); + $textInvoice = str_replace('[[type]]', $trx['type'], $textInvoice); + $textInvoice = str_replace('[[plan_name]]', $trx['plan_name'], $textInvoice); + $textInvoice = str_replace('[[plan_price]]', Lang::moneyFormat($trx['price']), $textInvoice); + $textInvoice = str_replace('[[name]]', $cust['fullname'], $textInvoice); + $textInvoice = str_replace('[[note]]', $cust['note'], $textInvoice); + $textInvoice = str_replace('[[user_name]]', $trx['username'], $textInvoice); + $textInvoice = str_replace('[[user_password]]', $cust['password'], $textInvoice); + $textInvoice = str_replace('[[username]]', $trx['username'], $textInvoice); + $textInvoice = str_replace('[[password]]', $cust['password'], $textInvoice); + $textInvoice = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($trx['expiration'], $trx['time']), $textInvoice); + $textInvoice = str_replace('[[footer]]', $config['note'], $textInvoice); + + if ($config['user_notification_payment'] == 'sms') { + Message::sendSMS($cust['phonenumber'], $textInvoice); + } else if ($config['user_notification_payment'] == 'wa') { + Message::sendWhatsapp($cust['phonenumber'], $textInvoice); + } + } +} diff --git a/system/autoload/Meta.php b/system/autoload/Meta.php new file mode 100644 index 0000000..5846529 --- /dev/null +++ b/system/autoload/Meta.php @@ -0,0 +1,118 @@ +set(1, 'point', '24'); + * it means tbl_plans with id 1 have point value 24, customer will get 24 point for loyalty if buy plan with id 1 + * You need to create the logic for that, Meta only hold the data + * + * Example to get data + * $point = Meta::for("tbl_plans")->get(1, 'point'); + * this will return the point value of plan with id 1 + * + * to get all key value + * $metas = Meta::for("tbl_plans")->getAll(1); + * + * to delete 1 data + * Meta::for("tbl_plans")->delete(1, 'point'); + * + * to delete all data + * Meta::for("tbl_plans")->deleteAll(1); + **/ + + +class Meta +{ + protected $table = ''; + + protected function __construct($table) + { + $this->table = $table; + } + + public static function for($table) + { + return new self($table); + } + + public function get($id, $key) + { + // get the Value + return ORM::for_table('tbl_meta') + ->select('value') + ->where('tbl', $this->table) + ->where('tbl_id', $id) + ->where('name', $key) + ->find_one()['value']; + } + + public function getAll($id) + { + //get all key Value + $metas = []; + $result = ORM::for_table('tbl_meta') + ->select('name') + ->select('value') + ->where('tbl', $this->table) + ->where('tbl_id', $id) + ->find_array(); + foreach ($result as $value) { + $metas[$value['name']] = $value['value']; + } + return $metas; + } + + public function set($id, $key, $value = '') + { + $meta = ORM::for_table('tbl_meta') + ->where('tbl', $this->table) + ->where('tbl_id', $id) + ->where('name', $key) + ->find_one(); + if (!$meta) { + $meta = ORM::for_table('tbl_meta')->create(); + $meta->tbl = $this->table; + $meta->tbl_id = $id; + $meta->name = $key; + $meta->value = $value; + $meta->save(); + $result = $meta->id(); + if ($result) { + return $result; + } + } else { + $meta->value = $value; + $meta->save(); + return $meta['id']; + } + } + + public function delete($id, $key = '') + { + // get the Value + return ORM::for_table('tbl_meta') + ->select('value') + ->where('tbl', $this->table) + ->where('tbl_id', $id) + ->where('name', $key) + ->delete(); + } + + public function deleteAll($id) + { + //get all key Value + return ORM::for_table('tbl_meta') + ->select('value') + ->where('tbl', $this->table) + ->where('tbl_id', $id) + ->delete_many(); + } +} diff --git a/system/autoload/Mikrotik.php b/system/autoload/Mikrotik.php new file mode 100644 index 0000000..d917582 --- /dev/null +++ b/system/autoload/Mikrotik.php @@ -0,0 +1,562 @@ +where('name', $name)->find_one(); + } + + public static function getClient($ip, $user, $pass) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $iport = explode(":", $ip); + return new RouterOS\Client($iport[0], $user, $pass, ($iport[1]) ? $iport[1] : null); + } + + public static function isUserLogin($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot active print', + RouterOS\Query::where('user', $username) + ); + return $client->sendSync($printRequest)->getProperty('.id'); + } + + public static function logMeIn($client, $user, $pass, $ip, $mac) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/hotspot/active/login'); + $client->sendSync( + $addRequest + ->setArgument('user', $user) + ->setArgument('password', $pass) + ->setArgument('ip', $ip) + ->setArgument('mac-address', $mac) + ); + } + + public static function logMeOut($client, $user) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot active print', + RouterOS\Query::where('user', $user) + ); + $id = $client->sendSync($printRequest)->getProperty('.id'); + $removeRequest = new RouterOS\Request('/ip/hotspot/active/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $id) + ); + } + + public static function addHotspotPlan($client, $name, $sharedusers, $rate) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/hotspot/user/profile/add'); + $client->sendSync( + $addRequest + ->setArgument('name', $name) + ->setArgument('shared-users', $sharedusers) + ->setArgument('rate-limit', $rate) + ); + } + + public static function setHotspotPlan($client, $name, $sharedusers, $rate) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot user profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + if (empty($profileID)) { + Mikrotik::addHotspotPlan($client, $name, $sharedusers, $rate); + } else { + $setRequest = new RouterOS\Request('/ip/hotspot/user/profile/set'); + $client->sendSync( + $setRequest + ->setArgument('numbers', $profileID) + ->setArgument('shared-users', $sharedusers) + ->setArgument('rate-limit', $rate) + ); + } + } + + public static function setHotspotExpiredPlan($client, $name, $pool) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot user profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + if (empty($profileID)) { + $addRequest = new RouterOS\Request('/ip/hotspot/user/profile/add'); + $client->sendSync( + $addRequest + ->setArgument('name', $name) + ->setArgument('shared-users', 3) + ->setArgument('address-pool', $pool) + ->setArgument('rate-limit', '512K/512K') + ); + } else { + $setRequest = new RouterOS\Request('/ip/hotspot/user/profile/set'); + $client->sendSync( + $setRequest + ->setArgument('numbers', $profileID) + ->setArgument('shared-users', 3) + ->setArgument('address-pool', $pool) + ->setArgument('rate-limit', '512K/512K') + ); + } + } + + public static function removeHotspotPlan($client, $name) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot user profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/hotspot/user/profile/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $profileID) + ); + } + + public static function removeHotspotUser($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot user print .proplist=.id', + RouterOS\Query::where('name', $username) + ); + $userID = $client->sendSync($printRequest)->getProperty('.id'); + $removeRequest = new RouterOS\Request('/ip/hotspot/user/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $userID) + ); + } + + public static function addHotspotUser($client, $plan, $customer) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/hotspot/user/add'); + if ($plan['typebp'] == "Limited") { + if ($plan['limit_type'] == "Time_Limit") { + if ($plan['time_unit'] == 'Hrs') + $timelimit = $plan['time_limit'] . ":00:00"; + else + $timelimit = "00:" . $plan['time_limit'] . ":00"; + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('profile', $plan['name_plan']) + ->setArgument('password', $customer['password']) + ->setArgument('comment', $customer['fullname']) + ->setArgument('email', $customer['email']) + ->setArgument('limit-uptime', $timelimit) + ); + } else if ($plan['limit_type'] == "Data_Limit") { + if ($plan['data_unit'] == 'GB') + $datalimit = $plan['data_limit'] . "000000000"; + else + $datalimit = $plan['data_limit'] . "000000"; + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('profile', $plan['name_plan']) + ->setArgument('password', $customer['password']) + ->setArgument('comment', $customer['fullname']) + ->setArgument('email', $customer['email']) + ->setArgument('limit-bytes-total', $datalimit) + ); + } else if ($plan['limit_type'] == "Both_Limit") { + if ($plan['time_unit'] == 'Hrs') + $timelimit = $plan['time_limit'] . ":00:00"; + else + $timelimit = "00:" . $plan['time_limit'] . ":00"; + if ($plan['data_unit'] == 'GB') + $datalimit = $plan['data_limit'] . "000000000"; + else + $datalimit = $plan['data_limit'] . "000000"; + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('profile', $plan['name_plan']) + ->setArgument('password', $customer['password']) + ->setArgument('comment', $customer['fullname']) + ->setArgument('email', $customer['email']) + ->setArgument('limit-uptime', $timelimit) + ->setArgument('limit-bytes-total', $datalimit) + ); + } + } else { + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('profile', $plan['name_plan']) + ->setArgument('comment', $customer['fullname']) + ->setArgument('email', $customer['email']) + ->setArgument('password', $customer['password']) + ); + } + } + + public static function setHotspotUser($client, $user, $pass) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ip/hotspot/user/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('password', $pass); + $client->sendSync($setRequest); + } + + public static function setHotspotUserPackage($client, $user, $plan) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ip/hotspot/user/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('profile', $plan); + $client->sendSync($setRequest); + } + + public static function removeHotspotActiveUser($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $onlineRequest = new RouterOS\Request('/ip/hotspot/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('user', $username)); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/hotspot/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + } + + public static function removePpoeUser($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + //$printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $username)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + $removeRequest = new RouterOS\Request('/ppp/secret/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + } + + public static function addPpoeUser($client, $plan, $customer) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ppp/secret/add'); + if (!empty($customer['pppoe_password'])) { + $pass = $customer['pppoe_password']; + } else { + $pass = $customer['password']; + } + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('service', 'pppoe') + ->setArgument('profile', $plan['name_plan']) + ->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email']) + ->setArgument('password', $pass) + ); + } + + public static function setPpoeUser($client, $user, $pass) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ppp/secret/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('password', $pass); + $client->sendSync($setRequest); + } + + public static function setPpoeUserPlan($client, $user, $plan) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ppp/secret/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('profile', $plan); + $client->sendSync($setRequest); + } + + public static function removePpoeActive($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $onlineRequest = new RouterOS\Request('/ppp/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('name', $username)); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ppp/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + } + + public static function removePool($client, $name) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip pool print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $poolID = $client->sendSync($printRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/pool/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $poolID) + ); + } + + public static function addPool($client, $name, $ip_address) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/pool/add'); + $client->sendSync( + $addRequest + ->setArgument('name', $name) + ->setArgument('ranges', $ip_address) + ); + } + + public static function setPool($client, $name, $ip_address) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip pool print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $poolID = $client->sendSync($printRequest)->getProperty('.id'); + + if (empty($poolID)) { + self::addPool($client, $name, $ip_address); + } else { + $setRequest = new RouterOS\Request('/ip/pool/set'); + $client->sendSync( + $setRequest + ->setArgument('numbers', $poolID) + ->setArgument('ranges', $ip_address) + ); + } + } + + + public static function addPpoePlan($client, $name, $pool, $rate) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ppp/profile/add'); + $client->sendSync( + $addRequest + ->setArgument('name', $name) + ->setArgument('local-address', $pool) + ->setArgument('remote-address', $pool) + ->setArgument('rate-limit', $rate) + ); + } + + public static function setPpoePlan($client, $name, $pool, $rate) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ppp profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + if (empty($profileID)) { + self::addPpoePlan($client, $name, $pool, $rate); + } else { + $setRequest = new RouterOS\Request('/ppp/profile/set'); + $client->sendSync( + $setRequest + ->setArgument('numbers', $profileID) + ->setArgument('local-address', $pool) + ->setArgument('remote-address', $pool) + ->setArgument('rate-limit', $rate) + ); + } + } + + public static function removePpoePlan($client, $name) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ppp profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ppp/profile/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $profileID) + ); + } + + public static function sendSMS($client, $to, $message) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $smsRequest = new RouterOS\Request('/tool sms send'); + $smsRequest + ->setArgument('phone-number', $to) + ->setArgument('message', $message); + $client->sendSync($smsRequest); + } + + public static function getIpHotspotUser($client, $username){ + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot active print', + RouterOS\Query::where('user', $username) + ); + return $client->sendSync($printRequest)->getProperty('address'); + } + + public static function addIpToAddressList($client, $ip, $listName, $comment = '') + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/firewall/address-list/add'); + $client->sendSync( + $addRequest + ->setArgument('address', $ip) + ->setArgument('comment', $comment) + ->setArgument('list', $listName) + ); + } + + public static function removeIpFromAddressList($client, $ip) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip firewall address-list print .proplist=.id', + RouterOS\Query::where('address', $ip) + ); + $id = $client->sendSync($printRequest)->getProperty('.id'); + $removeRequest = new RouterOS\Request('/ip/firewall/address-list/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $id) + ); + } +} diff --git a/system/autoload/Package.php b/system/autoload/Package.php new file mode 100644 index 0000000..a2210d2 --- /dev/null +++ b/system/autoload/Package.php @@ -0,0 +1,817 @@ +where('id', $id_customer)->find_one(); + $p = ORM::for_table('tbl_plans')->where('id', $plan_id)->find_one(); + + if ($c['status'] != 'Active') { + _alert(Lang::T('This account status') . ' : ' . Lang::T($c['status']), 'danger', ""); + } + + $add_cost = 0; + $bills = []; + // Zero cost recharge + if (isset($zero) && $zero == 1) { + $p['price'] = 0; + } else { + // Additional cost + list($bills, $add_cost) = User::getBills($id_customer); + if ($add_cost > 0 && $router_name != 'balance') { + foreach ($bills as $k => $v) { + $note .= $k . " : " . Lang::moneyFormat($v) . "\n"; + } + $note .= $p['name_plan'] . " : " . Lang::moneyFormat($p['price']) . "\n"; + } + } + + + if (!$p['enabled']) { + if (!isset($admin) || !isset($admin['id']) || empty($admin['id'])) { + r2(U . 'home', 'e', Lang::T('Plan Not found')); + } + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) { + r2(U . 'dashboard', 'e', Lang::T('Plan Not found')); + } + } + + if ($p['validity_unit'] == 'Period') { + $day_exp = User::getAttribute("Expired Date", $c['id']); //ORM::for_table('tbl_customers_fields')->where('field_name', 'Expired Date')->where('customer_id', $c['id'])->find_one(); + if (!$day_exp) { + $day_exp = 20; + // $day_exp = date('d', strtotime($c['created_at'])); + // if (empty($day_exp) || $day_exp > 28) { + // $day_exp = 1; + // } + $f = ORM::for_table('tbl_customers_fields')->create(); + $f->customer_id = $c['id']; + $f->field_name = 'Expired Date'; + $f->field_value = $day_exp; + $f->save(); + } + } + + if ($router_name == 'balance') { + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = $inv = "INV-" . Package::_raid(); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->recharged_time = date("H:i:s"); + $t->expiration = $date_only; + $t->time = $time; + $t->method = "$gateway - $channel"; + $t->routers = $router_name; + $t->type = "Balance"; + if ($admin) { + $t->admin_id = ($admin['id']) ? $admin['id'] : '0'; + } else { + $t->admin_id = '0'; + } + $t->save(); + + $balance_before = $c['balance']; + Balance::plus($id_customer, $p['price']); + $balance = $c['balance'] + $p['price']; + + $textInvoice = Lang::getNotifText('invoice_balance'); + $textInvoice = str_replace('[[company_name]]', $config['CompanyName'], $textInvoice); + $textInvoice = str_replace('[[address]]', $config['address'], $textInvoice); + $textInvoice = str_replace('[[phone]]', $config['phone'], $textInvoice); + $textInvoice = str_replace('[[invoice]]', $inv, $textInvoice); + $textInvoice = str_replace('[[date]]', Lang::dateTimeFormat($date_now), $textInvoice); + $textInvoice = str_replace('[[payment_gateway]]', $gateway, $textInvoice); + $textInvoice = str_replace('[[payment_channel]]', $channel, $textInvoice); + $textInvoice = str_replace('[[type]]', 'Balance', $textInvoice); + $textInvoice = str_replace('[[plan_name]]', $p['name_plan'], $textInvoice); + $textInvoice = str_replace('[[plan_price]]', Lang::moneyFormat($p['price']), $textInvoice); + $textInvoice = str_replace('[[name]]', $c['fullname'], $textInvoice); + $textInvoice = str_replace('[[user_name]]', $c['username'], $textInvoice); + $textInvoice = str_replace('[[user_password]]', $c['password'], $textInvoice); + $textInvoice = str_replace('[[footer]]', $config['note'], $textInvoice); + $textInvoice = str_replace('[[balance_before]]', Lang::moneyFormat($balance_before), $textInvoice); + $textInvoice = str_replace('[[balance]]', Lang::moneyFormat($balance), $textInvoice); + + if ($config['user_notification_payment'] == 'sms') { + Message::sendSMS($c['phonenumber'], $textInvoice); + } else if ($config['user_notification_payment'] == 'wa') { + Message::sendWhatsapp($c['phonenumber'], $textInvoice); + } + + return true; + } + + /** + * 1 Customer only can have 1 PPPOE and 1 Hotspot Plan, 1 prepaid and 1 postpaid + */ + $b = ORM::for_table('tbl_user_recharges') + ->select('tbl_user_recharges.id', 'id') + ->select('customer_id') + ->select('username') + ->select('plan_id') + ->select('namebp') + ->select('recharged_on') + ->select('recharged_time') + ->select('expiration') + ->select('time') + ->select('status') + ->select('method') + ->select('tbl_user_recharges.routers', 'routers') + ->select('tbl_user_recharges.type', 'type') + ->select('admin_id') + ->select('prepaid') + ->where('customer_id', $id_customer) + ->where('tbl_user_recharges.routers', $router_name) + ->where('tbl_user_recharges.Type', $p['type']) + # PPPOE or Hotspot only can have 1 per customer prepaid or postpaid + # because 1 customer can have 1 PPPOE and 1 Hotspot Plan in mikrotik + //->where('prepaid', $p['prepaid']) + ->left_outer_join('tbl_plans', array('tbl_plans.id', '=', 'tbl_user_recharges.plan_id')) + ->find_one(); + + run_hook("recharge_user"); + + + $mikrotik = Mikrotik::info($router_name); + if ($p['validity_unit'] == 'Months') { + $date_exp = date("Y-m-d", strtotime('+' . $p['validity'] . ' month')); + } else if ($p['validity_unit'] == 'Period') { + $date_tmp = date("Y-m-$day_exp", strtotime('+' . $p['validity'] . ' month')); + $dt1 = new DateTime("$date_only"); + $dt2 = new DateTime("$date_tmp"); + $diff = $dt2->diff($dt1); + $sum = $diff->format("%a"); // => 453 + if ($sum >= 35 * $p['validity']) { + $date_exp = date("Y-m-$day_exp", strtotime('+0 month')); + } else { + $date_exp = date("Y-m-$day_exp", strtotime('+' . $p['validity'] . ' month')); + }; + $time = date("23:59:00"); + } else if ($p['validity_unit'] == 'Days') { + $datetime = explode(' ', date("Y-m-d H:i:s", strtotime('+' . $p['validity'] . ' day'))); + $date_exp = $datetime[0]; + $time = $datetime[1]; + } else if ($p['validity_unit'] == 'Hrs') { + $datetime = explode(' ', date("Y-m-d H:i:s", strtotime('+' . $p['validity'] . ' hour'))); + $date_exp = $datetime[0]; + $time = $datetime[1]; + } else if ($p['validity_unit'] == 'Mins') { + $datetime = explode(' ', date("Y-m-d H:i:s", strtotime('+' . $p['validity'] . ' minute'))); + $date_exp = $datetime[0]; + $time = $datetime[1]; + } + $isChangePlan = false; + if ($p['type'] == 'Hotspot') { + if ($b) { + if ($plan_id != $b['plan_id']) { + $isChangePlan = true; + } + if ($config['extend_expiry'] === 'yes') { + if ($b['namebp'] == $p['name_plan'] && $b['status'] == 'on') { + // if it same internet plan, expired will extend + if ($p['validity_unit'] == 'Months') { + $date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' months')); + $time = $b['time']; + } else if ($p['validity_unit'] == 'Period') { + $date_exp = date("Y-m-$day_exp", strtotime($b['expiration'] . ' +' . $p['validity'] . ' months')); + $time = date("23:59:00"); + } else if ($p['validity_unit'] == 'Days') { + $date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' days')); + $time = $b['time']; + } else if ($p['validity_unit'] == 'Hrs') { + $datetime = explode(' ', date("Y-m-d H:i:s", strtotime($b['expiration'] . ' ' . $b['time'] . ' +' . $p['validity'] . ' hours'))); + $date_exp = $datetime[0]; + $time = $datetime[1]; + } else if ($p['validity_unit'] == 'Mins') { + $datetime = explode(' ', date("Y-m-d H:i:s", strtotime($b['expiration'] . ' ' . $b['time'] . ' +' . $p['validity'] . ' minutes'))); + $date_exp = $datetime[0]; + $time = $datetime[1]; + } + } + } + if ($isChangePlan || $b['status'] == 'off') { + if ($p['is_radius']) { + Radius::customerAddPlan($c, $p, "$date_exp $time"); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removeHotspotUser($client, $c['username']); + Mikrotik::removeHotspotActiveUser($client, $c['username']); + Mikrotik::addHotspotUser($client, $p, $c); + } + } + + $b->customer_id = $id_customer; + $b->username = $c['username']; + $b->plan_id = $plan_id; + $b->namebp = $p['name_plan']; + $b->recharged_on = $date_only; + $b->recharged_time = $time_only; + $b->expiration = $date_exp; + $b->time = $time; + $b->status = "on"; + $b->method = "$gateway - $channel"; + $b->routers = $router_name; + $b->type = "Hotspot"; + if ($admin) { + $b->admin_id = ($admin['id']) ? $admin['id'] : '0'; + } else { + $b->admin_id = '0'; + } + $b->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = $inv = "INV-" . Package::_raid(); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + if ($p['validity_unit'] == 'Period') { + // Postpaid price from field + $add_inv = User::getAttribute("Invoice", $id_customer); + if (empty($add_inv) or $add_inv == 0) { + $t->price = $p['price'] + $add_cost; + } else { + $t->price = $add_inv + $add_cost; + } + } else { + $t->price = $p['price'] + $add_cost; + } + $t->recharged_on = $date_only; + $t->recharged_time = $time_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "$gateway - $channel"; + $t->routers = $router_name; + $t->note = $note; + $t->type = "Hotspot"; + if ($admin) { + $t->admin_id = ($admin['id']) ? $admin['id'] : '0'; + } else { + $t->admin_id = '0'; + } + $t->save(); + + if ($p['validity_unit'] == 'Period') { + // insert price to fields for invoice next month + $fl = ORM::for_table('tbl_customers_fields')->where('field_name', 'Invoice')->where('customer_id', $c['id'])->find_one(); + if (!$fl) { + $fl = ORM::for_table('tbl_customers_fields')->create(); + $fl->customer_id = $c['id']; + $fl->field_name = 'Invoice'; + $fl->field_value = $p['price']; + $fl->save(); + } else { + $fl->customer_id = $c['id']; + $fl->field_value = $p['price']; + $fl->save(); + } + } + + + Message::sendTelegram("#u$c[username] $c[fullname] #recharge #Hotspot \n" . $p['name_plan'] . + "\nRouter: " . $router_name . + "\nGateway: " . $gateway . + "\nChannel: " . $channel . + "\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) . + "\nNote:\n" . $note); + } else { + if ($p['is_radius']) { + Radius::customerAddPlan($c, $p, "$date_exp $time"); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removeHotspotUser($client, $c['username']); + Mikrotik::removeHotspotActiveUser($client, $c['username']); + Mikrotik::addHotspotUser($client, $p, $c); + } + + $d = ORM::for_table('tbl_user_recharges')->create(); + $d->customer_id = $id_customer; + $d->username = $c['username']; + $d->plan_id = $plan_id; + $d->namebp = $p['name_plan']; + $d->recharged_on = $date_only; + $d->recharged_time = $time_only; + $d->expiration = $date_exp; + $d->time = $time; + $d->status = "on"; + $d->method = "$gateway - $channel"; + $d->routers = $router_name; + $d->type = "Hotspot"; + if ($admin) { + $d->admin_id = ($admin['id']) ? $admin['id'] : '0'; + } else { + $d->admin_id = '0'; + } + $d->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = $inv = "INV-" . Package::_raid(); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + if ($p['validity_unit'] == 'Period') { + // Postpaid price always zero for first time + $t->price = 0 + $add_cost; + } else { + $t->price = $p['price'] + $add_cost; + } + $t->recharged_on = $date_only; + $t->recharged_time = $time_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "$gateway - $channel"; + $t->routers = $router_name; + $t->note = $note; + $t->type = "Hotspot"; + if ($admin) { + $t->admin_id = ($admin['id']) ? $admin['id'] : '0'; + } else { + $t->admin_id = '0'; + } + $t->save(); + + if ($p['validity_unit'] == 'Period' && $p['price'] != 0) { + // insert price to fields for invoice next month + $fl = ORM::for_table('tbl_customers_fields')->where('field_name', 'Invoice')->where('customer_id', $c['id'])->find_one(); + if (!$fl) { + $fl = ORM::for_table('tbl_customers_fields')->create(); + $fl->customer_id = $c['id']; + $fl->field_name = 'Invoice'; + // Calculating Price + $sd = new DateTime("$date_only"); + $ed = new DateTime("$date_exp"); + $td = $ed->diff($sd); + $fd = $td->format("%a"); + $gi = ($p['price'] / (30 * $p['validity'])) * $fd; + if ($gi > $p['price']) { + $fl->field_value = $p['price']; + } else { + $fl->field_value = $gi; + } + $fl->save(); + } else { + $fl->customer_id = $c['id']; + $fl->field_value = $p['price']; + $fl->save(); + } + } + + Message::sendTelegram("#u$c[username] $c[fullname] #buy #Hotspot \n" . $p['name_plan'] . + "\nRouter: " . $router_name . + "\nGateway: " . $gateway . + "\nChannel: " . $channel . + "\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) . + "\nNote:\n" . $note); + } + } else { + + if ($b) { + if ($plan_id != $b['plan_id']) { + $isChangePlan = true; + } + if ($config['extend_expiry'] === 'yes') { + if ($b['namebp'] == $p['name_plan'] && $b['status'] == 'on') { + // if it same internet plan, expired will extend + if ($p['validity_unit'] == 'Months') { + $date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' months')); + $time = $b['time']; + } else if ($p['validity_unit'] == 'Period') { + $date_exp = date("Y-m-$day_exp", strtotime($b['expiration'] . ' +' . $p['validity'] . ' months')); + $time = date("23:59:00"); + } else if ($p['validity_unit'] == 'Days') { + $date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' days')); + $time = $b['time']; + } else if ($p['validity_unit'] == 'Hrs') { + $datetime = explode(' ', date("Y-m-d H:i:s", strtotime($b['expiration'] . ' ' . $b['time'] . ' +' . $p['validity'] . ' hours'))); + $date_exp = $datetime[0]; + $time = $datetime[1]; + } else if ($p['validity_unit'] == 'Mins') { + $datetime = explode(' ', date("Y-m-d H:i:s", strtotime($b['expiration'] . ' ' . $b['time'] . ' +' . $p['validity'] . ' minutes'))); + $date_exp = $datetime[0]; + $time = $datetime[1]; + } + } + } + + if ($isChangePlan || $b['status'] == 'off') { + if ($p['is_radius']) { + Radius::customerAddPlan($c, $p, "$date_exp $time"); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removePpoeUser($client, $c['username']); + Mikrotik::removePpoeActive($client, $c['username']); + Mikrotik::addPpoeUser($client, $p, $c); + } + } + + $b->customer_id = $id_customer; + $b->username = $c['username']; + $b->plan_id = $plan_id; + $b->namebp = $p['name_plan']; + $b->recharged_on = $date_only; + $b->recharged_time = $time_only; + $b->expiration = $date_exp; + $b->time = $time; + $b->status = "on"; + $b->method = "$gateway - $channel"; + $b->routers = $router_name; + $b->type = "PPPOE"; + if ($admin) { + $b->admin_id = ($admin['id']) ? $admin['id'] : '0'; + } else { + $b->admin_id = '0'; + } + $b->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = $inv = "INV-" . Package::_raid(); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + if ($p['validity_unit'] == 'Period') { + // Postpaid price from field + $add_inv = User::getAttribute("Invoice", $id_customer); + if (empty($add_inv) or $add_inv == 0) { + $t->price = $p['price'] + $add_cost; + } else { + $t->price = $add_inv + $add_cost; + } + } else { + $t->price = $p['price'] + $add_cost; + } + $t->recharged_on = $date_only; + $t->recharged_time = $time_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "$gateway - $channel"; + $t->routers = $router_name; + $t->note = $note; + $t->type = "PPPOE"; + if ($admin) { + $t->admin_id = ($admin['id']) ? $admin['id'] : '0'; + } else { + $t->admin_id = '0'; + } + $t->save(); + + if ($p['validity_unit'] == 'Period' && $p['price'] != 0) { + // insert price to fields for invoice next month + $fl = ORM::for_table('tbl_customers_fields')->where('field_name', 'Invoice')->where('customer_id', $c['id'])->find_one(); + if (!$fl) { + $fl = ORM::for_table('tbl_customers_fields')->create(); + $fl->customer_id = $c['id']; + $fl->field_name = 'Invoice'; + $fl->field_value = $p['price']; + $fl->save(); + } else { + $fl->customer_id = $c['id']; + $fl->field_value = $p['price']; + $fl->save(); + } + } + + Message::sendTelegram("#u$c[username] $c[fullname] #recharge #PPPOE \n" . $p['name_plan'] . + "\nRouter: " . $router_name . + "\nGateway: " . $gateway . + "\nChannel: " . $channel . + "\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) . + "\nNote:\n" . $note); + } else { + if ($p['is_radius']) { + Radius::customerAddPlan($c, $p, "$date_exp $time"); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removePpoeUser($client, $c['username']); + Mikrotik::removePpoeActive($client, $c['username']); + Mikrotik::addPpoeUser($client, $p, $c); + } + + $d = ORM::for_table('tbl_user_recharges')->create(); + $d->customer_id = $id_customer; + $d->username = $c['username']; + $d->plan_id = $plan_id; + $d->namebp = $p['name_plan']; + $d->recharged_on = $date_only; + $d->recharged_time = $time_only; + $d->expiration = $date_exp; + $d->time = $time; + $d->status = "on"; + $d->method = "$gateway - $channel"; + $d->routers = $router_name; + $d->type = "PPPOE"; + if ($admin) { + $d->admin_id = ($admin['id']) ? $admin['id'] : '0'; + } else { + $d->admin_id = '0'; + } + $d->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = $inv = "INV-" . Package::_raid(); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + if ($p['validity_unit'] == 'Period') { + // Postpaid price always zero for first time + $note = ''; + $bills = []; + $t->price = 0; + } else { + $t->price = $p['price'] + $add_cost; + } + $t->recharged_on = $date_only; + $t->recharged_time = $time_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "$gateway - $channel"; + $t->note = $note; + $t->routers = $router_name; + if ($admin) { + $t->admin_id = ($admin['id']) ? $admin['id'] : '0'; + } else { + $t->admin_id = '0'; + } + $t->type = "PPPOE"; + $t->save(); + + if ($p['validity_unit'] == 'Period' && $p['price'] != 0) { + // insert price to fields for invoice next month + $fl = ORM::for_table('tbl_customers_fields')->where('field_name', 'Invoice')->where('customer_id', $c['id'])->find_one(); + if (!$fl) { + $fl = ORM::for_table('tbl_customers_fields')->create(); + $fl->customer_id = $c['id']; + $fl->field_name = 'Invoice'; + // Calculating Price + $sd = new DateTime("$date_only"); + $ed = new DateTime("$date_exp"); + $td = $ed->diff($sd); + $fd = $td->format("%a"); + $gi = ($p['price'] / (30 * $p['validity'])) * $fd; + if ($gi > $p['price']) { + $fl->field_value = $p['price']; + } else { + $fl->field_value = $gi; + } + $fl->save(); + } else { + $fl->customer_id = $c['id']; + $fl->field_value = $p['price']; + $fl->save(); + } + } + + Message::sendTelegram("#u$c[username] $c[fullname] #buy #PPPOE \n" . $p['name_plan'] . + "\nRouter: " . $router_name . + "\nGateway: " . $gateway . + "\nChannel: " . $channel . + "\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) . + "\nNote:\n" . $note); + } + } + if (is_array($bills) && count($bills) > 0) { + User::billsPaid($bills, $id_customer); + } + run_hook("recharge_user_finish"); + Message::sendInvoice($c, $t); + if ($trx) { + $trx->trx_invoice = $inv; + } + return $inv; + } + + public static function changeTo($username, $plan_id, $from_id) + { + $c = ORM::for_table('tbl_customers')->where('username', $username)->find_one(); + $p = ORM::for_table('tbl_plans')->where('id', $plan_id)->find_one(); + $b = ORM::for_table('tbl_user_recharges')->find_one($from_id); + if ($p['routers'] == $b['routers'] && $b['routers'] != 'radius') { + $mikrotik = Mikrotik::info($p['routers']); + } else { + $mikrotik = Mikrotik::info($b['routers']); + } + // delete first + if ($p['type'] == 'Hotspot') { + if ($b) { + if (!$p['is_radius']) { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removeHotspotUser($client, $c['username']); + Mikrotik::removeHotspotActiveUser($client, $c['username']); + } + } else { + if (!$p['is_radius']) { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removeHotspotUser($client, $c['username']); + Mikrotik::removeHotspotActiveUser($client, $c['username']); + } + } + } else { + if ($b) { + if (!$p['is_radius']) { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removePpoeUser($client, $c['username']); + Mikrotik::removePpoeActive($client, $c['username']); + } + } else { + if (!$p['is_radius']) { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removePpoeUser($client, $c['username']); + Mikrotik::removePpoeActive($client, $c['username']); + } + } + } + // call the next mikrotik + if ($p['routers'] != $b['routers'] && $p['routers'] != 'radius') { + $mikrotik = Mikrotik::info($p['routers']); + } + if ($p['type'] == 'Hotspot') { + if ($b) { + if ($p['is_radius']) { + Radius::customerAddPlan($c, $p, $b['expiration'] . '' . $b['time']); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::addHotspotUser($client, $p, $c); + } + } else { + if ($p['is_radius']) { + Radius::customerAddPlan($c, $p, $b['expiration'] . '' . $b['time']); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::addHotspotUser($client, $p, $c); + } + } + } else { + if ($b) { + if ($p['is_radius']) { + Radius::customerAddPlan($c, $p); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::addPpoeUser($client, $p, $c); + } + } else { + if ($p['is_radius']) { + Radius::customerAddPlan($c, $p); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::addPpoeUser($client, $p, $c); + } + } + } + } + + + public static function _raid() + { + return ORM::for_table('tbl_transactions')->max('id') + 1; + } + + /** + * @param in tbl_transactions + * @param string $router_name router name for this package + * @param int $plan_id plan id for this package + * @param string $gateway payment gateway name + * @param string $channel channel payment gateway + * @return boolean + */ + public static function createInvoice($in) + { + global $config, $admin, $ui; + $date = Lang::dateAndTimeFormat($in['recharged_on'], $in['recharged_time']); + if ($admin['id'] != $in['admin_id'] && $in['admin_id'] > 0) { + $_admin = Admin::_info($in['admin_id']); + // if admin not deleted + if ($_admin) $admin = $_admin; + } else { + $admin['fullname'] = 'Customer'; + } + $cust = ORM::for_table('tbl_customers')->where('username', $in['username'])->findOne(); + + $note = ''; + //print + $invoice = Lang::pad($config['CompanyName'], ' ', 2) . "\n"; + $invoice .= Lang::pad($config['address'], ' ', 2) . "\n"; + $invoice .= Lang::pad($config['phone'], ' ', 2) . "\n"; + $invoice .= Lang::pad("", '=') . "\n"; + $invoice .= Lang::pads("Invoice", $in['invoice'], ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Date'), $date, ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Sales'), $admin['fullname'], ' ') . "\n"; + $invoice .= Lang::pad("", '=') . "\n"; + $invoice .= Lang::pads(Lang::T('Type'), $in['type'], ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Plan Name'), $in['plan_name'], ' ') . "\n"; + if (!empty($in['note'])) { + $in['note'] = str_replace("\r", "", $in['note']); + $tmp = explode("\n", $in['note']); + foreach ($tmp as $t) { + if (strpos($t, " : ") === false) { + if (!empty($t)) { + $note .= "$t\n"; + } + } else { + $tmp2 = explode(" : ", $t); + $invoice .= Lang::pads($tmp2[0], $tmp2[1], ' ') . "\n"; + } + } + } + $invoice .= Lang::pads(Lang::T('Total'), Lang::moneyFormat($in['price']), ' ') . "\n"; + $method = explode("-", $in['method']); + $invoice .= Lang::pads($method[0], $method[1], ' ') . "\n"; + if (!empty($note)) { + $invoice .= Lang::pad("", '=') . "\n"; + $invoice .= Lang::pad($note, ' ', 2) . "\n"; + } + $invoice .= Lang::pad("", '=') . "\n"; + if ($cust) { + $invoice .= Lang::pads(Lang::T('Full Name'), $cust['fullname'], ' ') . "\n"; + } + $invoice .= Lang::pads(Lang::T('Username'), $in['username'], ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Password'), '**********', ' ') . "\n"; + if ($in['type'] != 'Balance') { + $invoice .= Lang::pads(Lang::T('Created On'), Lang::dateAndTimeFormat($in['recharged_on'], $in['recharged_time']), ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Expires On'), Lang::dateAndTimeFormat($in['expiration'], $in['time']), ' ') . "\n"; + } + $invoice .= Lang::pad("", '=') . "\n"; + $invoice .= Lang::pad($config['note'], ' ', 2) . "\n"; + $ui->assign('invoice', $invoice); + $config['printer_cols'] = 30; + //whatsapp + $invoice = Lang::pad($config['CompanyName'], ' ', 2) . "\n"; + $invoice .= Lang::pad($config['address'], ' ', 2) . "\n"; + $invoice .= Lang::pad($config['phone'], ' ', 2) . "\n"; + $invoice .= Lang::pad("", '=') . "\n"; + $invoice .= Lang::pads("Invoice", $in['invoice'], ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Date'), $date, ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Sales'), $admin['fullname'], ' ') . "\n"; + $invoice .= Lang::pad("", '=') . "\n"; + $invoice .= Lang::pads(Lang::T('Type'), $in['type'], ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Plan Name'), $in['plan_name'], ' ') . "\n"; + if (!empty($in['note'])) { + $invoice .= Lang::pad("", '=') . "\n"; + foreach ($tmp as $t) { + if (strpos($t, " : ") === false) { + if (!empty($t)) { + $invoice .= Lang::pad($t, ' ', 2) . "\n"; + } + } else { + $tmp2 = explode(" : ", $t); + $invoice .= Lang::pads($tmp2[0], $tmp2[1], ' ') . "\n"; + } + } + } + $invoice .= Lang::pads(Lang::T('Total'), Lang::moneyFormat($in['price']), ' ') . "\n"; + $invoice .= Lang::pads($method[0], $method[1], ' ') . "\n"; + if (!empty($note)) { + $invoice .= Lang::pad("", '=') . "\n"; + $invoice .= Lang::pad($note, ' ', 2) . "\n"; + } + $invoice .= Lang::pad("", '=') . "\n"; + if ($cust) { + $invoice .= Lang::pads(Lang::T('Full Name'), $cust['fullname'], ' ') . "\n"; + } + $invoice .= Lang::pads(Lang::T('Username'), $in['username'], ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Password'), '**********', ' ') . "\n"; + if ($in['type'] != 'Balance') { + $invoice .= Lang::pads(Lang::T('Created On'), Lang::dateAndTimeFormat($in['recharged_on'], $in['recharged_time']), ' ') . "\n"; + $invoice .= Lang::pads(Lang::T('Expires On'), Lang::dateAndTimeFormat($in['expiration'], $in['time']), ' ') . "\n"; + } + $invoice .= Lang::pad("", '=') . "\n"; + $invoice .= Lang::pad($config['note'], ' ', 2) . "\n"; + $ui->assign('whatsapp', urlencode("```$invoice```")); + $ui->assign('in', $in); + } + public static function tax($price, $tax_rate = 1) + { + // Convert tax rate to decimal + $tax_rate_decimal = $tax_rate / 100; + $tax = $price * $tax_rate_decimal; + return $tax; + } +} diff --git a/system/autoload/Paginator.php b/system/autoload/Paginator.php new file mode 100644 index 0000000..85441b8 --- /dev/null +++ b/system/autoload/Paginator.php @@ -0,0 +1,366 @@ + 0) { + $url .= '&' . http_build_query($search); + } + $url .= '&p='; + $totalReq = $query->count(); + $lastpage = ceil($totalReq / $per_page); + $lpm1 = $lastpage - 1; + $limit = $per_page; + $startpoint = ($page * $limit) - $limit; + if ($lastpage >= 1) { + $pages = []; + if ($lastpage < 7 + ($adjacents * 2)) { + for ($counter = 1; $counter <= $lastpage; $counter++) { + $pages[] = $counter; + } + } elseif ($lastpage > 5 + ($adjacents * 2)) { + if ($page < 1 + ($adjacents * 2)) { + for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++) { + $pages[] = $counter; + } + $pages[] = "..."; + $pages[] = $lpm1; + $pages[] = $lastpage; + } elseif ($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2)) { + $pages[] = "1"; + $pages[] = "2"; + $pages[] = "..."; + for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++) { + $pages[] = $counter; + } + $pages[] = "..."; + $pages[] = $lpm1; + $pages[] = $lastpage; + } else { + $pages[] = "1"; + $pages[] = "2"; + $pages[] = "..."; + for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++) { + $pages[] = $counter; + } + } + } + + $result = [ + 'count' => $lastpage, + 'limit' => $per_page, + 'startpoint' => $startpoint, + 'url' => $url, + 'page' => $page, + 'pages' => $pages, + 'prev' => ($page > 0) ? ($page - 1) : "0", + 'next' => ($page >= $lastpage) ? $lastpage : $page + 1 + ]; + if ($ui) { + $ui->assign('paginator', $result); + } + return $query->offset($startpoint)->limit($per_page)->find_many(); + } + } + + public static function build($table, $colVal = [], $query = '', $per_page = '10') + { + global $routes; + global $_L; + $url = U . implode('/', $routes); + $query = urlencode($query); + $adjacents = "2"; + $page = (int)(empty(_get('p')) ? 1 : _get('p')); + $pagination = ""; + foreach ($colVal as $k => $v) { + if (!is_array($v) && strpos($v, '%') === false) { + $table = $table->where($k, $v); + } else { + if (is_array($v)) { + $table = $table->where_in($k, $v); + } else { + $table = $table->where_like($k, $v); + } + } + } + $totalReq = $table->count(); + $page = ($page == 0 ? 1 : $page); + $next = $page + 1; + $lastpage = ceil($totalReq / $per_page); + $lpm1 = $lastpage - 1; + $limit = $per_page; + $startpoint = ($page * $limit) - $limit; + if ($lastpage >= 1) { + $pagination .= '"; + $pagination = ''; + return array("startpoint" => $startpoint, "limit" => $limit, "found" => $totalReq, "page" => $page, "lastpage" => $lastpage, "contents" => $pagination); + } + } + + public static function bootstrap($table, $w1 = '', $c1 = '', $w2 = '', $c2 = '', $w3 = '', $c3 = '', $w4 = '', $c4 = '', $per_page = '10') + { + global $routes; + global $_L; + $url = U . $routes['0'] . '/' . $routes['1'] . '/'; + $adjacents = "2"; + $page = (int)(!isset($routes['2']) ? 1 : $routes['2']); + $pagination = ""; + + if (is_object($table)) { + if ($w1 != '') { + $totalReq = $table->where($w1, $c1)->count(); + } elseif ($w2 != '') { + $totalReq = $table->where($w1, $c1)->where($w2, $c2)->count(); + } elseif ($w3 != '') { + $totalReq = $table->where($w1, $c1)->where($w2, $c2)->where($w3, $c3)->count(); + } elseif ($w4 != '') { + $totalReq = $table->where($w1, $c1)->where($w2, $c2)->where($w3, $c3)->where($w4, $c4)->count(); + } else { + $totalReq = $table->count(); + } + } else { + if ($w1 != '') { + $totalReq = ORM::for_table($table)->where($w1, $c1)->count(); + } elseif ($w2 != '') { + $totalReq = ORM::for_table($table)->where($w1, $c1)->where($w2, $c2)->count(); + } elseif ($w3 != '') { + $totalReq = ORM::for_table($table)->where($w1, $c1)->where($w2, $c2)->where($w3, $c3)->count(); + } elseif ($w4 != '') { + $totalReq = ORM::for_table($table)->where($w1, $c1)->where($w2, $c2)->where($w3, $c3)->where($w4, $c4)->count(); + } else { + $totalReq = ORM::for_table($table)->count(); + } + } + + $i = 0; + $page = ($page == 0 ? 1 : $page); + $start = ($page - 1) * $per_page; + + $prev = $page - 1; + $next = $page + 1; + $lastpage = ceil($totalReq / $per_page); + + $lpm1 = $lastpage - 1; + $limit = $per_page; + $startpoint = ($page * $limit) - $limit; + + if ($lastpage >= 1) { + $pagination .= '"; + $pagination = ''; + + $gen = array("startpoint" => $startpoint, "limit" => $limit, "found" => $totalReq, "page" => $page, "lastpage" => $lastpage, "contents" => $pagination); + return $gen; + } + } + + public static function bootstrapRaw($table, $w1 = '', $c1 = [], $per_page = '10') + { + global $routes; + global $_L; + $url = U . $routes['0'] . '/' . $routes['1'] . '/'; + $adjacents = "2"; + $page = (int)(!isset($routes['2']) ? 1 : $routes['2']); + $pagination = ""; + if (is_object($table)) { + if ($w1 != '') { + $totalReq = $table->where_raw($w1, $c1)->count(); + } else { + $totalReq = $table->count(); + } + } else { + if ($w1 != '') { + $totalReq = ORM::for_table($table)->where_raw($w1, $c1)->count(); + } else { + $totalReq = ORM::for_table($table)->count(); + } + } + + $i = 0; + $page = ($page == 0 ? 1 : $page); + $start = ($page - 1) * $per_page; + + $prev = $page - 1; + $next = $page + 1; + $lastpage = ceil($totalReq / $per_page); + + $lpm1 = $lastpage - 1; + $limit = $per_page; + $startpoint = ($page * $limit) - $limit; + + if ($lastpage >= 1) { + $pagination .= '"; + $pagination = ''; + + $gen = array("startpoint" => $startpoint, "limit" => $limit, "found" => $totalReq, "page" => $page, "lastpage" => $lastpage, "contents" => $pagination); + return $gen; + } + } +} -- 2.47.2 From 783c79045f06624b1cfefb456c2e8fbe490cadea Mon Sep 17 00:00:00 2001 From: nestict Date: Sat, 24 May 2025 10:55:48 +0200 Subject: [PATCH 2/2] Upload files to "system/autoload" Signed-off-by: nestict --- system/autoload/Parsedown.php | 1994 +++++++++++++++++++++++++++++++++ system/autoload/Password.php | 35 + system/autoload/Radius.php | 349 ++++++ system/autoload/Text.php | 64 ++ system/autoload/Timezone.php | 50 + 5 files changed, 2492 insertions(+) create mode 100644 system/autoload/Parsedown.php create mode 100644 system/autoload/Password.php create mode 100644 system/autoload/Radius.php create mode 100644 system/autoload/Text.php create mode 100644 system/autoload/Timezone.php diff --git a/system/autoload/Parsedown.php b/system/autoload/Parsedown.php new file mode 100644 index 0000000..ae0cbde --- /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('/^\[(.+?)\]:[ ]*+?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $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 ? '' : ''; + } + 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/autoload/Password.php b/system/autoload/Password.php new file mode 100644 index 0000000..989e27b --- /dev/null +++ b/system/autoload/Password.php @@ -0,0 +1,35 @@ +create(); + $n->nasname = $ip; + $n->shortname = $name; + $n->type = $type; + $n->ports = $ports; + $n->secret = $secret; + $n->description = $description; + $n->server = $server; + $n->community = $community; + $n->routers = $routers; + $n->save(); + return $n->id(); + } + + public static function nasUpdate($id, $name, $ip, $ports, $secret, $routers = "", $description = "", $type = 'other', $server = null, $community = null) + { + $n = Radius::getTableNas()->find_one($id); + if (empty($n)) { + return false; + } + $n->nasname = $ip; + $n->shortname = $name; + $n->type = $type; + $n->ports = $ports; + $n->secret = $secret; + $n->description = $description; + $n->server = $server; + $n->community = $community; + $n->routers = $routers; + return $n->save(); + } + + public static function planUpSert($plan_id, $rate, $pool = null) + { + $rates = explode('/', $rate); + ##burst fixed + if (strpos($rate, ' ')) { + $ratos = $rates[0] . '/' . $rates[1] . ' ' . $rates[2] . '/' . $rates[3] . '/' . $rates[4] . '/' . $rates[5] . '/' . $rates[6]; + } else { + $ratos = $rates[0] . '/' . $rates[1]; + } + + Radius::upsertPackage($plan_id, 'Ascend-Data-Rate', $rates[1], ':='); + Radius::upsertPackage($plan_id, 'Ascend-Xmit-Rate', $rates[0], ':='); + Radius::upsertPackage($plan_id, 'Mikrotik-Rate-Limit', $ratos, ':='); + // if ($pool != null) { + // Radius::upsertPackage($plan_id, 'Framed-Pool', $pool, ':='); + // } + } + + public static function planDelete($plan_id) + { + // Delete Plan + Radius::getTablePackage()->where_equal('plan_id', "plan_" . $plan_id)->delete_many(); + // Reset User Plan + $c = Radius::getTableUserPackage()->where_equal('groupname', "plan_" . $plan_id)->findMany(); + if ($c) { + foreach ($c as $u) { + $u->groupname = ''; + $u->save(); + } + } + } + + + public static function customerChangeUsername($from, $to) + { + $c = Radius::getTableCustomer()->where_equal('username', $from)->findMany(); + if ($c) { + foreach ($c as $u) { + $u->username = $to; + $u->save(); + } + } + $c = Radius::getTableUserPackage()->where_equal('username', $from)->findMany(); + if ($c) { + foreach ($c as $u) { + $u->username = $to; + $u->save(); + } + } + } + + public static function customerDeactivate($username, $radiusDisconnect = true) + { { + global $radius_pass; + $r = Radius::getTableCustomer()->where_equal('username', $username)->whereEqual('attribute', 'Cleartext-Password')->findOne(); + if ($r) { + // no need to delete, because it will make ID got higher + // we just change the password + $r->value = md5(time() . $username . $radius_pass); + $r->save(); + if ($radiusDisconnect) + return Radius::disconnectCustomer($username); + } + } + return ''; + } + + public static function customerDelete($username) + { + Radius::getTableCustomer()->where_equal('username', $username)->delete_many(); + Radius::getTableUserPackage()->where_equal('username', $username)->delete_many(); + } + + /** + * When add a plan to Customer, use this + */ + public static function customerAddPlan($customer, $plan, $expired = null) + { + global $config; + if (Radius::customerUpsert($customer, $plan)) { + $p = Radius::getTableUserPackage()->where_equal('username', $customer['username'])->findOne(); + if ($p) { + // if exists + Radius::delAtribute(Radius::getTableCustomer(), 'Max-All-Session', 'username', $customer['username']); + Radius::delAtribute(Radius::getTableCustomer(), 'Max-Volume', 'username', $customer['username']); + $p->groupname = "plan_" . $plan['id']; + $p->save(); + } else { + $p = Radius::getTableUserPackage()->create(); + $p->username = $customer['username']; + $p->groupname = "plan_" . $plan['id']; + $p->priority = 1; + $p->save(); + } + if ($plan['type'] == 'Hotspot' && $plan['typebp'] == "Limited") { + if ($plan['limit_type'] == "Time_Limit") { + if ($plan['time_unit'] == 'Hrs') + $timelimit = $plan['time_limit'] * 60 * 60; + else + $timelimit = $plan['time_limit'] * 60; + Radius::upsertCustomer($customer['username'], 'Max-All-Session', $timelimit); + //Radius::upsertCustomer($customer['username'], 'Expire-After', $timelimit); + } else if ($plan['limit_type'] == "Data_Limit") { + if ($plan['data_unit'] == 'GB') + $datalimit = $plan['data_limit'] . "000000000"; + else + $datalimit = $plan['data_limit'] . "000000"; + Radius::upsertCustomer($customer['username'], 'Max-Volume', $datalimit); + // Mikrotik Spesific + //Radius::upsertCustomer($customer['username'], 'Max-Data', $datalimit); + //Radius::upsertCustomer($customer['username'], 'Mikrotik-Total-Limit', $datalimit); + } else if ($plan['limit_type'] == "Both_Limit") { + if ($plan['time_unit'] == 'Hrs') + $timelimit = $plan['time_limit'] * 60 * 60; + else + $timelimit = $plan['time_limit'] * 60; + if ($plan['data_unit'] == 'GB') + $datalimit = $plan['data_limit'] . "000000000"; + else + $datalimit = $plan['data_limit'] . "000000"; + Radius::upsertCustomer($customer['username'], 'Max-Volume', $datalimit); + Radius::upsertCustomer($customer['username'], 'Max-All-Session', $timelimit); + // Mikrotik Spesific + //Radius::upsertCustomer($customer['username'], 'Max-Data', $datalimit); + //Radius::upsertCustomer($customer['username'], 'Mikrotik-Total-Limit', $datalimit); + + + + + } + } else { + Radius::delAtribute(Radius::getTableCustomer(), 'Max-Volume', 'username', $customer['username']); + Radius::delAtribute(Radius::getTableCustomer(), 'Max-All-Session', 'username', $customer['username']); + //Radius::delAtribute(Radius::getTableCustomer(), 'Max-Data', 'username', $customer['username']); + } + + Radius::disconnectCustomer($customer['username']); + Radius::getTableAcct()->where_equal('username', $customer['username'])->delete_many(); + + + // expired user + if ($expired != null) { + //Radius::upsertCustomer($customer['username'], 'access-period', strtotime($expired) - time()); + Radius::upsertCustomer($customer['username'], 'Max-All-Session', strtotime($expired) - time()); + //Radius::upsertCustomer($customer['username'], 'expiration', date('d M Y H:i:s', strtotime($expired))); + // Mikrotik Spesific + Radius::upsertCustomer( + $customer['username'], + 'WISPr-Session-Terminate-Time', + date('Y-m-d', strtotime($expired)) . 'T' . date('H:i:s', strtotime($expired)) . Timezone::getTimeOffset($config['timezone']) + ); + } else { + Radius::delAtribute(Radius::getTableCustomer(), 'Max-All-Session', 'username', $customer['username']); + //Radius::delAtribute(Radius::getTableCustomer(), 'access-period', 'username', $customer['username']); + //Radius::delAtribute(Radius::getTableCustomer(), 'expiration', 'username', $customer['username']); + } + + if ($plan['type'] == 'PPPOE') { + Radius::upsertCustomerAttr($customer['username'], 'Framed-Pool', $plan['pool'], ':='); + } + + + return true; + } + return false; + } + + public static function customerUpsert($customer, $plan) + { + if ($plan['type'] == 'PPPOE') { + Radius::upsertCustomer($customer['username'], 'Cleartext-Password', (empty($customer['pppoe_password'])) ? $customer['password'] : $customer['pppoe_password']); + } else { + Radius::upsertCustomer($customer['username'], 'Cleartext-Password', $customer['password']); + } + Radius::upsertCustomer($customer['username'], 'Simultaneous-Use', ($plan['type'] == 'PPPOE') ? 1 : $plan['shared_users']); + // Mikrotik Spesific + Radius::upsertCustomer($customer['username'], 'Port-Limit', ($plan['type'] == 'PPPOE') ? 1 : $plan['shared_users']); + Radius::upsertCustomer($customer['username'], 'Mikrotik-Wireless-Comment', $customer['fullname']); + return true; + } + + private static function delAtribute($tabel, $attribute, $key, $value) + { + $r = $tabel->where_equal($key, $value)->whereEqual('attribute', $attribute)->findOne(); + if ($r) $r->delete(); + } + + /** + * To insert or update existing plan + */ + private static function upsertPackage($plan_id, $attr, $value, $op = ':=') + { + $r = Radius::getTablePackage()->where_equal('plan_id', $plan_id)->whereEqual('attribute', $attr)->find_one(); + if (!$r) { + $r = Radius::getTablePackage()->create(); + $r->groupname = "plan_" . $plan_id; + $r->plan_id = $plan_id; + } + $r->attribute = $attr; + $r->op = $op; + $r->value = $value; + return $r->save(); + } + + /** + * To insert or update existing customer + */ + public static function upsertCustomer($username, $attr, $value, $op = ':=') + { + $r = Radius::getTableCustomer()->where_equal('username', $username)->whereEqual('attribute', $attr)->find_one(); + if (!$r) { + $r = Radius::getTableCustomer()->create(); + $r->username = $username; + } + $r->attribute = $attr; + $r->op = $op; + $r->value = $value; + return $r->save(); + } + /** + * To insert or update existing customer Attribute + */ + public static function upsertCustomerAttr($username, $attr, $value, $op = ':=') + { + $r = Radius::getTableCustomerAttr()->where_equal('username', $username)->whereEqual('attribute', $attr)->find_one(); + if (!$r) { + $r = Radius::getTableCustomerAttr()->create(); + $r->username = $username; + } + $r->attribute = $attr; + $r->op = $op; + $r->value = $value; + return $r->save(); + } + + public static function disconnectCustomer($username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + /** + * Fix loop to all Nas but still detecting Hotspot Multylogin from other Nas + */ + $act = ORM::for_table('radacct')->where_raw("acctstoptime IS NULL")->where('username', $username)->find_one(); + $nas = Radius::getTableNas()->where('nasname', $act['nasipaddress'])->find_many(); + $count = count($nas) * 15; + set_time_limit($count); + $result = []; + foreach ($nas as $n) { + $port = 3799; + if (!empty($n['ports'])) { + $port = $n['ports']; + } + $result[] = $n['nasname'] . ': ' . @shell_exec("echo 'User-Name = $username,Framed-IP-Address = " . $act['framedipaddress'] . "' | radclient -x " . trim($n['nasname']) . ":$port disconnect '" . $n['secret'] . "'"); + } + return $result; + } +} diff --git a/system/autoload/Text.php b/system/autoload/Text.php new file mode 100644 index 0000000..bc094e1 --- /dev/null +++ b/system/autoload/Text.php @@ -0,0 +1,64 @@ + (int)$currentTimezone->getOffset($utcTime), + 'identifier' => $timezoneIdentifier + ); + } + + // Sort the array by offset,identifier ascending + usort($tempTimezones, function($a, $b) { + return ($a['offset'] == $b['offset']) + ? strcmp($a['identifier'], $b['identifier']) + : $a['offset'] - $b['offset']; + }); + + $timezoneList = array(); + foreach ($tempTimezones as $tz) { + $sign = ($tz['offset'] > 0) ? '+' : '-'; + $offset = gmdate('H:i', abs($tz['offset'])); + $timezoneList[$tz['identifier']] = '(UTC ' . $sign . $offset . ') ' . + $tz['identifier']; + } + + return $timezoneList; + } + + public static function getTimeOffset($tz = 'Asia/Jakarta'){ + $utcTime = new DateTime('now', new DateTimeZone('UTC')); + $currentTimezone = new DateTimeZone($tz); + $offset = $currentTimezone->getOffset($utcTime); + $sign = ($offset > 0) ? '+' : '-'; + $offset = gmdate('H:i', abs($offset)); + return $sign.$offset; + } +} \ No newline at end of file -- 2.47.2