diff --git a/system/controllers/callback.php b/system/controllers/callback.php new file mode 100644 index 0000000..352f2eb --- /dev/null +++ b/system/controllers/callback.php @@ -0,0 +1,22 @@ +assign('_title', 'CodeCanyon.net'); +$ui->assign('_system_menu', 'settings'); + +$plugin_repository = 'https://hotspotbilling.github.io/Plugin-Repository/repository.json'; + +$action = $routes['1']; +$ui->assign('_admin', $admin); +$cache = File::pathFixer($CACHE_PATH . '/codecanyon.json'); + +if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) { + _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard"); +} +if (empty($config['envato_token'])) { + r2(U . 'settings/app', 'w', 'Envato Personal Access Token is not set'); +} + +switch ($action) { + + case 'install': + if (!is_writeable(File::pathFixer($CACHE_PATH . '/'))) { + r2(U . "codecanyon", 'e', 'Folder system/cache/ is not writable'); + } + if (!is_writeable($PLUGIN_PATH)) { + r2(U . "codecanyon", 'e', 'Folder plugin/ is not writable'); + } + if (!is_writeable($PAYMENTGATEWAY_PATH)) { + r2(U . "codecanyon", 'e', 'Folder paymentgateway/ is not writable'); + } + set_time_limit(-1); + $item_id = $routes['2']; + $tipe = $routes['3']; + $result = Http::getData('https://api.envato.com/v3/market/buyer/download?item_id=' . $item_id, ['Authorization: Bearer ' . $config['envato_token']]); + $json = json_decode($result, true); + if (!isset($json['download_url'])) { + r2(U . 'codecanyon', 'e', 'Failed to get download url. ' . $json['description']); + } + $file = File::pathFixer($CACHE_PATH . '/codecanyon/'); + if (!file_exists($file)) { + mkdir($file); + } + $file .= $item_id . '.zip'; + if (file_exists($file)) + unlink($file); + //download + $fp = fopen($file, 'w+'); + $ch = curl_init($json['download_url']); + curl_setopt($ch, CURLOPT_POST, 0); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120); + curl_setopt($ch, CURLOPT_TIMEOUT, 120); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_exec($ch); + curl_close($ch); + fclose($fp); + //extract + $target = File::pathFixer($CACHE_PATH . '/codecanyon/' . $item_id . '/'); + $zip = new ZipArchive(); + $zip->open($file); + $zip->extractTo($target); + $zip->close(); + //moving + if (file_exists($target . 'plugin')) { + File::copyFolder($target . 'plugin', $PLUGIN_PATH . DIRECTORY_SEPARATOR); + } else if (file_exists($target . 'paymentgateway')) { + File::copyFolder($target . 'paymentgateway', $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR); + } else if (file_exists($target . 'theme')) { + File::copyFolder($target . 'theme', File::pathFixer('ui/themes/')); + } + //Cleaning + File::deleteFolder($target); + unlink($file); + r2(U . "codecanyon", 's', 'Installation success'); + case 'reload': + if (file_exists($cache)) + unlink($cache); + default: + if (class_exists('ZipArchive')) { + $zipExt = true; + } else { + $zipExt = false; + } + $ui->assign('zipExt', $zipExt); + + if (file_exists($cache) && time() - filemtime($cache) < (24 * 60 * 60)) { + $txt = file_get_contents($cache); + $plugins = json_decode($txt, true); + $ui->assign('chached_until', date($config['date_format'] . ' H:i', filemtime($cache) + (24 * 60 * 60))); + if (count($plugins) == 0) { + unlink($cache); + r2(U . 'codecanyon'); + } + } else { + $plugins = []; + $page = _get('page', 1); + back: + $result = Http::getData('https://api.envato.com/v3/market/buyer/list-purchases?&page=' . $page, ['Authorization: Bearer ' . $config['envato_token']]); + $items = json_decode($result, true); + if ($items && count($items['results']) > 0) { + foreach ($items['results'] as $item) { + $name = strtolower($item['item']['name']); + if (strpos($name, 'phpnuxbill') !== false) { + $plugins[] = $item; + } + } + $page++; + goto back; + } + if (count($plugins) > 0) { + file_put_contents($cache, json_encode($plugins)); + if (file_exists($cache)) { + $ui->assign('chached_until', date($config['date_format'] . ' H:i', filemtime($cache) + (24 * 60 * 60))); + } + } + } + $ui->assign('plugins', $plugins); + $ui->display('codecanyon.tpl'); +} diff --git a/system/controllers/community.php b/system/controllers/community.php new file mode 100644 index 0000000..df60c08 --- /dev/null +++ b/system/controllers/community.php @@ -0,0 +1,26 @@ +assign('_title', 'Community'); +$ui->assign('_system_menu', 'community'); + +$action = $routes['1']; +$ui->assign('_admin', $admin); + +switch ($action) { + case 'rollback': + $ui->assign('_title', 'Rollback Update'); + $masters = json_decode(Http::getData("https://api.github.com/repos/hotspotbilling/phpnuxbill/commits?per_page=100",['User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0']), true); + $devs = json_decode(Http::getData("https://api.github.com/repos/hotspotbilling/phpnuxbill/commits?sha=Development&per_page=100",['User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0']), true); + + $ui->assign('masters', $masters); + $ui->assign('devs', $devs); + $ui->display('community-rollback.tpl'); + break; + default: + $ui->display('community.tpl'); +} \ No newline at end of file diff --git a/system/controllers/customers.php b/system/controllers/customers.php new file mode 100644 index 0000000..93477f8 --- /dev/null +++ b/system/controllers/customers.php @@ -0,0 +1,705 @@ +assign('_title', Lang::T('Customer')); +$ui->assign('_system_menu', 'customers'); + +$action = $routes['1']; +$ui->assign('_admin', $admin); + +if (empty($action)) { + $action = 'list'; +} + +$leafletpickerHeader = << +EOT; + +switch ($action) { + case 'csv': + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) { + _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard"); + } + + $cs = ORM::for_table('tbl_customers') + ->select('tbl_customers.id', 'id') + ->select('tbl_customers.username', 'username') + ->select('fullname') + ->select('address') + ->select('phonenumber') + ->select('email') + ->select('balance') + ->select('service_type') + ->order_by_asc('tbl_customers.id') + ->find_array(); + + $h = false; + set_time_limit(-1); + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header("Content-type: text/csv"); + header('Content-Disposition: attachment;filename="phpnuxbill_customers_' . date('Y-m-d_H_i') . '.csv"'); + header('Content-Transfer-Encoding: binary'); + + $headers = [ + 'id', + 'username', + 'fullname', + 'address', + 'phonenumber', + 'email', + 'balance', + 'service_type', + ]; + + if (!$h) { + echo '"' . implode('","', $headers) . "\"\n"; + $h = true; + } + + foreach ($cs as $c) { + $row = [ + $c['id'], + $c['username'], + $c['fullname'], + $c['address'], + $c['phonenumber'], + $c['email'], + $c['balance'], + $c['service_type'], + ]; + echo '"' . implode('","', $row) . "\"\n"; + } + break; + //case csv-prepaid can be moved later to (plan.php) php file dealing with prepaid users + case 'csv-prepaid': + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) { + _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard"); + } + + $cs = ORM::for_table('tbl_customers') + ->select('tbl_customers.id', 'id') + ->select('tbl_customers.username', 'username') + ->select('fullname') + ->select('address') + ->select('phonenumber') + ->select('email') + ->select('balance') + ->select('service_type') + ->select('namebp') + ->select('routers') + ->select('status') + ->select('method', 'Payment') + ->left_outer_join('tbl_user_recharges', array('tbl_customers.id', '=', 'tbl_user_recharges.customer_id')) + ->order_by_asc('tbl_customers.id') + ->find_array(); + + $h = false; + set_time_limit(-1); + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header("Content-type: text/csv"); + header('Content-Disposition: attachment;filename="phpnuxbill_prepaid_users' . date('Y-m-d_H_i') . '.csv"'); + header('Content-Transfer-Encoding: binary'); + + $headers = [ + 'id', + 'username', + 'fullname', + 'address', + 'phonenumber', + 'email', + 'balance', + 'service_type', + 'namebp', + 'routers', + 'status', + 'Payment' + ]; + + if (!$h) { + echo '"' . implode('","', $headers) . "\"\n"; + $h = true; + } + + foreach ($cs as $c) { + $row = [ + $c['id'], + $c['username'], + $c['fullname'], + $c['address'], + $c['phonenumber'], + $c['email'], + $c['balance'], + $c['service_type'], + $c['namebp'], + $c['routers'], + $c['status'], + $c['Payment'] + ]; + echo '"' . implode('","', $row) . "\"\n"; + } + break; + case 'add': + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) { + _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard"); + } + $ui->assign('xheader', $leafletpickerHeader); + run_hook('view_add_customer'); #HOOK + $ui->display('customers-add.tpl'); + break; + case 'recharge': + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) { + _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard"); + } + $id_customer = $routes['2']; + $plan_id = $routes['3']; + $b = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('plan_id', $plan_id)->find_one(); + if ($b) { + $gateway = 'Recharge'; + $channel = $admin['fullname']; + $cust = User::_info($id_customer); + $plan = ORM::for_table('tbl_plans')->find_one($b['plan_id']); + list($bills, $add_cost) = User::getBills($id_customer); + if ($using == 'balance' && $config['enable_balance'] == 'yes') { + if (!$cust) { + r2(U . 'plan/recharge', 'e', Lang::T('Customer not found')); + } + if (!$plan) { + r2(U . 'plan/recharge', 'e', Lang::T('Plan not found')); + } + if ($cust['balance'] < ($plan['price'] + $add_cost)) { + r2(U . 'plan/recharge', 'e', Lang::T('insufficient balance')); + } + $gateway = 'Recharge Balance'; + } + if ($using == 'zero') { + $zero = 1; + $gateway = 'Recharge Zero'; + } + $usings = explode(',', $config['payment_usings']); + $usings = array_filter(array_unique($usings)); + if (count($usings) == 0) { + $usings[] = Lang::T('Cash'); + } + $ui->assign('usings', $usings); + $ui->assign('bills', $bills); + $ui->assign('add_cost', $add_cost); + $ui->assign('cust', $cust); + $ui->assign('gateway', $gateway); + $ui->assign('channel', $channel); + $ui->assign('server', $b['routers']); + $ui->assign('plan', $plan); + $ui->display('recharge-confirm.tpl'); + } else { + r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan'); + } + break; + case 'deactivate': + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) { + _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard"); + } + $id_customer = $routes['2']; + $plan_id = $routes['3']; + $b = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('plan_id', $plan_id)->find_one(); + if ($b) { + $p = ORM::for_table('tbl_plans')->where('id', $b['plan_id'])->find_one(); + if ($p) { + if ($p['is_radius']) { + Radius::customerDeactivate($b['username']); + } else { + $mikrotik = Mikrotik::info($b['routers']); + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + if ($b['type'] == 'Hotspot') { + Mikrotik::removeHotspotUser($client, $b['username']); + Mikrotik::removeHotspotActiveUser($client, $b['username']); + } else if ($b['type'] == 'PPPOE') { + Mikrotik::removePpoeUser($client, $b['username']); + Mikrotik::removePpoeActive($client, $b['username']); + } + } + $b->status = 'off'; + $b->expiration = date('Y-m-d'); + $b->time = date('H:i:s'); + $b->save(); + _log('Admin ' . $admin['username'] . ' Deactivate ' . $b['namebp'] . ' for ' . $b['username'], 'User', $b['customer_id']); + Message::sendTelegram('Admin ' . $admin['username'] . ' Deactivate ' . $b['namebp'] . ' for u' . $b['username']); + r2(U . 'customers/view/' . $id_customer, 's', 'Success deactivate customer to Mikrotik'); + } + } + r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan'); + break; + case 'sync': + $id_customer = $routes['2']; + $bs = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('status', 'on')->findMany(); + if ($bs) { + $routers = []; + foreach ($bs as $b) { + $c = ORM::for_table('tbl_customers')->find_one($id_customer); + $p = ORM::for_table('tbl_plans')->where('id', $b['plan_id'])->where('enabled', '1')->find_one(); + if ($p) { + $routers[] = $b['routers']; + if ($p['is_radius']) { + Radius::customerAddPlan($c, $p, $p['expiration'] . ' ' . $p['time']); + } else { + $mikrotik = Mikrotik::info($b['routers']); + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + if ($b['type'] == 'Hotspot') { + Mikrotik::addHotspotUser($client, $p, $c); + } else if ($b['type'] == 'PPPOE') { + Mikrotik::addPpoeUser($client, $p, $c); + } + } + } + } + r2(U . 'customers/view/' . $id_customer, 's', 'Sync success to ' . implode(", ", $routers)); + } + r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan'); + break; + case 'viewu': + $customer = ORM::for_table('tbl_customers')->where('username', $routes['2'])->find_one(); + case 'view': + $id = $routes['2']; + run_hook('view_customer'); #HOOK + if (!$customer) { + $customer = ORM::for_table('tbl_customers')->find_one($id); + } + if ($customer) { + + + // Fetch the Customers Attributes values from the tbl_customer_custom_fields table + $customFields = ORM::for_table('tbl_customers_fields') + ->where('customer_id', $customer['id']) + ->find_many(); + $v = $routes['3']; + if (empty($v)) { + $v = 'activation'; + } + if ($v == 'order') { + $v = 'order'; + $query = ORM::for_table('tbl_transactions')->where('username', $customer['username'])->order_by_desc('id'); + $order = Paginator::findMany($query); + $ui->assign('order', $order); + } else if ($v == 'activation') { + $query = ORM::for_table('tbl_transactions')->where('username', $customer['username'])->order_by_desc('id'); + $activation = Paginator::findMany($query); + $ui->assign('activation', $activation); + } + $ui->assign('packages', User::_billing($customer['id'])); + $ui->assign('v', $v); + $ui->assign('d', $customer); + $ui->assign('customFields', $customFields); + $ui->assign('xheader', $leafletpickerHeader); + $ui->display('customers-view.tpl'); + } else { + r2(U . 'customers/list', 'e', Lang::T('Account Not Found')); + } + break; + case 'edit': + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent'])) { + _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard"); + } + $id = $routes['2']; + run_hook('edit_customer'); #HOOK + $d = ORM::for_table('tbl_customers')->find_one($id); + // Fetch the Customers Attributes values from the tbl_customers_fields table + $customFields = ORM::for_table('tbl_customers_fields') + ->where('customer_id', $id) + ->find_many(); + if ($d) { + $ui->assign('d', $d); + $ui->assign('statuses', ORM::for_table('tbl_customers')->getEnum("status")); + $ui->assign('customFields', $customFields); + $ui->assign('xheader', $leafletpickerHeader); + $ui->display('customers-edit.tpl'); + } else { + r2(U . 'customers/list', 'e', Lang::T('Account Not Found')); + } + break; + + case 'delete': + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) { + _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard"); + } + $id = $routes['2']; + run_hook('delete_customer'); #HOOK + $d = ORM::for_table('tbl_customers')->find_one($id); + if ($d) { + // Delete the associated Customers Attributes records from tbl_customer_custom_fields table + ORM::for_table('tbl_customers_fields')->where('customer_id', $id)->delete_many(); + $c = ORM::for_table('tbl_user_recharges')->where('username', $d['username'])->find_one(); + if ($c) { + $p = ORM::for_table('tbl_plans')->find_one($c['plan_id']); + if ($p['is_radius']) { + Radius::customerDelete($d['username']); + } else { + $mikrotik = Mikrotik::info($c['routers']); + if ($c['type'] == 'Hotspot') { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removeHotspotUser($client, $d['username']); + Mikrotik::removeHotspotActiveUser($client, $d['username']); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::removePpoeUser($client, $d['username']); + Mikrotik::removePpoeActive($client, $d['username']); + } + try { + $d->delete(); + } catch (Exception $e) { + } catch (Throwable $e) { + } + try { + $c->delete(); + } catch (Exception $e) { + } + } + } else { + try { + $d->delete(); + } catch (Exception $e) { + } catch (Throwable $e) { + } + try { + if ($c) + $c->delete(); + } catch (Exception $e) { + } catch (Throwable $e) { + } + } + + r2(U . 'customers/list', 's', Lang::T('User deleted Successfully')); + } + break; + + case 'add-post': + $username = _post('username'); + $fullname = _post('fullname'); + $password = _post('password'); + $pppoe_password = _post('pppoe_password'); + $email = _post('email'); + $address = _post('address'); + $phonenumber = _post('phonenumber'); + $service_type = _post('service_type'); + $account_type = _post('account_type'); + $coordinates = _post('coordinates'); + //post Customers Attributes + $custom_field_names = (array) $_POST['custom_field_name']; + $custom_field_values = (array) $_POST['custom_field_value']; + //additional information + $city = _post('city'); + $district = _post('district'); + $state = _post('state'); + $zip = _post('zip'); + + run_hook('add_customer'); #HOOK + $msg = ''; + if (Validator::Length($username, 35, 2) == false) { + $msg .= 'Username should be between 3 to 55 characters' . '
'; + } + if (Validator::Length($fullname, 36, 2) == false) { + $msg .= 'Full Name should be between 3 to 25 characters' . '
'; + } + if (!Validator::Length($password, 36, 2)) { + $msg .= 'Password should be between 3 to 35 characters' . '
'; + } + + $d = ORM::for_table('tbl_customers')->where('username', $username)->find_one(); + if ($d) { + $msg .= Lang::T('Account already axist') . '
'; + } + + if ($msg == '') { + $d = ORM::for_table('tbl_customers')->create(); + $d->username = Lang::phoneFormat($username); + $d->password = $password; + $d->pppoe_password = $pppoe_password; + $d->email = $email; + $d->account_type = $account_type; + $d->fullname = $fullname; + $d->address = $address; + $d->created_by = $admin['id']; + $d->phonenumber = Lang::phoneFormat($phonenumber); + $d->service_type = $service_type; + $d->coordinates = $coordinates; + $d->city = $city; + $d->district = $district; + $d->state = $state; + $d->zip = $zip; + $d->save(); + + // Retrieve the customer ID of the newly created customer + $customerId = $d->id(); + // Save Customers Attributes details + if (!empty($custom_field_names) && !empty($custom_field_values)) { + $totalFields = min(count($custom_field_names), count($custom_field_values)); + for ($i = 0; $i < $totalFields; $i++) { + $name = $custom_field_names[$i]; + $value = $custom_field_values[$i]; + + if (!empty($name)) { + $customField = ORM::for_table('tbl_customers_fields')->create(); + $customField->customer_id = $customerId; + $customField->field_name = $name; + $customField->field_value = $value; + $customField->save(); + } + } + } + r2(U . 'customers/list', 's', Lang::T('Account Created Successfully')); + } else { + r2(U . 'customers/add', 'e', $msg); + } + break; + + case 'edit-post': + $username = Lang::phoneFormat(_post('username')); + $fullname = _post('fullname'); + $account_type = _post('account_type'); + $password = _post('password'); + $pppoe_password = _post('pppoe_password'); + $email = _post('email'); + $address = _post('address'); + $phonenumber = Lang::phoneFormat(_post('phonenumber')); + $service_type = _post('service_type'); + $coordinates = _post('coordinates'); + $status = _post('status'); + //additional information + $city = _post('city'); + $district = _post('district'); + $state = _post('state'); + $zip = _post('zip'); + run_hook('edit_customer'); #HOOK + $msg = ''; + if (Validator::Length($username, 35, 2) == false) { + $msg .= 'Username should be between 3 to 15 characters' . '
'; + } + if (Validator::Length($fullname, 36, 1) == false) { + $msg .= 'Full Name should be between 2 to 25 characters' . '
'; + } + if ($password != '') { + if (!Validator::Length($password, 36, 2)) { + $msg .= 'Password should be between 3 to 15 characters' . '
'; + } + } + + $id = _post('id'); + $d = ORM::for_table('tbl_customers')->find_one($id); + + //lets find user Customers Attributes using id + $customFields = ORM::for_table('tbl_customers_fields') + ->where('customer_id', $id) + ->find_many(); + + if (!$d) { + $msg .= Lang::T('Data Not Found') . '
'; + } + + $oldusername = $d['username']; + $oldPppoePassword = $d['password']; + $oldPassPassword = $d['pppoe_password']; + $userDiff = false; + $pppoeDiff = false; + $passDiff = false; + if ($oldusername != $username) { + $c = ORM::for_table('tbl_customers')->where('username', $username)->find_one(); + if ($c) { + $msg .= Lang::T('Account already exist') . '
'; + } + $userDiff = true; + } + if ($oldPppoePassword != $pppoe_password) { + $pppoeDiff = true; + } + if ($password != '' && $oldPassPassword != $password) { + $passDiff = true; + } + + if ($msg == '') { + if ($userDiff) { + $d->username = $username; + } + if ($password != '') { + $d->password = $password; + } + $d->pppoe_password = $pppoe_password; + $d->fullname = $fullname; + $d->email = $email; + $d->account_type = $account_type; + $d->address = $address; + $d->status = $status; + $d->phonenumber = $phonenumber; + $d->service_type = $service_type; + $d->coordinates = $coordinates; + $d->city = $city; + $d->district = $district; + $d->state = $state; + $d->zip = $zip; + $d->save(); + + + // Update Customers Attributes values in tbl_customers_fields table + foreach ($customFields as $customField) { + $fieldName = $customField['field_name']; + if (isset($_POST['custom_fields'][$fieldName])) { + $customFieldValue = $_POST['custom_fields'][$fieldName]; + $customField->set('field_value', $customFieldValue); + $customField->save(); + } + } + + // Add new Customers Attributess + if (isset($_POST['custom_field_name']) && isset($_POST['custom_field_value'])) { + $newCustomFieldNames = $_POST['custom_field_name']; + $newCustomFieldValues = $_POST['custom_field_value']; + + // Check if the number of field names and values match + if (count($newCustomFieldNames) == count($newCustomFieldValues)) { + $numNewFields = count($newCustomFieldNames); + + for ($i = 0; $i < $numNewFields; $i++) { + $fieldName = $newCustomFieldNames[$i]; + $fieldValue = $newCustomFieldValues[$i]; + + // Insert the new Customers Attributes + $newCustomField = ORM::for_table('tbl_customers_fields')->create(); + $newCustomField->set('customer_id', $id); + $newCustomField->set('field_name', $fieldName); + $newCustomField->set('field_value', $fieldValue); + $newCustomField->save(); + } + } + } + + // Delete Customers Attributess + if (isset($_POST['delete_custom_fields'])) { + $fieldsToDelete = $_POST['delete_custom_fields']; + foreach ($fieldsToDelete as $fieldName) { + // Delete the Customers Attributes with the given field name + ORM::for_table('tbl_customers_fields') + ->where('field_name', $fieldName) + ->where('customer_id', $id) + ->delete_many(); + } + } + + if ($userDiff || $pppoeDiff || $passDiff) { + $c = ORM::for_table('tbl_user_recharges')->where('username', ($userDiff) ? $oldusername : $username)->find_one(); + if ($c) { + $c->username = $username; + $c->save(); + $p = ORM::for_table('tbl_plans')->find_one($c['plan_id']); + if ($p['is_radius']) { + if ($userDiff) { + Radius::customerChangeUsername($oldusername, $username); + } + Radius::customerAddPlan($d, $p, $p['expiration'] . ' ' . $p['time']); + } else { + $mikrotik = Mikrotik::info($c['routers']); + if ($c['type'] == 'Hotspot') { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + Mikrotik::setHotspotUser($client, $c['username'], $password); + Mikrotik::removeHotspotActiveUser($client, $d['username']); + } else { + $client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + if (!empty($d['pppoe_password'])) { + Mikrotik::setPpoeUser($client, $c['username'], $d['pppoe_password']); + } else { + Mikrotik::setPpoeUser($client, $c['username'], $password); + } + Mikrotik::removePpoeActive($client, $d['username']); + } + } + } + } + r2(U . 'customers/view/' . $id, 's', 'User Updated Successfully'); + } else { + r2(U . 'customers/edit/' . $id, 'e', $msg); + } + break; + + default: + run_hook('list_customers'); #HOOK + $search = _post('search'); + $order = _post('order', 'username'); + $filter = _post('filter', 'Active'); + $orderby = _post('orderby', 'asc'); + $order_pos = [ + 'username' => 0, + 'created_at' => 8, + 'balance' => 3, + 'status' => 7 + ]; + + if ($search != '') { + $query = ORM::for_table('tbl_customers') + ->whereRaw("username LIKE '%$search%' OR fullname LIKE '%$search%' OR address LIKE '%$search%' " . + "OR phonenumber LIKE '%$search%' OR email LIKE '%$search%' AND status='$filter'"); + } else { + $query = ORM::for_table('tbl_customers'); + $query->where("status", $filter); + } + if ($orderby == 'asc') { + $query->order_by_asc($order); + } else { + $query->order_by_desc($order); + } + $d = $query->findMany(); + if (_post('export', '') == 'csv') { + $h = false; + set_time_limit(-1); + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header("Content-type: text/csv"); + header('Content-Disposition: attachment;filename="phpnuxbill_customers_' . $filter . '_' . date('Y-m-d_H_i') . '.csv"'); + header('Content-Transfer-Encoding: binary'); + + $headers = [ + 'id', + 'username', + 'fullname', + 'address', + 'phonenumber', + 'email', + 'balance', + 'service_type', + ]; + $fp = fopen('php://output', 'wb'); + if (!$h) { + fputcsv($fp, $headers, ";"); + $h = true; + } + foreach ($d as $c) { + $row = [ + $c['id'], + $c['username'], + $c['fullname'], + str_replace("\n", " ", $c['address']), + $c['phonenumber'], + $c['email'], + $c['balance'], + $c['service_type'], + ]; + fputcsv($fp, $row, ";"); + } + fclose($fp); + die(); + } + $ui->assign('xheader', ''); + $ui->assign('d', $d); + $ui->assign('statuses', ORM::for_table('tbl_customers')->getEnum("status")); + $ui->assign('filter', $filter); + $ui->assign('search', $search); + $ui->assign('order', $order); + $ui->assign('order_pos', $order_pos[$order]); + $ui->assign('orderby', $orderby); + $ui->display('customers.tpl'); + break; +} diff --git a/system/controllers/dashboard.php b/system/controllers/dashboard.php new file mode 100644 index 0000000..ad8afe3 --- /dev/null +++ b/system/controllers/dashboard.php @@ -0,0 +1,204 @@ +assign('_title', Lang::T('Dashboard')); +$ui->assign('_admin', $admin); + +if(isset($_GET['refresh'])){ + $files = scandir($CACHE_PATH); + foreach ($files as $file) { + $ext = pathinfo($file, PATHINFO_EXTENSION); + if (is_file($CACHE_PATH . DIRECTORY_SEPARATOR . $file) && $ext == 'temp') { + unlink($CACHE_PATH . DIRECTORY_SEPARATOR . $file); + } + } + r2(U . 'dashboard', 's', 'Data Refreshed'); +} + +$fdate = date('Y-m-01'); +$tdate = date('Y-m-t'); +//first day of month +$first_day_month = date('Y-m-01'); +$mdate = date('Y-m-d'); +$month_n = date('n'); + +$iday = ORM::for_table('tbl_transactions') + ->where('recharged_on', $mdate) + ->where_not_equal('method', 'Customer - Balance') + ->where_not_equal('method', 'Recharge Balance - Administrator') + ->sum('price'); + +if ($iday == '') { + $iday = '0.00'; +} +$ui->assign('iday', $iday); + +$imonth = ORM::for_table('tbl_transactions')->where_not_equal('method', 'Customer - Balance')->where_not_equal('method', 'Recharge Balance - Administrator')->where_gte('recharged_on', $first_day_month)->where_lte('recharged_on', $mdate)->sum('price'); +if ($imonth == '') { + $imonth = '0.00'; +} +$ui->assign('imonth', $imonth); + +$u_act = ORM::for_table('tbl_user_recharges')->where('status', 'on')->count(); +if (empty($u_act)) { + $u_act = '0'; +} +$ui->assign('u_act', $u_act); + +$u_all = ORM::for_table('tbl_user_recharges')->count(); +if (empty($u_all)) { + $u_all = '0'; +} +$ui->assign('u_all', $u_all); + + +$c_all = ORM::for_table('tbl_customers')->count(); +if (empty($c_all)) { + $c_all = '0'; +} +$ui->assign('c_all', $c_all); + +if ($config['hide_uet'] != 'yes') { + //user expire + $query = ORM::for_table('tbl_user_recharges') + ->where_lte('expiration', $mdate) + ->order_by_desc('expiration'); + $expire = Paginator::findMany($query); + + // Get the total count of expired records for pagination + $totalCount = ORM::for_table('tbl_user_recharges') + ->where_lte('expiration', $mdate) + ->count(); + + // Pass the total count and current page to the paginator + $paginator['total_count'] = $totalCount; + + // Assign the pagination HTML to the template variable + $ui->assign('expire', $expire); +} + +//activity log +$dlog = ORM::for_table('tbl_logs')->limit(5)->order_by_desc('id')->find_many(); +$ui->assign('dlog', $dlog); +$log = ORM::for_table('tbl_logs')->count(); +$ui->assign('log', $log); + + +if ($config['hide_vs'] != 'yes') { + $cacheStocksfile = $CACHE_PATH . File::pathFixer('/VoucherStocks.temp'); + $cachePlanfile = $CACHE_PATH . File::pathFixer('/VoucherPlans.temp'); + //Cache for 5 minutes + if (file_exists($cacheStocksfile) && time() - filemtime($cacheStocksfile) < 600) { + $stocks = json_decode(file_get_contents($cacheStocksfile), true); + $plans = json_decode(file_get_contents($cachePlanfile), true); + } else { + // Count stock + $tmp = $v = ORM::for_table('tbl_plans')->select('id')->select('name_plan')->find_many(); + $plans = array(); + $stocks = array("used" => 0, "unused" => 0); + $n = 0; + foreach ($tmp as $plan) { + $unused = ORM::for_table('tbl_voucher') + ->where('id_plan', $plan['id']) + ->where('status', 0)->count(); + $used = ORM::for_table('tbl_voucher') + ->where('id_plan', $plan['id']) + ->where('status', 1)->count(); + if ($unused > 0 || $used > 0) { + $plans[$n]['name_plan'] = $plan['name_plan']; + $plans[$n]['unused'] = $unused; + $plans[$n]['used'] = $used; + $stocks["unused"] += $unused; + $stocks["used"] += $used; + $n++; + } + } + file_put_contents($cacheStocksfile, json_encode($stocks)); + file_put_contents($cachePlanfile, json_encode($plans)); + } +} + +$cacheMRfile = File::pathFixer('/monthlyRegistered.temp'); +//Cache for 1 hour +if (file_exists($cacheMRfile) && time() - filemtime($cacheMRfile) < 3600) { + $monthlyRegistered = json_decode(file_get_contents($cacheMRfile), true); +} else { + //Monthly Registered Customers + $result = ORM::for_table('tbl_customers') + ->select_expr('MONTH(created_at)', 'month') + ->select_expr('COUNT(*)', 'count') + ->where_raw('YEAR(created_at) = YEAR(NOW())') + ->group_by_expr('MONTH(created_at)') + ->find_many(); + + $monthlyRegistered = []; + foreach ($result as $row) { + $monthlyRegistered[] = [ + 'date' => $row->month, + 'count' => $row->count + ]; + } + file_put_contents($cacheMRfile, json_encode($monthlyRegistered)); +} + +$cacheMSfile = $CACHE_PATH . File::pathFixer('/monthlySales.temp'); +//Cache for 12 hours +if (file_exists($cacheMSfile) && time() - filemtime($cacheMSfile) < 43200) { + $monthlySales = json_decode(file_get_contents($cacheMSfile), true); +} else { + // Query to retrieve monthly data + $results = ORM::for_table('tbl_transactions') + ->select_expr('MONTH(recharged_on)', 'month') + ->select_expr('SUM(price)', 'total') + ->where_raw("YEAR(recharged_on) = YEAR(CURRENT_DATE())") // Filter by the current year + ->where_not_equal('method', 'Customer - Balance') + ->where_not_equal('method', 'Recharge Balance - Administrator') + ->group_by_expr('MONTH(recharged_on)') + ->find_many(); + + // Create an array to hold the monthly sales data + $monthlySales = array(); + + // Iterate over the results and populate the array + foreach ($results as $result) { + $month = $result->month; + $totalSales = $result->total; + + $monthlySales[$month] = array( + 'month' => $month, + 'totalSales' => $totalSales + ); + } + + // Fill in missing months with zero sales + for ($month = 1; $month <= 12; $month++) { + if (!isset($monthlySales[$month])) { + $monthlySales[$month] = array( + 'month' => $month, + 'totalSales' => 0 + ); + } + } + + // Sort the array by month + ksort($monthlySales); + + // Reindex the array + $monthlySales = array_values($monthlySales); + file_put_contents($cacheMSfile, json_encode($monthlySales)); +} + +// Assign the monthly sales data to Smarty +$ui->assign('monthlySales', $monthlySales); +$ui->assign('xfooter', ''); +$ui->assign('monthlyRegistered', $monthlyRegistered); +$ui->assign('stocks', $stocks); +$ui->assign('plans', $plans); + +run_hook('view_dashboard'); #HOOK +$ui->display('dashboard.tpl');