diff --git a/system/autoload/Admin.php b/system/autoload/Admin.php new file mode 100644 index 0000000..c39ee81 --- /dev/null +++ b/system/autoload/Admin.php @@ -0,0 +1,61 @@ +find_one($id); + } else { + return null; + } + } +} diff --git a/system/autoload/App.php b/system/autoload/App.php new file mode 100644 index 0000000..71f1614 --- /dev/null +++ b/system/autoload/App.php @@ -0,0 +1,29 @@ +where('id', $id_customer)->find_one(); + $c->balance = $amount + $c['balance']; + $c->save(); + } + + public static function transfer($id_customer, $phoneTarget, $amount) + { + global $config; + if (Balance::min($id_customer, $amount)) { + return Balance::plusByPhone($phoneTarget, $amount); + } else { + return false; + } + } + + public static function min($id_customer, $amount) + { + $c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one(); + if ($c && $c['balance'] >= $amount) { + $c->balance = $c['balance'] - $amount; + $c->save(); + return true; + } else { + return false; + } + } + + public static function plusByPhone($phone_customer, $amount) + { + $c = ORM::for_table('tbl_customers')->where('username', $phone_customer)->find_one(); + if ($c) { + $c->balance = $amount + $c['balance']; + $c->save(); + return true; + } + return false; + } + + public static function minByPhone($phone_customer, $amount) + { + $c = ORM::for_table('tbl_customers')->where('username', $phone_customer)->find_one(); + if ($c && $c['balance'] >= $amount) { + $c->balance = $c['balance'] - $amount; + $c->save(); + return true; + } else { + return false; + } + } +} diff --git a/system/autoload/CronLog.php b/system/autoload/CronLog.php new file mode 100644 index 0000000..2b5e414 --- /dev/null +++ b/system/autoload/CronLog.php @@ -0,0 +1,283 @@ +create(); + $log->cron_type = $cronType; + $log->started_at = date('Y-m-d H:i:s'); + $log->status = 'running'; + $log->save(); + + self::$logId = $log->id(); + return self::$logId; + } + + /** + * Update log with current statistics + * @param array $stats Statistics array + */ + public static function updateStats($stats = []) + { + if (!self::$logId) return; + + $log = ORM::for_table('tbl_cron_logs')->find_one(self::$logId); + if (!$log) return; + + foreach ($stats as $key => $value) { + if (property_exists($log, $key)) { + $log->$key = $value; + } + } + + $log->save(); + } + + /** + * Increment a counter + * @param string $field Field to increment + * @param int $amount Amount to increment by + */ + public static function increment($field, $amount = 1) + { + if (!self::$logId) return; + + $log = ORM::for_table('tbl_cron_logs')->find_one(self::$logId); + if (!$log) return; + + if (property_exists($log, $field)) { + $log->$field = $log->$field + $amount; + $log->save(); + } + } + + /** + * Complete the cron job successfully + * @param array $finalStats Final statistics + */ + public static function complete($finalStats = []) + { + if (!self::$logId) return; + + $log = ORM::for_table('tbl_cron_logs')->find_one(self::$logId); + if (!$log) return; + + $log->finished_at = date('Y-m-d H:i:s'); + $log->status = 'completed'; + + // Calculate execution time + if (self::$startTime) { + $log->execution_time = round(microtime(true) - self::$startTime, 3); + } + + // Calculate memory usage + if (self::$startMemory) { + $currentMemory = memory_get_usage(); + $peakMemory = memory_get_peak_usage(); + $log->memory_usage = self::formatBytes($peakMemory - self::$startMemory); + } + + // Update final stats + foreach ($finalStats as $key => $value) { + if (property_exists($log, $key)) { + $log->$key = $value; + } + } + + $log->save(); + + // Clean up old logs (keep last 1000 records) + self::cleanup(); + } + + /** + * Mark cron job as failed + * @param string $errorMessage Error message + * @param array $finalStats Final statistics + */ + public static function fail($errorMessage, $finalStats = []) + { + if (!self::$logId) return; + + $log = ORM::for_table('tbl_cron_logs')->find_one(self::$logId); + if (!$log) return; + + $log->finished_at = date('Y-m-d H:i:s'); + $log->status = 'failed'; + $log->error_message = $errorMessage; + + // Calculate execution time + if (self::$startTime) { + $log->execution_time = round(microtime(true) - self::$startTime, 3); + } + + // Update final stats + foreach ($finalStats as $key => $value) { + if (property_exists($log, $key)) { + $log->$key = $value; + } + } + + $log->save(); + } + + /** + * Get recent cron logs + * @param int $limit Number of records to return + * @param string $cronType Filter by cron type + * @return array + */ + public static function getRecent($limit = 50, $cronType = null) + { + $query = ORM::for_table('tbl_cron_logs'); + + if ($cronType) { + $query->where('cron_type', $cronType); + } + + return $query->order_by_desc('started_at') + ->limit($limit) + ->find_many(); + } + + /** + * Get cron job statistics + * @param int $days Number of days to look back + * @return array + */ + public static function getStats($days = 7) + { + $since = date('Y-m-d H:i:s', strtotime("-$days days")); + + $stats = ORM::for_table('tbl_cron_logs') + ->where_gte('started_at', $since) + ->find_many(); + + $result = [ + 'total_runs' => count($stats), + 'successful_runs' => 0, + 'failed_runs' => 0, + 'total_expired_users' => 0, + 'total_notifications_sent' => 0, + 'total_auto_renewals' => 0, + 'avg_execution_time' => 0, + 'last_run' => null, + 'last_success' => null, + 'last_failure' => null + ]; + + $totalExecutionTime = 0; + $executionCount = 0; + + foreach ($stats as $log) { + if ($log->status == 'completed') { + $result['successful_runs']++; + $result['last_success'] = $log->finished_at; + } elseif ($log->status == 'failed') { + $result['failed_runs']++; + $result['last_failure'] = $log->finished_at; + } + + $result['total_expired_users'] += $log->expired_users_processed; + $result['total_notifications_sent'] += $log->notifications_sent; + $result['total_auto_renewals'] += $log->auto_renewals_successful; + + if ($log->execution_time) { + $totalExecutionTime += $log->execution_time; + $executionCount++; + } + + if (!$result['last_run'] || $log->started_at > $result['last_run']) { + $result['last_run'] = $log->started_at; + } + } + + if ($executionCount > 0) { + $result['avg_execution_time'] = round($totalExecutionTime / $executionCount, 3); + } + + return $result; + } + + /** + * Clean up old logs (keep last 1000 records) + */ + private static function cleanup() + { + $count = ORM::for_table('tbl_cron_logs')->count(); + + if ($count > 1000) { + $logsToDelete = ORM::for_table('tbl_cron_logs') + ->order_by_asc('started_at') + ->limit($count - 1000) + ->find_many(); + + foreach ($logsToDelete as $log) { + $log->delete(); + } + } + } + + /** + * Format bytes to human readable format + * @param int $bytes + * @return string + */ + private static function formatBytes($bytes) + { + $units = ['B', 'KB', 'MB', 'GB']; + $bytes = max($bytes, 0); + $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); + $pow = min($pow, count($units) - 1); + + $bytes /= pow(1024, $pow); + + return round($bytes, 2) . ' ' . $units[$pow]; + } + + /** + * Check if cron is running (has a running log in last 5 minutes) + * @return bool + */ + public static function isRunning() + { + $fiveMinutesAgo = date('Y-m-d H:i:s', strtotime('-5 minutes')); + + $runningLog = ORM::for_table('tbl_cron_logs') + ->where('status', 'running') + ->where_gte('started_at', $fiveMinutesAgo) + ->find_one(); + + return $runningLog ? true : false; + } + + /** + * Get the last successful cron run + * @return object|null + */ + public static function getLastSuccess() + { + return ORM::for_table('tbl_cron_logs') + ->where('status', 'completed') + ->order_by_desc('finished_at') + ->find_one(); + } +} diff --git a/system/autoload/Csrf.php b/system/autoload/Csrf.php new file mode 100644 index 0000000..57752a0 --- /dev/null +++ b/system/autoload/Csrf.php @@ -0,0 +1,55 @@ + self::$tokenExpiration) { + self::clearToken(); + return false; + } + + return self::validateToken($token, $storedToken); + } + return false; + } + return true; + } + + public static function generateAndStoreToken() + { + $token = self::generateToken(); + $_SESSION['csrf_token'] = $token; + $_SESSION['csrf_token_time'] = time(); + return $token; + } + + public static function clearToken() + { + unset($_SESSION['csrf_token'], $_SESSION['csrf_token_time']); + } +} diff --git a/system/autoload/File.php b/system/autoload/File.php new file mode 100644 index 0000000..156d7be --- /dev/null +++ b/system/autoload/File.php @@ -0,0 +1,108 @@ + $name, + "admin" => $admin, + "position" => $position, + "icon" => $icon, + "function" => $function, + "label" => $label, + "color" => $color, + "auth" => $auth + ]; +} + +$hook_registered = array(); + +function register_hook($action, $function){ + global $hook_registered; + $hook_registered[] = [ + 'action' => $action, + 'function' => $function + ]; +} + +function run_hook($action){ + global $hook_registered; + foreach($hook_registered as $hook){ + if($hook['action'] == $action){ + if(function_exists($hook['function'])){ + call_user_func($hook['function']); + } + } + } +} diff --git a/system/autoload/Http.php b/system/autoload/Http.php new file mode 100644 index 0000000..7728075 --- /dev/null +++ b/system/autoload/Http.php @@ -0,0 +1,117 @@ +0){ + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + if (!empty($http_proxy)) { + curl_setopt($ch, CURLOPT_PROXY, $http_proxy); + if (!empty($http_proxyauth)) { + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $http_proxyauth); + } + } + $server_output = curl_exec($ch); + if (curl_errno($ch)) { + $error_msg = curl_error($ch); + } + curl_close($ch); + if($admin && $error_msg){ + r2(U . 'dashboard', 'd', $error_msg); + } + return ($server_output) ? $server_output : $error_msg; + } + + public static function postJsonData($url, $array_post, $headers = [], $basic = null) + { + global $http_proxy, $http_proxyauth, $admin; + $headers[] = 'Content-Type: application/json'; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); + curl_setopt($ch, CURLOPT_TIMEOUT, 15); + curl_setopt($ch, CURLOPT_VERBOSE, false); + curl_setopt($ch, CURLINFO_HEADER_OUT, false); + if (!empty($http_proxy)) { + curl_setopt($ch, CURLOPT_PROXY, $http_proxy); + if (!empty($http_proxyauth)) { + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $http_proxyauth); + } + } + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($array_post)); + if(is_array($headers) && count($headers)>0){ + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + if (!empty($basic)) { + curl_setopt($ch, CURLOPT_USERPWD, $basic); + } + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $server_output = curl_exec($ch); + if (curl_errno($ch)) { + $error_msg = curl_error($ch); + } + curl_close($ch); + if($admin && $error_msg){ + r2(U . 'dashboard', 'd', $error_msg); + } + return ($server_output) ? $server_output : $error_msg; + } + + + public static function postData($url, $array_post, $headers = [], $basic = null) + { + global $http_proxy, $http_proxyauth, $admin; + $headers[] = 'Content-Type: application/x-www-form-urlencoded'; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); + curl_setopt($ch, CURLOPT_TIMEOUT, 15); + curl_setopt($ch, CURLOPT_VERBOSE, false); + curl_setopt($ch, CURLINFO_HEADER_OUT, false); + if (!empty($http_proxy)) { + curl_setopt($ch, CURLOPT_PROXY, $http_proxy); + if (!empty($http_proxyauth)) { + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $http_proxyauth); + } + } + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($array_post)); + if(is_array($headers) && count($headers)>0){ + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + if (!empty($basic)) { + curl_setopt($ch, CURLOPT_USERPWD, $basic); + } + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $server_output = curl_exec($ch); + if (curl_errno($ch)) { + $error_msg = curl_error($ch); + } + curl_close($ch); + if($admin && $error_msg){ + r2(U . 'dashboard', 'd', $error_msg); + } + return ($server_output) ? $server_output : $error_msg; + } +} diff --git a/system/autoload/Lang.php b/system/autoload/Lang.php new file mode 100644 index 0000000..9a5cc18 --- /dev/null +++ b/system/autoload/Lang.php @@ -0,0 +1,262 @@ +diff($ago); + + $diff->w = floor($diff->d / 7); + $diff->d -= $diff->w * 7; + + $string = array( + 'y' => Lang::T('year'), + 'm' => Lang::T('month'), + 'w' => Lang::T('week'), + 'd' => Lang::T('day'), + 'h' => Lang::T('hour'), + 'i' => Lang::T('minute'), + 's' => Lang::T('second'), + ); + foreach ($string as $k => &$v) { + if ($diff->$k) { + $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : ''); + } else { + unset($string[$k]); + } + } + + if (!$full) + $string = array_slice($string, 0, 1); + return $string ? implode(', ', $string) .' '. Lang::T('ago') : Lang::T('just now'); + } + + public static function nl2br($text) + { + return nl2br($text); + } + + public static function arrayCount($arr) + { + if (is_array($arr)) { + return count($arr); + } else if (is_object($arr)) { + // Handle IdiormResultSet and other countable objects + if (method_exists($arr, 'count')) { + return $arr->count(); + } elseif (method_exists($arr, 'toArray')) { + return count($arr->toArray()); + } elseif (is_countable($arr)) { + return count($arr); + } else { + // Try to iterate and count + $count = 0; + foreach ($arr as $item) { + $count++; + } + return $count; + } + } else { + return 0; + } + } + + public static function getNotifText($key) + { + global $_notifmsg, $_notifmsg_default; + if (isset($_notifmsg[$key])) { + return $_notifmsg[$key]; + } else { + return $_notifmsg_default[$key]; + } + } + + public static function ucWords($text) + { + return ucwords(str_replace('_', ' ', $text)); + } + + public static function randomUpLowCase($text) + { + $jml = strlen($text); + $result = ''; + for ($i = 0; $i < $jml; $i++) { + if (rand(0, 99) % 2) { + $result .= strtolower(substr($text, $i, 1)); + } else { + $result .= substr($text, $i, 1); + } + } + return $result; + } + + /** + * $pad_type + * 0 Left + * 1 right + * 2 center + * */ + public static function pad($text, $pad_string = ' ', $pad_type = 0) + { + global $config; + $cols = 37; + if ($config['printer_cols']) { + $cols = $config['printer_cols']; + } + $text = trim($text); + $texts = explode("\n", $text); + if (count($texts) > 1) { + $text = ''; + foreach ($texts as $t) { + $text .= self::pad(trim($t), $pad_string, $pad_type) . "\n"; + } + return $text; + } else { + return str_pad(trim($text), $cols, $pad_string, $pad_type); + } + } + + public static function pads($textLeft, $textRight, $pad_string = ' ') + { + global $config; + $cols = 37; + if ($config['printer_cols']) { + $cols = $config['printer_cols']; + } + return $textLeft . str_pad($textRight, $cols - strlen($textLeft), $pad_string, 0); + } + + public static function translate($txt, $to = 'id') + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "https://translate.google.com/m?hl=en&sl=en&tl=$to&ie=UTF-8&prev=_m&q=" . urlencode($txt)); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (iPhone; CPU OS 13_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/28.1 Mobile/15E148 Safari/605.1.15"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_HEADER, 0); + $hasil = curl_exec($ch); + curl_close($ch); + $temp = explode('
', $hasil); + if (count($temp) > 0) { + $temp = explode("create(); + $d->date = date('Y-m-d H:i:s'); + $d->type = $type; + $d->description = $description; + $d->userid = $userid; + $d->ip = (empty($username)) ? $_SERVER["REMOTE_ADDR"] : $username; + $d->save(); + } + + public static function arrayToText($array, $start = '', $result = '') + { + foreach ($array as $k => $v) { + if (is_array($v)) { + $result = Log::arrayToText($v, "$start$k.", $result); + } else { + $result .= $start.$k ." : ". strval($v) ."\n"; + } + } + return $result; + } +} \ No newline at end of file diff --git a/system/autoload/Message.php b/system/autoload/Message.php new file mode 100644 index 0000000..1252366 --- /dev/null +++ b/system/autoload/Message.php @@ -0,0 +1,333 @@ + 4 && substr($config['sms_url'], 0, 4) != "http") { + if (strlen($txt) > 160) { + $txts = str_split($txt, 160); + try { + $mikrotik = Mikrotik::info($config['sms_url']); + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + foreach ($txts as $txt) { + Mikrotik::sendSMS($client, $phone, $txt); + } + } catch (Exception $e) { + // ignore, add to logs + _log("Failed to send SMS using Mikrotik.\n" . $e->getMessage(), 'SMS', 0); + } + } else { + try { + $mikrotik = Mikrotik::info($config['sms_url']); + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::sendSMS($client, $phone, $txt); + } catch (Exception $e) { + // ignore, add to logs + _log("Failed to send SMS using Mikrotik.\n" . $e->getMessage(), 'SMS', 0); + } + } + } else { + $smsurl = str_replace('[number]', urlencode($phone), $config['sms_url']); + $smsurl = str_replace('[text]', urlencode($txt), $smsurl); + return Http::getData($smsurl); + } + } + } + + public static function sendWhatsapp($phone, $txt) + { + global $config; + if(empty($txt)){ + return ""; + } + run_hook('send_whatsapp'); #HOOK + if (!empty($config['wa_url'])) { + $waurl = str_replace('[number]', urlencode(Lang::phoneFormat($phone)), $config['wa_url']); + $waurl = str_replace('[text]', urlencode($txt), $waurl); + return Http::getData($waurl); + } + } + + public static function sendEmail($to, $subject, $body) + { + global $config; + if(empty($body)){ + return ""; + } + run_hook('send_email'); #HOOK + if (empty($config['smtp_host'])) { + $attr = ""; + if (!empty($config['mail_from'])) { + $attr .= "From: " . $config['mail_from'] . "\r\n"; + } + if (!empty($config['mail_reply_to'])) { + $attr .= "Reply-To: " . $config['mail_reply_to'] . "\r\n"; + } + mail($to, $subject, $body, $attr); + } else { + $mail = new PHPMailer(); + $mail->isSMTP(); + $mail->SMTPDebug = SMTP::DEBUG_SERVER; + $mail->Host = $config['smtp_host']; + $mail->SMTPAuth = true; + $mail->Username = $config['smtp_user']; + $mail->Password = $config['smtp_pass']; + $mail->SMTPSecure = $config['smtp_ssltls']; + $mail->Port = $config['smtp_port']; + if (!empty($config['mail_from'])) { + $mail->setFrom($config['mail_from']); + } + if (!empty($config['mail_reply_to'])) { + $mail->addReplyTo($config['mail_reply_to']); + } + $mail->isHTML(false); + $mail->addAddress($to); + $mail->Subject = $subject; + $mail->Body = $body; + $mail->send(); + } + } + + public static function sendPackageNotification($customer, $package, $price, $message, $via, $plan_type = null) + { + global $ds; + if(empty($message)){ + return ""; + } + + // Get plan-specific template if plan_type is provided + $template = self::getPlanSpecificTemplate($message, $plan_type); + + $msg = str_replace('[[name]]', $customer['fullname'], $template); + $msg = str_replace('[[username]]', $customer['username'], $msg); + $msg = str_replace('[[plan]]', $package, $msg); + $msg = str_replace('[[package]]', $package, $msg); + $msg = str_replace('[[price]]', Lang::moneyFormat($price), $msg); + $msg = str_replace('[[plan_type]]', $plan_type ? ucfirst(strtolower($plan_type)) : 'Internet', $msg); + + // Add service-specific variables + global $config; + $msg = str_replace('[[service_portal]]', !empty($config['hotspot_url']) ? $config['hotspot_url'] : APP_URL, $msg); + $msg = str_replace('[[support_contact]]', !empty($config['phone']) ? $config['phone'] : 'our support team', $msg); + + list($bills, $add_cost) = User::getBills($customer['id']); + if($add_cost>0){ + $note = ""; + foreach ($bills as $k => $v) { + $note .= $k . " : " . Lang::moneyFormat($v) . "\n"; + } + $note .= "Total : " . Lang::moneyFormat($add_cost+$price) . "\n"; + $msg = str_replace('[[bills]]', $note, $msg); + }else{ + $msg = str_replace('[[bills]]', '', $msg); + } + if ($ds) { + $msg = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($ds['expiration'], $ds['time']), $msg); + }else{ + $msg = str_replace('[[expired_date]]', "", $msg); + } + if ( + !empty($customer['phonenumber']) && strlen($customer['phonenumber']) > 5 + && !empty($template) && in_array($via, ['sms', 'wa']) + ) { + if ($via == 'sms') { + echo Message::sendSMS($customer['phonenumber'], $msg); + } else if ($via == 'wa') { + echo Message::sendWhatsapp($customer['phonenumber'], $msg); + } + } + return "$via: $msg"; + } + + public static function sendBalanceNotification($phone, $name, $balance, $balance_now, $message, $via) + { + $msg = str_replace('[[name]]', $name, $message); + $msg = str_replace('[[current_balance]]', Lang::moneyFormat($balance_now), $msg); + $msg = str_replace('[[balance]]', Lang::moneyFormat($balance), $msg); + if ( + !empty($phone) && strlen($phone) > 5 + && !empty($message) && in_array($via, ['sms', 'wa']) + ) { + if ($via == 'sms') { + Message::sendSMS($phone, $msg); + } else if ($via == 'wa') { + Message::sendWhatsapp($phone, $msg); + } + } + return "$via: $msg"; + } + + public static function sendInvoice($cust, $trx) + { + global $config; + $textInvoice = Lang::getNotifText('invoice_paid'); + $textInvoice = str_replace('[[company_name]]', $config['CompanyName'], $textInvoice); + $textInvoice = str_replace('[[address]]', $config['address'], $textInvoice); + $textInvoice = str_replace('[[phone]]', $config['phone'], $textInvoice); + $textInvoice = str_replace('[[invoice]]', $trx['invoice'], $textInvoice); + $textInvoice = str_replace('[[date]]', Lang::dateAndTimeFormat($trx['recharged_on'], $trx['recharged_time']), $textInvoice); + if (!empty($trx['note'])) { + $textInvoice = str_replace('[[note]]', $trx['note'], $textInvoice); + } + $gc = explode("-", $trx['method']); + $textInvoice = str_replace('[[payment_gateway]]', trim($gc[0]), $textInvoice); + $textInvoice = str_replace('[[payment_channel]]', trim($gc[1]), $textInvoice); + $textInvoice = str_replace('[[type]]', $trx['type'], $textInvoice); + $textInvoice = str_replace('[[plan_name]]', $trx['plan_name'], $textInvoice); + $textInvoice = str_replace('[[plan_price]]', Lang::moneyFormat($trx['price']), $textInvoice); + $textInvoice = str_replace('[[name]]', $cust['fullname'], $textInvoice); + $textInvoice = str_replace('[[note]]', $cust['note'], $textInvoice); + $textInvoice = str_replace('[[user_name]]', $trx['username'], $textInvoice); + $textInvoice = str_replace('[[user_password]]', $cust['password'], $textInvoice); + $textInvoice = str_replace('[[username]]', $trx['username'], $textInvoice); + $textInvoice = str_replace('[[password]]', $cust['password'], $textInvoice); + $textInvoice = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($trx['expiration'], $trx['time']), $textInvoice); + $textInvoice = str_replace('[[footer]]', $config['note'], $textInvoice); + + if ($config['user_notification_payment'] == 'sms') { + Message::sendSMS($cust['phonenumber'], $textInvoice); + } else if ($config['user_notification_payment'] == 'wa') { + Message::sendWhatsapp($cust['phonenumber'], $textInvoice); + } + } + + public static function sendRegistrationNotification($customer) + { + global $config; + + if(empty($customer['phonenumber']) || strlen($customer['phonenumber']) < 5){ + return ""; + } + + $textRegistration = Lang::getNotifText('user_registration'); + if(empty($textRegistration)) { + return ""; + } + + $textRegistration = str_replace('[[company_name]]', $config['CompanyName'], $textRegistration); + $textRegistration = str_replace('[[name]]', $customer['fullname'], $textRegistration); + $textRegistration = str_replace('[[user_name]]', $customer['username'], $textRegistration); + $textRegistration = str_replace('[[username]]', $customer['username'], $textRegistration); + $textRegistration = str_replace('[[password]]', $customer['password'], $textRegistration); + $textRegistration = str_replace('[[service_type]]', $customer['service_type'], $textRegistration); + $textRegistration = str_replace('[[footer]]', $config['note'], $textRegistration); + + // Use the same notification setting as recharge process + if ($config['user_notification_payment'] == 'sms') { + Message::sendSMS($customer['phonenumber'], $textRegistration); + } else if ($config['user_notification_payment'] == 'wa') { + Message::sendWhatsapp($customer['phonenumber'], $textRegistration); + } + + return $textRegistration; + } + + /** + * Get plan-specific template based on plan type + * @param string $message_key The message key (e.g., 'expired', 'reminder_7_day') + * @param string $plan_type The plan type ('Hotspot', 'PPPOE', etc.) + * @return string The appropriate template + */ + public static function getPlanSpecificTemplate($message_key, $plan_type = null) + { + global $config; + + // Load notification templates + $notifications = self::loadNotificationTemplates(); + + // If no plan type provided or template doesn't exist, return original message + if (empty($plan_type) || !isset($notifications[$message_key])) { + return $message_key; + } + + // Check if it's a new structure with plan-specific templates + if (is_array($notifications[$message_key])) { + $plan_type_lower = strtolower($plan_type); + + // Try to get plan-specific template + if (isset($notifications[$message_key][$plan_type_lower])) { + return $notifications[$message_key][$plan_type_lower]; + } + + // Fallback to default template + if (isset($notifications[$message_key]['default'])) { + return $notifications[$message_key]['default']; + } + } + + // Fallback to original message for backward compatibility + return $notifications[$message_key]; + } + + /** + * Load notification templates from file + * @return array Notification templates + */ + public static function loadNotificationTemplates() + { + global $root_path; + $notifications_file = $root_path . 'system/uploads/notifications.default.json'; + + if (file_exists($notifications_file)) { + $content = file_get_contents($notifications_file); + $notifications = json_decode($content, true); + return $notifications ?: []; + } + + return []; + } + + /** + * Detect plan type from customer and package information + * @param array $customer Customer information + * @param string $package Package name + * @return string|null Plan type + */ + public static function detectPlanType($customer, $package) + { + // Try to get plan type from customer's service_type + if (!empty($customer['service_type']) && in_array($customer['service_type'], ['Hotspot', 'PPPoE'])) { + return $customer['service_type']; + } + + // Try to detect from package name (basic detection) + $package_lower = strtolower($package); + if (strpos($package_lower, 'hotspot') !== false) { + return 'Hotspot'; + } elseif (strpos($package_lower, 'pppoe') !== false) { + return 'PPPOE'; + } + + return null; + } +} 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..274e1dc --- /dev/null +++ b/system/autoload/Mikrotik.php @@ -0,0 +1,562 @@ +where('name', $name)->find_one(); + } + + public static function getClient($ip, $user, $pass, $timeout = 10) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $iport = explode(":", $ip); + return new RouterOS\Client($iport[0], $user, $pass, ($iport[1]) ? $iport[1] : null, false, $timeout); + } + + public static function isUserLogin($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot active print', + RouterOS\Query::where('user', $username) + ); + return $client->sendSync($printRequest)->getProperty('.id'); + } + + public static function logMeIn($client, $user, $pass, $ip, $mac) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/hotspot/active/login'); + $client->sendSync( + $addRequest + ->setArgument('user', $user) + ->setArgument('password', $pass) + ->setArgument('ip', $ip) + ->setArgument('mac-address', $mac) + ); + } + + public static function logMeOut($client, $user) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot active print', + RouterOS\Query::where('user', $user) + ); + $id = $client->sendSync($printRequest)->getProperty('.id'); + $removeRequest = new RouterOS\Request('/ip/hotspot/active/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $id) + ); + } + + public static function addHotspotPlan($client, $name, $sharedusers, $rate) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/hotspot/user/profile/add'); + $client->sendSync( + $addRequest + ->setArgument('name', $name) + ->setArgument('shared-users', $sharedusers) + ->setArgument('rate-limit', $rate) + ); + } + + public static function setHotspotPlan($client, $name, $sharedusers, $rate) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot user profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + if (empty($profileID)) { + Mikrotik::addHotspotPlan($client, $name, $sharedusers, $rate); + } else { + $setRequest = new RouterOS\Request('/ip/hotspot/user/profile/set'); + $client->sendSync( + $setRequest + ->setArgument('numbers', $profileID) + ->setArgument('shared-users', $sharedusers) + ->setArgument('rate-limit', $rate) + ); + } + } + + public static function setHotspotExpiredPlan($client, $name, $pool) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot user profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + if (empty($profileID)) { + $addRequest = new RouterOS\Request('/ip/hotspot/user/profile/add'); + $client->sendSync( + $addRequest + ->setArgument('name', $name) + ->setArgument('shared-users', 3) + ->setArgument('address-pool', $pool) + ->setArgument('rate-limit', '512K/512K') + ); + } else { + $setRequest = new RouterOS\Request('/ip/hotspot/user/profile/set'); + $client->sendSync( + $setRequest + ->setArgument('numbers', $profileID) + ->setArgument('shared-users', 3) + ->setArgument('address-pool', $pool) + ->setArgument('rate-limit', '512K/512K') + ); + } + } + + public static function removeHotspotPlan($client, $name) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot user profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/hotspot/user/profile/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $profileID) + ); + } + + public static function removeHotspotUser($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot user print .proplist=.id', + RouterOS\Query::where('name', $username) + ); + $userID = $client->sendSync($printRequest)->getProperty('.id'); + $removeRequest = new RouterOS\Request('/ip/hotspot/user/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $userID) + ); + } + + public static function addHotspotUser($client, $plan, $customer) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/hotspot/user/add'); + if ($plan['typebp'] == "Limited") { + if ($plan['limit_type'] == "Time_Limit") { + if ($plan['time_unit'] == 'Hrs') + $timelimit = $plan['time_limit'] . ":00:00"; + else + $timelimit = "00:" . $plan['time_limit'] . ":00"; + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('profile', $plan['name_plan']) + ->setArgument('password', $customer['password']) + ->setArgument('comment', $customer['fullname']) + ->setArgument('email', $customer['email']) + ->setArgument('limit-uptime', $timelimit) + ); + } else if ($plan['limit_type'] == "Data_Limit") { + if ($plan['data_unit'] == 'GB') + $datalimit = $plan['data_limit'] . "000000000"; + else + $datalimit = $plan['data_limit'] . "000000"; + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('profile', $plan['name_plan']) + ->setArgument('password', $customer['password']) + ->setArgument('comment', $customer['fullname']) + ->setArgument('email', $customer['email']) + ->setArgument('limit-bytes-total', $datalimit) + ); + } else if ($plan['limit_type'] == "Both_Limit") { + if ($plan['time_unit'] == 'Hrs') + $timelimit = $plan['time_limit'] . ":00:00"; + else + $timelimit = "00:" . $plan['time_limit'] . ":00"; + if ($plan['data_unit'] == 'GB') + $datalimit = $plan['data_limit'] . "000000000"; + else + $datalimit = $plan['data_limit'] . "000000"; + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('profile', $plan['name_plan']) + ->setArgument('password', $customer['password']) + ->setArgument('comment', $customer['fullname']) + ->setArgument('email', $customer['email']) + ->setArgument('limit-uptime', $timelimit) + ->setArgument('limit-bytes-total', $datalimit) + ); + } + } else { + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('profile', $plan['name_plan']) + ->setArgument('comment', $customer['fullname']) + ->setArgument('email', $customer['email']) + ->setArgument('password', $customer['password']) + ); + } + } + + public static function setHotspotUser($client, $user, $pass) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ip/hotspot/user/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('password', $pass); + $client->sendSync($setRequest); + } + + public static function setHotspotUserPackage($client, $user, $plan) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ip/hotspot/user/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('profile', $plan); + $client->sendSync($setRequest); + } + + public static function removeHotspotActiveUser($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $onlineRequest = new RouterOS\Request('/ip/hotspot/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('user', $username)); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/hotspot/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + } + + public static function removePpoeUser($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + //$printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $username)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + $removeRequest = new RouterOS\Request('/ppp/secret/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + } + + public static function addPpoeUser($client, $plan, $customer) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ppp/secret/add'); + if (!empty($customer['pppoe_password'])) { + $pass = $customer['pppoe_password']; + } else { + $pass = $customer['password']; + } + $client->sendSync( + $addRequest + ->setArgument('name', $customer['username']) + ->setArgument('service', 'pppoe') + ->setArgument('profile', $plan['name_plan']) + ->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email']) + ->setArgument('password', $pass) + ); + } + + public static function setPpoeUser($client, $user, $pass) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ppp/secret/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('password', $pass); + $client->sendSync($setRequest); + } + + public static function setPpoeUserPlan($client, $user, $plan) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user)); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ppp/secret/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('profile', $plan); + $client->sendSync($setRequest); + } + + public static function removePpoeActive($client, $username) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $onlineRequest = new RouterOS\Request('/ppp/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('name', $username)); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ppp/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + } + + public static function removePool($client, $name) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip pool print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $poolID = $client->sendSync($printRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/pool/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $poolID) + ); + } + + public static function addPool($client, $name, $ip_address) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/pool/add'); + $client->sendSync( + $addRequest + ->setArgument('name', $name) + ->setArgument('ranges', $ip_address) + ); + } + + public static function setPool($client, $name, $ip_address) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip pool print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $poolID = $client->sendSync($printRequest)->getProperty('.id'); + + if (empty($poolID)) { + self::addPool($client, $name, $ip_address); + } else { + $setRequest = new RouterOS\Request('/ip/pool/set'); + $client->sendSync( + $setRequest + ->setArgument('numbers', $poolID) + ->setArgument('ranges', $ip_address) + ); + } + } + + + public static function addPpoePlan($client, $name, $pool, $rate) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ppp/profile/add'); + $client->sendSync( + $addRequest + ->setArgument('name', $name) + ->setArgument('local-address', $pool) + ->setArgument('remote-address', $pool) + ->setArgument('rate-limit', $rate) + ); + } + + public static function setPpoePlan($client, $name, $pool, $rate) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ppp profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + if (empty($profileID)) { + self::addPpoePlan($client, $name, $pool, $rate); + } else { + $setRequest = new RouterOS\Request('/ppp/profile/set'); + $client->sendSync( + $setRequest + ->setArgument('numbers', $profileID) + ->setArgument('local-address', $pool) + ->setArgument('remote-address', $pool) + ->setArgument('rate-limit', $rate) + ); + } + } + + public static function removePpoePlan($client, $name) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ppp profile print .proplist=.id', + RouterOS\Query::where('name', $name) + ); + $profileID = $client->sendSync($printRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ppp/profile/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $profileID) + ); + } + + public static function sendSMS($client, $to, $message) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $smsRequest = new RouterOS\Request('/tool sms send'); + $smsRequest + ->setArgument('phone-number', $to) + ->setArgument('message', $message); + $client->sendSync($smsRequest); + } + + public static function getIpHotspotUser($client, $username){ + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip hotspot active print', + RouterOS\Query::where('user', $username) + ); + return $client->sendSync($printRequest)->getProperty('address'); + } + + public static function addIpToAddressList($client, $ip, $listName, $comment = '') + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $addRequest = new RouterOS\Request('/ip/firewall/address-list/add'); + $client->sendSync( + $addRequest + ->setArgument('address', $ip) + ->setArgument('comment', $comment) + ->setArgument('list', $listName) + ); + } + + public static function removeIpFromAddressList($client, $ip) + { + global $_app_stage; + if ($_app_stage == 'demo') { + return null; + } + $printRequest = new RouterOS\Request( + '/ip firewall address-list print .proplist=.id', + RouterOS\Query::where('address', $ip) + ); + $id = $client->sendSync($printRequest)->getProperty('.id'); + $removeRequest = new RouterOS\Request('/ip/firewall/address-list/remove'); + $client->sendSync( + $removeRequest + ->setArgument('numbers', $id) + ); + } +} 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; + } + } +} 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 diff --git a/system/autoload/User.php b/system/autoload/User.php new file mode 100644 index 0000000..c8b0919 --- /dev/null +++ b/system/autoload/User.php @@ -0,0 +1,202 @@ + $v) { + // if has : then its an installment + if (strpos($v, ":") === false) { + // Not installment + $bills[$k] = $v; + $addcost += $v; + } else { + // installment + list($cost, $rem) = explode(":", $v); + // :0 installment is done + if (!empty($rem)) { + $bills[$k] = $cost; + $addcost += $cost; + } + } + } + return [$bills, $addcost]; + } + + public static function billsPaid($bills, $id = 0) + { + if (!$id) { + $id = User::getID(); + if (!$id) { + return []; + } + } + foreach ($bills as $k => $v) { + // if has : then its an installment + $v = User::getAttribute($k, $id); + if (strpos($v, ":") === false) { + // Not installment, no need decrement + } else { + // installment + list($cost, $rem) = explode(":", $v); + // :0 installment is done + if ($rem != 0) { + User::setAttribute($k, "$cost:" . ($rem - 1), $id); + } + } + } + } + + public static function setAttribute($name, $value, $id = 0) + { + if (!$id) { + $id = User::getID(); + if (!$id) { + return ''; + } + } + $f = ORM::for_table('tbl_customers_fields')->where('field_name', $name)->where('customer_id', $id)->find_one(); + if (!$f) { + $f = ORM::for_table('tbl_customers_fields')->create(); + $f->customer_id = $id; + $f->field_name = $name; + $f->field_value = $value; + $f->save(); + $result = $f->id(); + if ($result) { + return $result; + } + } else { + $f->field_value = $value; + $f->save(); + return $f['id']; + } + return 0; + } + + public static function getAttribute($name, $id = 0) + { + if (!$id) { + $id = User::getID(); + if (!$id) { + return []; + } + } + $f = ORM::for_table('tbl_customers_fields')->where('field_name', $name)->where('customer_id', $id)->find_one(); + if ($f) { + return $f['field_value']; + } + return ''; + } + + public static function getAttributes($endWith, $id = 0) + { + if (!$id) { + $id = User::getID(); + if (!$id) { + return []; + } + } + $attrs = []; + $f = ORM::for_table('tbl_customers_fields')->where_like('field_name', "%$endWith")->where('customer_id', $id)->find_many(); + if ($f) { + foreach ($f as $k) { + $attrs[$k['field_name']] = $k['field_value']; + } + return $attrs; + } + return []; + } + + public static function setCookie($uid) + { + global $db_password; + if (isset($uid)) { + $time = time(); + setcookie('uid', $uid . '.' . $time . '.' . sha1($uid . '.' . $time . '.' . $db_password), time() + 86400 * 30); + } + } + + public static function removeCookie() + { + if (isset($_COOKIE['uid'])) { + setcookie('uid', '', time() - 86400); + } + } + + public static function _info($id = 0) + { + global $config; + if ($config['maintenance_mode'] == true) { + if ($config['maintenance_mode_logout'] == true) { + r2(U . 'logout', 'd', ''); + } else { + displayMaintenanceMessage(); + } + } + if (!$id) { + $id = User::getID(); + } + $d = ORM::for_table('tbl_customers')->find_one($id); + if ($d['status'] == 'Banned') { + _alert(Lang::T('This account status') . ' : ' . Lang::T($d['status']), 'danger', "logout"); + } + if (empty($d['username'])) { + r2(U . 'logout', 'd', ''); + } + return $d; + } + + public static function _billing($id = 0) + { + if (!$id) { + $id = User::getID(); + } + $d = ORM::for_table('tbl_user_recharges') + ->select('tbl_user_recharges.id', 'id') + ->selects([ + 'customer_id', 'username', 'plan_id', 'namebp', 'recharged_on', 'recharged_time', 'expiration', 'time', + 'status', 'method', 'plan_type', + ['tbl_user_recharges.routers', 'routers'], + ['tbl_user_recharges.type', 'type'], + 'admin_id', 'prepaid' + ]) + ->where('customer_id', $id) + ->left_outer_join('tbl_plans', array('tbl_plans.id', '=', 'tbl_user_recharges.plan_id')) + ->find_many(); + return $d; + } +} diff --git a/system/autoload/Validator.php b/system/autoload/Validator.php new file mode 100644 index 0000000..c200a2b --- /dev/null +++ b/system/autoload/Validator.php @@ -0,0 +1,323 @@ += $max) return false; + return true; + } + + /** + * Email addres check + * + * @access public + * @param string $string + * @param array $exclude + * @return bool + */ + public static function Email($string, $exclude = "") + { + if (self::textHit($string, $exclude)) return false; + return (bool)preg_match("/^([a-z0-9])(([-a-z0-9._])*([a-z0-9]))*\@([a-z0-9])(([a-z0-9-])*([a-z0-9]))+(\.([a-z0-9])([-a-z0-9_-])?([a-z0-9])+)+$/i", $string); + } + + /** + * URL check + * + * @access public + * @param strin $string + * @return bool + */ + public static function Url($string, $exclude = "") + { + if (self::textHit($string, $exclude)) return false; + return (bool)preg_match("/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i", $string); + } + + /** + * IP + * + * @access public + * @param string $string + * @return void + */ + public static function Ip($string) + { + return (bool)preg_match("/^(1?\d{1,2}|2([0-4]\d|5[0-5]))(\.(1?\d{1,2}|2([0-4]\d|5[0-5]))){3}$/", $string); + } + + /** + * Check if it is an number + * + * @access public + * @param int $integer + * @param int $max + * @param int $min + * @return bool + */ + public static function Number($integer, $max = null, $min = 0) + { + if (preg_match("/^\-?\+?[0-9e1-9]+$/", $integer)) { + if (!self::numberBetween($integer, $max, $min)) return false; + return true; + } + return false; + } + + /** + * Check if it is an unsigned number + * + * @access public + * @param int $integer + * @return bool + */ + public static function UnsignedNumber($integer) + { + return (bool)preg_match("/^\+?[0-9]+$/", $integer); + } + + /** + * Float + * + * @access public + * @param string $string + * @return bool + */ + public static function Float($string) + { + return (bool)($string == strval(floatval($string))) ? true : false; + } + + /** + * Alpha check + * + * @access public + * @param string $string + * @return void + */ + public static function Alpha($string) + { + return (bool)preg_match("/^[a-zA-Z]+$/", $string); + } + + /** + * Alpha numeric check + * + * @access public + * @param string $string + * @return void + */ + public static function AlphaNumeric($string) + { + return (bool)preg_match("/^[0-9a-zA-Z]+$/", $string); + } + + /** + * Specific chars check + * + * @access public + * @param string $string + * @param array $allowed + * @return void + */ + public static function Chars($string, $allowed = array("a-z")) + { + return (bool)preg_match("/^[" . implode("", $allowed) . "]+$/", $string); + } + + /** + * Check length of an string + * + * @access public + * @param string $stirng + * @param int $max + * @param int $min + * @return bool + */ + public static function Length($string, $max = null, $min = 0) + { + $length = strlen($string); + if (!self::numberBetween($length, $max, $min)) return false; + return true; + } + + /** + * Hex color check + * + * @access public + * @param string $string + * @return void + */ + public static function HexColor($string) + { + return (bool)preg_match("/^(#)?([0-9a-f]{1,2}){3}$/i", $string); + } + + /** + * Data validation + * + * Does'nt matter how you provide the date + * dd/mm/yyyy + * dd-mm-yyyy + * yyyy/mm/dd + * yyyy-mm-dd + * + * @access public + * @param string $string + * @return bool + */ + public static function Date($string) + { + $date = date('Y', strtotime($string)); + return ($date == "1970" || $date == '') ? false : true; + } + + /** + * Older than check + * + * @access public + * @param string $string + * @param int $age + * @return bool + */ + public static function OlderThan($string, $age) + { + $date = date('Y', strtotime($string)); + if ($date == "1970" || $date == '') return false; + return (date('Y') - $date) > $age ? true : false; + } + + /** + * XML valid + * + * @access public + * @param string $string + * @return bool + */ + public static function Xml($string) + { + $Xml = @simplexml_load_string($string); + return ($Xml === false) ? false : true; + } + + /** + * Is filesize between + * + * @access public + * @param string $file + * @param int $max + * @param int $min + * @return bool + */ + public static function FilesizeBetween($file, $max = null, $min = 0) + { + $filesize = filesize($file); + return self::numberBetween($filesize, $max, $min); + } + + /** + * Is image width between + * + * @access public + * @param string $image + * @param int $max_width + * @param int $min_width + * @param int $max_height + * @param int $min_height + * @return void + */ + public static function ImageSizeBetween($image, $max_width = "", $min_width = 0, $max_height = "", $min_height = 0) + { + $size = getimagesize($image); + if (!self::numberBetween($size[0], $max_width, $min_width)) return false; + if (!self::numberBetween($size[1], $max_height, $min_height)) return false; + return true; + } + + /** + * Phone numbers + * + * @access public + * @param string $phone + * @return bool + */ + public static function Phone($phone) + { + $formats = array( + '###-###-####', + '####-###-###', + '(###) ###-###', + '####-####-####', + '##-###-####-####', + '####-####', + '###-###-###', + '#####-###-###', + '##########', + '####-##-##-##' + ); + $format = trim(preg_replace("/[0-9]/", "#", $phone)); + return (bool)in_array($format, $formats); + } + + public static function countRouterPlan($plans, $router){ + $n = 0; + foreach ($plans as $plan){ + if($plan['routers'] == $router){ + $n++; + } + } + return $n; + } + + public static function isRouterHasPlan($plans, $router){ + foreach ($plans as $plan){ + if($plan['routers'] == $router){ + return true; + } + } + return false; + } + +} diff --git a/system/autoload/Widget.php b/system/autoload/Widget.php new file mode 100644 index 0000000..0a368c2 --- /dev/null +++ b/system/autoload/Widget.php @@ -0,0 +1,50 @@ +'; + foreach($rows as $row){ + + } + $result .= '
'; + } + + public static function columns($cols, $result){ + $c = count($cols); + switch($c){ + case 1: + $result .= '
'; + break; + case 2: + $result .= '
'; + break; + case 3: + $result .= '
'; + break; + case 4: + $result .= '
'; + break; + case 5: + $result .= '
'; + break; + default: + $result .= '
'; + break; + } + + foreach($cols as $col){ + } + $result .= '
'; + } +} \ No newline at end of file diff --git a/system/autoload/index.html b/system/autoload/index.html new file mode 100644 index 0000000..9757970 --- /dev/null +++ b/system/autoload/index.html @@ -0,0 +1,8 @@ + + + 403 Forbidden + + +

Directory access is forbidden.

+ + \ No newline at end of file