Compare commits

..

853 Commits

Author SHA1 Message Date
5a2e75017e 2025.2.3 2025-02-03 14:11:48 +07:00
609a24df80 pretty url error page 2025-02-03 12:10:33 +07:00
31dda69d3d get ready for Pretty URL 2025-01-31 16:54:22 +07:00
d60b1827d9 add $app_url 2025-01-31 16:23:16 +07:00
1e43ac210a url using getUrl 2025-01-31 16:22:58 +07:00
9bf80467a1 Merge pull request #383 from agstrxyz/patch-24
accsessiontime
2025-01-26 05:14:15 +01:00
093ac7e336 Update phpnuxbill.sql (#384)
acctsessiontime
2025-01-26 05:13:38 +01:00
a67681424e Update updates.json (#385)
accsessiontime
2025-01-26 05:12:10 +01:00
7799bf1aee Update radius.php
nasipaddress
acctsessiontime
2025-01-23 18:02:52 +07:00
3502be2c78 Merge pull request #382 from ahmadhusein17/Development
Language fixes
2025-01-19 15:31:36 +07:00
f6084f6ca4 Update balance-edit.tpl 2025-01-19 15:26:48 +07:00
123b74001a Update bandwidth-add.tpl 2025-01-19 15:25:45 +07:00
a39bbc3b34 Update bandwidth-edit.tpl 2025-01-19 15:24:47 +07:00
a15f759dcb Update coupons-add.tpl 2025-01-19 15:22:38 +07:00
883eb1bbee Update coupons-edit.tpl 2025-01-19 15:18:57 +07:00
8af46e5e51 Update customers-add.tpl 2025-01-19 15:16:11 +07:00
1f1e529e3a Update customers-edit.tpl 2025-01-19 15:09:05 +07:00
bed81136a1 Update hotspot-edit.tpl 2025-01-19 14:24:48 +07:00
f322282c6d Update language-add.tpl 2025-01-19 14:20:06 +07:00
396ece50f4 Update pool-add.tpl 2025-01-19 14:19:07 +07:00
681d0214c8 Update pool-edit.tpl 2025-01-19 14:18:30 +07:00
c7d9f8e8c6 Update port-add.tpl 2025-01-19 14:17:58 +07:00
4dd190a61d Update port-edit.tpl 2025-01-19 14:17:14 +07:00
88bb3a29f1 Update pppoe-add.tpl 2025-01-19 14:16:34 +07:00
5e8414aa61 Update pppoe-edit.tpl 2025-01-19 14:13:03 +07:00
01e832a955 Update radius-nas-add.tpl 2025-01-19 14:07:57 +07:00
ada655a074 Update radius-nas-edit.tpl 2025-01-19 14:07:10 +07:00
3f3435157c Update routers-add.tpl 2025-01-19 14:04:57 +07:00
ec6d6cf203 Update routers-edit.tpl 2025-01-19 14:03:42 +07:00
7026438acc Update voucher-add.tpl 2025-01-19 14:02:30 +07:00
8a6ee9490b Update vpn-add.tpl 2025-01-19 14:00:36 +07:00
89808f3291 Update vpn-edit.tpl 2025-01-19 13:57:23 +07:00
8f6bc9f66e Update balance-add.tpl 2025-01-19 13:51:09 +07:00
87ebabe1c0 Add files via upload 2025-01-19 13:48:10 +07:00
bf95713512 Update hotspot-add.tpl 2025-01-19 13:26:53 +07:00
0f19c80cfc change to google maps 2025-01-17 10:37:24 +07:00
b232ccddbf online_customer using pppoe_username 2025-01-16 09:54:48 +07:00
f4944866a4 fix sql installer and update 2025-01-16 09:52:24 +07:00
61fafcd5cf Merge pull request #380 from ahmadhusein17/Development
Update routers.tpl
2025-01-16 09:51:50 +07:00
0b2a9bac21 PPPOE allow Monthly instead Period 2025-01-10 15:30:48 +07:00
cbc66fa470 Update routers.tpl 2025-01-07 21:07:42 +07:00
e2f2146f1e Merge branch 'Development' 2025-01-07 16:44:09 +07:00
0dc50a68f5 change status online to • 2025-01-07 16:43:41 +07:00
690e5912c0 change disable_coupons != yes to enable_coupons == yes 2025-01-07 16:43:41 +07:00
0e302a5171 change active users to active customers 2025-01-07 16:43:41 +07:00
45e2bb5a96 merge coupon and voucer menu 2025-01-07 16:43:41 +07:00
9aff4dbf26 send back plan on update accounting 2025-01-07 16:43:15 +07:00
0eb900ffd6 Update plan.php 2025-01-07 16:43:15 +07:00
73e85a97ce Update Package.php 2025-01-07 16:42:52 +07:00
da84015aab change status online to • 2025-01-07 16:41:52 +07:00
754b23b53f change disable_coupons != yes to enable_coupons == yes 2025-01-07 16:26:33 +07:00
f997ecc702 change active users to active customers 2025-01-07 16:19:28 +07:00
5b7ffec115 merge coupon and voucer menu 2025-01-07 16:16:38 +07:00
1cbdf5a9b2 send back plan on update accounting 2025-01-07 14:46:51 +07:00
273b6d80c2 Merge pull request #378 from agstrxyz/patch-23
Update plan.php
2025-01-07 14:46:32 +07:00
1a5a7124f7 Update plan.php 2025-01-04 01:59:20 +07:00
4fe0e8d58c Merge pull request #376 from Focuslinkstech/Development
Development
2025-01-02 14:42:20 +07:00
bbdf561b3b Merge pull request #377 from agstrxyz/patch-22
Update Package.php
2025-01-02 14:40:33 +07:00
96fccecbcf Update Package.php 2025-01-01 20:44:10 +07:00
0780438ca8 Update radius.php
Fix Package not showing on voucher when sorting using date created while printing

Its should be Invalid Voucher... in radius-rest
2024-12-30 22:57:37 +01:00
cfe0a14f31 Enhancements and Fixes
Added Vouchers Printing Per Page option, default is 36 for A4 papers.
Fix voucher printing date showing time and seconds (its annoying), replaced with group by days created, and also added  number of vouchers created on each days to make it more unique and classy
2024-12-30 22:49:46 +01:00
b1e145f0dc by mac and nasid 2024-12-30 09:56:59 +07:00
357b7932e9 Merge pull request #375 from agstrxyz/patch-21
Update radius.php
2024-12-30 08:37:00 +07:00
b3f6417bbe Merge pull request #374 from agstrxyz/patch-20
Update app-miscellaneous.tpl
2024-12-30 08:35:34 +07:00
051a8de5f2 Merge pull request #373 from agstrxyz/patch-19
Interim update rest radius
2024-12-30 08:35:02 +07:00
1a931ef617 Update radius.php
Interim-Update freeradius rest
2024-12-29 21:03:46 +07:00
c5907c194b Update app-miscellaneous.tpl
radius rest interim update
2024-12-29 19:17:55 +07:00
1906b1aefd Update cron.php
radius rest interim update check
2024-12-29 19:15:19 +07:00
62c447da9b Merge pull request #372 from agstrxyz/patch-18
Update App.php
2024-12-27 19:16:47 +07:00
51b68b648c Update App.php 2024-12-27 17:13:53 +07:00
86407f731b Merge pull request #371 from Focuslinkstech/Development
Development
2024-12-26 18:47:25 +07:00
d2d52d0ad0 Merge branch 'hotspotbilling:Development' into Development 2024-12-25 17:35:06 +01:00
c527f1cc7c fix radius rest pppoe 2024-12-25 21:12:58 +07:00
47469df558 Merge branch 'hotspotbilling:Development' into Development 2024-12-25 14:52:58 +01:00
c5fd7c0249 Improvement and Fixes
Add print now features when generating vouchers and print them immediately after generation.

Add multiple voucher code deletion, admin can mark multiple vouchers codes and delete them.

Fix issue of plan names displays in the voucher table even when voucher are empty, very annoying
2024-12-25 14:52:10 +01:00
1ea971d3f5 fix acctSessionId 2024-12-25 20:51:44 +07:00
7d5e0c7a8e Merge pull request #370 from Focuslinkstech/Development
Development
2024-12-25 20:41:42 +07:00
cf6f89b3f2 fixed coupon bugs
fix add and edit bug
2024-12-25 10:20:29 +01:00
2dc50c3637 Update coupons.php
fixed change status error
2024-12-25 10:04:20 +01:00
da0ca4f087 Merge pull request #369 from Focuslinkstech/Development
Development
2024-12-23 16:36:38 +07:00
486c849eee Merge branch 'hotspotbilling:Development' into Development 2024-12-22 09:30:20 +01:00
c7a09c60cb fix lang 2024-12-22 15:22:08 +07:00
fce910455a Lang 2024-12-22 15:16:00 +07:00
ade4e22454 Merge pull request #368 from ahmadhusein17/Development
Update selectGateway.tpl
2024-12-22 15:13:36 +07:00
487d31358d Merge branch 'master' into Development 2024-12-22 15:12:35 +07:00
daf1bb7d67 fix radius rest pppoe_username 2024-12-22 15:10:01 +07:00
7c547c967e fix whereRaw 2024-12-22 12:23:49 +07:00
2e61c89d89 Update autoload.php
Add null condition too
2024-12-21 11:16:29 +01:00
27c736fa69 Merge pull request #367 from gerandonk/Development
add voucher date created and print by date
2024-12-20 17:18:01 +07:00
b3fd1d5a3e add voucher date created and print by date
add voucher date created and print by date
2024-12-20 16:48:06 +07:00
5d492def3c Update selectGateway.tpl
Update translation
2024-12-20 16:08:29 +07:00
0dfa412be8 Update phpnuxbill.sql
why integer has  CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL ??
2024-12-20 14:19:22 +07:00
c6cb63a1c5 Update phpnuxbill.sql
why integer has character sets?
`user_id` int(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
to
`user_id` int(11) ,
2024-12-20 14:16:31 +07:00
32418f05fb Merge pull request #366 from Focuslinkstech/Development
Development
2024-12-20 14:15:20 +07:00
3c1d8dbb8a Merge branch 'Development' of https://github.com/Focuslinkstech/phpnuxbill into Development 2024-12-17 20:00:34 +01:00
67a097fddf Enhancement
Add coupon unlimited usage 0 is unlimited
Add more logic to coupon brute force attack
2024-12-17 19:59:04 +01:00
e03b250fb7 Merge pull request #365 from Focuslinkstech/Development
New Feature and Bux Fix
2024-12-17 15:28:27 +07:00
8a36746a0f New Feature and Bux Fix
Lots of changes has been made that i cant recall
Added Coupon
Fix installation bug
Add more nav list
Fix this and that
2024-12-16 17:45:13 +01:00
fd920748be Urgent Fix registration logic 2024-12-06 15:30:43 +07:00
e32e0aba16 2024.12.6 2024-12-06 14:56:44 +07:00
98589cd283 Merge pull request #362 from Focuslinkstech/Development
Development
2024-12-06 14:33:12 +07:00
fd20ede7aa Merge branch 'hotspotbilling:Development' into Development 2024-12-05 17:04:12 +01:00
226ed41075 patch: Update
add css and plan price to user billing
2024-12-05 17:01:16 +01:00
9ec090a621 Merge pull request #359 from Focuslinkstech/Development
Patch Update: Enhancement
2024-12-05 11:52:10 +07:00
c8d5861f2e Patch Update: Enhancement
Fixed Activation and Order History disappear when customer username is  Changed
2024-12-04 14:37:01 +01:00
92491e22ec check if method exists 2024-12-03 13:38:29 +07:00
f9e7a8dbb4 Merge pull request #358 from gerandonk/Development
add sync function
2024-12-03 13:35:03 +07:00
5559069aac Merge pull request #357 from ahmadhusein17/Development
Language fixes
2024-12-03 11:47:44 +07:00
d6144537c4 add sync function
add sync function
fix radius time monthly validity
fix sync button for hotspot with uptime limit and data limit
2024-12-03 11:15:15 +07:00
04742535ba Update activation-list.tpl 2024-11-30 23:25:13 +07:00
f76753b247 Update dashboard.tpl 2024-11-30 23:22:28 +07:00
e4f209030f Update forgot.tpl 2024-11-30 23:16:37 +07:00
5c54223454 Update login-custom-moon.tpl 2024-11-30 23:13:57 +07:00
13a2de2664 Update login-noreg.tpl 2024-11-30 23:12:32 +07:00
538cf4e675 Update login.tpl 2024-11-30 23:11:35 +07:00
9edb0b4d2f Update orderBalance.tpl 2024-11-30 23:10:12 +07:00
d0a7f2b991 Update orderHistory.tpl 2024-11-30 23:09:06 +07:00
f4c15c3475 Update orderView.tpl 2024-11-30 23:06:31 +07:00
50dd4b4839 Update register-otp.tpl 2024-11-30 23:00:21 +07:00
1d7dca8ada Update register.tpl 2024-11-30 22:45:34 +07:00
694b4d7bac Update selectGateway.tpl 2024-11-30 22:43:05 +07:00
82dce56209 Update sendPlan.tpl 2024-11-30 22:38:38 +07:00
b2ac84900e Merge pull request #355 from ahmadhusein17/Development
Development
2024-11-29 13:17:14 +07:00
16de814091 Update profile.tpl
Correct the wrong Indonesian
2024-11-29 12:35:13 +07:00
4ce4633999 Update dashboard.tpl
Correct the wrong Indonesian
2024-11-29 12:29:54 +07:00
5788c9c0a7 Merge pull request #354 from Focuslinkstech/Development
Patch Addon
2024-11-29 11:27:40 +07:00
7acab4d50e New Feature
Add New self registration notification for admin
2024-11-28 18:35:01 +01:00
79d0921ebe Dynamic Custom Login Page
Add Dynamic image changing functions, for swift action for image update
2024-11-28 17:59:12 +01:00
77d25738b1 Patch Addon
Add container dark mode
Add Registration Mandatory Fields
2024-11-28 16:16:05 +01:00
b34b9064d0 2024.11.28 2024-11-28 09:58:02 +07:00
13adfc8254 Merge pull request #353 from Focuslinkstech/Development
Development
2024-11-28 09:51:44 +07:00
73b829b8c1 Update init.php
remove lang debug
2024-11-25 11:36:30 +01:00
91e7caae80 dont translate english to english 2024-11-21 18:10:24 +07:00
40a39f0ced Don't translate english to english 2024-11-21 18:10:05 +07:00
699ffc1302 Fix ajax api-get-text 2024-11-21 18:09:03 +07:00
5e2eaa5578 Custom Mikrotik SMS Command 2024-11-21 18:08:51 +07:00
38f6738c45 fix register with otp and hide dashboard 2024-11-21 15:03:28 +07:00
63e564c9dd Fix otp registration as it still check is sms url exists, so i remove it 2024-11-21 10:09:36 +07:00
27a5d2d45a check setting 2024-11-21 09:56:14 +07:00
153da7c63b registration can force to upload photo 2024-11-20 14:44:23 +07:00
c9778e71b9 fix option select custom field 2024-11-19 18:18:51 +07:00
eb970b257a Custom Fields for registration and Profile, but not yet finished for upload picture 2024-11-19 18:11:34 +07:00
242dda2e99 remove You do not have permission to access this page in demo mode 2024-11-19 13:20:53 +07:00
5445aedcd1 how do dev test if it has "You do not have permission to access this page in demo mode"? 2024-11-19 13:20:38 +07:00
3f77dcf129 Merge pull request #352 from Focuslinkstech/Development 2024-11-18 20:04:38 +07:00
357df8d986 change case 2024-11-18 13:25:03 +01:00
16199b4c18 Patch Update
fix moon custom login page logo not showing after upload
add registration now allowed in demo mode, add warning when registration is disabled
2024-11-16 16:59:42 +01:00
318b52905d Merge pull request #350 from Focuslinkstech/Development
Development
2024-11-12 10:06:28 +07:00
581b765c28 add default values
add default images
fix bugs template bugs
add tawk.to and add footer links
Fix moon template
2024-11-11 19:54:44 +01:00
c3355ad8f7 New Feature: Customer login settings
you can change or add multiple custom login-page template

you can also add your own login page

just add a login page with name
"login-custom-[template-name].tpl"

e.g.

login-custom-star.tpl
login-custom-moon.tpl
login-custom-sun.tpl
login-custom-venus.tpl
2024-11-11 17:42:57 +01:00
5bb724b9b3 11.11 2024-11-11 15:32:26 +07:00
e431f07a71 fix show bw 2024-11-11 15:32:03 +07:00
b5efe81637 Merge pull request #349 from ahmadhusein17/patch-1
Update indonesia.json
2024-11-08 16:17:16 +07:00
c827c983ce Update indonesia.json
Indonesian translation fix
2024-11-08 11:58:57 +07:00
a81ddd68dd Merge pull request #347 from Focuslinkstech/Development
Development
2024-11-08 11:03:51 +07:00
9c06225cc2 Merge pull request #348 from gerandonk/Development
add tax on bills tag
2024-11-08 06:54:07 +07:00
6a5fd9d11a add tax on bills tag
add tax inside bills tag notification
show tax in admin recharge page
2024-11-07 23:28:52 +07:00
d20fea1cdf Update app-devices.tpl
enhance device list view
2024-11-07 14:01:05 +01:00
3bd8ebdc76 Update app-miscellaneous.tpl
Enhance the template
2024-11-07 07:42:48 +01:00
8dcd00bc6b Update app-miscellaneous.tpl
fix not responsive on mobile
2024-11-06 17:32:59 +01:00
56ddf65fdf maybe fix this and fix that, i lost track 2024-11-06 16:08:16 +07:00
a2347dd241 enable button after 5 seconds 2024-11-06 13:50:22 +07:00
b316e4a242 change confirm() to ask() 2024-11-06 13:45:36 +07:00
2b7361cf6b Merge pull request #346 from gerandonk/Development
update
2024-11-06 11:46:10 +07:00
5ccddf11be update
postpaid logic for more than 1 period
fix mikrotiksms not working
2024-11-06 11:34:28 +07:00
dbf1623ca6 Merge pull request #345 from Focuslinkstech/Development
Update Package.php
2024-11-06 09:32:50 +07:00
e17f07f410 Merge branch 'hotspotbilling:Development' into Development 2024-11-05 19:56:01 +01:00
23bba0b189 Update Package.php
Reversed failed  logic
2024-11-05 19:53:31 +01:00
167732094b Merge pull request #344 from Focuslinkstech/Development
Bugs Fixes and bring back our dear extend expiry feature
2024-11-05 23:21:50 +07:00
9569847612 Fix Security
javascript not add to general settings
2024-11-05 16:45:39 +01:00
4c1f49ea81 Update app-miscellaneous.tpl
reduced the help text size
2024-11-05 16:25:01 +01:00
b7fdc90aa7 Bugs Fixes
fix extend expiry bug
2024-11-05 16:05:31 +01:00
fec7780f50 Merge pull request #341 from Focuslinkstech/Development
send welcome email bug
2024-11-05 19:15:49 +07:00
7d24b719b5 Merge pull request #342 from gerandonk/Development
fix postpaid more than 1 period
2024-11-05 19:14:10 +07:00
a34f1bb0fc fix postpaid more than 1 period 2024-11-05 19:00:03 +07:00
2863d70214 Fix typo
fixed typo in device, change demo to Demo
2024-11-05 11:32:08 +01:00
949a3d27eb Update app-settings.tpl
fixed callout text
2024-11-05 11:16:27 +01:00
1b7f449d96 Update app-settings.tpl
replace footer not with callout to make it more decent
2024-11-05 11:13:26 +01:00
0295ab44e5 send welcome email bug
fixed welcome email sending bug
2024-11-05 10:13:56 +01:00
7f35e7fb68 fix Tawk.to 2024-11-05 12:07:54 +07:00
b28b40280f Remove debug 2024-11-05 11:09:56 +07:00
35243cb9b0 fix [[payment_link]] 2024-11-05 10:42:41 +07:00
de4c6c730f [[payment_link]] documentation 2024-11-05 09:36:56 +07:00
53b64548fc to use [[payment_link]] you need to add url manually https://domain.tld/[[payment_link]] 2024-11-05 08:55:21 +07:00
bf11fd2f2c Merge pull request #340 from gerandonk/Development
fix payment_link
2024-11-05 08:52:35 +07:00
e0f150633c fix payment_link
if url detected localhost, change your host in config.php to make work
2024-11-05 03:53:47 +07:00
06c68ccebd Critical update 2024-11-04 15:13:17 +07:00
6db2f2bf0d fix critical bug customer can recharge without balance when using balance. and move Balance to select Gateway 2024-11-04 15:10:58 +07:00
32a64d944a remove index.php 2024-11-04 13:57:28 +07:00
1903dc6b45 generate token 2024-11-04 13:54:45 +07:00
27bd2590f2 add Security settings 2024-11-04 12:05:17 +07:00
8908f4bdc3 enable/disable CSRF 2024-11-04 12:05:08 +07:00
7fb08eb76f fix unpaid order 2024-11-04 10:44:39 +07:00
8a0c17b319 Add order by Firstname or lastname requested by CountryCom LLC, thanks 2024-11-01 17:03:00 +07:00
afbb39b1d9 Reports using select2 2024-11-01 14:56:52 +07:00
7006ad8c40 Merge pull request #339 from gerandonk/Development
change postpaid logic
2024-11-01 06:55:31 +07:00
99b30f747f change postpaid logic
fix postpaid expired date error for new customer or customer without plan
2024-11-01 02:46:27 +07:00
1f1430fd21 Fix delete old photo 2024-10-31 14:46:51 +07:00
93583afb65 Allow Customer buy balance with any amount settings 2024-10-31 14:01:51 +07:00
9779d77f22 fix gateway selected 2024-10-31 13:56:32 +07:00
3dfb12d830 Customer Photo 2024-10-31 13:49:51 +07:00
6e446192d8 set photo to header customer 2024-10-31 13:38:54 +07:00
51811bd753 REMOVE CSRF (annoying when refresh page) when open edit customer page, add upload photo when edit Customer 2024-10-31 13:36:43 +07:00
db8affce1f Add photo to customer table 2024-10-31 13:34:54 +07:00
c740820731 Delete extend_expiry settings 2024-10-31 13:15:36 +07:00
80e0dc6485 update face detection 2024-10-30 18:05:39 +07:00
5552fb20d5 fix facedetection 2024-10-30 18:05:12 +07:00
f08211a83a fix variable header 2024-10-30 18:00:37 +07:00
71d6024d62 Upload Admin face with Face Detection 2024-10-30 17:49:21 +07:00
73993c4f25 add field data to tbl_users 2024-10-30 17:49:21 +07:00
25be52fe9c add photo field 2024-10-30 17:49:21 +07:00
5d79f9d842 Merge pull request #338 from Focuslinkstech/Development
Customer can buy Custom Balance
2024-10-29 15:06:39 +07:00
70d8cd8e01 Feature: Custom Balance
Add custom balance in customer dashboard

Note: i only test it with Paystack and Razorpay payment gateway and it works as expected

test it very well and report any error or bug
2024-10-29 08:46:03 +01:00
df7293e3d0 Merge branch 'Development' of https://github.com/Focuslinkstech/phpnuxbill into Development 2024-10-29 08:23:56 +01:00
7614422bd8 Merge branch 'master' into Development 2024-10-29 14:17:31 +07:00
5ff9c74f63 Merge pull request #337 from Focuslinkstech/master
security
2024-10-29 14:16:23 +07:00
8fdbe0ec1d security
add csrf token to customers post and get methods
2024-10-28 11:57:48 +01:00
e4fb835f2c Update header.tpl
Fix Additional Information header button
2024-10-28 10:48:47 +01:00
d69086d99b dont show echo 2024-10-28 15:49:44 +07:00
dc42a09685 Merge branch 'hotspotbilling:Development' into Development 2024-10-27 15:07:00 +01:00
08b2e16e73 fix footer set language 2024-10-26 19:22:35 +07:00
8e41749cd4 fix error message 2024-10-26 19:15:38 +07:00
383a1e55d7 Merge pull request #334 from Focuslinkstech/Development
patch
2024-10-26 19:14:45 +07:00
3778499611 Merge pull request #333 from taukir007/master
Registration Form: Separate username and phone number fields if OTP Required is enabled.
2024-10-26 19:14:29 +07:00
fda935b3a6 Update header.tpl
fix panel header
2024-10-25 13:00:29 +01:00
d5c2c72a74 Update register-otp.tpl 2024-10-25 15:03:28 +06:00
0df7027851 Refactor registration form: Separate username and phone number fields
- Changed the phone number input field to be readonly and renamed it to 'phone_number'.
- Introduced a new input field for 'username' in the registration section.
- Adjusted the panel headings and layout for clarity and consistency.
2024-10-25 15:01:14 +06:00
00081d40e5 Update registration form
- Change input name from 'username' to 'phone_number'
- Add inputmode and pattern attributes for better validation
2024-10-25 14:58:02 +06:00
01c2808e43 Enhance registration process with OTP verification
- Separate username and phone number handling
- Improve Phone Number validation and handling
- Stop changes to the registration form when the wrong password is entered
- Enhance error messages for better user experience
2024-10-25 14:51:01 +06:00
e02bf9863f fix [[trx_date]] 2024-10-25 14:38:04 +07:00
7f72b0c34a cannot deactivate postpaid 2024-10-25 14:05:57 +07:00
a04c3dd82b don't show Deactivate if it postpaid 2024-10-25 13:48:45 +07:00
1a0b3ffaf3 hook cronjob_end 2024-10-25 13:45:11 +07:00
3ddfc92b18 Merge pull request #332 from ahmadhusein17/Development
Added Confirmation Alert
2024-10-25 13:44:34 +07:00
9de7a07de4 Merge pull request #331 from gerandonk/Development
fix warning array key
2024-10-25 13:44:00 +07:00
52f65ec143 Merge pull request #330 from Focuslinkstech/Development
patch update
2024-10-25 13:43:31 +07:00
e8a800bbd6 fix log that beed IP, that make plugin CLI error 2024-10-24 14:11:10 +07:00
36162381cb Update pool-add.tpl 2024-10-23 19:27:44 +07:00
d14599f720 Update pool-edit.tpl 2024-10-23 19:26:43 +07:00
244e552eb6 Update port-add.tpl 2024-10-23 19:25:47 +07:00
6bb9b001dc Update port-edit.tpl 2024-10-23 19:12:36 +07:00
7c42ab5b89 Update pppoe-add.tpl 2024-10-23 19:11:49 +07:00
717f29c95d Update pppoe-edit.tpl 2024-10-23 19:09:35 +07:00
ee52a2ec1e Update radius-nas-add.tpl 2024-10-23 19:08:31 +07:00
2b3d3a6796 Update radius-nas-edit.tpl 2024-10-23 19:07:41 +07:00
859e0ce6af Update recharge.tpl 2024-10-23 19:06:14 +07:00
8c5ed98f09 Update refill.tpl 2024-10-23 19:05:33 +07:00
8347a982c5 Update routers-edit.tpl 2024-10-23 19:04:24 +07:00
60188c736b Update voucher-add.tpl 2024-10-23 19:03:40 +07:00
ba729f44fc Update plan-edit.tpl 2024-10-23 18:58:46 +07:00
954a892f86 Update language-add.tpl 2024-10-23 18:55:31 +07:00
fe41b92a7c Update hotspot-edit.tpl 2024-10-23 18:54:19 +07:00
b835520ccf Update hotspot-add.tpl 2024-10-23 18:53:25 +07:00
3adfd2f2c3 Update hotspot-edit.tpl 2024-10-23 18:52:36 +07:00
434d7d8cf9 Update deposit.tpl 2024-10-23 18:49:53 +07:00
c617bb9a4d Update customers-add.tpl 2024-10-23 18:48:55 +07:00
853af04362 Update customers-edit.tpl 2024-10-23 18:48:03 +07:00
d8894ab296 Update balance-add.tpl 2024-10-23 18:42:19 +07:00
0c2249daf9 Update balance-edit.tpl 2024-10-23 18:41:36 +07:00
e7f4c88648 Update bandwidth-add.tpl 2024-10-23 18:40:08 +07:00
da541595a4 Update bandwidth-edit.tpl 2024-10-23 18:39:25 +07:00
495da312a8 Update admin-edit.tpl 2024-10-23 18:38:15 +07:00
71cc8290a3 Update admin-add.tpl 2024-10-23 18:37:39 +07:00
575ef1c117 Update routers-add.tpl 2024-10-23 18:36:49 +07:00
bcb39f114a Update routers-edit.tpl 2024-10-23 18:35:28 +07:00
544706d82e Update voucher-add.tpl 2024-10-23 18:34:40 +07:00
6f4f788d8f Update vpn-edit.tpl 2024-10-23 18:33:01 +07:00
0f59250215 Update vpn-add.tpl 2024-10-23 18:32:21 +07:00
bcff2253ee Update pool-edit.tpl 2024-10-23 18:28:42 +07:00
676caec8ca Update port-add.tpl 2024-10-23 18:26:53 +07:00
9d6cd6756c Update port-edit.tpl 2024-10-23 18:25:40 +07:00
b375460789 Update pppoe-add.tpl 2024-10-23 18:24:56 +07:00
b7aee6f91c Update pppoe-edit.tpl 2024-10-23 18:24:11 +07:00
b4907ed661 Update pool-add.tpl 2024-10-23 18:23:13 +07:00
44d091cae8 Update plan-edit.tpl 2024-10-23 18:21:38 +07:00
766c2c268e Update message.tpl 2024-10-23 18:20:28 +07:00
14f9559033 Update message-bulk.tpl 2024-10-23 18:19:48 +07:00
53885a6f7d Update language-add.tpl 2024-10-23 18:17:55 +07:00
46a83d4e30 Update customers-add.tpl 2024-10-23 18:16:52 +07:00
b313f1de36 Update bandwidth-add.tpl 2024-10-23 18:15:31 +07:00
a6df867ca1 Update deposit.tpl 2024-10-23 18:14:20 +07:00
7339be29f8 Update balance-add.tpl 2024-10-23 18:13:14 +07:00
8ae88adc17 Update hotspot-add.tpl 2024-10-23 18:11:54 +07:00
c960baaf1f Add files via upload 2024-10-23 18:09:57 +07:00
5e310cba29 fix warning array key
fix Warning: Undefined array key "HTTP_HOST"
fix Warning: Undefined array key "SERVER_PORT"
2024-10-23 14:59:27 +07:00
eb1d804a4d patch update
make settings form individual to reduce conflicts
2024-10-23 08:28:00 +01:00
6ffa396b05 2024.10.23 2024-10-23 14:15:47 +07:00
cd5f9101f2 only admin can edit customer 2024-10-23 14:13:32 +07:00
f8878ad8b6 only admin can show password 2024-10-23 14:12:55 +07:00
dc55957a53 Refill Balance with Custom Amount Requested by Javi Tech 2024-10-23 14:04:11 +07:00
bdda199523 center invoice 2024-10-22 11:35:08 +07:00
8086802bf6 tawkto bring customer data 2024-10-22 10:34:44 +07:00
53aa187d1f register with otp, force username to phone number 2024-10-21 14:19:13 +07:00
7685323bd6 phone_otp_type forgot to add 2024-10-21 14:15:39 +07:00
1fe2dac580 Merge pull request #328 from agstrxyz/patch-17
Update cron.php
2024-10-21 07:29:47 +07:00
0428d9620e Update cron.php
voucher expired tidak terhapus dari mikrotik sebab tidak menemukan data pada tabel customer $c  untuk di eksekusi ke device.php
2024-10-20 21:47:08 +07:00
ad862640cc Merge pull request #327 from gerandonk/Development
make sync button not remove active hotspot user
2024-10-20 12:05:01 +07:00
a4e8ae8c5c make sync button not remove active hotspot user
make sync button not remove active hotspot user
2024-10-20 01:07:00 +07:00
d4c34afb8d Merge pull request #326 from ahmadhusein17/Development
Fix Footer
2024-10-18 15:30:13 +07:00
0dc4d22da3 Fix Footer 2024-10-18 14:18:48 +07:00
169a7afe67 Merge pull request #324 from ahmadhusein17/Development
Fix footer that is too long.
2024-10-18 13:33:28 +07:00
67890881f3 Fix footer that is too long. 2024-10-18 13:29:29 +07:00
3febb60253 fix variable sms_otp_registration 2024-10-18 12:57:59 +07:00
005155e2a5 fix variable settings 2024-10-18 11:37:45 +07:00
f8a15e4754 ## 2024.10.18
- Single Session Admin Can be set in the Settings
- Auto expired unpaid transaction
- Registration Type
- Can Login as User from Customer View
- Can select customer register must using OTP or not
- Add Meta.php for additional information
2024-10-18 10:59:52 +07:00
f0da633808 Single session Admin can be set in the misc settings 2024-10-18 10:59:52 +07:00
5f6b14ab7e Merge pull request #320 from ahmadhusein17/master
For Header and Footer in Customer!
2024-10-17 18:34:02 +07:00
3475a6086d For Header and Footer in Customer!
Minor improvements to the appearance of the footer. (⁠◕⁠ᴗ⁠◕⁠✿⁠)
2024-10-17 16:06:56 +07:00
49f194a7f2 Admin can Login as Customer 2024-10-17 15:14:39 +07:00
b3744a5007 fix Customer view to view tbl_payment_gateway 2024-10-17 14:09:11 +07:00
234e5e3967 add try catch to handle invalid value 2024-10-17 13:38:50 +07:00
97296abf06 fix unpaid expired check 2024-10-17 13:14:53 +07:00
a7232e2b92 fix unpaid logic 2024-10-17 11:39:15 +07:00
7c0bdeea41 CREATE TABLE IF NOT EXISTS tbl_meta 2024-10-17 11:32:39 +07:00
5566a7ebb5 Add Meta class for meta data attributes 2024-10-17 11:28:52 +07:00
de49a9992f Price Before Discount unrequired 2024-10-17 09:54:06 +07:00
0cf5483353 check expired payments 2024-10-17 09:44:21 +07:00
b15fdf1d6a Setting sAllow Registration = Yes/Voucher/No Registration 2024-10-17 09:35:26 +07:00
ca98ca2223 profile logic Username 2024-10-16 16:01:34 +07:00
5ccb8520d3 change logic username field 2024-10-16 13:41:12 +07:00
ff4e620b75 add registration settings to set username 2024-10-16 13:40:54 +07:00
1b7e5c7510 Setting for registration using OTP or not 2024-10-16 11:40:52 +07:00
084cc0e0fb miscellaneous.tpl to app-miscellaneous.tpl 2024-10-16 11:36:25 +07:00
25d9524f53 fix header 2024-10-16 11:35:24 +07:00
4308765bec Merge pull request #319 from Focuslinkstech/Development
Template Redesiged:
2024-10-15 20:22:22 +07:00
70dbe59319 Template Redesiged:
Settings page redesigned
2024-10-15 13:29:48 +01:00
577ed31f57 ## 2024.10.15
- CSRF Security
- Admin can only have 1 active session
- Move Miscellaneous Settings to new page
- Fix Customer Online
- Count Shared user online for Radius REST
- Fix Invoice Print
2024-10-15 16:19:43 +07:00
064e4c80ed code to code 2024-10-15 16:10:34 +07:00
70bcff7679 Merge pull request #317 from gerandonk/Development
($plan['is_radius'] == '1')
2024-10-11 22:53:03 +07:00
47b729867d Merge pull request #318 from Focuslinkstech/Development
move miscellaneous to settings sub-menu for quick access
2024-10-11 22:52:37 +07:00
696b2e4789 move miscellaneous to settings sub-menu for quick access
add csrf token check for settings and its environments
2024-10-11 16:11:03 +01:00
0f0929db2a ($plan['is_radius'] == '1') 2024-10-11 18:59:38 +07:00
7267bd082a Api always Valid 2024-10-11 11:42:38 +07:00
d5cba4b3c1 add option for check is customer online 2024-10-11 11:37:45 +07:00
155b2959b5 fix api-get-text 2024-10-11 11:29:57 +07:00
a35137b7ab fix logic Session Admin especially isApi 2024-10-11 11:09:27 +07:00
83dd564e53 fix position Admin::_info(); 2024-10-11 11:07:47 +07:00
8e8a52d807 login_token VARCHAR(40) 2024-10-11 11:07:23 +07:00
5bc273a9dd session_destroy(); inside removeCookie() 2024-10-11 10:38:24 +07:00
f9fe261e55 session_destroy(); 2024-10-11 10:37:35 +07:00
0dc79cd5c4 ->select('login_token') 2024-10-11 10:37:23 +07:00
b32e2901af Merge pull request #316 from Focuslinkstech/Development
Development
2024-10-11 08:01:40 +07:00
f77d7051c1 remove unused variable 2024-10-10 17:02:04 +01:00
60e1eacc59 fix login loop 2024-10-10 16:24:36 +01:00
82ffc15c03 Merge branch 'hotspotbilling:Development' into Development 2024-10-10 15:49:32 +01:00
6e5450d104 CSRF added to customer acounts update 2024-10-10 15:48:32 +01:00
6458b792f6 Merge pull request #315 from Focuslinkstech/Development
Fight Against Insecurity Ongoing
2024-10-10 21:44:40 +07:00
6be0da383c fixed template issue 2024-10-10 15:13:29 +01:00
534886f8f3 Fix app stage issue 2024-10-10 15:04:12 +01:00
c9b9808112 Fight Against Insecurity : Prevent Admin multiple Login Sessions, its a security threat to phpnuxbill.
plase note: if you are running nuxbill on localhost please set app_stage to something else e.g.
$_app_stage = 'Demo';
its very important
2024-10-10 14:33:27 +01:00
e737ae9d29 $routes['2'] = 0; 2024-10-10 17:25:21 +07:00
78e3f2e8fb Merge 2024-10-10 10:52:13 +07:00
3eaa302128 add CSRF Token on customer login 2024-10-10 10:50:48 +07:00
9bc3ccc02b Added token expiration: 30 minutes by default 2024-10-10 10:50:48 +07:00
99e8b20bb3 Testing CSRF from admin login, if works well then we will make it official 2024-10-10 10:50:48 +07:00
bd30261e84 move the CSRF Function to global function for easy access 2024-10-10 10:50:48 +07:00
96365eef2a Added more security flags to prevent XSS attack from cookie. 2024-10-10 10:50:48 +07:00
c08c069479 Critical Updates, Fight Against Insecurity 2024-10-10 10:50:48 +07:00
71d653f3d1 Merge pull request #314 from gerandonk/Development
fix send plan radius
2024-10-10 10:48:23 +07:00
b1919555e5 Fix Lang function again 2024-10-10 10:24:01 +07:00
2522f3112e Spanish Lang by @ORConsulTech 2024-10-10 10:23:47 +07:00
ba63face92 change folder ui/ui/user-ui to ui/ui/customer 2024-10-10 10:19:01 +07:00
8f78f5184e maybe fix buy plan for friend 2024-10-10 10:18:35 +07:00
e9ae2e04ce Merge branch 'hotspotbilling:Development' into Development 2024-10-10 08:18:12 +07:00
56c69122e4 fix send plan radius
tolong di koreksi siapa tau ada kesalahan ambil code nya
2024-10-10 08:11:53 +07:00
2198c00d52 add radius to htaccess firewall 2024-10-09 17:00:33 +07:00
695b3c7a6f Fix radius rest for shared user online, check user online by count Start status. 2024-10-09 16:01:32 +07:00
f7cb6c196f Merge branch 'Development' 2024-10-08 11:22:39 +07:00
5adb09efcf show customer is online or not and Rearange Customer View 2024-10-08 10:19:59 +07:00
e70a6c4dae show is customer online in the Active List 2024-10-08 10:19:59 +07:00
4f3647beae Show is Customer Online in the customer list 2024-10-08 10:19:59 +07:00
9016ecbb98 Fix Invoice Print 2024-10-08 10:19:59 +07:00
277ab87645 Update radius.php
*Fix shared user limit
2024-10-08 10:19:59 +07:00
3a929c5418 Update radius.php
*Fix shared user limit
2024-10-08 10:19:59 +07:00
2771eba0ee Update invoice-print.tpl
Change print template design
2024-10-08 10:19:59 +07:00
5b51af1b6e change variable user to name to check pppoe online customer 2024-10-08 09:50:23 +07:00
05eab22062 show customer is online or not and Rearange Customer View 2024-10-07 15:05:22 +07:00
d4eaaad535 show is customer online in the Active List 2024-10-07 15:05:22 +07:00
3407571474 Show is Customer Online in the customer list 2024-10-07 15:05:22 +07:00
da1341d971 Fix Invoice Print 2024-10-07 15:05:22 +07:00
e4e70c5104 Merge pull request #311 from agstrxyz/patch-16
Update radius.php
2024-10-04 22:36:16 +07:00
fbde8b0056 Update radius.php
*Fix shared user limit
2024-10-04 22:20:42 +07:00
9552783eee Update radius.php
*Fix shared user limit
2024-10-04 21:16:26 +07:00
44f852ab2f Merge pull request #310 from ahmadhusein17/Development
Update invoice-print.tpl
2024-10-01 13:59:14 +07:00
3da0c356de Update invoice-print.tpl
Change print template design
2024-09-28 21:02:29 +07:00
d2db2bef79 Remove debug die() 2024-09-26 17:27:45 +07:00
44af731c4c PPPOE don't set IP for expired Plan 2024-09-26 09:43:12 +07:00
0a9733b0b3 Fix Unset IP 2024-09-25 17:14:50 +07:00
79c25a64d6 add option to show bandwith plan in the Miscellaneous settings 2024-09-25 15:06:15 +07:00
e4d3aff618 Fix Bug email 2024-09-25 15:02:51 +07:00
6462572fb8 show bandwidth plan 2024-09-25 15:00:13 +07:00
a51462ef1a show remaining bill 2024-09-24 10:55:07 +07:00
4ffbac878c Merge pull request #309 from gerandonk/Development
if exist remote ip on pppoe, it will reset on expired
2024-09-24 06:45:49 +07:00
3a2c55e0d8 if exist remote ip on pppoe, it will reset on expired
fix bugs expired user not going expired pool if remote-ip exist
2024-09-23 20:25:48 +07:00
229eae5c8f additional cost can be minus 2024-09-23 17:11:32 +07:00
aa0432df38 Discount price requested by Fiberwan 2024-09-23 16:43:24 +07:00
8e3a16a123 add Burst preset for bandwith 2024-09-23 16:09:59 +07:00
aa1ef2c41c When forgot password, tell to wait for n seconds before resend 2024-09-23 13:43:53 +07:00
4567f82a06 Merge pull request #306 from gerandonk/Development
Change Local address to remote address
2024-09-21 22:18:45 +07:00
ce61df9057 Merge pull request #308 from ahmadhusein17/Development
Update profile.tpl
2024-09-21 22:17:56 +07:00
6a24c6c11e Update activation-list.tpl 2024-09-21 15:37:50 +07:00
b1ad007db0 Update pool.tpl 2024-09-21 15:27:35 +07:00
bfd014417b Update profile.tpl
Display improvements
2024-09-20 19:37:28 +07:00
9b1adb15da Forgot Password and Forgot Username for Customer, requested by fiberwan 2024-09-20 17:07:18 +07:00
686d2a188a Add Public Header and Footer in user-ui folder, and use it for public page 2024-09-20 10:23:01 +07:00
1a65c04666 Merge branch 'Development' of https://github.com/gerandonk/phpnuxbill into Development 2024-09-18 19:13:45 +07:00
7e9f6123e8 Merge pull request #304 from Focuslinkstech/master
add seperate css for cron monitor
2024-09-15 00:26:18 +07:00
f738c9b120 add seperate css for cron monitor 2024-09-13 12:03:18 +01:00
5f7f785d99 Merge pull request #303 from agstrxyz/patch-15
Update MikrotikVpn.php
2024-09-13 16:44:06 +07:00
c0c54ce767 Merge pull request #302 from Focuslinkstech/master
fix admin dark mode switch button
2024-09-13 16:43:58 +07:00
f81f3df700 add mailer error reporting 2024-09-13 10:43:42 +01:00
3c6ef3bcf3 Update MikrotikVpn.php 2024-09-13 16:31:03 +07:00
985ddda41f fix panel warning bottom radius 2024-09-13 09:55:58 +01:00
a31286d781 fix toggle switch button mobile view 2024-09-13 09:47:43 +01:00
c2916da215 fix dark button 2024-09-13 09:33:59 +01:00
82569239e2 update changelog 2024-09-13 14:46:14 +07:00
bc0b3abe92 Merge pull request #301 from agstrxyz/Development
Paket vpn tunnel remot
2024-09-13 06:15:28 +07:00
f5d9649f97 Add files via upload 2024-09-13 00:46:40 +07:00
095e8937a2 Paket vpn tunnel remot 2024-09-13 00:43:46 +07:00
5f50d725f1 Merge pull request #298 from Focuslinkstech/Development
add styles to some tables
2024-09-12 23:40:11 +07:00
68de3a71b9 if admin session time error, it logout admin out whether admin are online or not, once time reach it logout you out 2024-09-12 11:39:45 +01:00
fe532a6238 change the time calculation logic 2024-09-11 16:52:34 +01:00
05b681df47 change current_time to current_date 2024-09-11 14:38:54 +01:00
723e99ebed fix panel headers in css, add cron job monitor to check if cron is running, has run, or not setup 2024-09-11 14:19:13 +01:00
13aabeea8e add styles to some tables 2024-09-09 20:45:13 +01:00
400672454c Merge pull request #297 from Focuslinkstech/Development
add htmlspecialchars_decode
2024-09-09 22:11:32 +07:00
aa3e522bc9 add htmlspecialchars_decode 2024-09-09 13:18:10 +01:00
b7fce955ac chenge _post to _get 2024-09-09 14:01:55 +07:00
594fab1151 fix toggle dark mode admin area 2024-09-09 13:55:39 +07:00
3aead7a98a Fix toggle dark mode 2024-09-09 13:23:26 +07:00
9a1321f597 Merge branch 'master' into Development 2024-09-09 10:47:14 +07:00
45bb3f04ee add back button on inbox 2024-09-09 10:26:28 +07:00
0d2b140bcf Fix Balance sent/received 2024-09-09 10:23:51 +07:00
f736868819 update remote-address instead local-address 2024-09-09 09:44:28 +07:00
84bc680f40 Change Local-Ip PPPoE to Remote-IP
Change Local-Ip PPPoE to Remote-IP in customer page and ignor attribute if empty
2024-09-09 07:19:48 +07:00
53512cfa58 Merge pull request #295 from Focuslinkstech/Development
more css style added
2024-09-08 17:50:50 +07:00
d0e66d8651 more css style added 2024-09-08 09:50:54 +01:00
c45ea74a29 Merge pull request #294 from Focuslinkstech/Development
fix pending transaction in customer dashboard
2024-09-07 18:29:40 +07:00
22615eb278 fix pending transaction in customer dashboard 2024-09-07 12:25:21 +01:00
ae550df078 Merge pull request #293 from Focuslinkstech/Development
We added bold to sidebar, we want your opinion if it looks good or we…
2024-09-06 20:16:27 +07:00
cc74667451 fix customer dashboard table list, , Package Bandwidth and Recharge Button 2024-09-06 13:56:33 +01:00
0fec69df89 We added bold to sidebar, we want your opinion if it looks good or we should remove it.
Please if you dont like the bold feature kindly report it
2024-09-06 13:19:23 +01:00
36b4a282ee Merge pull request #292 from hotspotbilling/revert-291-master
Revert "Just Fixes"
2024-09-06 18:00:50 +07:00
cb5ffb4236 Revert "Just Fixes" 2024-09-06 18:00:33 +07:00
2328489a14 Merge pull request #291 from Focuslinkstech/master 2024-09-06 17:56:44 +07:00
f8351e46f6 Merge pull request #290 from Focuslinkstech/Development 2024-09-06 17:56:17 +07:00
cf9a11ef94 add margin to the reports table list 2024-09-06 11:48:20 +01:00
80d4c904d0 Merge branch 'hotspotbilling:Development' into Development 2024-09-06 11:44:40 +01:00
9e63d7ce20 add margin to the table list 2024-09-06 11:36:30 +01:00
b517ef2b73 fix ignore list 2024-09-06 11:00:09 +07:00
ea0c663278 MikrotikPppoeCustom for PPPOE Custom Username, IP and Password 2024-09-06 10:59:26 +07:00
299fd90949 fix dark mode drop-down menu 2024-09-05 16:13:14 +01:00
c255a7be49 fix modal in dark mode 2024-09-05 15:47:28 +01:00
4f74aa0bff Please if you dont like the bold feature kindly report it 2024-09-05 14:14:50 +01:00
93d53cc6d8 We added bold to sidebar, we want your opinion if it looks good or we should remove it 2024-09-05 13:59:19 +01:00
40f452cff8 just little bold 2024-09-05 13:46:07 +01:00
aa787af017 fix bandwidth name not displayed on active packages on customer dashboard 2024-09-05 10:36:28 +01:00
af753d3a52 remove excess div 2024-09-05 10:07:22 +01:00
d060e21032 Added to Balance list 2024-09-05 10:05:23 +01:00
f43a2dcd7a Merge branch 'hotspotbilling:Development' into Development 2024-09-05 09:51:21 +01:00
0b5bc1fe71 Add margin to tables list in customer dashboard to give it unique view 2024-09-05 09:48:21 +01:00
4f969c787a Merge pull request #289 from Focuslinkstech/Development
Fix disabled on table list in dark mode
2024-09-05 14:13:40 +07:00
087ce1d0b6 Shift table down to fit the container 2024-09-05 08:11:17 +01:00
d7d76dde94 Fix disabled on table list in dark mode 2024-09-05 07:51:15 +01:00
7cbb874029 Merge pull request #288 from Focuslinkstech/Development
Development
2024-09-04 16:46:01 +07:00
44fe6a0ff7 fix list group item 2024-09-04 09:24:42 +01:00
74ec6128ab fixed box header color 2024-09-04 09:17:22 +01:00
899c27f1a7 Add Dark Mode to Customer Dashboard 2024-09-04 08:57:53 +01:00
f7a86666c2 Merge pull request #287 from Focuslinkstech/Development
Dark Mode Journey Has Started
2024-09-04 09:54:47 +07:00
a22e2e1b2c Dark Mode Journey Has Started 2024-09-03 17:43:39 +01:00
cc6babf04a Merge pull request #286 from Focuslinkstech/Development
fix WhatsApp welcome message not sending
2024-09-01 12:01:31 +07:00
9e7698f83d fix WhatsApp welcome message not sending 2024-08-31 12:37:20 +01:00
e1008ae61f move error customer to folder user-ui 2024-08-30 14:02:23 +07:00
ef0d19393f forgot to check enabled router when showing offline router 2024-08-30 13:53:13 +07:00
8e2a40d670 Error page for customer 2024-08-30 11:44:57 +07:00
34482ff0d4 move register button to right 2024-08-30 11:22:18 +07:00
35ace619d2 Merge pull request #283 from ahmadhusein17/Development
Development
2024-08-29 23:39:07 +07:00
6da6041cb8 Update admin-add.tpl 2024-08-29 23:33:43 +07:00
463ee27116 Update sendPlan.tpl 2024-08-29 19:03:57 +07:00
fe930a1012 Update selectGateway.tpl 2024-08-29 19:01:28 +07:00
abd22418ce Update profile.tpl 2024-08-29 18:55:21 +07:00
9099584d7c Update orderView.tpl 2024-08-29 18:46:21 +07:00
5a84e10b70 Update orderHistory.tpl 2024-08-29 18:30:55 +07:00
89786c64af Update orderBalance.tpl 2024-08-29 18:25:29 +07:00
e7ec68872b Update header.tpl 2024-08-29 18:17:53 +07:00
82d756567e add Registration code sms verification code to Language, some carrier block text Verification 2024-08-29 15:33:49 +07:00
9436a6e8f1 remove active when extend 2024-08-29 11:23:57 +07:00
e8885e91ec Merge pull request #282 from ahmadhusein17/Development
Development
2024-08-29 09:04:37 +07:00
8e96f1457a Update page-edit.tpl 2024-08-28 22:55:30 +07:00
2a2e9c3bd4 Update message.tpl 2024-08-28 22:53:55 +07:00
b8e856518f Update message.tpl 2024-08-28 22:36:11 +07:00
4e19894152 Update invoice.tpl 2024-08-28 22:34:25 +07:00
414f9929e5 Update invoice-print.tpl 2024-08-28 22:33:23 +07:00
5bcf278733 Update deposit.tpl 2024-08-28 22:30:08 +07:00
453ea564cf Update dbstatus.tpl 2024-08-28 22:29:12 +07:00
a167d97e3c Update dashboard.tpl 2024-08-28 22:25:25 +07:00
55349b40dd Update bandwidth.tpl 2024-08-28 22:15:01 +07:00
bac8819c31 Update bandwidth-edit.tpl 2024-08-28 22:14:06 +07:00
aaeaa9305a Update bandwidth-add.tpl 2024-08-28 22:13:24 +07:00
f37987a4c6 Update balance.tpl 2024-08-28 22:12:19 +07:00
393939fab4 Update balance-edit.tpl 2024-08-28 22:10:51 +07:00
0189234b56 Update app-settings.tpl 2024-08-28 22:08:33 +07:00
8d4d88f702 Update app-localisation.tpl 2024-08-28 21:46:15 +07:00
5d79cb65ce Update admin-edit.tpl 2024-08-28 21:42:45 +07:00
1d08e8d204 Update admin-add.tpl 2024-08-28 21:38:56 +07:00
afaafd6195 remove debug 2024-08-28 14:50:26 +07:00
bd9989eaf2 hide when no router offline 2024-08-28 14:39:19 +07:00
ea814dcaf8 remove Wise in the community page 2024-08-28 14:24:07 +07:00
11ec4185e7 Show Offline routers in the dashboard 2024-08-28 14:18:19 +07:00
7963c2f388 fix update script to add status in tbl_routers 2024-08-28 14:04:42 +07:00
9b418ed078 Add Info to enable Router check, don't show Router status if not enabled 2024-08-28 13:59:09 +07:00
d31274b983 Fix Cron Router check, go to cache instead uploads folder. and check if Mikrotik using Default port 2024-08-28 13:58:40 +07:00
9772760146 Merge pull request #280 from Focuslinkstech/Development
Development
2024-08-28 13:49:41 +07:00
7e8913f0b4 Merge pull request #279 from ahmadhusein17/Development
Development
2024-08-28 13:49:26 +07:00
2cf0981030 Merge branch 'hotspotbilling:Development' into Development 2024-08-28 07:26:21 +01:00
ca26f8baa6 We have tried to mitigate the impact of overlapping and also tried to prevent it or reduced it, we still advice admin to be vigilant 2024-08-28 07:23:58 +01:00
d99a51d40e Update plan.tpl 2024-08-28 08:57:02 +07:00
20e212eaa6 Update balance-add.tpl 2024-08-27 18:46:21 +07:00
00f65c6aab Update hotspot.tpl 2024-08-27 18:44:41 +07:00
dcaa7f9cea Update hotspot-add.tpl 2024-08-27 18:41:11 +07:00
1bbc7a30f0 Update hotspot-edit.tpl 2024-08-27 18:38:39 +07:00
e8526e294a Merge pull request #276 from ahmadhusein17/Development
Indonesian language fix
2024-08-27 15:02:00 +07:00
554479b79b Merge pull request #278 from Focuslinkstech/Development
Development
2024-08-27 15:01:46 +07:00
edfa98e6b2 Add condition to router check, if enable or not 2024-08-27 08:17:28 +01:00
079b308bf1 Merge branch 'hotspotbilling:Development' into Development 2024-08-27 00:35:23 +01:00
76ac9431b3 added router online status, also add monitor and also report to admin when goes offline, report depend on cron runtime 2024-08-27 00:26:52 +01:00
86b441f41f Merge pull request #277 from agstrxyz/Development
Development
2024-08-26 21:00:04 +07:00
1a70e2232c Update order.php 2024-08-26 20:09:45 +07:00
e31aeca2dd Update order.php
fix gagal beli voucher paket radius rest
2024-08-26 19:15:40 +07:00
502a0a2b0c Update hotspot-edit.tpl 2024-08-26 18:01:13 +07:00
e78a1ca15e Update hotspot-add.tpl 2024-08-26 17:58:22 +07:00
b9c31a856e Update hotspot.tpl 2024-08-26 17:54:52 +07:00
4889853e53 Update header.tpl
Language fixes
2024-08-26 17:49:49 +07:00
178dad8e03 Update pppoe.tpl
Language fixes
2024-08-26 17:43:54 +07:00
50a3f0a175 Merge pull request #275 from Focuslinkstech/Development
Development
2024-08-26 08:52:00 +07:00
2f85240689 add border color 2024-08-25 17:37:02 +01:00
9e917536c0 fix border-radius 2024-08-25 17:23:18 +01:00
1d5341cdfe reduce some border radius in template 2024-08-25 17:18:22 +01:00
73e070b3ed Merge pull request #273 from Focuslinkstech/Development
Development
2024-08-23 15:27:41 +07:00
d34f6919c7 more fix 2024-08-23 09:20:25 +01:00
1e8cf726c3 more fix 2024-08-23 09:03:36 +01:00
71f0fc47a6 more template fixes 2024-08-23 08:19:36 +01:00
c188a8a91d More template fixes 2024-08-23 07:54:11 +01:00
c57cdad9cf Merge pull request #272 from ahmadhusein17/Development
Fix & Update Translation
2024-08-23 10:32:06 +07:00
2385d5f4c0 Merge pull request #271 from Focuslinkstech/Development
New Template Journey
2024-08-23 10:31:24 +07:00
7b2acec181 Merge pull request #270 from agstrxyz/Development
Update pppoe.tpl
2024-08-23 10:31:13 +07:00
04bc749eae add more 2024-08-22 22:35:11 +01:00
b2d9c13c51 Fixed 2024-08-22 21:31:37 +01:00
9ca0b3907c fixed 2024-08-22 21:30:12 +01:00
9f2f739ba6 Fixed 2024-08-22 21:29:09 +01:00
cde2706c04 Still on the Journey 2024-08-22 21:26:34 +01:00
0265304069 Add more 2024-08-22 20:50:00 +01:00
9955c89024 Still on New Template Journey 2024-08-22 20:42:44 +01:00
7d4ca685bd Update app-settings.tpl
Translation updates and fixes
2024-08-22 23:50:43 +07:00
e95d5611f9 Update indonesia.json
Fix weird translation
2024-08-22 23:30:38 +07:00
d6184f7be2 New Template Journey 2024-08-22 14:59:23 +01:00
1d993795d2 Update pppoe.tpl
fix shortcut pppoe expired plan
2024-08-22 15:40:22 +07:00
0b84d54ef2 Fix Nas Template 2024-08-22 11:19:07 +07:00
81e30f0c54 info if pppoe IP has been used by another customer 2024-08-22 10:35:17 +07:00
34af35f0e1 Merge pull request #269 from Focuslinkstech/Development
new template
2024-08-22 09:44:42 +07:00
244f477f27 Merge pull request #268 from agstrxyz/Development
Update Radius.php
2024-08-22 09:43:06 +07:00
34f904ccc4 Add User Search Bar 2024-08-22 02:00:23 +01:00
8c206ba1f8 fix user footer 2024-08-21 22:20:06 +01:00
be3db52578 Lets start the journey of our new template from here, lots need to be done 2024-08-21 22:09:10 +01:00
342fbe25a8 Update Radius.php
lokal ip dan profil expired pppoe
2024-08-21 21:31:27 +07:00
55232ac4b3 null to '' to delete pppoe_ip 2024-08-21 17:09:22 +07:00
7172cfbb79 check if pppoe IP or Username has been used by other customer 2024-08-21 17:06:10 +07:00
0bb1f8ffa0 fix show extend_expired 2024-08-21 17:06:10 +07:00
61ac04d62c Merge pull request #264 from ahmadhusein17/Development
Update voucher.tpl
2024-08-21 16:50:29 +07:00
f53e60161a Update voucher.tpl
Update translation
2024-08-21 15:45:41 +07:00
49a3d363cb Merge pull request #263 from ahmadhusein17/Development
Development
2024-08-21 15:41:04 +07:00
e15faf605d Update routers.tpl
Update translation
2024-08-21 15:40:29 +07:00
0617fd4cc8 Merge branch 'hotspotbilling:Development' into Development 2024-08-21 15:38:26 +07:00
803c85c6f4 Update routers-edit.tpl
Update translation
2024-08-21 15:36:16 +07:00
f0f9daf72a Update routers-add.tpl
Update translation
2024-08-21 15:35:13 +07:00
d152124eea Update router-error.tpl
Update translation
2024-08-21 15:29:55 +07:00
e54e5641e6 add comment 2024-08-21 15:17:39 +07:00
a265b6e766 add placeholder user-header 2024-08-21 15:16:29 +07:00
e21b766a7a Update reports.tpl
Update translation
2024-08-21 15:15:27 +07:00
c4281f1d54 Update reports-activation.tpl
Update translation
2024-08-21 15:09:22 +07:00
505bb1dd71 Update recharge.tpl
Update translation
2024-08-21 15:08:11 +07:00
b0b687dd38 Update recharge.tpl
Update translation
2024-08-21 15:07:18 +07:00
95620da151 Update radius-nas.tpl
Update translation
2024-08-21 15:05:41 +07:00
26d14794e2 Update radius-nas-edit.tpl
Update translation
2024-08-21 15:03:34 +07:00
8bf5c71800 Update radius-nas-add.tpl
Update translation
2024-08-21 15:01:00 +07:00
dc78167bcb Merge branch 'master' into Development 2024-08-21 14:54:48 +07:00
57ce1c816c Update pppoe.tpl 2024-08-21 14:40:21 +07:00
ca2df9c02e Update pppoe-edit.tpl 2024-08-21 14:40:21 +07:00
ae8815c668 Update pppoe-add.tpl 2024-08-21 14:40:21 +07:00
0a66553112 Update pool.tpl 2024-08-21 14:40:21 +07:00
be33c6ae01 Update pool-edit.tpl 2024-08-21 14:40:21 +07:00
ad14ef58ca Update pool-edit.tpl 2024-08-21 14:40:21 +07:00
e94d3cdb4a Update plugin-manager.tpl 2024-08-21 14:40:21 +07:00
4b29fab73f Update paymentgateway-audit.tpl 2024-08-21 14:40:21 +07:00
267095d68b Update paymentgateway-audit-view.tpl 2024-08-21 14:40:21 +07:00
1c2f9ddca5 Update message-bulk.tpl 2024-08-21 14:40:21 +07:00
83efa89dfc Update logs.tpl 2024-08-21 14:40:21 +07:00
3e0b8b66f8 Update logs-radius.tpl 2024-08-21 14:40:21 +07:00
31253fd14d Update selectGateway.tpl 2024-08-21 14:40:21 +07:00
a1ec9e8040 Update orderPlan.tpl 2024-08-21 14:40:21 +07:00
0d97d0d753 Update orderBalance.tpl 2024-08-21 14:40:21 +07:00
d4584e6288 Update hotspot-edit.tpl 2024-08-21 14:40:20 +07:00
2f9780cf62 Merge branch 'Development' 2024-08-21 14:40:03 +07:00
04bb8dca9a Merge pull request #261 from ahmadhusein17/Development
Development
2024-08-21 14:37:23 +07:00
8c5f4bd9dc Merge pull request #262 from Focuslinkstech/master
fix typo error
2024-08-21 14:37:00 +07:00
c1d93f1fe2 fix logic compability 2024-08-21 14:33:26 +07:00
8e9aeec517 fix logic compability 2024-08-21 14:33:11 +07:00
02e1989745 fix typo error 2024-08-21 08:09:37 +01:00
14c4ab8fbc show bandwidth plan in the customer view admin 2024-08-21 14:00:44 +07:00
24e49e4eb9 compability for old plugin 2024-08-21 13:39:06 +07:00
75907d5c3f Update pppoe.tpl 2024-08-20 20:30:08 +07:00
a96f808318 Update pppoe-edit.tpl 2024-08-20 20:26:43 +07:00
7643b24f31 Update pppoe-add.tpl 2024-08-20 20:21:49 +07:00
1801b5b436 Update pool.tpl 2024-08-20 20:19:34 +07:00
aeeb9a2b63 Update pool-edit.tpl 2024-08-20 20:13:33 +07:00
e36db6d16c Update pool-edit.tpl 2024-08-20 20:10:01 +07:00
ee7aa60979 Update plugin-manager.tpl 2024-08-20 20:08:05 +07:00
34ea48fd7d Update paymentgateway-audit.tpl 2024-08-20 20:00:19 +07:00
1d4eff11ba Update paymentgateway-audit-view.tpl 2024-08-20 19:58:09 +07:00
c41fe7435f Update message-bulk.tpl 2024-08-20 19:54:22 +07:00
d0b60d5e7d Update logs.tpl 2024-08-20 19:48:29 +07:00
c6346aabc3 Update logs-radius.tpl 2024-08-20 19:46:49 +07:00
49f3c1a141 Update selectGateway.tpl 2024-08-20 19:40:17 +07:00
e4c8be2c16 Update orderPlan.tpl 2024-08-20 19:35:07 +07:00
fa27ccf5ca Update orderBalance.tpl 2024-08-20 19:31:18 +07:00
5ae4dde522 Update hotspot-edit.tpl 2024-08-20 19:23:14 +07:00
b7d88e9b8d Merge branch 'Development' 2024-08-19 16:23:28 +07:00
d83c2ec037 Payment info 2024-08-19 16:13:51 +07:00
e908ade918 payment Info 2024-08-19 15:58:51 +07:00
d11cd83089 allow set default attribute in the getAttribute 2024-08-19 15:01:25 +07:00
df5e03a827 add user_language to attributes 2024-08-19 14:58:52 +07:00
0d966a5e03 Email Verification when change email 2024-08-19 14:23:02 +07:00
da985f27e1 2024.8.19 2024-08-19 12:00:43 +07:00
a67943c75f 2024.8.18 2024-08-19 12:00:29 +07:00
cb3caef529 Merge branch 'Development' into TemplateRedesigned 2024-08-19 11:57:35 +07:00
08c7de1559 same template name will overwrite 2024-08-19 11:57:14 +07:00
b8a02bb837 Update version.json 2024-08-19 11:57:03 +07:00
ef6672d786 Update init.php
- replace ismysqlRadius with isTableExist , to use it any many diffrent position.
2024-08-19 11:56:23 +07:00
3c094e7674 user-header.tpl to header.tpl 2024-08-19 11:56:09 +07:00
f92e9612b5 Merge pull request #260 from ahmadhusein17/Development
Development
2024-08-19 11:55:44 +07:00
5e43fe3882 Merge pull request #258 from Focuslinkstech/Development
Update customers.php
2024-08-19 11:54:59 +07:00
cc2589e9f5 Merge branch 'Development' into TemplateRedesigned 2024-08-19 11:50:36 +07:00
6c113b5b6d Merge branch 'Development' into TemplateRedesigned 2024-08-19 11:49:22 +07:00
66f0390288 add payment info 2024-08-19 11:48:06 +07:00
1e0036465f 1 payment Gateway still show gateway chooser, so customer can see how much they will paid 2024-08-19 11:26:33 +07:00
be0bbcb570 Update hotspot.tpl 2024-08-18 00:13:33 +07:00
fdaa87d02c Update hotspot-add.tpl 2024-08-18 00:12:14 +07:00
9f997ec68f Update dbstatus.tpl 2024-08-18 00:07:43 +07:00
147e0f5d92 Update reports.tpl 2024-08-17 23:59:35 +07:00
7d9a4623b1 Update page-edit.tpl 2024-08-17 23:58:27 +07:00
6dc957e34a Update bandwidth.tpl
Update Translation
2024-08-17 23:56:20 +07:00
92c6022c85 Update page-edit.tpl
Update Translation
2024-08-17 23:52:50 +07:00
85bebebb3a same template name will overwrite 2024-08-17 23:09:27 +07:00
1b56680a6d Voucher Template List, you can save multiple template for voucher 2024-08-17 23:09:15 +07:00
eb5d1f5819 change from niceedit to summernet, it support table 2024-08-17 23:09:15 +07:00
1c8e2c0422 10 minutes for request resend OTP, 20 minutes for expired 2024-08-17 23:09:15 +07:00
cf4f708f83 set customer language to cookie 2024-08-17 23:09:15 +07:00
1c82378837 Update community.tpl 2024-08-17 23:09:15 +07:00
04321c9e4b Update translate 2024-08-17 23:09:14 +07:00
887d0566bc same template name will overwrite 2024-08-17 23:08:20 +07:00
b36e96b9fc Voucher Template List, you can save multiple template for voucher 2024-08-17 23:07:42 +07:00
5c30be70c0 change from niceedit to summernet, it support table 2024-08-17 22:18:15 +07:00
03497cb289 Update customers.php
fix typo error
2024-08-17 14:17:59 +01:00
5242b19f6f 10 minutes for request resend OTP, 20 minutes for expired 2024-08-17 11:18:39 +07:00
5070cd9ed7 set customer language to cookie 2024-08-17 11:18:39 +07:00
74e806886c Update version.json 2024-08-17 11:07:55 +07:00
d8a91ebda9 Merge pull request #252 from ahmadhusein17/Development
Update translate
2024-08-16 18:09:30 +07:00
edae267a39 Merge branch 'hotspotbilling:Development' into Development 2024-08-16 17:36:14 +07:00
eaa982c5c3 Merge pull request #255 from Focuslinkstech/master
Template Redesigned
2024-08-16 16:28:58 +07:00
0eead7cea1 Template Redesigned
we have move all user template files to folder ui/ui/user-ui for easy customisation
2024-08-16 09:39:41 +01:00
19ea56df3f Merge pull request #254 from amolood/patch-3
Update init.php
2024-08-16 11:17:44 +07:00
c158d49965 fix Binary in the plan.php 2024-08-16 09:17:46 +07:00
73aa663b25 Update init.php
- replace ismysqlRadius with isTableExist , to use it any many diffrent position.
2024-08-15 18:46:44 +03:00
a4303a4804 Merge branch 'hotspotbilling:Development' into Development 2024-08-15 16:48:38 +07:00
d496d89f95 Update community.tpl 2024-08-15 16:19:01 +07:00
70eea6dd37 Update translate 2024-08-15 15:54:52 +07:00
6740b0212d adjust language selector 2024-08-15 15:33:44 +07:00
960d141246 Merge branch 'Development' 2024-08-15 15:10:50 +07:00
7af2c57703 roolback menu header 2024-08-15 15:09:07 +07:00
768e2f203d Update app-settings.tpl 2024-08-15 15:09:07 +07:00
a06735faaa Update app-devices.tpl 2024-08-15 15:09:07 +07:00
5de893d16d Update 404.tpl 2024-08-15 15:09:07 +07:00
ebb41f114a Update dbstatus.tpl 2024-08-15 15:09:07 +07:00
02024fdcc7 strtolower keywords 2024-08-15 15:08:52 +07:00
e96a9677bc Merge pull request #251 from Focuslinkstech/master
Customer can set their own language
2024-08-15 15:08:08 +07:00
21f930dfa3 Update user-header.tpl 2024-08-15 09:04:12 +01:00
4f6339330f Merge branch 'master' of https://github.com/Focuslinkstech/phpnuxbill 2024-08-15 09:00:11 +01:00
86bd2a8dd4 Merge pull request #250 from ahmadhusein17/Development
Update app-settings.tpl
2024-08-15 14:13:24 +07:00
c548f8e307 Merge branch 'Development' into Development 2024-08-15 14:13:17 +07:00
8b26374000 Fix Lang 2024-08-15 14:10:27 +07:00
30554983ec fix acctStatusType 2024-08-15 14:07:00 +07:00
1c0cc75417 Update app-settings.tpl 2024-08-15 14:06:57 +07:00
63c1d4ca76 Merge pull request #249 from ahmadhusein17/Development
Update app-settings.tpl
2024-08-15 14:06:12 +07:00
3894a54b96 Update app-settings.tpl
There is a < that is deleted
2024-08-15 14:00:46 +07:00
a1bae5c7a5 roolback menu header 2024-08-15 11:06:34 +07:00
4d7db37edc Update user-header.tpl
Forgot to add/create url not working
2024-08-15 11:03:48 +07:00
fd58188771 fix user URL 2024-08-15 11:03:48 +07:00
e85ad38d1d Update user-header.tpl
fix omitted "/"
2024-08-15 11:03:48 +07:00
fa5a42fc0c Update radius.php 2024-08-15 11:03:48 +07:00
625254852d Create DejaVuSansMono.ttf
add missing mpdf fonts
2024-08-15 11:03:48 +07:00
6b39863418 Merge pull request #248 from ahmadhusein17/Development
Update dbstatus.tpl
2024-08-15 11:01:14 +07:00
d52f751997 Merge pull request #247 from ahmadhusein17/master
Update user-header.tpl
2024-08-15 10:53:24 +07:00
7564b8f244 Update app-settings.tpl 2024-08-15 10:41:17 +07:00
522d05f53c fix user URL 2024-08-15 10:32:59 +07:00
765d2d91da Update app-devices.tpl 2024-08-15 10:23:20 +07:00
f238bf7a76 Update 404.tpl 2024-08-15 10:21:10 +07:00
62867c0559 Update dbstatus.tpl 2024-08-15 10:16:02 +07:00
cf3209b3e8 Update user-header.tpl
Forgot to add/create url not working
2024-08-15 10:12:03 +07:00
5cb1a901c3 Language Switching
customer can now choose preferred language from available language list.

we are store language in session for now, if customer logout the language will reset back to default, we will move it to database in the coming update.
2024-08-14 18:24:43 +01:00
676dcbe478 Update user-header.tpl
fix bug in header
2024-08-14 15:42:07 +01:00
ef53835fbf Merge pull request #245 from Focuslinkstech/master
update
2024-08-14 20:38:23 +07:00
e8104b87e6 Update user-header.tpl
fix omitted "/"
2024-08-14 14:32:01 +01:00
baab016135 Merge branch 'hotspotbilling:master' into master 2024-08-14 14:26:44 +01:00
6f8ec3caeb Merge pull request #244 from amolood/patch-2
Update radius.php
2024-08-14 20:07:20 +07:00
b3bd0c31e4 Update radius.php 2024-08-14 15:27:06 +03:00
b4a37a4722 get ready for pretty url 2024-08-14 16:54:14 +07:00
68959b5a2e fix restore old version for mobile version 2024-08-14 13:28:49 +07:00
b9fc39a148 Merge pull request #243 from amolood/patch-1
Update README.md
2024-08-14 10:29:24 +07:00
ced0d487e7 Fix rad_acct 2024-08-14 09:11:11 +07:00
9fc4dfa94d Update README.md
- Thanks for the contributor
2024-08-14 02:21:12 +03:00
afa0616487 Create DejaVuSansMono.ttf
add missing mpdf fonts
2024-08-13 12:45:32 +01:00
0fae434c1b Reports with chart 2024-08-13 11:42:22 +07:00
0fa78966ef radius rest, check pppoe_username and pppoe_password. allow empty password for voucher 2024-08-13 10:32:34 +07:00
5e080f18fe graph report 2024-08-12 16:53:40 +07:00
6765a6b17c fix logic password. case sensitive username 2024-08-12 13:34:19 +07:00
3f7c17d9b1 case sensitive voucher check 2024-08-11 19:54:33 +07:00
4d7c2bd373 if has keyword, then add to user recharge 2024-08-09 19:28:17 +07:00
2b104d2ed7 intval acctInputOctets 2024-08-09 17:36:45 +07:00
6666fd0ed4 Mikrotik Hotspot remove active after move to expired 2024-08-09 17:25:52 +07:00
b8f15992d1 fix reports link 2024-08-09 17:24:43 +07:00
b7b23fc39a disconnect customer when moving to expired 2024-08-09 16:29:26 +07:00
c7e1dc963f Fix calculate data 2024-08-09 16:25:39 +07:00
8adf41bf75 accounting dont use Mac as identity, just username 2024-08-09 14:48:19 +07:00
85ede07d77 Zero datausage if expired 2024-08-09 14:41:06 +07:00
3f5ea7251e log data usage in th start status 2024-08-09 14:33:43 +07:00
af157cdff9 fix voucher logic 2024-08-09 14:18:54 +07:00
d39af7d404 add 0 default 2024-08-09 14:09:46 +07:00
c26f08e5c9 Merge pull request #241 from amolood/patch-4
Update Text.php
2024-08-09 09:29:47 +07:00
c1588d88c3 Update Text.php
- Fix the function convertDataUnit
2024-08-09 01:23:55 +03:00
d19cdf3897 Add Device to Plugin Manager 2024-08-08 15:35:40 +07:00
7fccf95eb0 Voucher Devices is ok to buy and redeem 2024-08-08 15:10:22 +07:00
630f20094a don't put to tbl_transactions if customer redeem voucher code Created by Voucher Device 2024-08-08 14:58:04 +07:00
9ad9ba45b1 for getting ready voucher Devices 2024-08-08 14:46:35 +07:00
d830ba4e06 removePpoeActive based pppoe_username 2024-08-08 14:44:42 +07:00
ce7d8859b0 getting ready for voucher selling only 2024-08-08 14:44:27 +07:00
bbed893759 add token to order plan 2024-08-08 14:42:50 +07:00
70de538f38 remove default value coordinates 2024-08-07 13:58:27 +07:00
3d127b6f54 remove default value coordinates 2024-08-07 13:57:16 +07:00
51c594b8d6 changelog update 2024-08-07 13:53:45 +07:00
b24ad24e89 Fix QRCode Scanner 2024-08-07 13:51:27 +07:00
f1a9ad6d63 simplify Chap verify RadiusRest 2024-08-07 11:10:43 +07:00
e5ff8c5675 fix radius change username 2024-08-07 10:52:31 +07:00
3c218d9ec9 Change UI and code payment gateway audit 2024-08-07 09:51:37 +07:00
cf04a50c35 Merge pull request #237 from hotspotbilling/revert-236-master
Revert "Fix the view"
2024-08-07 09:32:52 +07:00
d35c7080d9 Revert "Fix the view" 2024-08-07 09:32:01 +07:00
9720822c79 Merge pull request #236 from amolood/master
Fix the view
2024-08-07 09:27:44 +07:00
dc4adbc0c6 Update paymentgateway-audit.tpl
- fix the view
2024-08-07 01:53:53 +03:00
6316992bc3 Update paymentgateway-audit-view.tpl
- fix the view
2024-08-07 01:53:31 +03:00
668c7af1e9 update acctOutputOctets for limiting data usage 2024-08-06 16:39:03 +07:00
8c00496a5b add acctinputoctets acctoutputoctets 2024-08-06 16:25:36 +07:00
da3ef535a2 Text::convertDataUnit($datalimit, $unit) 2024-08-06 16:19:55 +07:00
bf71393fff sync PPPOE IP when it change 2024-08-06 15:42:57 +07:00
a242d3b98b Merge branch 'Development' 2024-08-06 10:29:08 +07:00
3b8a5ad2dc update CHANGELOG 2024-08-06 10:26:47 +07:00
3ca43c69a8 allow $pppoe_username so it will back to customer username 2024-08-06 10:26:47 +07:00
cb0e77523d check if customer username already used by pppoe_username 2024-08-06 10:26:47 +07:00
8ab1939dd9 check if pppoe customer already used by another customer 2024-08-06 10:26:46 +07:00
a3c255e77a what if admin want to revert using customer username? now it can revert username 2024-08-06 10:26:46 +07:00
2a7563f43d if expired plan deleted, go delete customer when expired 2024-08-06 10:26:46 +07:00
95899b4eba check if admin change username pppoe or not 2024-08-06 10:26:46 +07:00
5d6a06b6fa Update init.php
- add Boolean function isMysqlRadius() to check if the system is using Radius mysql or not.
2024-08-06 10:26:46 +07:00
7a95d79315 remove customer check pppoe_username 2024-08-06 10:26:46 +07:00
096d3ea56d getIDby customer 2024-08-06 10:26:46 +07:00
a09bfc01bf remove customer check pppoe_username 2024-08-06 09:23:12 +07:00
271dd2934a getIDby customer 2024-08-06 09:23:12 +07:00
ce47ee1533 Merge pull request #235 from amolood/patch-2
Update init.php
2024-08-06 09:22:50 +07:00
0214b6436d Merge pull request #234 from amolood/patch-1
Create arabic.json
2024-08-06 09:22:14 +07:00
810c5ae827 Update init.php
- add Boolean function isMysqlRadius() to check if the system is using Radius mysql or not.
2024-08-05 15:52:14 +03:00
bcdad02274 Create arabic.json
- add arabic translation
2024-08-05 15:47:25 +03:00
d138e1b36f move I'd to first MikrotikPppoe.php 2024-08-05 18:03:59 +07:00
7d2f69f96f Router Maps 2024-08-05 15:20:18 +07:00
46a4c5f24d Remove Datatables in customer list 2024-08-05 11:45:27 +07:00
509658be9c green sync button 2024-08-05 11:22:04 +07:00
9f96da34b5 pppoe ip and username 2024-08-05 11:22:04 +07:00
be507a013a User Inbox 2024-08-05 11:22:04 +07:00
150f9a5c41 Merge pull request #233 from hotspotbilling/revert-231-master
Revert "Fixed Customer List Bug"
2024-08-05 11:15:58 +07:00
e9daa2e5e7 Revert "Fixed Customer List Bug" 2024-08-05 11:13:43 +07:00
fc1173c534 Merge pull request #231 from Focuslinkstech/master
Fixed Customer List Bug
2024-08-05 11:12:39 +07:00
a632bac3a6 Merge pull request #230 from rickicode/master
Refactor username validation to allow special characters for autologin using MAC address hotspot
2024-08-05 10:53:52 +07:00
8358e8b885 Merge pull request #232 from sabtech254/master
Update customers.tpl
2024-08-05 10:51:58 +07:00
01b3b66e3d Update customers.tpl
Added a sync button
2024-08-03 22:10:02 +03:00
1065bfc017 Update customers.tpl
add default value to package row
2024-08-03 14:32:28 +01:00
dec4258a6f Fixed Customer List Bug
fix the package not listing on next page in customer list
2024-08-03 14:16:51 +01:00
f5042cc665 Refactor username validation to allow special characters for autologin using MAC address hotspot 2024-08-02 18:13:35 +07:00
084ee83ec3 hapus "alphanumeric(_post('username'), "+_.@-")" agar bisa digunakan untuk autologin menggunakan mac address hotspot 2024-08-02 18:07:17 +07:00
587437c93c customer inbox 2024-08-02 17:22:42 +07:00
1843ff222d tbl_customers_inbox 2024-08-02 16:34:18 +07:00
de85d30a9e Fix view order if it not unpaid 2024-08-02 16:03:20 +07:00
82904b9302 check master or main 2024-08-02 15:48:31 +07:00
face1dfa57 send expired date to telegram when customer recharge 2024-08-02 10:28:34 +07:00
7e5a718ba8 balance send to email 2024-08-02 10:24:24 +07:00
c2fffdab88 plugin manager detect old installation file 2024-08-02 10:24:11 +07:00
9578006e11 add required field for installation 2024-08-02 09:42:09 +07:00
828ad7f880 Fix locked out bug 2024-08-01 21:25:14 +07:00
ed4bd4861a Fix sync Bug 2024-08-01 20:21:58 +07:00
b64e841bde Payment Gateway Audit page 2024-08-01 17:55:58 +07:00
efdb0fc6bc show paid plugin 2024-08-01 13:39:40 +07:00
0b5c24239d Customer can sync plan 2024-08-01 11:08:01 +07:00
a5971bfff3 show bandwidth plan 2024-08-01 11:05:40 +07:00
aac8306d7b disconnect PPPOE when plan change 2024-08-01 10:12:54 +07:00
721ef32e12 continue activate package, when it cannot connect to Device, let admin sync it manual 2024-08-01 09:25:13 +07:00
67e383375e still error, why 2024-07-31 22:09:46 +07:00
4434e7d217 session_timeout_duration make crash when no data 2024-07-31 22:04:28 +07:00
fc6cd59ea0 critical fix operand when save settings 2024-07-31 22:01:01 +07:00
608c9e5b6f Merge pull request #227 from Focuslinkstech/master
add url to default welcome message
2024-07-31 16:07:46 +07:00
d494df3321 Merge branch 'hotspotbilling:master' into master 2024-07-31 10:00:05 +01:00
9ec0148800 Update notifications.default.json 2024-07-31 09:59:06 +01:00
deb6cceeb9 Merge branch 'Development' 2024-07-31 15:57:51 +07:00
210178be84 Fix Show additional Bill when customer order 2024-07-31 15:57:43 +07:00
063ae834bc update tbl_payment_gateway to support Stripe 2024-07-31 15:56:00 +07:00
4c65248823 Show Additional bills when select gateway 2024-07-31 15:55:59 +07:00
c417fe63e2 update tbl_payment_gateway to support Stripe 2024-07-31 15:55:15 +07:00
6ca5054fe8 Merge pull request #226 from Focuslinkstech/master
Add send welcome message
2024-07-31 15:54:40 +07:00
0bcac10eec Merge branch 'master' of https://github.com/Focuslinkstech/phpnuxbill 2024-07-31 09:50:03 +01:00
ada529ce97 Show Additional bills when select gateway 2024-07-31 13:42:19 +07:00
8e55330bcb Add send welcome message
send welcome message to new  registered customers.

Goto settings -> User Notification -> Welcome Message to setup message
2024-07-31 00:57:53 +01:00
eca40e0faf if trx status already 2, don't proceed 2024-07-29 16:21:01 +07:00
8e1d608e5f fix compability 2024-07-29 16:20:27 +07:00
50b4db1723 update docs 2024-07-29 14:28:03 +07:00
b3e97ecf6d fix inactive customer and check enable_balance 2024-07-29 14:12:26 +07:00
a85e9ee95a Merge branch 'Development' 2024-07-29 11:18:45 +07:00
fa154b007f change $db_password to $db_pass 2024-07-29 09:06:27 +07:00
3b9c5d16f8 2024.7.27
fix logic bug
2024-07-29 08:59:39 +07:00
dd7f9dc94e let the device decide again 2024-07-29 08:59:39 +07:00
2a9b50e015 Merge pull request #225 from Focuslinkstech/master
Add session expiration settings
2024-07-29 08:59:12 +07:00
1c0ff671a5 Merge pull request #224 from Focuslinkstech/master
Update Radius.php
2024-07-29 08:57:49 +07:00
bd48bb0591 2024.7.27
fix logic bug
2024-07-27 23:47:38 +07:00
278def959b let the device decide again 2024-07-27 22:57:45 +07:00
5a47da013b Add session expiration settings
You can now set session expiration in settings -> General Settings -> Miscellaneous

if admin is Idles for more than minutes set, he will required to login again, just for account security concerns.

you can enable or disable
2024-07-27 00:56:48 +01:00
282bf6190c Update Radius.php
Fix Radius Delete Plan
2024-07-26 18:21:48 +01:00
2f1ee0cfbf Merge pull request #223 from Focuslinkstech/master
Update Package.php and devices/Radius.php
2024-07-26 16:07:30 +07:00
5783ea05f9 show Total Customer Balance 2024-07-26 15:55:17 +07:00
5ca81ea92e hide and unhide github token 2024-07-26 15:55:01 +07:00
6496f49df6 Report only show to admin and report user 2024-07-26 15:54:39 +07:00
7d3afa091f Bugs Fix
lots has been done that i cant even recalled, but i think all identify bugs is fix, i urge you all to give it test and report any issues arising.
2024-07-26 09:49:52 +01:00
cc68770f51 remove check change plan, let the device decide 2024-07-25 17:13:18 +07:00
20f98ab066 allow download from private/paid repository 2024-07-25 10:59:23 +07:00
1697f18986 fix that fix this 2024-07-25 10:30:53 +07:00
c711f82bba fix customer view 2024-07-25 09:17:01 +07:00
2727d53d62 Merge branch 'master' into Development 2024-07-25 09:12:59 +07:00
e68b5555ab Merge pull request #222 from agstrxyz/patch-14
Add chap support
2024-07-25 07:42:06 +07:00
4b6204391b fix installation file step4.php 2024-07-25 07:26:05 +07:00
570c93d4a4 Update radius.php
menambahkan metode otentikasi CHAP
2024-07-24 22:40:43 +07:00
378 changed files with 32769 additions and 6253 deletions

4
.gitignore vendored
View File

@ -46,9 +46,11 @@ system/devices/**
!system/devices/Radius.php
!system/devices/RadiusRest.php
!system/devices/MikrotikPppoe.php
!system/devices/MikrotikPppoeCustom.php
!system/devices/MikrotikHotspot.php
/.vs
docker-compose.yml
docs/**
!docs/*.html
!docs/*.md
!docs/*.md
.htaccess

View File

@ -11,4 +11,17 @@
<Files update.php>
Order Allow,Deny
Allow from all
</Files>
</Files>
<Files radius.php>
Order Allow,Deny
Allow from all
</Files>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

View File

@ -2,6 +2,99 @@
# CHANGELOG
## 2024.10.23
- Custom Balance admin refill Requested by Javi Tech
- Only Admin can edit Customer Requested by Fiberwan
- Only Admin can show password Requested by Fiberwan
## 2024.10.18
- Single Session Admin Can be set in the Settings
- Auto expired unpaid transaction
- Registration Type
- Can Login as User from Customer View
- Can select customer register must using OTP or not
- Add Meta.php for additional information
## 2024.10.15
- CSRF Security
- Admin can only have 1 active session
- Move Miscellaneous Settings to new page
- Fix Customer Online
- Count Shared user online for Radius REST
- Fix Invoice Print
## 2024.10.7
- Show Customer is Online or not
- Change Invoice Theme for printing
- Rearange Customer View
## 2024.9.23
- Discount Price
- Burst Preset
## 2024.9.20
- Forgot Password
- Forgot Username
- Public header template
## 2024.9.13
- Add Selling Mikrotik VPN By @agstrxyz
- Theme Redesign by @Focuslinkstech
- Fix That and this
## 2024.8.28
- add Router Status Offline/Online by @Focuslinkstech
- Show Router Offline in the Dashbord
- Fix Translation by by @ahmadhusein17
- Add Payment Info Page, to show to customer before buy
- Voucher Template
- Change Niceedit to summernote
- Customer can change their language by @Focuslinkstech
- Fix Voucher case sensitive
- 3 Tabs Plugin Manager
## 2024.8.19
- New Page, Payment Info, To Inform Customer, which payment gateway is good
- Move Customer UI to user-ui folder
- Voucher Template
- Change editor to summernote
- Customer can change language
## 2024.8.6
- Fix QRCode Scanner
- Simplify Chap verification password
- Quota based Freeradius Rest
- Fix Payment Gateway Audit
## 2024.8.6
- Fix Customer pppoe username
## 2024.8.5
- Add Customer Mail Inbox
- Add pppoe customer and pppoe IP to make static username and IP
- Add Sync button
- Allow Mac Address Username
- Router Maps
## 2024.8.1
- Show Bandwidth Plan in the customer dashboard
- Add Audit Payment Gateway
- Fix Plugin Manager
## 2024.7.23
- add Voucher Used Date

View File

@ -36,7 +36,7 @@ Most current web servers with PHP & MySQL installed will be capable of running P
Minimum Requirements
- Linux or Windows OS
- Minimum PHP Version 7.4
- Minimum PHP Version 8.2
- Both PDO & MySQLi Support
- PHP-GD2 Image Library
- PHP-CURL
@ -98,5 +98,13 @@ a.n Ibnu Maksum
## SPONSORS
- [mixradius.com](https://mixradius.com/) Paid Services Billing Radius
- [mlink.id](https://mlink.id)
- [https://github.com/sonyinside](https://github.com/sonyinside)
## Thanks
We appreciate all people who are participating in this project.
<a href="https://github.com/hotspotbilling/phpnuxbill/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hotspotbilling/phpnuxbill" />
</a>

View File

@ -5,4 +5,4 @@
**/
header('location: ../index.php?_route=admin/');
header('location: ../?_route=admin/');

View File

@ -1,16 +1,23 @@
<?php
$db_host = "localhost"; # Database Host
$db_port = ""; # Database Port. Keep it blank if you are un sure.
$db_user = "root"; # Database Username
$db_password = ""; # Database Password
$db_name = "phpnuxbill"; # Database Name
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
$host = $_SERVER['HTTP_HOST'];
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ||
(isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)) ? "https://" : "http://";
// Check if HTTP_HOST is set, otherwise use a default value or SERVER_NAME
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost');
$baseDir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
define('APP_URL', $protocol . $host . $baseDir);
#Please include http and do not use trailing slash after the url. For example use in this format- http://www.example.com Or http://www.example.com/finance
$_app_stage = 'Live'; # Do not change this
$db_host = "localhost"; # Database Host
$db_port = ""; # Database Port. Keep it blank if you are un sure.
$db_user = "root"; # Database Username
$db_pass = ""; # Database Password
$db_name = "phpnuxbill"; # Database Name

File diff suppressed because one or more lines are too long

View File

@ -42,12 +42,13 @@ spl_autoload_register('_autoloader');
if (!file_exists($root_path . 'config.php')) {
$root_path .= '..' . DIRECTORY_SEPARATOR;
if (!file_exists($root_path . 'config.php')) {
r2('install');
r2(getUrl('install'));
}
}
if (!file_exists($root_path . File::pathFixer('system/orm.php'))) {
die($root_path . "orm.php file not found");
echo $root_path . "orm.php file not found";
die();
}
$DEVICE_PATH = $root_path . File::pathFixer('system/devices');
@ -59,7 +60,8 @@ $PAYMENTGATEWAY_PATH = $root_path . File::pathFixer('system/paymentgateway');
$UI_PATH = 'ui';
if (!file_exists($UPLOAD_PATH . File::pathFixer('/notifications.default.json'))) {
die($UPLOAD_PATH . File::pathFixer("/notifications.default.json file not found"));
echo $UPLOAD_PATH . File::pathFixer("/notifications.default.json file not found");
die();
}
require_once $root_path . 'config.php';
@ -67,10 +69,14 @@ require_once $root_path . File::pathFixer('system/orm.php');
require_once $root_path . File::pathFixer('system/autoload/PEAR2/Autoload.php');
include $root_path . File::pathFixer('system/autoload/Hookers.php');
if(!empty($db_password)){
if ($db_password != null && ($db_pass == null || empty($db_pass))) {
// compability for old version
$db_pass = $db_password;
}
if ($db_pass != null) {
// compability for old version
$db_password = $db_pass;
}
ORM::configure("mysql:host=$db_host;dbname=$db_name");
ORM::configure('username', $db_user);
ORM::configure('password', $db_pass);
@ -81,7 +87,7 @@ if ($_app_stage != 'Live') {
if ($isApi) {
define('U', APP_URL . '/system/api.php?r=');
} else {
define('U', APP_URL . '/index.php?_route=');
define('U', APP_URL . '/?_route=');
}
// notification message
@ -115,7 +121,7 @@ if (empty($http_proxy) && !empty($config['http_proxy'])) {
date_default_timezone_set($config['timezone']);
if ((!empty($radius_user) && $config['radius_enable']) || _post('radius_enable')) {
if(!empty($radius_password)){
if (!empty($radius_password)) {
// compability for old version
$radius_pass = $radius_password;
}
@ -127,20 +133,29 @@ if ((!empty($radius_user) && $config['radius_enable']) || _post('radius_enable')
}
// Check if the user has selected a language
if (!empty($_SESSION['user_language'])) {
$config['language'] = $_SESSION['user_language'];
} else if (!empty($_COOKIE['user_language'])) {
$config['language'] = $_COOKIE['user_language'];
} else if (User::getID() > 0) {
$lang = User::getAttribute("Language");
if (!empty($lang)) {
$config['language'] = $lang;
}
}
if (empty($config['language'])) {
$config['language'] = 'english';
}
$lan_file = $root_path . File::pathFixer('system/lan/' . $config['language'] . '.json');
if (file_exists($lan_file)) {
$_L = json_decode(file_get_contents($lan_file), true);
$_SESSION['Lang'] = $_L;
} else {
$_L['author'] = 'Auto Generated by iBNuX Script';
$_SESSION['Lang'] = $_L;
$_L['author'] = 'Auto Generated by PHPNuxBill Script';
file_put_contents($lan_file, json_encode($_L));
}
function safedata($value)
{
$value = trim($value);
@ -181,7 +196,7 @@ function _auth($login = true)
return true;
} else {
if ($login) {
r2(U . 'login');
r2(getUrl('login'));
} else {
return false;
}
@ -194,7 +209,7 @@ function _admin($login = true)
return true;
} else {
if ($login) {
r2(U . 'login');
r2(getUrl('login'));
} else {
return false;
}
@ -218,8 +233,12 @@ function _log($description, $type = '', $userid = '0')
} elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) //to check ip from share internet
{
$d->ip = $_SERVER['HTTP_CLIENT_IP'];
} else {
} else if (isset($_SERVER["REMOTE_ADDR"])) {
$d->ip = $_SERVER["REMOTE_ADDR"];
} else if (php_sapi_name() == 'cli') {
$d->ip = 'CLI';
} else {
$d->ip = 'Unknown';
}
$d->save();
}
@ -242,6 +261,13 @@ function showResult($success, $message = '', $result = [], $meta = [])
die();
}
/**
* make url canonical or standar
*/
function getUrl($url)
{
return Text::url($url);
}
function generateUniqueNumericVouchers($totalVouchers, $length = 8)
{
@ -314,10 +340,10 @@ function _alert($text, $type = 'success', $url = "home", $time = 3)
if (!isset($ui)) return;
if (strlen($url) > 4) {
if (substr($url, 0, 4) != "http") {
$url = U . $url;
$url = getUrl($url);
}
} else {
$url = U . $url;
$url = getUrl($url);
}
$ui->assign('text', $text);
$ui->assign('type', $type);
@ -329,14 +355,14 @@ function _alert($text, $type = 'success', $url = "home", $time = 3)
if (!isset($api_secret)) {
$api_secret = $db_password;
$api_secret = $db_pass;
}
function displayMaintenanceMessage(): void
{
global $config, $ui;
$date = $config['maintenance_date'];
if ($date){
if ($date) {
$ui->assign('date', $date);
}
http_response_code(503);
@ -344,3 +370,13 @@ function displayMaintenanceMessage(): void
$ui->display('maintenance.tpl');
die();
}
function isTableExist($table)
{
try {
$record = ORM::forTable($table)->find_one();
return $record !== false;
} catch (Exception $e) {
return false;
}
}

View File

@ -27,7 +27,10 @@ CREATE TABLE `tbl_customers` (
`id` int NOT NULL,
`username` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`photo` VARCHAR(128) NOT NULL DEFAULT '/user.default.jpg',
`pppoe_username` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login',
`pppoe_password` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login',
`pppoe_ip` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login',
`fullname` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`address` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
`city` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
@ -36,7 +39,7 @@ CREATE TABLE `tbl_customers` (
`zip` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`phonenumber` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0',
`email` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1',
`coordinates` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '6.465422, 3.406448' COMMENT 'Latitude and Longitude coordinates',
`coordinates` varchar(50) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates',
`account_type` enum('Business','Personal') COLLATE utf8mb4_general_ci DEFAULT 'Personal' COMMENT 'For selecting account type',
`balance` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT 'For Money Deposit',
`service_type` enum('Hotspot','PPPoE','Others') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'Others' COMMENT 'For selecting user type',
@ -69,14 +72,15 @@ DROP TABLE IF EXISTS `tbl_payment_gateway`;
CREATE TABLE `tbl_payment_gateway` (
`id` int NOT NULL,
`username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`user_id` int(11) NOT NULL DEFAULT 0,
`gateway` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'xendit | midtrans',
`gateway_trx_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`gateway_trx_id` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`plan_id` int NOT NULL,
`plan_name` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`routers_id` int NOT NULL,
`routers` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`price` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`pg_url_payment` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`pg_url_payment` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`payment_method` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`payment_channel` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`pg_request` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
@ -135,6 +139,10 @@ CREATE TABLE `tbl_routers` (
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`description` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`coordinates` VARCHAR(50) NOT NULL DEFAULT '',
`status` ENUM('Online', 'Offline') DEFAULT 'Online',
`last_seen` DATETIME,
`coverage` VARCHAR(8) NOT NULL DEFAULT '0',
`enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT '0 disabled'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@ -143,6 +151,7 @@ CREATE TABLE `tbl_transactions` (
`id` int NOT NULL,
`invoice` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`user_id` int(11) NOT NULL DEFAULT 0,
`plan_name` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`price` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`recharged_on` date NOT NULL,
@ -160,6 +169,7 @@ DROP TABLE IF EXISTS `tbl_users`;
CREATE TABLE `tbl_users` (
`id` int UNSIGNED NOT NULL,
`root` int NOT NULL DEFAULT '0' COMMENT 'for sub account',
`photo` VARCHAR(128) NOT NULL DEFAULT '/admin.default.png',
`username` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`fullname` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
@ -170,7 +180,9 @@ CREATE TABLE `tbl_users` (
`ward` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'kelurahan',
`user_type` enum('SuperAdmin','Admin','Report','Agent','Sales') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`status` enum('Active','Inactive') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'Active',
`data` TEXT NULL DEFAULT NULL COMMENT 'to put additional data',
`last_login` datetime DEFAULT NULL,
`login_token` VARCHAR(40),
`creationdate` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@ -201,6 +213,7 @@ CREATE TABLE `tbl_voucher` (
`code` varchar(55) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`user` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`status` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`used_date` DATETIME NULL DEFAULT NULL,
`generated_by` int NOT NULL DEFAULT '0' COMMENT 'id admin'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@ -217,11 +230,52 @@ CREATE TABLE `rad_acct` (
`nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`acctsessiontime` BIGINT NOT NULL DEFAULT '0',
`acctinputoctets` BIGINT NOT NULL DEFAULT '0',
`acctoutputoctets` BIGINT NOT NULL DEFAULT '0',
`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
DROP TABLE IF EXISTS `tbl_customers_inbox`;
CREATE TABLE `tbl_customers_inbox` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`customer_id` int NOT NULL,
`date_created` datetime NOT NULL,
`date_read` datetime DEFAULT NULL,
`subject` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,
`body` TEXT NULL DEFAULT NULL,
`from` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'System' COMMENT 'System or Admin or Else',
`admin_id` int NOT NULL DEFAULT '0' COMMENT 'other than admin is 0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `tbl_meta` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`tbl` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Table name',
`tbl_id` int NOT NULL COMMENT 'table value id',
`name` varchar(32) COLLATE utf8mb4_general_ci NOT NULL,
`value` mediumtext COLLATE utf8mb4_general_ci,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='This Table to add additional data for any table';
CREATE TABLE IF NOT EXISTS `tbl_coupons` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`code` VARCHAR(50) NOT NULL UNIQUE,
`type` ENUM('fixed', 'percent') NOT NULL,
`value` DECIMAL(10,2) NOT NULL,
`description` TEXT NOT NULL,
`max_usage` INT NOT NULL DEFAULT 1,
`usage_count` INT NOT NULL DEFAULT 0,
`status` ENUM('active', 'inactive') NOT NULL,
`min_order_amount` DECIMAL(10,2) NOT NULL,
`max_discount_amount` DECIMAL(10,2) NOT NULL,
`start_date` DATE NOT NULL,
`end_date` DATE NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
ALTER TABLE `rad_acct`
ADD PRIMARY KEY (`id`),

View File

@ -42,20 +42,20 @@
</div>
<div class="form-group">
<label for="dbhost">Database Host</label>
<input type="text" class="form-control" id="dbhost" name="dbhost">
<input type="text" class="form-control" id="dbhost" required name="dbhost">
</div>
<div class="form-group">
<label for="dbuser">Database Username</label>
<input type="text" class="form-control" id="dbuser" name="dbuser">
<input type="text" class="form-control" id="dbuser" required name="dbuser">
</div>
<div class="form-group">
<label for="dbpass">Database Password</label>
<input type="text" class="form-control" id="dbpass" name="dbpass">
<input type="text" class="form-control" id="dbpass" required name="dbpass">
</div>
<div class="form-group">
<label for="dbname">Database Name</label>
<input type="text" class="form-control" id="dbname" name="dbname">
<input type="text" class="form-control" id="dbname" required name="dbname">
</div>
<div class="form-group">

View File

@ -9,14 +9,14 @@
$appurl = $_POST['appurl'];
$db_host = $_POST['dbhost'];
$db_user = $_POST['dbuser'];
$db_password = $_POST['dbpass'];
$db_pass = $_POST['dbpass'];
$db_name = $_POST['dbname'];
$cn = '0';
try {
$dbh = new pdo(
"mysql:host=$db_host;dbname=$db_name",
"$db_user",
"$db_password",
"$db_pass",
array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
);
$cn = '1';
@ -30,7 +30,7 @@ if ($cn == '1') {
$protocol = (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off" || $_SERVER["SERVER_PORT"] == 443) ? "https://" : "http://";
$host = $_SERVER["HTTP_HOST"];
$baseDir = rtrim(dirname($_SERVER["SCRIPT_NAME"]), "/\\");
$baseDir = rtrim(dirname($_SERVER["SCRIPT_NAME"]), "/\\\\");
define("APP_URL", $protocol . $host . $baseDir);
// Live, Dev, Demo
@ -39,13 +39,13 @@ $_app_stage = "Live";
// Database PHPNuxBill
$db_host = "' . $db_host . '";
$db_user = "' . $db_user . '";
$db_pass = "' . $db_password . '";
$db_pass = "' . $db_pass . '";
$db_name = "' . $db_name . '";
// Database Radius
$radius_host = "' . $db_host . '";
$radius_user = "' . $db_user . '";
$radius_pass = "' . $db_password . '";
$radius_pass = "' . $db_pass . '";
$radius_name = "' . $db_name . '";
if($_app_stage!="Live"){
@ -61,7 +61,7 @@ if($_app_stage!="Live"){
$input = '<?php
$protocol = (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off" || $_SERVER["SERVER_PORT"] == 443) ? "https://" : "http://";
$host = $_SERVER["HTTP_HOST"];
$baseDir = rtrim(dirname($_SERVER["SCRIPT_NAME"]), "/\\");
$baseDir = rtrim(dirname($_SERVER["SCRIPT_NAME"]), "/\\\\");
define("APP_URL", $protocol . $host . $baseDir);
// Live, Dev, Demo
@ -70,7 +70,7 @@ $_app_stage = "Live";
// Database PHPNuxBill
$db_host = "' . $db_host . '";
$db_user = "' . $db_user . '";
$db_pass = "' . $db_password . '";
$db_pass = "' . $db_pass . '";
$db_name = "' . $db_name . '";
if($_app_stage!="Live"){

View File

@ -26,7 +26,7 @@
try{
$dbh = new pdo( "mysql:host=$db_host;dbname=$db_name",
"$db_user",
"$db_password",
"$db_pass",
array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
echo "CREATE TABLE `tbl_payment_gateway` (

View File

@ -0,0 +1,27 @@
<table border="0" cellspacing="0" cellpadding="2">
<tbody><tr>
<td valign="middle">
<center><strong style="font-size:38px">[[company_name]]</strong></center>
<table width="100%" border="1" cellspacing="0" cellpadding="1" bordercolor="#757575">
<tbody>
<tr>
<td rowspan="4" width="1">[[qrcode]]
</td>
</tr>
<tr>
<td valign="middle" align="center" style="font-size:25px">
[[price]]</td>
</tr>
<tr>
<td valign="middle" align="center" style="font-size:20px">
[[voucher_code]]</td>
</tr>
<tr>
<td valign="middle" align="center" style="font-size:15px">
[[plan]] - [[counter]]<br></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody></table>

View File

@ -0,0 +1,24 @@
<table border="0" cellspacing="0" cellpadding="2">
<tbody><tr>
<td valign="middle">
<center><img src="system/uploads/logo.png"></center>
<table width="100%" border="1" cellspacing="0" cellpadding="1" bordercolor="#757575">
<tbody>
<tr>
<td valign="middle" align="center" style="font-size:25px">
[[qrcode]]</td>
</tr>
<tr>
<td valign="middle" align="center" style="font-size:20px">
[[voucher_code]]</td>
</tr>
<tr>
<td valign="middle" align="center" style="font-size:15px">
[[plan]] [[price]]<br></td>
</tr>
</tbody>
</table>
<center>[[company_name]]</center>
</td>
</tr>
</tbody></table>

View File

@ -0,0 +1,28 @@
<table border="0" cellspacing="0" cellpadding="2">
<tbody><tr>
<td valign="middle">
<center><img src="system/uploads/logo.png"></center>
<table width="100%" border="1" cellspacing="0" cellpadding="1" bordercolor="#757575">
<tbody>
<tr>
<td rowspan="4" width="1">[[qrcode]]
</td>
</tr>
<tr>
<td valign="middle" align="center" style="font-size:25px">
[[price]]</td>
</tr>
<tr>
<td valign="middle" align="center" style="font-size:20px">
[[voucher_code]]</td>
</tr>
<tr>
<td valign="middle" align="center" style="font-size:15px">
[[plan]] - [[counter]]<br></td>
</tr>
</tbody>
</table>
<center>[[company_name]]</center>
</td>
</tr>
</tbody></table>

View File

@ -38,16 +38,80 @@ try {
case 'authenticate':
$username = _req('username');
$password = _req('password');
if (empty($username) || empty($password)) {
show_radius_result([
"control:Auth-Type" => "Reject",
"reply:Reply-Message" => 'Login invalid'
], 401);
$CHAPassword = _req('CHAPassword');
$CHAPchallenge = _req('CHAPchallenge');
$isCHAP = false;
if (!empty($CHAPassword)) {
$c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username' AND status = 'Active'")->find_one();
if ($c) {
if (Password::chap_verify($c['password'], $CHAPassword, $CHAPchallenge)) {
$password = $c['password'];
$isVoucher = false;
$isCHAP = true;
} else if (!empty($c['pppoe_password']) && Password::chap_verify($c['pppoe_password'], $CHAPassword, $CHAPchallenge)) {
$password = $c['pppoe_password'];
$isVoucher = false;
$isCHAP = true;
} else {
// check if voucher
if (Password::chap_verify($username, $CHAPassword, $CHAPchallenge)) {
$isVoucher = true;
$password = $username;
} else {
// no password is voucher
if (Password::chap_verify('', $CHAPassword, $CHAPchallenge)) {
$isVoucher = true;
$password = $username;
} else {
show_radius_result(['Reply-Message' => 'Username or Password is wrong'], 401);
}
}
}
} else {
$c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username' AND status = 'Active'")->find_one();
if ($c) {
if (Password::chap_verify($c['password'], $CHAPassword, $CHAPchallenge)) {
$password = $c['password'];
$isVoucher = false;
$isCHAP = true;
} else if (!empty($c['pppoe_password']) && Password::chap_verify($c['pppoe_password'], $CHAPassword, $CHAPchallenge)) {
$password = $c['pppoe_password'];
$isVoucher = false;
$isCHAP = true;
} else {
// check if voucher
if (Password::chap_verify($username, $CHAPassword, $CHAPchallenge)) {
$isVoucher = true;
$password = $username;
} else {
// no password is voucher
if (Password::chap_verify('', $CHAPassword, $CHAPchallenge)) {
$isVoucher = true;
$password = $username;
} else {
show_radius_result(['Reply-Message' => 'Username or Password is wrong'], 401);
}
}
}
}
}
} else {
if (!empty($username) && empty($password)) {
// Voucher with empty password
$isVoucher = true;
$password = $username;
} else if (empty($username) || empty($password)) {
show_radius_result([
"control:Auth-Type" => "Reject",
"reply:Reply-Message" => 'Login invalid......'
], 401);
}
}
if ($username == $password) {
$d = ORM::for_table('tbl_voucher')->where('code', $username)->find_one();
$username = Text::alphanumeric($username, "-_.,");
$d = ORM::for_table('tbl_voucher')->whereRaw("BINARY code = '$username'")->find_one();
} else {
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
$d = ORM::for_table('tbl_customers')->whereRaw("BINARY username = '$username' AND status = 'Active'")->find_one();
if ($d['password'] != $password) {
if ($d['pppoe_password'] != $password) {
unset($d);
@ -68,33 +132,118 @@ try {
$username = _req('username');
$password = _req('password');
$isVoucher = ($username == $password);
if (empty($username) || empty($password)) {
show_radius_result([
"control:Auth-Type" => "Reject",
"reply:Reply-Message" => 'Login invalid......'
], 401);
$CHAPassword = _req('CHAPassword');
$CHAPchallenge = _req('CHAPchallenge');
$isCHAP = false;
if (!empty($CHAPassword)) {
$c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username' AND status = 'Active'")->find_one();
if ($c) {
if (Password::chap_verify($c['password'], $CHAPassword, $CHAPchallenge)) {
$password = $c['password'];
$isVoucher = false;
$isCHAP = true;
} else if (!empty($c['pppoe_password']) && Password::chap_verify($c['pppoe_password'], $CHAPassword, $CHAPchallenge)) {
$password = $c['pppoe_password'];
$isVoucher = false;
$isCHAP = true;
} else {
// check if voucher
if (Password::chap_verify($username, $CHAPassword, $CHAPchallenge)) {
$isVoucher = true;
$password = $username;
} else {
// no password is voucher
if (Password::chap_verify('', $CHAPassword, $CHAPchallenge)) {
$isVoucher = true;
$password = $username;
} else {
show_radius_result(['Reply-Message' => 'Username or Password is wrong'], 401);
}
}
}
} else {
$c = ORM::for_table('tbl_customers')->select('password')->select('username')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username' AND status = 'Active'")->find_one();
if ($c) {
if (Password::chap_verify($c['password'], $CHAPassword, $CHAPchallenge)) {
$password = $c['password'];
$username = $c['username'];
$isVoucher = false;
$isCHAP = true;
} else if (!empty($c['pppoe_password']) && Password::chap_verify($c['pppoe_password'], $CHAPassword, $CHAPchallenge)) {
$password = $c['pppoe_password'];
$username = $c['username'];
$isVoucher = false;
$isCHAP = true;
} else {
// check if voucher
if (Password::chap_verify($username, $CHAPassword, $CHAPchallenge)) {
$isVoucher = true;
$password = $username;
} else {
// no password is voucher
if (Password::chap_verify('', $CHAPassword, $CHAPchallenge)) {
$isVoucher = true;
$password = $username;
} else {
show_radius_result(['Reply-Message' => 'Username or Password is wrong'], 401);
}
}
}
}
}
} else {
if (!empty($username) && empty($password)) {
// Voucher with empty password
$isVoucher = true;
$password = $username;
} else if (empty($username) || empty($password)) {
show_radius_result([
"control:Auth-Type" => "Reject",
"reply:Reply-Message" => 'Login invalid......'
], 401);
}
}
$tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->find_one();
if (!$tur) {
// if check if pppoe_username
$c = ORM::for_table('tbl_customers')->select('username')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username'")->find_one();
if($c){
$username = $c['username'];
$tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->find_one();
}
}
$tur = ORM::for_table('tbl_user_recharges')->where('username', $username)->find_one();
if ($tur) {
if (!$isVoucher) {
$d = ORM::for_table('tbl_customers')->select('password')->where('username', $username)->find_one();
if ($d['password'] != $password) {
if ($d['pppoe_password'] != $password) {
show_radius_result(['Reply-Message' => 'Username or Password is wrong'], 401);
if (!$isVoucher && !$isCHAP) {
$d = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username' AND status = 'Active'")->find_one();
if ($d) {
if ($d['password'] != $password) {
if ($d['pppoe_password'] != $password) {
show_radius_result(['Reply-Message' => 'Username or Password is wrong'], 401);
}
}
} else {
$d = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username' AND status = 'Active'")->find_one();
if ($d) {
if ($d['password'] != $password) {
if ($d['pppoe_password'] != $password) {
show_radius_result(['Reply-Message' => 'Username or Password is wrong'], 401);
}
}
}
}
}
process_radiust_rest($tur, $code);
} else {
if ($isVoucher) {
$v = ORM::for_table('tbl_voucher')->where('code', $username)->where('routers', 'radius')->find_one();
$username = Text::alphanumeric($username, "-_.,");
$v = ORM::for_table('tbl_voucher')->whereRaw("BINARY code = '$username' AND routers = 'radius'")->find_one();
if ($v) {
if ($v['status'] == 0) {
if (Package::rechargeUser(0, $v['routers'], $v['id_plan'], "Voucher", $username)) {
$v->status = "1";
$v->used_date = date('Y-m-d H:i:s');
$v->save();
$tur = ORM::for_table('tbl_user_recharges')->where('username', $username)->find_one();
$tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->find_one();
if ($tur) {
process_radiust_rest($tur, $code);
} else {
@ -107,7 +256,7 @@ try {
show_radius_result(['Reply-Message' => 'Voucher Expired...'], 401);
}
} else {
show_radius_result(['Reply-Message' => 'Voucher Expired..'], 401);
show_radius_result(['Reply-Message' => 'Invalid Voucher..'], 401);
}
} else {
show_radius_result(['Reply-Message' => 'Internet Plan Expired..'], 401);
@ -125,25 +274,51 @@ try {
}
header("HTTP/1.1 200 ok");
$d = ORM::for_table('rad_acct')
->where('username', $username)
->where('macaddr', _post('macAddr'))
->where('acctstatustype', _post('acctStatusType'))
->whereRaw("BINARY username = '$username' AND macaddr = '"._post('macAddr')."' AND nasid = '"._post('nasid')."'")
->findOne();
if (!$d) {
$d = ORM::for_table('rad_acct')->create();
}
$acctOutputOctets = _post('acctOutputOctets', 0);
$acctInputOctets = _post('acctInputOctets', 0);
if ($acctOutputOctets !== false && $acctInputOctets !== false) {
$d->acctOutputOctets += intval($acctOutputOctets);
$d->acctInputOctets += intval($acctInputOctets);
} else {
$d->acctOutputOctets = 0;
$d->acctInputOctets = 0;
}
$d->acctsessionid = _post('acctSessionId');
$d->username = $username;
$d->realm = _post('realm');
$d->nasipaddress = _post('nasip');
$d->nasipaddress = _post('nasIpAddress');
$d->acctsessiontime = intval(_post('acctSessionTime'));
$d->nasid = _post('nasid');
$d->nasportid = _post('nasPortId');
$d->nasporttype = _post('nasPortType');
$d->framedipaddress = _post('framedIPAddress');
$d->acctstatustype = _post('acctStatusType');
if(in_array(_post('acctStatusType'), ['Start', 'Stop'])){
$d->acctstatustype = _post('acctStatusType');
}
$d->macaddr = _post('macAddr');
$d->dateAdded = date('Y-m-d H:i:s');
$d->save();
// pastikan data akunting yang disimpan memang customer aktif phpnuxbill
$tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username' AND `status` = 'on' AND `routers` = 'radius'")->find_one();
if($tur){
$d->save();
if (_post('acctStatusType') == 'Start') {
$plan = ORM::for_table('tbl_plans')->where('id', $tur['plan_id'])->find_one();
if ($plan['limit_type'] == "Data_Limit" || $plan['limit_type'] == "Both_Limit") {
$totalUsage = $d['acctOutputOctets'] + $d['acctInputOctets'];
$attrs['reply:Mikrotik-Total-Limit'] = Text::convertDataUnit($plan['data_limit'], $plan['data_unit']) - $totalUsage;
if ($attrs['reply:Mikrotik-Total-Limit'] < 0) {
$attrs['reply:Mikrotik-Total-Limit'] = 0;
show_radius_result(["control:Auth-Type" => "Accept", 'Reply-Message' => 'You have exceeded your data limit.'], 401);
}
}
}
process_radiust_rest($tur, 200);
}
show_radius_result([
"control:Auth-Type" => "Accept",
"reply:Reply-Message" => 'Saved'
@ -173,6 +348,16 @@ function process_radiust_rest($tur, $code)
global $config;
$plan = ORM::for_table('tbl_plans')->where('id', $tur['plan_id'])->find_one();
$bw = ORM::for_table("tbl_bandwidth")->find_one($plan['id_bw']);
// Count User Onlines
$USRon = ORM::for_table('rad_acct')
->whereRaw("BINARY username = '".$tur['username']."' AND acctStatusType = 'Start'")
->find_array();
// get all the IP
$ips = array_column($USRon, 'framedipaddress');
// check if user reach shared_users limit but IP is not in the list active
if (count($USRon) >= $plan['shared_users'] && $plan['type'] == 'Hotspot' && !in_array(_post('framedIPAddress'), $ips)) {
show_radius_result(["control:Auth-Type" => "Accept", 'Reply-Message' => 'You are already logged in - access denied ('.$USRon.')'], 401);
}
if ($bw['rate_down_unit'] == 'Kbps') {
$unitdown = 'K';
} else {
@ -211,6 +396,15 @@ function process_radiust_rest($tur, $code)
}
if ($plan['typebp'] == "Limited") {
if ($plan['limit_type'] == "Data_Limit" || $plan['limit_type'] == "Both_Limit") {
$raddact = ORM::for_table('rad_acct')->whereRaw("BINARY username = '$tur[username]'")->where('acctstatustype', 'Start')->find_one();
$totalUsage = intval($raddact['acctOutputOctets']) + intval($raddact['acctInputOctets']);
$attrs['reply:Mikrotik-Total-Limit'] = Text::convertDataUnit($plan['data_limit'], $plan['data_unit']) - $totalUsage;
if ($attrs['reply:Mikrotik-Total-Limit'] < 0) {
$attrs['reply:Mikrotik-Total-Limit'] = 0;
show_radius_result(["control:Auth-Type" => "Accept", 'Reply-Message' => 'You have exceeded your data limit.'], 401);
}
}
if ($plan['limit_type'] == "Time_Limit") {
if ($plan['time_unit'] == 'Hrs')
$timelimit = $plan['time_limit'] * 60 * 60;
@ -259,5 +453,4 @@ function show_radius_result($array, $code = 200)
die();
}
die(json_encode($array));
die();
}

View File

@ -1,281 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>QR Scanner</title>
<style>
video {
width: 100% !important;
height: auto !important
}
#reload {
margin-top: 5px;
margin-bottom: 10px;
padding: 5px;
background-color: #0336FF;
color: #f3f4f5;
border-radius: 3px;
width: 150px;
cursor: pointer
}
a {
text-decoration: none;
color: #0336FF
}
.progress {
padding: 10px;
margin-top: 5px
}
#camera {
position: relative;
width: auto;
height: 100%
}
#camera:after,
#camera:before,
#camera>:first-child:after,
#camera>:first-child:before {
position: absolute;
width: 80px;
height: 80px;
border-color: red;
border-style: solid;
content: ' '
}
#camera:before {
top: 0;
left: 0;
border-width: 5px 0 0 5px
}
#camera:after {
top: 0;
right: 0;
border-width: 5px 5px 0 0
}
#camera>:first-child:before {
bottom: 0;
right: 0;
border-width: 0 5px 5px 0
}
#camera>:first-child:after {
bottom: 0;
left: 0;
border-width: 0 0 5px 5px
}
#camera-inside {
padding: 10px;
margin-bottom: -5px
}
#hasil {
font-size: 20px;
font-weight: 600px;
background-color: #76ff03;
color: #202020
}
</style>
<script type="text/javascript" src="llqrcode.js"></script>
</head>
<body>
<center>
<div id="main">
<div id="mainbody">
<div class="progress">
<div id="hasil"></div>
</div>
<div id="camera">
<div id="camera-inside"></div>
</div>
</div>
</div>
<br>
<div id="reload" onclick="location.reload();">Refresh Camera</div>
<p><small>Point Camera to Barcode</small></p>
<br>
</center>
<canvas style="display:none;" id="qr-canvas" width="800" height="600"></canvas>
<script type="text/javascript">
function getAllUrlParams(e) {
var t = e ? e.split("?")[1] : window.location.search.slice(1),
a = {};
if (t)
for (var n = (t = t.split("#")[0]).split("&"), o = 0; o < n.length; o++) {
var i = n[o].split("="),
r = void 0,
d = i[0].replace(/\[\d*\]/, function (e) {
return (r = e.slice(1, -1)), "";
}),
s = void 0 === i[1] || i[1];
a[(d = d.toLowerCase())]
? ("string" == typeof a[d] && (a[d] = [a[d]]),
void 0 === r ? a[d].push(s) : (a[d][r] = s))
: (a[d] = s);
}
return a;
}
var gCtx = null,
gCanvas = null,
c = 0,
stype = 0,
gUM = !1,
webkit = !1,
moz = !1,
v = null,
imghtml =
'<div id="qrfile"><canvas id="out-canvas" width="320" height="240"></canvas><div id="imghelp">drag and drop a QRCode here<br>or select a file<input type="file" onchange="handleFiles(this.files)"/></div></div>',
vidhtml = '<video id="v" autoplay></video>';
function dragenter(e) {
e.stopPropagation(), e.preventDefault();
}
function dragover(e) {
e.stopPropagation(), e.preventDefault();
}
function drop(e) {
e.stopPropagation(), e.preventDefault();
var t = e.dataTransfer,
a = t.files;
a.length > 0
? handleFiles(a)
: t.getData("URL") && qrcode.decode(t.getData("URL"));
}
function handleFiles(e) {
for (var t = 0; t < e.length; t++) {
var a = new FileReader();
(a.onload =
(e[t],
function (e) {
gCtx.clearRect(0, 0, gCanvas.width, gCanvas.height),
qrcode.decode(e.target.result);
})),
a.readAsDataURL(e[t]);
}
}
function initCanvas(e, t) {
((gCanvas = document.getElementById("qr-canvas")).style.width = e + "px"),
(gCanvas.style.height = t + "px"),
(gCanvas.width = e),
(gCanvas.height = t),
(gCtx = gCanvas.getContext("2d")).clearRect(0, 0, e, t);
}
function captureToCanvas() {
if (1 == stype && gUM)
try {
gCtx.drawImage(v, 0, 0);
try {
qrcode.decode();
} catch (e) {
console.log(e), setTimeout(captureToCanvas, 500);
}
} catch (e) {
console.log(e), setTimeout(captureToCanvas, 500);
}
}
function htmlEntities(e) {
return String(e)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function read(e) {
(document.getElementById("hasil").innerHTML =
e),
(window.location = unescape(getAllUrlParams().back) + e)
;
}
function isCanvasSupported() {
var e = document.createElement("canvas");
return !(!e.getContext || !e.getContext("2d"));
}
function success(e) {
(v.srcObject = e), v.play(), (gUM = !0), setTimeout(captureToCanvas, 500);
}
function error(e) {
gUM = !1;
}
function load() {
isCanvasSupported() && window.File && window.FileReader
? (initCanvas(800, 600),
(qrcode.callback = read),
(document.getElementById("mainbody").style.display = "inline"),
setwebcam())
: ((document.getElementById("mainbody").style.display = "inline"),
(document.getElementById("mainbody").innerHTML =
'<p id="mp1">QR code scanner for HTML5 capable browsers</p><br><br><p id="mp2">sorry your browser is not supported</p><br><br><p id="mp1">try <a href="http://www.mozilla.com/firefox"><img src="firefox.png"/></a> or <a href="http://chrome.google.com"><img src="chrome_logo.gif"/></a> or <a href="http://www.opera.com"><img src="Opera-logo.png"/></a></p>'));
}
function setwebcam() {
var e = !0;
if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices)
try {
navigator.mediaDevices.enumerateDevices().then(function (t) {
t.forEach(function (t) {
"videoinput" === t.kind &&
t.label.toLowerCase().search("back") > -1 &&
(e = {
deviceId: { exact: t.deviceId },
facingMode: "environment",
}),
console.log(t.kind + ": " + t.label + " id = " + t.deviceId);
}),
setwebcam2(e);
});
} catch (e) {
console.log(e);
}
else console.log("no navigator.mediaDevices.enumerateDevices"), setwebcam2(e);
}
function setwebcam2(e) {
if ((console.log(e), 1 != stype)) {
var t = navigator;
(document.getElementById("camera-inside").innerHTML = vidhtml),
(v = document.getElementById("v")),
t.mediaDevices.getUserMedia
? t.mediaDevices
.getUserMedia({ video: e, audio: !1 })
.then(function (e) {
success(e);
})
.catch(function (e) {
e(e);
})
: t.getUserMedia
? ((webkit = !0),
t.getUserMedia({ video: e, audio: !1 }, success, error))
: t.webkitGetUserMedia &&
((webkit = !0),
t.webkitGetUserMedia({ video: e, audio: !1 }, success, error)),
(stype = 1),
setTimeout(captureToCanvas, 500);
} else setTimeout(captureToCanvas, 500);
}
function setimg() {
if (((document.getElementById("result").innerHTML = ""), 2 != stype)) {
(document.getElementById("camera-inside").innerHTML = imghtml),
(document.getElementById("qrimg").style.opacity = 1),
(document.getElementById("webcamimg").style.opacity = 0.2);
var e = document.getElementById("qrfile");
e.addEventListener("dragenter", dragenter, !1),
e.addEventListener("dragover", dragover, !1),
e.addEventListener("drop", drop, !1),
(stype = 2);
}
}
load();
</script>
</body>
</html>

80
scan/index.php Normal file
View File

@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>QRCode Scanner</title>
<style>
button {
margin-top: 30px;
margin-bottom: 30px;
}
</style>
</head>
<body>
<div id="qr-reader" style="width:100%; height:auto"></div>
<div id="qr-reader-results"></div>
<script src="qrcode.min.js"></script>
<script>
function docReady(fn) {
// see if DOM is already available
if (document.readyState === "complete" ||
document.readyState === "interactive") {
// call on next available tick
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
function getAllUrlParams(e) {
var t = e ? e.split("?")[1] : window.location.search.slice(1),
a = {};
if (t)
for (var n = (t = t.split("#")[0]).split("&"), o = 0; o < n.length; o++) {
var i = n[o].split("="),
r = void 0,
d = i[0].replace(/\[\d*\]/, function (e) {
return (r = e.slice(1, -1)), "";
}),
s = void 0 === i[1] || i[1];
a[(d = d.toLowerCase())]
? ("string" == typeof a[d] && (a[d] = [a[d]]),
void 0 === r ? a[d].push(s) : (a[d][r] = s))
: (a[d] = s);
}
return a;
}
docReady(function() {
var resultContainer = document.getElementById('qr-reader-results');
var lastResult, countResults = 0;
function onScanSuccess(decodedText, decodedResult) {
if (decodedText !== lastResult) {
++countResults;
lastResult = decodedText;
if(getAllUrlParams().back != undefined){
window.location = unescape(getAllUrlParams().back) + escape(decodedText);
}else{
if(decodedText.startsWith('http')){
window.location = decodedText;
}else{
alert(decodedText);
}
}
}
}
var html5QrcodeScanner = new Html5QrcodeScanner(
"qr-reader", {
fps: 10,
qrbox: 250
});
html5QrcodeScanner.render(onScanSuccess);
});
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

1
scan/qrcode.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

View File

@ -11,39 +11,122 @@ class Admin
public static function getID()
{
global $db_password;
if (isset($_SESSION['aid'])) {
global $db_pass, $config, $isApi;
$enable_session_timeout = $config['enable_session_timeout'] == 1;
$session_timeout_duration = $config['session_timeout_duration'] ? intval($config['session_timeout_duration'] * 60) : intval(60 * 60); // Convert minutes to seconds
if ($isApi) {
$enable_session_timeout = false;
}
if ($enable_session_timeout && !empty($_SESSION['aid']) && !empty($_SESSION['aid_expiration'])) {
if ($_SESSION['aid_expiration'] > time()) {
$isValid = self::validateToken($_SESSION['aid'], $_COOKIE['aid']);
if (!$isValid) {
self::removeCookie();
_alert(Lang::T('Token has expired. Please log in again.'), 'danger', "admin");
return 0;
}
// extend timeout duration
$_SESSION['aid_expiration'] = time() + $session_timeout_duration;
return $_SESSION['aid'];
} else {
// Session expired, log out the user
self::removeCookie();
_alert(Lang::T('Session has expired. Please log in again.'), 'danger', "admin");
return 0;
}
} else if (!empty($_SESSION['aid'])) {
$isValid = self::validateToken($_SESSION['aid'], $_COOKIE['aid']);
if (!$isValid) {
self::removeCookie();
_alert(Lang::T('Token has expired. Please log in again.') . '.'.$_SESSION['aid'], 'danger', "admin");
return 0;
}
return $_SESSION['aid'];
} else if (isset($_COOKIE['aid'])) {
// id.time.sha1
}
// Check if the cookie is set and valid
elseif (isset($_COOKIE['aid'])) {
$tmp = explode('.', $_COOKIE['aid']);
if (sha1($tmp[0] . '.' . $tmp[1] . '.' . $db_password) == $tmp[2]) {
if (time() - $tmp[1] < 86400 * 7) {
$_SESSION['aid'] = $tmp[0];
return $tmp[0];
if (sha1("$tmp[0].$tmp[1].$db_pass") == $tmp[2]) {
// Validate the token in the cookie
$isValid = self::validateToken($tmp[0], $_COOKIE['aid']);
if ($isApi) {
// For now API need to always return true, next need to add revoke token API
$isValid = true;
}
if (!empty($_COOKIE['aid']) && !$isValid) {
self::removeCookie();
_alert(Lang::T('Token has expired. Please log in again.') . '..', 'danger', "admin");
return 0;
} else {
if (time() - $tmp[1] < 86400 * 7) {
$_SESSION['aid'] = $tmp[0];
if ($enable_session_timeout) {
$_SESSION['aid_expiration'] = time() + $session_timeout_duration;
}
return $tmp[0];
}
}
}
}
return 0;
}
public static function setCookie($aid)
{
global $db_password;
global $db_pass, $config;
$enable_session_timeout = $config['enable_session_timeout'];
$session_timeout_duration = intval($config['session_timeout_duration']) * 60; // Convert minutes to seconds
if (isset($aid)) {
$time = time();
$token = $aid . '.' . $time . '.' . sha1($aid . '.' . $time . '.' . $db_password);
setcookie('aid', $token, time() + 86400 * 7);
$token = $aid . '.' . $time . '.' . sha1("$aid.$time.$db_pass");
// Detect the current protocol
$isSecure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
// Set cookie with security flags
setcookie('aid', $token, [
'expires' => time() + 86400 * 7, // 7 days
'path' => '/',
'domain' => '',
'secure' => $isSecure,
'httponly' => true,
'samesite' => 'Lax', // or Strict
]);
$_SESSION['aid'] = $aid;
if ($enable_session_timeout) {
$_SESSION['aid_expiration'] = $time + $session_timeout_duration;
}
self::upsertToken($aid, $token);
return $token;
}
return '';
}
public static function removeCookie()
{
global $_app_stage;
if (isset($_COOKIE['aid'])) {
setcookie('aid', '', time() - 86400);
$isSecure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
setcookie('aid', '', [
'expires' => time() - 3600,
'path' => '/',
'domain' => '',
'secure' => $isSecure,
'httponly' => true,
'samesite' => 'Lax',
]);
session_destroy();
session_unset();
session_start();
unset($_COOKIE['aid'], $_SESSION['aid']);
}
}
@ -58,4 +141,24 @@ class Admin
return null;
}
}
public static function upsertToken($aid, $token)
{
$query = ORM::for_table('tbl_users')->findOne($aid);
$query->login_token = sha1($token);
$query->save();
}
public static function validateToken($aid, $cookieToken)
{
global $config;
$query = ORM::for_table('tbl_users')->select('login_token')->findOne($aid);
if ($config['single_session'] != 'yes') {
return true; // For multi-session, any token is valid
}
if (empty($query)) {
return true;
}
return $query->login_token === sha1($cookieToken);
}
}

View File

@ -19,6 +19,9 @@ class App{
}
public static function getTokenValue($key){
if(empty($key)){
return "";
}
if(isset($_SESSION[$key])){
return $_SESSION[$key];
}else{
@ -26,4 +29,23 @@ class App{
}
}
}
public static function getVoucher(){
return md5(microtime());
}
public static function setVoucher($token, $value){
$_SESSION[$token] = $value;
}
public static function getVoucherValue($key){
if(empty($key)){
return "";
}
if(isset($_SESSION[$key])){
return $_SESSION[$key];
}else{
return "";
}
}
}

View File

@ -30,13 +30,9 @@ class Balance
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;
}
$c->balance = $c['balance'] - $amount;
$c->save();
return true;
}
public static function plusByPhone($phone_customer, $amount)

55
system/autoload/Csrf.php Normal file
View File

@ -0,0 +1,55 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
class Csrf
{
private static $tokenExpiration = 1800; // 30 minutes
public static function generateToken($length = 16)
{
return bin2hex(random_bytes($length));
}
public static function validateToken($token, $storedToken)
{
return hash_equals($token, $storedToken);
}
public static function check($token)
{
global $config;
if($config['csrf_enabled'] == 'yes') {
if (isset($_SESSION['csrf_token'], $_SESSION['csrf_token_time'], $token)) {
$storedToken = $_SESSION['csrf_token'];
$tokenTime = $_SESSION['csrf_token_time'];
if (time() - $tokenTime > self::$tokenExpiration) {
self::clearToken();
return false;
}
return self::validateToken($token, $storedToken);
}
return false;
}
return true;
}
public static function generateAndStoreToken()
{
$token = self::generateToken();
$_SESSION['csrf_token'] = $token;
$_SESSION['csrf_token_time'] = time();
return $token;
}
public static function clearToken()
{
unset($_SESSION['csrf_token'], $_SESSION['csrf_token_time']);
}
}

View File

@ -1,4 +1,5 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
@ -93,6 +94,48 @@ class File
return file_exists($dst_dir);
}
public static function makeThumb($srcFile, $thumbFile, $thumbSize = 200)
{
/* Determine the File Type */
$type = substr($srcFile, strrpos($srcFile, '.') + 1);
$imgsize = getimagesize($srcFile);
$oldW = $imgsize[0];
$oldH = $imgsize[1];
$mime = $imgsize['mime'];
switch ($mime) {
case 'image/gif':
$src = imagecreatefromgif($srcFile);
break;
case 'image/png':
$src = imagecreatefrompng($srcFile);
break;
case 'image/jpeg':
$src = imagecreatefromjpeg($srcFile);
break;
default:
return false;
break;
}
/* Calculate the New Image Dimensions */
$limiting_dim = 0;
if ($oldH > $oldW) {
/* Portrait */
$limiting_dim = $oldW;
} else {
/* Landscape */
$limiting_dim = $oldH;
}
/* Create the New Image */
$new = imagecreatetruecolor($thumbSize, $thumbSize);
/* Transcribe the Source Image into the New (Square) Image */
imagecopyresampled($new, $src, 0, 0, ($oldW - $limiting_dim) / 2, ($oldH - $limiting_dim) / 2, $thumbSize, $thumbSize, $limiting_dim, $limiting_dim);
imagejpeg($new, $thumbFile, 100);
imagedestroy($new);
return file_exists($thumbFile);
}
/**
* file path fixer

View File

@ -11,9 +11,7 @@ class Lang
public static function T($key)
{
global $_L, $lan_file, $config;
if(is_array($_SESSION['Lang'])){
$_L = array_merge($_L, $_SESSION['Lang']);
}
$key = preg_replace('/\s+/', ' ', $key);
if (!empty($_L[$key])) {
return $_L[$key];
@ -29,15 +27,14 @@ class Lang
if (empty($iso)) {
return $val;
}
if (!empty($iso) && !empty($val)) {
if (!empty($iso) && !empty($val) && $iso != 'en') {
$temp = Lang::translate($val, $iso);
if (!empty($temp)) {
$val = $temp;
}
}
$_L[$key] = $val;
$_SESSION['Lang'][$key] = $val;
file_put_contents($lan_file, json_encode($_SESSION['Lang'], JSON_PRETTY_PRINT));
file_put_contents($lan_file, json_encode($_L, JSON_PRETTY_PRINT));
return $val;
}
}
@ -124,20 +121,20 @@ class Lang
}
}
$when = "";
if(time()>strtotime($datetime)){
if (time() > strtotime($datetime)) {
$when = Lang::T('ago');
}else{
} else {
$when = '';
}
if (!$full)
$string = array_slice($string, 0, 1);
if($string){
if(empty($when)){
return '<b>'. implode(', ', $string) .'</b>';
}else{
return implode(', ', $string) .' '. $when;
if ($string) {
if (empty($when)) {
return '<b>' . implode(', ', $string) . '</b>';
} else {
return implode(', ', $string) . ' ' . $when;
}
}else{
} else {
return Lang::T('just now');
}
}
@ -245,16 +242,18 @@ class Lang
return $txt;
}
public static function maskText($text){
public static function maskText($text)
{
$len = strlen($text);
if($len < 3){
if ($len < 3) {
return "***";
}else if($len<5){
return substr($text,0,1)."***".substr($text,-1,1);
}else if($len<8){
return substr($text,0,2)."***".substr($text,-2,2);
}else{
return substr($text,0,4)."******".substr($text,-3,3);
} else if ($len < 5) {
return substr($text, 0, 1) . "***" . substr($text, -1, 1);
} else if ($len < 8) {
return substr($text, 0, 2) . "***" . substr($text, -2, 2);
} else {
return substr($text, 0, 4) . "******" . substr($text, -3, 3);
}
}
}

View File

@ -17,12 +17,15 @@ require $root_path . 'system/autoload/mail/SMTP.php';
class Message
{
public static function sendTelegram($txt)
public static function sendTelegram($txt, $chat_id = null)
{
global $config;
run_hook('send_telegram', [$txt]); #HOOK
if (!empty($config['telegram_bot']) && !empty($config['telegram_target_id'])) {
return Http::getData('https://api.telegram.org/bot' . $config['telegram_bot'] . '/sendMessage?chat_id=' . $config['telegram_target_id'] . '&text=' . urlencode($txt));
run_hook('send_telegram', [$txt, $chat_id = null]); #HOOK
if (!empty($config['telegram_bot'])) {
if (empty($chat_id)) {
$chat_id = $config['telegram_target_id'];
}
return Http::getData('https://api.telegram.org/bot' . $config['telegram_bot'] . '/sendMessage?chat_id=' . $chat_id . '&text=' . urlencode($txt));
}
}
@ -48,7 +51,7 @@ class Message
}
} else {
try {
self::sendSMS($config['sms_url'], $phone, $txt);
self::MikrotikSendSMS($config['sms_url'], $phone, $txt);
} catch (Exception $e) {
// ignore, add to logs
_log("Failed to send SMS using Mikrotik.\n" . $e->getMessage(), 'SMS', 0);
@ -64,7 +67,7 @@ class Message
public static function MikrotikSendSMS($router_name, $to, $message)
{
global $_app_stage, $client_m;
global $_app_stage, $client_m, $config;
if ($_app_stage == 'demo') {
return null;
}
@ -73,7 +76,10 @@ class Message
$iport = explode(":", $mikrotik['ip_address']);
$client_m = new RouterOS\Client($iport[0], $mikrotik['username'], $mikrotik['password'], ($iport[1]) ? $iport[1] : null);
}
$smsRequest = new RouterOS\Request('/tool sms send');
if (empty($config['mikrotik_sms_command'])) {
$config['mikrotik_sms_command'] = "/tool sms send";
}
$smsRequest = new RouterOS\Request($config['mikrotik_sms_command']);
$smsRequest
->setArgument('phone-number', $to)
->setArgument('message', $message);
@ -96,7 +102,7 @@ class Message
public static function sendEmail($to, $subject, $body)
{
global $config, $PAGES_PATH, $_app_stage;
global $config, $PAGES_PATH, $debug_mail;
if (empty($body)) {
return "";
}
@ -116,7 +122,7 @@ class Message
} else {
$mail = new PHPMailer();
$mail->isSMTP();
if ($_app_stage == 'Dev') {
if (isset($debug_mail) && $debug_mail == 'Dev') {
$mail->SMTPDebug = SMTP::DEBUG_SERVER;
}
$mail->Host = $config['smtp_host'];
@ -153,7 +159,9 @@ class Message
$mail->isHTML(false);
$mail->Body = $body;
}
$mail->send();
if (!$mail->send()) {
_log(Lang::T("Email not sent, Mailer Error: ") . $mail->ErrorInfo);
}
//<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">
}
@ -170,41 +178,86 @@ class Message
$msg = str_replace('[[plan]]', $package, $msg);
$msg = str_replace('[[package]]', $package, $msg);
$msg = str_replace('[[price]]', Lang::moneyFormat($price), $msg);
// Calculate bills and additional costs
list($bills, $add_cost) = User::getBills($customer['id']);
if ($add_cost > 0) {
$note = "";
// Initialize note and total variables
$note = "";
$total = $price;
// Add bills to the note if there are any additional costs
if ($add_cost != 0) {
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);
$total += $add_cost;
}
// Calculate tax
$tax = 0;
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
if ($tax_enable === 'yes') {
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
$tax_rate = ($tax_rate_setting === 'custom') ? $custom_tax_rate : $tax_rate_setting;
$tax = Package::tax($price, $tax_rate);
if ($tax != 0) {
$note .= "Tax : " . Lang::moneyFormat($tax) . "\n";
$total += $tax;
}
}
// Add total to the note
$note .= "Total : " . Lang::moneyFormat($total) . "\n";
// Replace placeholders in the message
$msg = str_replace('[[bills]]', $note, $msg);
if ($ds) {
$msg = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($ds['expiration'], $ds['time']), $msg);
} else {
$msg = str_replace('[[expired_date]]', "", $msg);
}
if (strpos($msg, '[[payment_link]]') !== false) {
// token only valid for 1 day, for security reason
$token = User::generateToken($customer['id'], 1);
if (!empty($token['token'])) {
$tur = ORM::for_table('tbl_user_recharges')
->where('customer_id', $customer['id'])
->where('namebp', $package)
->find_one();
if ($tur) {
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
$msg = str_replace('[[payment_link]]', $url, $msg);
}
} else {
$msg = str_replace('[[payment_link]]', '', $msg);
}
}
if (
!empty($customer['phonenumber']) && strlen($customer['phonenumber']) > 5
&& !empty($message) && in_array($via, ['sms', 'wa'])
) {
if ($via == 'sms') {
echo Message::sendSMS($customer['phonenumber'], $msg);
Message::sendSMS($customer['phonenumber'], $msg);
} else if ($via == 'email') {
self::sendEmail($customer['email'], '[' . $config['CompanyName'] . '] ' . Lang::T("Internet Plan Reminder"), $msg);
} else if ($via == 'wa') {
echo Message::sendWhatsapp($customer['phonenumber'], $msg);
Message::sendWhatsapp($customer['phonenumber'], $msg);
}
}
return "$via: $msg";
}
public static function sendBalanceNotification($cust, $balance, $balance_now, $message, $via)
public static function sendBalanceNotification($cust, $target, $balance, $balance_now, $message, $via)
{
global $config;
$msg = str_replace('[[name]]', $cust['fullname'] . ' (' . $cust['username'] . ')', $message);
$msg = str_replace('[[name]]', $target['fullname'] . ' (' . $target['username'] . ')', $message);
$msg = str_replace('[[current_balance]]', Lang::moneyFormat($balance_now), $msg);
$msg = str_replace('[[balance]]', Lang::moneyFormat($balance), $msg);
$phone = $cust['phonenumber'];
@ -214,11 +267,12 @@ class Message
) {
if ($via == 'sms') {
Message::sendSMS($phone, $msg);
} else if ($config['user_notification_payment'] == 'email') {
} else if ($via == 'email') {
self::sendEmail($cust['email'], '[' . $config['CompanyName'] . '] ' . Lang::T("Balance Notification"), $msg);
} else if ($via == 'wa') {
Message::sendWhatsapp($phone, $msg);
}
self::addToInbox($cust['id'], Lang::T('Balance Notification'), $msg);
}
return "$via: $msg";
}
@ -232,6 +286,7 @@ class Message
$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);
$textInvoice = str_replace('[[trx_date]]', Lang::dateAndTimeFormat($trx['recharged_on'], $trx['recharged_time']), $textInvoice);
if (!empty($trx['note'])) {
$textInvoice = str_replace('[[note]]', $trx['note'], $textInvoice);
}
@ -258,4 +313,16 @@ class Message
Message::sendWhatsapp($cust['phonenumber'], $textInvoice);
}
}
public static function addToInbox($to_customer_id, $subject, $body, $from = 'System')
{
$v = ORM::for_table('tbl_customers_inbox')->create();
$v->from = $from;
$v->customer_id = $to_customer_id;
$v->subject = $subject;
$v->date_created = date('Y-m-d H:i:s');
$v->body = nl2br($body);
$v->save();
}
}

118
system/autoload/Meta.php Normal file
View File

@ -0,0 +1,118 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
*
* Meta is for save adtional information in database for every existing table
*
* * Good for Plugin, so plugin don't need to modify existing tables
*
* Example to put data
* if plan need to add point value for loyalty poin
* Meta::for("tbl_plans")->set(1, 'point', '24');
* it means tbl_plans with id 1 have point value 24, customer will get 24 point for loyalty if buy plan with id 1
* You need to create the logic for that, Meta only hold the data
*
* Example to get data
* $point = Meta::for("tbl_plans")->get(1, 'point');
* this will return the point value of plan with id 1
*
* to get all key value
* $metas = Meta::for("tbl_plans")->getAll(1);
*
* to delete 1 data
* Meta::for("tbl_plans")->delete(1, 'point');
*
* to delete all data
* Meta::for("tbl_plans")->deleteAll(1);
**/
class Meta
{
protected $table = '';
protected function __construct($table)
{
$this->table = $table;
}
public static function for($table)
{
return new self($table);
}
public function get($id, $key)
{
// get the Value
return ORM::for_table('tbl_meta')
->select('value')
->where('tbl', $this->table)
->where('tbl_id', $id)
->where('name', $key)
->find_one()['value'];
}
public function getAll($id)
{
//get all key Value
$metas = [];
$result = ORM::for_table('tbl_meta')
->select('name')
->select('value')
->where('tbl', $this->table)
->where('tbl_id', $id)
->find_array();
foreach ($result as $value) {
$metas[$value['name']] = $value['value'];
}
return $metas;
}
public function set($id, $key, $value = '')
{
$meta = ORM::for_table('tbl_meta')
->where('tbl', $this->table)
->where('tbl_id', $id)
->where('name', $key)
->find_one();
if (!$meta) {
$meta = ORM::for_table('tbl_meta')->create();
$meta->tbl = $this->table;
$meta->tbl_id = $id;
$meta->name = $key;
$meta->value = $value;
$meta->save();
$result = $meta->id();
if ($result) {
return $result;
}
} else {
$meta->value = $value;
$meta->save();
return $meta['id'];
}
}
public function delete($id, $key = '')
{
// get the Value
return ORM::for_table('tbl_meta')
->select('value')
->where('tbl', $this->table)
->where('tbl_id', $id)
->where('name', $key)
->delete();
}
public function deleteAll($id)
{
//get all key Value
return ORM::for_table('tbl_meta')
->select('value')
->where('tbl', $this->table)
->where('tbl_id', $id)
->delete_many();
}
}

View File

@ -20,30 +20,33 @@ class Package
*/
public static function rechargeUser($id_customer, $router_name, $plan_id, $gateway, $channel, $note = '')
{
global $config, $admin, $c, $p, $b, $t, $d, $zero, $trx, $_app_stage;
$date_now = date("Y-m-d H:i:s");
global $config, $admin, $c, $p, $b, $t, $d, $zero, $trx, $_app_stage, $isChangePlan;
$date_only = date("Y-m-d");
$time_only = date("H:i:s");
$time = date("H:i:s");
$inv = "";
$isVoucher = false;
$c = [];
if ($trx && $trx['status'] == 2) {
// if its already paid, return it
return;
}
if ($id_customer == '' or $router_name == '' or $plan_id == '') {
return false;
}
if(trim($gateway) == 'Voucher' && $id_customer == 0){
if (trim($gateway) == 'Voucher' && $id_customer == 0) {
$isVoucher = true;
}
$p = ORM::for_table('tbl_plans')->where('id', $plan_id)->find_one();
if(!$isVoucher){
if (!$isVoucher) {
$c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one();
if ($c['status'] != 'Active') {
_alert(Lang::T('This account status') . ' : ' . Lang::T($c['status']), 'danger', "");
}
}else{
} else {
$c = [
'fullname' => $gateway,
'email' => '',
@ -60,7 +63,7 @@ class Package
} else {
// Additional cost
list($bills, $add_cost) = User::getBills($id_customer);
if ($add_cost > 0 && $router_name != 'balance') {
if ($add_cost != 0 && $router_name != 'balance') {
foreach ($bills as $k => $v) {
$note .= $k . " : " . Lang::moneyFormat($v) . "\n";
}
@ -71,10 +74,10 @@ class Package
if (!$p['enabled']) {
if (!isset($admin) || !isset($admin['id']) || empty($admin['id'])) {
r2(U . 'home', 'e', Lang::T('Plan Not found'));
r2(getUrl('home'), 'e', Lang::T('Plan Not found'));
}
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
r2(U . 'dashboard', 'e', Lang::T('Plan Not found'));
r2(getUrl('dashboard'), 'e', Lang::T('You do not have permission to access this page'));
}
}
@ -82,7 +85,7 @@ class Package
// if customer has attribute Expired Date use it
$day_exp = User::getAttribute("Expired Date", $c['id']);
if (!$day_exp) {
// if customer no attribute Expired Date use plan expired date
// if customer no attribute Expired Date use plan expired date
$day_exp = 20;
if ($p['prepaid'] == 'no') {
$day_exp = $p['expired_date'];
@ -96,55 +99,11 @@ class Package
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();
return self::rechargeBalance($c, $p, $gateway, $channel);
}
$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;
if ($router_name == 'Custom Balance') {
return self::rechargeCustomBalance($c, $p, $gateway, $channel);
}
/**
@ -173,9 +132,9 @@ class Package
# 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'));
if($isVoucher){
if ($isVoucher) {
$query->where('username', $c['username']);
}else{
} else {
$query->where('customer_id', $id_customer);
}
$b = $query->find_one();
@ -185,17 +144,40 @@ class Package
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");
$current_date = new DateTime($date_only);
$exp_date = clone $current_date;
$exp_date->modify('first day of next month');
$exp_date->setDate($exp_date->format('Y'), $exp_date->format('m'), $day_exp);
$min_days = 7 * $p['validity'];
$max_days = 35 * $p['validity'];
$days_until_exp = $exp_date->diff($current_date)->days;
// If less than min_days away, move to the next period
while ($days_until_exp < $min_days) {
$exp_date->modify('+1 month');
$days_until_exp = $exp_date->diff($current_date)->days;
}
// If more than max_days away, move to the previous period
while ($days_until_exp > $max_days) {
$exp_date->modify('-1 month');
$days_until_exp = $exp_date->diff($current_date)->days;
}
// Final check to ensure we're not less than min_days or in the past
if ($days_until_exp < $min_days || $exp_date <= $current_date) {
$exp_date->modify('+1 month');
}
// Adjust for multiple periods
if ($p['validity'] > 1) {
$exp_date->modify('+' . ($p['validity'] - 1) . ' months');
}
$date_exp = $exp_date->format('Y-m-d');
$time = "23:59:59";
} else if ($p['validity_unit'] == 'Days') {
$datetime = explode(' ', date("Y-m-d H:i:s", strtotime('+' . $p['validity'] . ' day')));
$date_exp = $datetime[0];
@ -209,83 +191,115 @@ class Package
$date_exp = $datetime[0];
$time = $datetime[1];
}
$isChangePlan = false;
if ($b) {
// plan exists
if ($plan_id != $b['plan_id']) {
$isChangePlan = true;
}
if ($config['extend_expiry'] != 'no') {
if ($b['namebp'] == $p['name_plan'] && $b['status'] == 'on') {
// if it same internet plan, expired will extend
if ($p['validity_unit'] == 'Months') {
$lastExpired = Lang::dateAndTimeFormat($b['expiration'], $b['time']);
$isChangePlan = false;
if ($b['namebp'] == $p['name_plan'] && $b['status'] == 'on' && $config['extend_expiry'] == 'yes') {
// if it same internet plan, expired will extend
switch ($p['validity_unit']) {
case 'Months':
$date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' months'));
$time = $b['time'];
} else if ($p['validity_unit'] == 'Period') {
break;
case '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') {
break;
case 'Days':
$date_exp = date("Y-m-d", strtotime($b['expiration'] . ' +' . $p['validity'] . ' days'));
$time = $b['time'];
} else if ($p['validity_unit'] == 'Hrs') {
break;
case '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') {
break;
case '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];
}
break;
}
} else {
$isChangePlan = true;
}
if ($isChangePlan || $b['status'] == 'off') {
$dvc = Package::getDevice($p);
if ($_app_stage != 'demo') {
//if ($b['status'] == 'on') {
$dvc = Package::getDevice($p);
if ($_app_stage != 'Demo') {
try {
if (file_exists($dvc)) {
require_once $dvc;
(new $p['device'])->add_customer($c, $p);
} else {
new Exception(Lang::T("Devices Not Found"));
}
} catch (Throwable $e) {
Message::sendTelegram(
"System Error. When activate Package. You need to sync manually\n" .
"Router: $router_name\n" .
"Customer: u$c[username]\n" .
"Plan: p$p[name_plan]\n" .
$e->getMessage() . "\n" .
$e->getTraceAsString()
);
} catch (Exception $e) {
Message::sendTelegram(
"System Error. When activate Package. You need to sync manually\n" .
"Router: $router_name\n" .
"Customer: u$c[username]\n" .
"Plan: p$p[name_plan]\n" .
$e->getMessage() . "\n" .
$e->getTraceAsString()
);
}
}
//}
$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 = $p['type'];
if ($admin) {
$b->admin_id = ($admin['id']) ? $admin['id'] : '0';
} else {
$b->admin_id = '0';
// if contains 'mikrotik', 'hotspot', 'pppoe', 'radius' then recharge it
if (Validator::containsKeyword($p['device'])) {
$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 = $p['type'];
if ($admin) {
$b->admin_id = ($admin['id']) ? $admin['id'] : '0';
} else {
$b->admin_id = '0';
}
$b->save();
}
$b->save();
// insert table transactions
$t = ORM::for_table('tbl_transactions')->create();
$t->invoice = $inv = "INV-" . Package::_raid();
$t->username = $c['username'];
$t->user_id = $id_customer;
$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;
}
if ($gateway == 'Voucher' && User::isUserVoucher($channel)) {
//its already paid
$t->price = 0;
} else {
$t->price = $p['price'] + $add_cost;
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;
@ -322,52 +336,83 @@ class Package
"\nRouter: " . $router_name .
"\nGateway: " . $gateway .
"\nChannel: " . $channel .
"\nLast Expired: $lastExpired" .
"\nNew Expired: " . Lang::dateAndTimeFormat($date_exp, $time) .
"\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) .
"\nNote:\n" . $note);
} else {
// active plan not exists
$dvc = Package::getDevice($p);
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
(new $p['device'])->add_customer($c, $p);
} else {
new Exception(Lang::T("Devices Not Found"));
if ($_app_stage != 'Demo') {
try {
if (file_exists($dvc)) {
require_once $dvc;
(new $p['device'])->add_customer($c, $p);
} else {
new Exception(Lang::T("Devices Not Found"));
}
} catch (Throwable $e) {
Message::sendTelegram(
"System Error. When activate Package. You need to sync manually\n" .
"Router: $router_name\n" .
"Customer: u$c[username]\n" .
"Plan: p$p[name_plan]\n" .
$e->getMessage() . "\n" .
$e->getTraceAsString()
);
} catch (Exception $e) {
Message::sendTelegram(
"System Error. When activate Package. You need to sync manually\n" .
"Router: $router_name\n" .
"Customer: u$c[username]\n" .
"Plan: p$p[name_plan]\n" .
$e->getMessage() . "\n" .
$e->getTraceAsString()
);
}
}
$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 = $p['type'];
if ($admin) {
$d->admin_id = ($admin['id']) ? $admin['id'] : '0';
} else {
$d->admin_id = '0';
// if contains 'mikrotik', 'hotspot', 'pppoe', 'radius' then recharge it
if (Validator::containsKeyword($p['device'])) {
$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 = $p['type'];
if ($admin) {
$d->admin_id = ($admin['id']) ? $admin['id'] : '0';
} else {
$d->admin_id = '0';
}
$d->save();
}
$d->save();
// insert table transactions
$t = ORM::for_table('tbl_transactions')->create();
$t->invoice = $inv = "INV-" . Package::_raid();
$t->username = $c['username'];
$t->user_id = $id_customer;
$t->plan_name = $p['name_plan'];
if ($p['validity_unit'] == 'Period') {
// Postpaid price always zero for first time
$note = '';
$bills = [];
if ($gateway == 'Voucher' && User::isUserVoucher($channel)) {
$t->price = 0;
// its already paid
} else {
$t->price = $p['price'] + $add_cost;
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;
@ -414,6 +459,7 @@ class Package
"\nRouter: " . $router_name .
"\nGateway: " . $gateway .
"\nChannel: " . $channel .
"\nExpired: " . Lang::dateAndTimeFormat($date_exp, $time) .
"\nPrice: " . Lang::moneyFormat($p['price'] + $add_cost) .
"\nNote:\n" . $note);
}
@ -429,6 +475,130 @@ class Package
return $inv;
}
public static function rechargeBalance($customer, $plan, $gateway, $channel, $note = '')
{
global $admin, $config;
// insert table transactions
$t = ORM::for_table('tbl_transactions')->create();
$t->invoice = $inv = "INV-" . Package::_raid();
$t->username = $customer['username'];
$t->user_id = $customer['id'];
$t->plan_name = $plan['name_plan'];
$t->price = $plan['price'];
$t->recharged_on = date("Y-m-d");
$t->recharged_time = date("H:i:s");
$t->expiration = date("Y-m-d");
$t->time = date("H:i:s");
$t->method = "$gateway - $channel";
$t->routers = 'balance';
$t->type = "Balance";
$t->note = $note;
if ($admin) {
$t->admin_id = ($admin['id']) ? $admin['id'] : '0';
} else {
$t->admin_id = '0';
}
$t->save();
$balance_before = $customer['balance'];
Balance::plus($customer['id'], $plan['price']);
$balance = $customer['balance'] + $plan['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("Y-m-d H:i:s")), $textInvoice);
$textInvoice = str_replace('[[trx_date]]', Lang::dateTimeFormat(date("Y-m-d H:i:s")), $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]]', $plan['name_plan'], $textInvoice);
$textInvoice = str_replace('[[plan_price]]', Lang::moneyFormat($plan['price']), $textInvoice);
$textInvoice = str_replace('[[name]]', $customer['fullname'], $textInvoice);
$textInvoice = str_replace('[[user_name]]', $customer['username'], $textInvoice);
$textInvoice = str_replace('[[user_password]]', $customer['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($customer['phonenumber'], $textInvoice);
} else if ($config['user_notification_payment'] == 'wa') {
Message::sendWhatsapp($customer['phonenumber'], $textInvoice);
} else if ($config['user_notification_payment'] == 'email') {
Message::sendEmail($customer['email'], '[' . $config['CompanyName'] . '] ' . Lang::T("Invoice") . ' ' . $inv, $textInvoice);
}
return $t->id();
}
public static function rechargeCustomBalance($customer, $plan, $gateway, $channel, $note = '')
{
global $admin, $config;
$plan = ORM::for_table('tbl_payment_gateway')
->where('username', $customer['username'])
->where('routers', 'Custom Balance')
->where('status', '1')
->find_one();
if (!$plan) {
return false;
}
// insert table transactions
$t = ORM::for_table('tbl_transactions')->create();
$t->invoice = $inv = "INV-" . Package::_raid();
$t->username = $customer['username'];
$t->user_id = $customer['id'];
$t->plan_name = 'Custom Balance';
$t->price = $plan['price'];
$t->recharged_on = date("Y-m-d");
$t->recharged_time = date("H:i:s");
$t->expiration = date("Y-m-d");
$t->time = date("H:i:s");
$t->method = "$gateway - $channel";
$t->routers = 'balance';
$t->type = "Balance";
$t->note = $note;
if ($admin) {
$t->admin_id = ($admin['id']) ? $admin['id'] : '0';
} else {
$t->admin_id = '0';
}
$t->save();
$balance_before = $customer['balance'];
Balance::plus($customer['id'], $plan['price']);
$balance = $customer['balance'] + $plan['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("Y-m-d H:i:s")), $textInvoice);
$textInvoice = str_replace('[[trx_date]]', Lang::dateTimeFormat(date("Y-m-d H:i:s")), $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]]', $plan['name_plan'], $textInvoice);
$textInvoice = str_replace('[[plan_price]]', Lang::moneyFormat($plan['price']), $textInvoice);
$textInvoice = str_replace('[[name]]', $customer['fullname'], $textInvoice);
$textInvoice = str_replace('[[user_name]]', $customer['username'], $textInvoice);
$textInvoice = str_replace('[[user_password]]', $customer['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($customer['phonenumber'], $textInvoice);
} else if ($config['user_notification_payment'] == 'wa') {
Message::sendWhatsapp($customer['phonenumber'], $textInvoice);
} else if ($config['user_notification_payment'] == 'email') {
Message::sendEmail($customer['email'], '[' . $config['CompanyName'] . '] ' . Lang::T("Invoice") . ' ' . $inv, $textInvoice);
}
return $t->id();
}
public static function _raid()
{
return ORM::for_table('tbl_transactions')->max('id') + 1;
@ -558,10 +728,10 @@ class Package
public static function getDevice($plan)
{
global $DEVICE_PATH;
if($plan === false){
if ($plan === false) {
return "none";
}
if(!isset($plan['device'])){
if (!isset($plan['device'])) {
return "none";
}
if (!empty($plan['device'])) {

View File

@ -8,13 +8,13 @@
class Paginator
{
public static function findMany($query, $search = [], $per_page = '10', $append_url = "")
public static function findMany($query, $search = [], $per_page = '10', $append_url = "", $toArray = false)
{
global $routes, $ui;
$adjacents = "2";
$page = _get('p', 1);
$page = (empty($page) ? 1 : $page);
$url = U . implode('/', $routes);
$url = getUrl(implode('/', $routes));
if (count($search) > 0) {
$url .= '&' . http_build_query($search);
}
@ -71,7 +71,11 @@ class Paginator
if ($ui) {
$ui->assign('paginator', $result);
}
return $query->offset($startpoint)->limit($per_page)->find_many();
if($toArray){
return $query->offset($startpoint)->limit($per_page)->find_array();
}else{
return $query->offset($startpoint)->limit($per_page)->find_many();
}
}
}
@ -79,7 +83,7 @@ class Paginator
{
global $routes;
global $_L;
$url = U . implode('/', $routes);
$url = getUrl(implode('/', $routes));
$query = urlencode($query);
$adjacents = "2";
$page = (int)(empty(_get('p')) ? 1 : _get('p'));
@ -165,7 +169,7 @@ class Paginator
{
global $routes;
global $_L;
$url = U . $routes['0'] . '/' . $routes['1'] . '/';
$url = getUrl($routes['0'] . '/' . $routes['1'] . '/');
$adjacents = "2";
$page = (int)(!isset($routes['2']) ? 1 : $routes['2']);
$pagination = "";
@ -273,7 +277,7 @@ class Paginator
{
global $routes;
global $_L;
$url = U . $routes['0'] . '/' . $routes['1'] . '/';
$url = getUrl($routes['0'] . '/' . $routes['1'] . '/');
$adjacents = "2";
$page = (int)(!isset($routes['2']) ? 1 : $routes['2']);
$pagination = "";

View File

@ -32,4 +32,19 @@ class Password
$pass = substr(str_shuffle(str_repeat('ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@#!123456789', 8)), 0, 8);
return $pass;
}
/**
* verify CHAP password
* @param string $realPassword
* @param string $CHAPassword
* @param string $CHAPChallenge
* @return bool
*/
public static function chap_verify($realPassword, $CHAPassword, $CHAPChallenge){
$CHAPassword = substr($CHAPassword, 2);
$chapid = substr($CHAPassword, 0, 2);
$result = hex2bin($chapid) . $realPassword . hex2bin(substr($CHAPChallenge, 2));
$response = $chapid . md5($result);
return ($response != $CHAPassword);
}
}

View File

@ -44,16 +44,17 @@ class Text
return $result;
}
public static function maskText($text){
public static function maskText($text)
{
$len = strlen($text);
if($len < 3){
if ($len < 3) {
return "***";
}else if($len<5){
return substr($text,0,1)."***".substr($text,-1,1);
}else if($len<8){
return substr($text,0,2)."***".substr($text,-2,2);
}else{
return substr($text,0,4)."******".substr($text,-3,3);
} else if ($len < 5) {
return substr($text, 0, 1) . "***" . substr($text, -1, 1);
} else if ($len < 8) {
return substr($text, 0, 2) . "***" . substr($text, -2, 2);
} else {
return substr($text, 0, 4) . "******" . substr($text, -3, 3);
}
}
@ -61,4 +62,67 @@ class Text
{
return preg_replace("/[^A-Za-z0-9]/", '_', $str);;
}
public static function is_html($string)
{
return preg_match("/<[^<]+>/", $string, $m) != 0;
}
public static function convertDataUnit($datalimit, $unit = 'MB')
{
$unit = strtoupper($unit);
if ($unit == 'KB') {
return $datalimit * 1024;
} elseif ($unit == 'MB') {
return $datalimit * 1048576;
} elseif ($unit == 'GB') {
return $datalimit * 1073741824;
} elseif ($unit == 'TB') {
return $datalimit * 1099511627776;
} else {
return $datalimit;
}
}
// echo Json array to text
public static function jsonArray2text($array, $start = '', $result = '')
{
foreach ($array as $k => $v) {
if (is_array($v)) {
$result .= self::jsonArray2text($v, "$start$k.", '');
} else {
$result .= "$start$k = " . strval($v) . "\n";
}
}
return $result;
}
public static function jsonArray21Array($array){
$text = self::jsonArray2text($array);
$lines = explode("\n", $text);
$result = [];
foreach($lines as $line){
$parts = explode(' = ', $line);
if(count($parts) == 2){
$result[trim($parts[0])] = trim($parts[1]);
}
}
return $result;
}
public static function url(...$data){
global $config;
$url = implode("", $data);
if ($config['url_canonical'] == 'yes') {
$u = str_replace('?_route=', '', U);
$pos = strpos($url, '&');
if ($pos === false) {
return $u . $url;
} else {
return $u . substr($url, 0, $pos) . '?' . substr($url, $pos + 1);
}
} else {
return U . $url;
}
}
}

View File

@ -10,13 +10,13 @@ class User
{
public static function getID()
{
global $db_password;
global $db_pass;
if (isset($_SESSION['uid']) && !empty($_SESSION['uid'])) {
return $_SESSION['uid'];
} else if (isset($_COOKIE['uid'])) {
// id.time.sha1
$tmp = explode('.', $_COOKIE['uid']);
if (sha1($tmp[0] . '.' . $tmp[1] . '.' . $db_password) == $tmp[2]) {
if (sha1($tmp[0] . '.' . $tmp[1] . '.' . $db_pass) == $tmp[2]) {
if (time() - $tmp[1] < 86400 * 30) {
$_SESSION['uid'] = $tmp[0];
return $tmp[0];
@ -26,6 +26,15 @@ class User
return 0;
}
public static function getTawkToHash($email)
{
global $config;
if (!empty($config['tawkto_api_key']) && !Empty($email)) {
return hash_hmac('sha256', $email, $config['tawkto_api_key']);
}
return '';
}
public static function getBills($id = 0)
{
if (!$id) {
@ -123,7 +132,7 @@ class User
return 0;
}
public static function getAttribute($name, $id = 0)
public static function getAttribute($name, $id = 0, $default = '')
{
if (!$id) {
$id = User::getID();
@ -135,7 +144,7 @@ class User
if ($f) {
return $f['field_value'];
}
return '';
return $default;
}
public static function getAttributes($endWith, $id = 0)
@ -157,12 +166,31 @@ class User
return [];
}
public static function generateToken($uid, $validDays = 30)
{
global $db_pass;
if($validDays>=30){
$time = time();
}else{
// for customer, deafult expired is 30 days
$time = strtotime('+ '.(30 - $validDays).' days');
}
return [
'time' => $time,
'token' => $uid . '.' . $time . '.' . sha1($uid . '.' . $time . '.' . $db_pass)
];
}
public static function setCookie($uid)
{
global $db_password;
global $db_pass;
if (isset($uid)) {
$time = time();
setcookie('uid', $uid . '.' . $time . '.' . sha1($uid . '.' . $time . '.' . $db_password), time() + 86400 * 30);
$token = self::generateToken($uid);
setcookie('uid', $token['token'], time() + 86400 * 30);
return $token;
} else {
return false;
}
}
@ -178,7 +206,7 @@ class User
global $config;
if ($config['maintenance_mode'] == true) {
if ($config['maintenance_mode_logout'] == true) {
r2(U . 'logout', 'd', '');
r2(getUrl('logout'), 'd', '');
} else {
displayMaintenanceMessage();
}
@ -190,12 +218,32 @@ class User
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 _infoByName($username)
{
global $config;
if ($config['maintenance_mode'] == true) {
if ($config['maintenance_mode_logout'] == true) {
r2(getUrl('logout'), 'd', '');
} else {
displayMaintenanceMessage();
}
}
$d = ORM::for_table('tbl_customers')->where("username", $username)->find_one();
if ($d['status'] == 'Banned') {
_alert(Lang::T('This account status') . ' : ' . Lang::T($d['status']), 'danger', "logout");
}
return $d;
}
public static function isUserVoucher($kode)
{
$regex = '/^GC\d+C.{10}$/';
return preg_match($regex, $kode);
}
public static function _billing($id = 0)
{
if (!$id) {
@ -204,15 +252,70 @@ class User
$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',
'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'
'admin_id',
'prepaid'
])
->left_outer_join('tbl_plans', ['tbl_plans.id', '=', 'tbl_user_recharges.plan_id'])
->left_outer_join('tbl_bandwidth', ['tbl_bandwidth.id', '=', 'tbl_plans.id_bw'])
->select('tbl_bandwidth.name_bw', 'name_bw')
->select('tbl_plans.price', 'price')
->where('customer_id', $id)
->left_outer_join('tbl_plans', array('tbl_plans.id', '=', 'tbl_user_recharges.plan_id'))
->find_many();
return $d;
}
public static function setFormCustomField($uid = 0){
global $UPLOAD_PATH;
$fieldPath = $UPLOAD_PATH . DIRECTORY_SEPARATOR . "customer_field.json";
if(!file_exists($fieldPath)){
return '';
}
$fields = json_decode(file_get_contents($fieldPath), true);
foreach($fields as $field){
if(!empty(_post($field['name']))){
self::setAttribute($field['name'], _post($field['name']), $uid);
}
}
}
public static function getFormCustomField($ui, $register = false, $uid = 0){
global $UPLOAD_PATH;
$fieldPath = $UPLOAD_PATH . DIRECTORY_SEPARATOR . "customer_field.json";
if(!file_exists($fieldPath)){
return '';
}
$fields = json_decode(file_get_contents($fieldPath), true);
$attrs = [];
if(!$register){
$attrs = self::getAttributes('', $uid);
$ui->assign('attrs', $attrs);
}
$html = '';
$ui->assign('register', $register);
foreach($fields as $field){
if($register){
if($field['register']){
$ui->assign('field', $field);
$html .= $ui->fetch('customer/custom_field.tpl');
}
}else{
$ui->assign('field', $field);
$html .= $ui->fetch('customer/custom_field.tpl');
}
}
return $html;
}
}

View File

@ -301,23 +301,30 @@ class Validator
return (bool)in_array($format, $formats);
}
public static function countRouterPlan($plans, $router){
public static function countRouterPlan($plans, $router)
{
$n = 0;
foreach ($plans as $plan){
if($plan['routers'] == $router){
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){
public static function isRouterHasPlan($plans, $router)
{
foreach ($plans as $plan) {
if ($plan['routers'] == $router) {
return true;
}
}
return false;
}
public static function containsKeyword($string, $keywords = ['mikrotik', 'hotspot', 'pppoe', 'radius', 'dummy'])
{
$regex = '/' . implode('|', $keywords) . '/i';
return preg_match($regex, strtolower($string));
}
}

View File

@ -43,9 +43,10 @@ $ui->setConfigDir(File::pathFixer($UI_PATH . '/conf/'));
$ui->setCacheDir(File::pathFixer($UI_PATH . '/cache/'));
$ui->assign('app_url', APP_URL);
$ui->assign('_domain', str_replace('www.', '', parse_url(APP_URL, PHP_URL_HOST)));
$ui->assign('_url', APP_URL . '/index.php?_route=');
$ui->assign('_url', APP_URL . '/?_route=');
$ui->assign('_path', __DIR__);
$ui->assign('_c', $config);
$ui->assign('user_language', $_SESSION['user_language']);
$ui->assign('UPLOAD_PATH', str_replace($root_path, '', $UPLOAD_PATH));
$ui->assign('CACHE_PATH', str_replace($root_path, '', $CACHE_PATH));
$ui->assign('PAGES_PATH', str_replace($root_path, '', $PAGES_PATH));
@ -66,16 +67,24 @@ if (isset($_SESSION['notify'])) {
unset($_SESSION['ntype']);
}
// Routing Engine
$req = _get('_route');
if(!isset($_GET['_route'])) {
$req = ltrim(parse_url($_SERVER['REQUEST_URI'])['path'], '/');
}else{
// Routing Engine
$req = _get('_route');
}
$routes = explode('/', $req);
$ui->assign('_routes', $routes);
$handler = $routes[0];
if ($handler == '') {
$handler = 'default';
}
$admin = Admin::_info();
try {
if(!empty($_GET['uid'])){
$_COOKIE['uid'] = $_GET['uid'];
}
$admin = Admin::_info();
$sys_render = $root_path . File::pathFixer('system/controllers/' . $handler . '.php');
if (file_exists($sys_render)) {
$menus = array();
@ -87,7 +96,7 @@ try {
foreach ($menu_registered as $menu) {
if ($menu['admin'] && _admin(false)) {
if (count($menu['auth']) == 0 || in_array($admin['user_type'], $menu['auth'])) {
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . U . 'plugin/' . $menu['function'] . '">';
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . getUrl('plugin/' . $menu['function']) . '">';
if (!empty($menu['icon'])) {
$menus[$menu['position']] .= '<i class="' . $menu['icon'] . '"></i>';
}
@ -98,7 +107,7 @@ try {
$menus[$menu['position']] .= '<span class="text">' . $menu['name'] . '</span></a></li>';
}
} else if (!$menu['admin'] && _auth(false)) {
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . U . 'plugin/' . $menu['function'] . '">';
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . getUrl('plugin/' . $menu['function']) . '">';
if (!empty($menu['icon'])) {
$menus[$menu['position']] .= '<i class="' . $menu['icon'] . '"></i>';
}
@ -115,7 +124,11 @@ try {
unset($menus, $menu_registered);
include($sys_render);
} else {
r2(U . 'dashboard', 'e', 'not found');
// header 404
header("HTTP/1.0 404 Not Found");
header("Content-Type: text/html; charset=utf-8");
echo "404 Not Found";
die();
}
} catch (Throwable $e) {
Message::sendTelegram(
@ -123,12 +136,12 @@ try {
$e->getMessage() . "\n" .
$e->getTraceAsString()
);
if (!Admin::getID()) {
r2(U . 'home', 'e', $e->getMessage());
if (empty($_SESSION['aid'])) {
$ui->display('customer/error.tpl'); die();
}
$ui->assign("error_message", $e->getMessage() . '<br><pre>' . $e->getTraceAsString() . '</pre>');
$ui->assign("error_title", "PHPNuxBill Crash");
$ui->display('router-error.tpl');
$ui->display('error.tpl');
die();
} catch (Exception $e) {
Message::sendTelegram(
@ -136,11 +149,11 @@ try {
$e->getMessage() . "\n" .
$e->getTraceAsString()
);
if (!Admin::getID()) {
r2(U . 'home', 'e', $e->getMessage());
if (empty($_SESSION['aid'])) {
$ui->display('customer/error.tpl'); die();
}
$ui->assign("error_message", $e->getMessage() . '<br><pre>' . $e->getTraceAsString() . '</pre>');
$ui->assign("error_title", "PHPNuxBill Crash");
$ui->display('router-error.tpl');
$ui->display('error.tpl');
die();
}

View File

@ -1,6 +1,7 @@
{
"require": {
"mpdf/mpdf": "^8.1",
"smarty/smarty": "=4.5.3"
"smarty/smarty": "=4.5.3",
"yosiazwan/php-facedetection": "^0.1.0"
}
}

44
system/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a33a5e0440af423877195440decefd29",
"content-hash": "a5f201dce3d594a500f2b9e9a8532e66",
"packages": [
{
"name": "mpdf/mpdf",
@ -476,6 +476,48 @@
"source": "https://github.com/smarty-php/smarty/tree/v4.5.3"
},
"time": "2024-05-28T21:46:01+00:00"
},
{
"name": "yosiazwan/php-facedetection",
"version": "0.1.0",
"source": {
"type": "git",
"url": "https://github.com/yosiazwan/php-facedetection.git",
"reference": "b016273ceceacd85562bbc50384fbabc947fe525"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yosiazwan/php-facedetection/zipball/b016273ceceacd85562bbc50384fbabc947fe525",
"reference": "b016273ceceacd85562bbc50384fbabc947fe525",
"shasum": ""
},
"require": {
"ext-gd": "*",
"php": ">=5.2.0"
},
"type": "library",
"autoload": {
"classmap": [
"FaceDetector.php",
"Exception/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0"
],
"authors": [
{
"name": "Maurice Svay",
"homepage": "https://github.com/mauricesvay/php-facedetection/graphs/contributors"
}
],
"description": "PHP class to detect one face in images. A pure PHP port of an existing JS code from Karthik Tharavad.",
"homepage": "https://github.com/mauricesvay/php-facedetection",
"support": {
"source": "https://github.com/yosiazwan/php-facedetection/tree/0.1.0"
},
"time": "2016-01-26T22:10:00+00:00"
}
],
"packages-dev": [],

View File

@ -18,11 +18,17 @@ switch ($action) {
case 'change-password':
run_hook('customer_view_change_password'); #HOOK
$ui->display('user-change-password.tpl');
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('customer/change-password.tpl');
break;
case 'change-password-post':
$password = _post('password');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('accounts/change-password'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
run_hook('customer_change_password'); #HOOK
if ($password != '') {
$d_pass = $user['password'];
@ -30,14 +36,14 @@ switch ($action) {
$cnpass = _post('cnpass');
if ($password == $d_pass) {
if (!Validator::Length($password, 36, 2)) {
r2(U . 'accounts/change-password', 'e', 'New Password must be 2 to 35 character');
r2(getUrl('accounts/change-password'), 'e', 'New Password must be 2 to 35 character');
}
if ($npass != $cnpass) {
r2(U . 'accounts/change-password', 'e', 'Both Password should be same');
r2(getUrl('accounts/change-password'), 'e', 'Both Password should be same');
}
$user->password = $npass;
$turs = ORM::for_table('tbl_user_recharges')->where('customer_id', $user['id'])->find_many();
foreach($turs as $tur) {
foreach ($turs as $tur) {
// if has active plan, change the password to devices
if ($tur['status'] == 'on') {
$p = ORM::for_table('tbl_plans')->where('id', $tur['plan_id'])->find_one();
@ -58,125 +64,172 @@ switch ($action) {
_log('[' . $user['username'] . ']: Password changed successfully', 'User', $user['id']);
_alert(Lang::T('Password changed successfully, Please login again'), 'success', "login");
} else {
die($password);
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('accounts/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
} else {
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('accounts/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
break;
case 'profile':
$d = ORM::for_table('tbl_customers')->find_one($user['id']);
if ($d) {
run_hook('customer_view_edit_profile'); #HOOK
$ui->assign('d', $d);
$ui->display('user-profile.tpl');
} else {
r2(U . 'home', 'e', Lang::T('Account Not Found'));
}
run_hook('customer_view_edit_profile'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->assign('customFields', User::getFormCustomField($ui, false, $user['id']));
$ui->display('customer/profile.tpl');
break;
case 'edit-profile-post':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('accounts/profile'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$fullname = _post('fullname');
$address = _post('address');
$email = _post('email');
$phonenumber = _post('phonenumber');
run_hook('customer_edit_profile'); #HOOK
$msg = '';
if (Validator::Length($fullname, 31, 2) == false) {
$msg .= 'Full Name should be between 3 to 30 characters' . '<br>';
if (Validator::Length($fullname, 31, 1) == false) {
$msg .= 'Full Name should be between 1 to 30 characters' . '<br>';
}
if (Validator::UnsignedNumber($phonenumber) == false) {
$msg .= 'Phone Number must be a number' . '<br>';
}
$d = ORM::for_table('tbl_customers')->find_one($user['id']);
if ($d) {
} else {
$msg .= Lang::T('Data Not Found') . '<br>';
}
if (empty($msg)) {
if (!empty($_FILES['photo']['name']) && file_exists($_FILES['photo']['tmp_name'])) {
if (function_exists('imagecreatetruecolor')) {
$hash = md5_file($_FILES['photo']['tmp_name']);
$subfolder = substr($hash, 0, 2);
$folder = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'photos' . DIRECTORY_SEPARATOR;
if (!file_exists($folder)) {
mkdir($folder);
}
$folder = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'photos' . DIRECTORY_SEPARATOR . $subfolder . DIRECTORY_SEPARATOR;
if (!file_exists($folder)) {
mkdir($folder);
}
$imgPath = $folder . $hash . '.jpg';
if (!file_exists($imgPath)) {
File::resizeCropImage($_FILES['photo']['tmp_name'], $imgPath, 1600, 1600, 100);
}
if (!file_exists($imgPath . '.thumb.jpg')) {
if (_post('faceDetect') == 'yes') {
try {
$detector = new svay\FaceDetector();
$detector->setTimeout(5000);
$detector->faceDetect($imgPath);
$detector->cropFaceToJpeg($imgPath . '.thumb.jpg', false);
} catch (Exception $e) {
File::makeThumb($imgPath, $imgPath . '.thumb.jpg', 200);
} catch (Throwable $e) {
File::makeThumb($imgPath, $imgPath . '.thumb.jpg', 200);
}
} else {
File::makeThumb($imgPath, $imgPath . '.thumb.jpg', 200);
}
}
if (file_exists($imgPath)) {
if ($user['photo'] != '' && strpos($user['photo'], 'default') === false) {
if (file_exists($UPLOAD_PATH . $user['photo'])) {
unlink($UPLOAD_PATH . $user['photo']);
if (file_exists($UPLOAD_PATH . $user['photo'] . '.thumb.jpg')) {
unlink($UPLOAD_PATH . $user['photo'] . '.thumb.jpg');
}
}
}
$user->photo = '/photos/' . $subfolder . '/' . $hash . '.jpg';
}
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
} else {
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
}
if ($msg == '') {
$d->fullname = $fullname;
$d->address = $address;
$d->email = $email;
$d->phonenumber = $phonenumber;
$d->save();
$user->fullname = $fullname;
$user->address = $address;
if ($_c['allow_phone_otp'] != 'yes') {
$user->phonenumber = $phonenumber;
}
if ($_c['allow_email_otp'] != 'yes') {
$user->email = $email;
}
User::setFormCustomField($user['id']);
$user->save();
_log('[' . $user['username'] . ']: ' . Lang::T('User Updated Successfully'), 'User', $user['id']);
r2(U . 'accounts/profile', 's', Lang::T('User Updated Successfully'));
} else {
r2(U . 'accounts/profile', 'e', $msg);
r2(getUrl('accounts/profile'), 's', Lang::T('User Updated Successfully'));
}else{
r2(getUrl('accounts/profile'), 'e', $msg);
}
break;
case 'phone-update':
$d = ORM::for_table('tbl_customers')->find_one($user['id']);
if ($d) {
//run_hook('customer_view_edit_profile'); #HOOK
$ui->assign('d', $d);
$ui->assign('new_phone', $_SESSION['new_phone']);
$ui->display('user-phone-update.tpl');
} else {
r2(U . 'home', 'e', Lang::T('Account Not Found'));
}
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->assign('new_phone', $_SESSION['new_phone']);
$ui->display('customer/phone-update.tpl');
break;
case 'phone-update-otp':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$phone = Lang::phoneFormat(_post('phone'));
$username = $user['username'];
$otpPath = $CACHE_PATH . '/sms/';
$_SESSION['new_phone'] = $phone;
// Validate the phone number format
if (!preg_match('/^[0-9]{10,}$/', $phone)) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
if (!preg_match('/^[0-9]{10,}$/', $phone) || empty($phone)) {
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Invalid phone number format'));
}
if (empty($config['sms_url'])) {
r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not Available, Please try again later'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('SMS server not Available, Please try again later'));
}
if (!empty($config['sms_url'])) {
if (!empty($phone)) {
$d = ORM::for_table('tbl_customers')->where('username', $username)->where('phonenumber', $phone)->find_one();
if ($d) {
r2(U . 'accounts/phone-update', 'e', Lang::T('You cannot use your current phone number'));
}
if (!file_exists($otpPath)) {
mkdir($otpPath);
touch($otpPath . 'index.html');
}
$otpFile = $otpPath . sha1($username . $db_password) . ".txt";
$phoneFile = $otpPath . sha1($username . $db_password) . "_phone.txt";
$d = ORM::for_table('tbl_customers')->whereNotEqual('username', $username)->where('phonenumber', $phone)->find_one();
if ($d) {
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Phone number already registered by another customer'));
}
if (!file_exists($otpPath)) {
mkdir($otpPath);
touch($otpPath . 'index.html');
}
$otpFile = $otpPath . sha1($username . $db_pass) . ".txt";
$phoneFile = $otpPath . sha1($username . $db_pass) . "_phone.txt";
// expired 10 minutes
if (file_exists($otpFile) && time() - filemtime($otpFile) < 1200) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Please wait ' . (1200 - (time() - filemtime($otpFile))) . ' seconds before sending another SMS'));
} else {
$otp = rand(100000, 999999);
file_put_contents($otpFile, $otp);
file_put_contents($phoneFile, $phone);
// send send OTP to user
if ($config['phone_otp_type'] === 'sms') {
Message::sendSMS($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
} elseif ($config['phone_otp_type'] === 'whatsapp') {
Message::sendWhatsapp($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
} elseif ($config['phone_otp_type'] === 'both') {
Message::sendSMS($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
Message::sendWhatsapp($phone, $config['CompanyName'] . "\n Your Verification code is: $otp");
}
//redirect after sending OTP
r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code has been sent to your phone'));
}
// expired 10 minutes
if (file_exists($otpFile) && time() - filemtime($otpFile) < 600) {
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Please wait ') . (600 - (time() - filemtime($otpFile))) . Lang::T(' seconds before sending another SMS'));
} else {
$otp = rand(100000, 999999);
file_put_contents($otpFile, $otp);
file_put_contents($phoneFile, $phone);
// send send OTP to user
if ($config['phone_otp_type'] === 'sms') {
Message::sendSMS($phone, $config['CompanyName'] . "\n\n" . Lang::T("Verification code") . "\n$otp");
} elseif ($config['phone_otp_type'] === 'whatsapp') {
Message::sendWhatsapp($phone, $config['CompanyName'] . "\n\n" . Lang::T("Verification code") . "\n$otp");
} elseif ($config['phone_otp_type'] === 'both') {
Message::sendSMS($phone, $config['CompanyName'] . "\n\n" . Lang::T("Verification code") . "\n$otp");
Message::sendWhatsapp($phone, $config['CompanyName'] . "\n\n" . Lang::T("Verification code") . "\n$otp");
}
//redirect after sending OTP
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Verification code has been sent to your phone'));
}
break;
case 'phone-update-post':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$phone = Lang::phoneFormat(_post('phone'));
$otp_code = _post('otp');
$username = $user['username'];
@ -184,59 +237,186 @@ switch ($action) {
// Validate the phone number format
if (!preg_match('/^[0-9]{10,}$/', $phone)) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Invalid phone number format'));
}
if (empty($config['sms_url'])) {
r2(getUrl('accounts/phone-update'), 'e', Lang::T('SMS server not Available, Please try again later'));
}
$otpFile = $otpPath . sha1($username . $db_pass) . ".txt";
$phoneFile = $otpPath . sha1($username . $db_pass) . "_phone.txt";
// Check if OTP file exists
if (!file_exists($otpFile)) {
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Please request OTP first'));
exit();
}
if (!empty($config['sms_url'])) {
$otpFile = $otpPath . sha1($username . $db_password) . ".txt";
$phoneFile = $otpPath . sha1($username . $db_password) . "_phone.txt";
// Check if OTP file exists
if (!file_exists($otpFile)) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Please request OTP first'));
exit();
}
// expired 10 minutes
if (time() - filemtime($otpFile) > 1200) {
unlink($otpFile);
unlink($phoneFile);
r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code expired'));
exit();
} else {
$code = file_get_contents($otpFile);
// Check if OTP code matches
if ($code != $otp_code) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Wrong Verification code'));
exit();
}
// Check if the phone number matches the one that requested the OTP
$savedPhone = file_get_contents($phoneFile);
if ($savedPhone !== $phone) {
r2(U . 'accounts/phone-update', 'e', Lang::T('The phone number does not match the one that requested the OTP'));
exit();
}
// OTP verification successful, delete OTP and phone number files
unlink($otpFile);
unlink($phoneFile);
}
} else {
r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not available'));
// expired 10 minutes
if (time() - filemtime($otpFile) > 1200) {
unlink($otpFile);
unlink($phoneFile);
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Verification code expired'));
exit();
} else {
$code = file_get_contents($otpFile);
// Check if OTP code matches
if ($code != $otp_code) {
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Wrong Verification code'));
exit();
}
// Check if the phone number matches the one that requested the OTP
$savedPhone = file_get_contents($phoneFile);
if ($savedPhone !== $phone) {
r2(getUrl('accounts/phone-update'), 'e', Lang::T('The phone number does not match the one that requested the OTP'));
exit();
}
// OTP verification successful, delete OTP and phone number files
unlink($otpFile);
unlink($phoneFile);
}
// Update the phone number in the database
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
if ($d) {
$d->phonenumber = Lang::phoneFormat($phone);
$d->save();
$user->phonenumber = Lang::phoneFormat($phone);
$user->save();
r2(getUrl('accounts/profile'), 's', Lang::T('Phone number updated successfully'));
break;
case 'email-update':
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->assign('new_email', $_SESSION['new_email']);
$ui->display('customer/email-update.tpl');
break;
case 'email-update-otp':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$email = trim(_post('email'));
$username = $user['username'];
$otpPath = $CACHE_PATH . '/email/';
$_SESSION['new_email'] = $email;
// Validate the phone number format
if (!Validator::Email($email)) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Invalid Email address format'));
}
r2(U . 'accounts/profile', 's', Lang::T('Phone number updated successfully'));
if (empty($config['smtp_host'])) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Email server not Available, Please ask admin to configure it'));
}
$d = ORM::for_table('tbl_customers')->whereNotEqual('username', $username)->where('email', $email)->find_one();
if ($d) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Email already used by another Customer'));
}
if (!file_exists($otpPath)) {
mkdir($otpPath);
touch($otpPath . 'index.html');
}
$otpFile = $otpPath . sha1($username . $db_pass) . ".txt";
$emailFile = $otpPath . sha1($username . $db_pass) . "_email.txt";
// expired 10 minutes
if (file_exists($otpFile) && time() - filemtime($otpFile) < 600) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Please wait ') . (600 - (time() - filemtime($otpFile))) . Lang::T(' seconds before sending another Email'));
} else {
$otp = rand(100000, 999999);
file_put_contents($otpFile, $otp);
file_put_contents($emailFile, $email);
// send OTP to user
$body = Lang::T("Hello") . ' ' . $user['fullname'] . ",\n\n" . Lang::T("Your Email Verification Code is:") . " $otp";
Message::sendEmail($email, Lang::T('Change Email Verification Code'), $body);
//redirect after sending OTP
r2(getUrl('accounts/email-update'), 'e', Lang::T('Verification code has been sent to your email. Check Spam folder if not found.'));
}
break;
case 'email-update-post':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$email = trim(_post('email'));
$otp_code = _post('otp');
$username = $user['username'];
$otpPath = $CACHE_PATH . '/email/';
// Validate the phone number format
if (!Validator::Email($email)) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Invalid Email address format'));
exit();
}
if (empty($config['smtp_host'])) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Email server not Available, Please ask admin to configure it'));
}
$otpFile = $otpPath . sha1($username . $db_pass) . ".txt";
$emailFile = $otpPath . sha1($username . $db_pass) . "_email.txt";
// Check if OTP file exists
if (!file_exists($otpFile)) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Please request OTP first'));
exit();
}
// expired 10 minutes
if (time() - filemtime($otpFile) > 1200) {
unlink($otpFile);
unlink($emailFile);
r2(getUrl('accounts/email-update'), 'e', Lang::T('Verification code expired'));
exit();
} else {
$code = file_get_contents($otpFile);
// Check if OTP code matches
if ($code != $otp_code) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('Wrong Verification code'));
exit();
}
// Check if the phone number matches the one that requested the OTP
$savedEmail = file_get_contents($emailFile);
if ($savedEmail !== $email) {
r2(getUrl('accounts/email-update'), 'e', Lang::T('The Email Address does not match the one that requested the OTP'));
exit();
}
// OTP verification successful, delete OTP and phone number files
unlink($otpFile);
unlink($emailFile);
}
$user->email = $email;
$user->save();
r2(getUrl('accounts/profile'), 's', Lang::T('Email Address updated successfully'));
break;
case 'language-update-post':
global $root_path;
$selected_language = _req('lang', 'english');
$_SESSION['user_language'] = $selected_language;
$lan_file = $root_path . File::pathFixer('system/lan/' . $selected_language . '.json');
if (file_exists($lan_file)) {
$_L = json_decode(file_get_contents($lan_file), true);
} else {
$_L['author'] = 'Auto Generated by iBNuX Script';
file_put_contents($lan_file, json_encode($_L));
}
User::setAttribute("Language", $selected_language);
r2($_SERVER['HTTP_REFERER'], 's', ucwords($selected_language));
break;
default:

View File

@ -5,8 +5,12 @@
* by https://t.me/ibnux
**/
if(Admin::getID()){
r2(U.'dashboard', "s", Lang::T("You are already logged in"));
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Expires: Tue, 01 Jan 2000 00:00:00 GMT");
header("Pragma: no-cache");
if (Admin::getID()) {
r2(getUrl('dashboard'), "s", Lang::T("You are already logged in"));
}
if (isset($routes['1'])) {
@ -19,6 +23,11 @@ switch ($do) {
case 'post':
$username = _post('username');
$password = _post('password');
//csrf token
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
_alert(Lang::T('Invalid or Expired CSRF Token') . ".", 'danger', "admin");
}
run_hook('admin_login'); #HOOK
if ($username != '' and $password != '') {
$d = ORM::for_table('tbl_users')->where('username', $username)->find_one();
@ -32,26 +41,28 @@ switch ($do) {
_log($username . ' ' . Lang::T('Login Successful'), $d['user_type'], $d['id']);
if ($isApi) {
if ($token) {
showResult(true, Lang::T('Login Successful'), ['token' => "a.".$token]);
showResult(true, Lang::T('Login Successful'), ['token' => "a." . $token]);
} else {
showResult(false, Lang::T('Invalid Username or Password'));
}
}
_alert(Lang::T('Login Successful'),'success', "dashboard");
_alert(Lang::T('Login Successful'), 'success', "dashboard");
} else {
_log($username . ' ' . Lang::T('Failed Login'), $d['user_type']);
_alert(Lang::T('Invalid Username or Password').".",'danger', "admin");
_alert(Lang::T('Invalid Username or Password') . ".", 'danger', "admin");
}
} else {
_alert(Lang::T('Invalid Username or Password')."..",'danger', "admin");
_alert(Lang::T('Invalid Username or Password') . "..", 'danger', "admin");
}
} else {
_alert(Lang::T('Invalid Username or Password')."...",'danger', "admin");
_alert(Lang::T('Invalid Username or Password') . "...", 'danger', "admin");
}
break;
default:
run_hook('view_login'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('admin-login.tpl');
break;
}

View File

@ -1,4 +1,5 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
@ -18,37 +19,83 @@ $ui->assign('_admin', $admin);
switch ($action) {
case 'pool':
$routers = _get('routers');
if(empty($routers)){
if (empty($routers)) {
$d = ORM::for_table('tbl_pool')->find_many();
}else{
} else {
$d = ORM::for_table('tbl_pool')->where('routers', $routers)->find_many();
}
$ui->assign('routers', $routers);
$ui->assign('d', $d);
$ui->display('autoload-pool.tpl');
break;
case 'bw_name':
$bw = ORM::for_table('tbl_bandwidth')->select("name_bw")->find_one($routes['2']);
echo $bw['name_bw'];
die();
case 'balance':
$balance = ORM::for_table('tbl_customers')->select("balance")->find_one($routes['2'])['balance'];
if ($routes['3'] == '1') {
echo Lang::moneyFormat($balance);
} else {
echo $balance;
}
die();
case 'server':
$d = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
$ui->assign('d', $d);
$ui->display('autoload-server.tpl');
break;
case 'pppoe_ip_used':
if (!empty(_get('ip'))) {
$cs = ORM::for_table('tbl_customers')
->select("username")
->where_not_equal('id', _get('id'))
->where("pppoe_ip", _get('ip'))
->findArray();
if (count($cs) > 0) {
$c = array_column($cs, 'username');
die(Lang::T("IP has been used by") . ' : ' . implode(", ", $c));
}
}
die();
case 'pppoe_username_used':
if (!empty(_get('u'))) {
$cs = ORM::for_table('tbl_customers')
->select("username")
->where_not_equal('id', _get('id'))
->where("pppoe_username", _get('u'))
->findArray();
if (count($cs) > 0) {
$c = array_column($cs, 'username');
die(Lang::T("Username has been used by") . ' : ' . implode(", ", $c));
}
}
die();
case 'plan':
$server = _post('server');
$jenis = _post('jenis');
if(in_array($admin['user_type'], array('SuperAdmin', 'Admin'))){
if($server=='radius'){
$d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->find_many();
}else{
$d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many();
if (in_array($admin['user_type'], array('SuperAdmin', 'Admin'))) {
switch ($server) {
case 'radius':
$d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->find_many();
break;
case '':
break;
default:
$d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many();
break;
}
}else{
if($server=='radius'){
$d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->where('enabled', '1')->find_many();
}else{
$d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->where('enabled', '1')->find_many();
} else {
switch ($server) {
case 'radius':
$d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->find_many();
break;
case '':
break;
default:
$d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many();
break;
}
}
$ui->assign('d', $d);
@ -56,15 +103,64 @@ switch ($action) {
$ui->display('autoload.tpl');
break;
case 'customer_is_active':
$d = ORM::for_table('tbl_user_recharges')->where('customer_id', $routes['2'])->findOne();
if ($d) {
if ($d['status'] == 'on') {
die('<span class="label label-success" title="Expired ' . Lang::dateAndTimeFormat($d['expiration'], $d['time']) . '">'.$d['namebp'].'</span>');
} else {
die('<span class="label label-danger" title="Expired ' . Lang::dateAndTimeFormat($d['expiration'], $d['time']) . '">'.$d['namebp'].'</span>');
if ($config['check_customer_online'] == 'yes') {
$c = ORM::for_table('tbl_customers')->where('username', $routes['2'])->find_one();
$p = ORM::for_table('tbl_plans')->find_one($routes['3']);
$dvc = Package::getDevice($p);
if ($_app_stage != 'Demo') {
if (file_exists($dvc)) {
require_once $dvc;
try {
//don't wait more than 5 seconds for response from device, otherwise we get timeout error.
ini_set('default_socket_timeout', 5);
if ((new $p['device'])->online_customer($c, $p['routers'])) {
echo '<span style="color: green;" title="online">&bull;</span>';
}else{
echo '<span style="color: yellow;" title="offline">&bull;</span>';
}
} catch (Exception $e) {
echo '<span style="color: red;" title="'.$e->getMessage().'">&bull;</span>';
}
}
}
}
break;
case 'plan_is_active':
$ds = ORM::for_table('tbl_user_recharges')->where('customer_id', $routes['2'])->find_array();
if ($ds) {
$ps = [];
$c = ORM::for_table('tbl_customers')->find_one($routes['2']);
foreach ($ds as $d) {
if ($d['status'] == 'on') {
if ($config['check_customer_online'] == 'yes') {
$p = ORM::for_table('tbl_plans')->find_one($d['plan_id']);
$dvc = Package::getDevice($p);
$status = "";
if ($_app_stage != 'Demo') {
if (file_exists($dvc)) {
require_once $dvc;
try {
//don't wait more than 5 seconds for response from device, otherwise we get timeout error.
ini_set('default_socket_timeout', 5);
if ((new $p['device'])->online_customer($c, $p['routers'])) {
$status = '<span style="color: green;" title="online">&bull;</span>';
}else{
$status = '<span style="color: yellow;" title="offline">&bull;</span>';
}
} catch (Exception $e) {
$status = '<span style="color: red;" title="'.$e->getMessage().'">&bull;</span>';
}
}
}
}
$ps[] = ('<span class="label label-primary m-1" title="Expired ' . Lang::dateAndTimeFormat($d['expiration'], $d['time']) . '">' . $d['namebp'] . ' ' . $status . '</span>');
} else {
$ps[] = ('<span class="label label-danger m-1" title="Expired ' . Lang::dateAndTimeFormat($d['expiration'], $d['time']) . '">' . $d['namebp'] . '</span>');
}
}
echo implode("<br>", $ps);
} else {
die('<span class="label label-danger">&bull;</span>');
die('');
}
break;
case 'customer_select2':

View File

@ -21,25 +21,63 @@ switch ($action) {
$p = ORM::for_table('tbl_plans')->find_one($bill['plan_id']);
$dvc = Package::getDevice($p);
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
if ((new $p['device'])->online_customer($user, $bill['routers'])) {
die('<a href="' . U . 'home&mikrotik=logout&id=' . $bill['id'] . '" onclick="return confirm(\'' . Lang::T('Disconnect Internet?') . '\')" class="btn btn-success btn-xs btn-block">' . Lang::T('You are Online, Logout?') . '</a>');
} else {
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
die('<a href="' . U . 'home&mikrotik=login&id=' . $bill['id'] . '" onclick="return confirm(\'' . Lang::T('Connect to Internet?') . '\')" class="btn btn-danger btn-xs btn-block">' . Lang::T('Not Online, Login now?') . '</a>');
try {
if (file_exists($dvc)) {
require_once $dvc;
if ((new $p['device'])->online_customer($user, $bill['routers'])) {
die('<a href="' . getUrl('home&mikrotik=logout&id=' . $bill['id']) . '" onclick="return confirm(\'' . Lang::T('Disconnect Internet?') . '\')" class="btn btn-success btn-xs btn-block">' . Lang::T('You are Online, Logout?') . '</a>');
} else {
die(Lang::T('-'));
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
die('<a href="' . getUrl('home&mikrotik=login&id=' . $bill['id']) . '" onclick="return confirm(\'' . Lang::T('Connect to Internet?') . '\')" class="btn btn-danger btn-xs btn-block">' . Lang::T('Not Online, Login now?') . '</a>');
} else {
die(Lang::T('-'));
}
}
} else {
die(Lang::T('-'));
}
} else {
new Exception(Lang::T("Devices Not Found"));
} catch (Exception $e) {
die(Lang::T('Failed to connect to device'));
}
}
die(Lang::T('-'));
} else {
die('--');
}
break;
case 'bw_name':
$bw = ORM::for_table('tbl_bandwidth')->select("name_bw")->find_one($routes['2']);
echo $bw['name_bw'];
die();
case 'inbox_unread':
$count = ORM::for_table('tbl_customers_inbox')->where('customer_id', $user['id'])->whereRaw('date_read is null')->count('id');
if ($count > 0) {
echo $count;
}
die();
case 'inbox':
$inboxs = ORM::for_table('tbl_customers_inbox')->selects(['id', 'subject', 'date_created'])->where('customer_id', $user['id'])->whereRaw('date_read is null')->order_by_desc('date_created')->limit(10)->find_many();
foreach ($inboxs as $inbox) {
echo '<li><a href="' . getUrl('mail/view/' . $inbox['id']) . '">' . $inbox['subject'] . '<br><sub class="text-muted">' . Lang::dateTimeFormat($inbox['date_created']) . '</sub></a></li>';
}
die();
case 'language':
$select = _get('select');
$folders = [];
$files = scandir('system/lan/');
foreach ($files as $file) {
if (is_file('system/lan/' . $file) && !in_array($file, ['index.html', 'country.json', '.DS_Store'])) {
$file = str_replace(".json", "", $file);
if(!empty($file)){
echo '<li><a href="' . getUrl('accounts/language-update-post&lang=' . $file) . '">';
if($select == $file){
echo '<span class="glyphicon glyphicon-ok"></span> ';
}
echo ucwords($file) . '</a></li>';
}
}
}
die();
default:
$ui->display('404.tpl');
}

View File

@ -13,12 +13,12 @@ $action = $routes['1'];
$ui->assign('_admin', $admin);
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
r2(U . "dashboard", 'e', Lang::T('You do not have permission to access this page'));
r2(getUrl('dashboard'), 'e', Lang::T('You do not have permission to access this page'));
}
switch ($action) {
case 'list':
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/bandwidth.js"></script>');
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/bandwidth.js"></script>');
run_hook('view_list_bandwidth'); #HOOK
$name = _post('name');
if ($name != '') {
@ -53,7 +53,7 @@ switch ($action) {
$ui->assign('d', $d);
$ui->display('bandwidth-edit.tpl');
} else {
r2(U . 'bandwidth/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('bandwidth/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -66,7 +66,7 @@ switch ($action) {
$d = ORM::for_table('tbl_bandwidth')->find_one($id);
if ($d) {
$d->delete();
r2(U . 'bandwidth/list', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('bandwidth/list'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -123,9 +123,9 @@ switch ($action) {
$d->burst = $burst;
$d->save();
r2(U . 'bandwidth/list', 's', Lang::T('Data Created Successfully'));
r2(getUrl('bandwidth/list'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'bandwidth/add', 'e', $msg);
r2(getUrl('bandwidth/add'), 'e', $msg);
}
break;
@ -179,9 +179,9 @@ switch ($action) {
$d->burst = $burst;
$d->save();
r2(U . 'bandwidth/list', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('bandwidth/list'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'bandwidth/edit/' . $id, 'e', $msg);
r2(getUrl('bandwidth/edit/') . $id, 'e', $msg);
}
break;

View File

@ -0,0 +1,315 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
* Coupons Controller by https://t.me/focuslinkstech
**/
_admin();
$ui->assign('_title', Lang::T('Coupons'));
$ui->assign('_system_menu', 'crm');
$action = $routes['1'];
$ui->assign('_admin', $admin);
switch ($action) {
case 'add':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]);
exit;
}
$ui->assign('_title', Lang::T('Add Coupon'));
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('coupons-add.tpl');
break;
case 'add-post':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]);
exit;
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$code = Text::alphanumeric(_post('code', ''));
$type = _post('type', '');
$value = floatval(_post('value', ''));
$description = _post('description', '');
$max_usage = _post('max_usage', '0');
$min_order_amount = _post('min_order_amount', '');
$max_discount_amount = intval(_post('max_discount_amount', ''));
$status = _post('status', 'active');
$start_date = strtotime(_post('start_date', '0000-00-00'));
$end_date = strtotime(_post('end_date', '0000-00-00'));
$error = [];
if (empty($code)) {
$error[] = Lang::T('Coupon Code is required');
}
if (empty($type)) {
$error[] = Lang::T('Coupon Type is required');
}
if (empty($value)) {
$error[] = Lang::T('Coupon Value is required');
}
if (empty($description)) {
$error[] = Lang::T('Coupon Description is required');
}
if ($max_usage < 0) {
$error[] = Lang::T('Coupon Maximum Usage must be greater than or equal to 0');
}
if (empty($min_order_amount)) {
$error[] = Lang::T('Coupon Minimum Order Amount is required');
}
if (empty($max_discount_amount)) {
$error[] = Lang::T('Coupon Maximum Discount Amount is required');
}
if (empty($status)) {
$error[] = Lang::T('Coupon Status is required');
}
if (empty($start_date)) {
$error[] = Lang::T('Coupon Start Date is required');
}
if (empty($end_date)) {
$error[] = Lang::T('Coupon End Date is required');
}
if (!empty($error)) {
r2(getUrl('coupons/add'), 'e', implode('<br>', $error));
exit;
}
//check if coupon code already exists
$coupon = ORM::for_table('tbl_coupons')->where('code', $code)->find_one();
if ($coupon) {
r2(getUrl('coupons/add'), 'e', Lang::T('Coupon Code already exists'));
exit;
}
$coupon = ORM::for_table('tbl_coupons')->create();
$coupon->code = $code;
$coupon->type = $type;
$coupon->value = $value;
$coupon->description = $description;
$coupon->max_usage = $max_usage;
$coupon->min_order_amount = $min_order_amount;
$coupon->max_discount_amount = $max_discount_amount;
$coupon->status = $status;
$coupon->start_date = date('Y-m-d', $start_date);
$coupon->end_date = date('Y-m-d', $end_date);
$coupon->created_at = date('Y-m-d H:i:s');
try {
$coupon->save();
r2(getUrl('coupons'), 's', Lang::T('Coupon has been added successfully'));
} catch (Exception $e) {
_log(Lang::T('Error adding coupon: ' . $e->getMessage()));
r2(getUrl('coupons/add'), 'e', Lang::T('Error adding coupon: ' . $e->getMessage()));
}
break;
case 'edit':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]);
exit;
}
$coupon_id = intval($routes['2']);
if (empty($coupon_id)) {
r2(getUrl('coupons'), 'e', Lang::T('Invalid Coupon ID'));
exit;
}
$coupon = ORM::for_table('tbl_coupons')->find_one($coupon_id);
if (!$coupon) {
r2(getUrl('coupons'), 'e', Lang::T('Coupon Not Found'));
exit;
}
$ui->assign('coupon', $coupon);
$ui->assign('_title', Lang::T('Edit Coupon: ' . $coupon['code']));
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('coupons-edit.tpl');
break;
case 'edit-post':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]);
exit;
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$code = Text::alphanumeric(_post('code', ''));
$type = _post('type', '');
$value = floatval(_post('value', ''));
$description = _post('description', '');
$max_usage = _post('max_usage', '');
$min_order_amount = _post('min_order_amount', '');
$max_discount_amount = intval(_post('max_discount_amount', ''));
$status = _post('status', 'active');
$start_date = strtotime(_post('start_date', '0000-00-00'));
$end_date = strtotime(_post('end_date', '0000-00-00'));
$error = [];
if (empty($code)) {
$error[] = Lang::T('Coupon code is required');
}
if (empty($type)) {
$error[] = Lang::T('Coupon type is required');
}
if (empty($value)) {
$error[] = Lang::T('Coupon value is required');
}
if (empty($description)) {
$error[] = Lang::T('Coupon description is required');
}
if ($max_usage < 0) {
$error[] = Lang::T('Coupon Maximum Usage must be greater than or equal to 0');
}
if (empty($min_order_amount)) {
$error[] = Lang::T('Coupon minimum order amount is required');
}
if (empty($max_discount_amount)) {
$error[] = Lang::T('Coupon maximum discount amount is required');
}
if (empty($status)) {
$error[] = Lang::T('Coupon status is required');
}
if (empty($start_date)) {
$error[] = Lang::T('Coupon start date is required');
}
if (empty($end_date)) {
$error[] = Lang::T('Coupon end date is required');
}
if (!empty($error)) {
r2(getUrl('coupons/edit/') . $coupon_id, 'e', implode('<br>', $error));
exit;
}
$coupon = ORM::for_table('tbl_coupons')->find_one($coupon_id);
$coupon->code = $code;
$coupon->type = $type;
$coupon->value = $value;
$coupon->description = $description;
$coupon->max_usage = $max_usage;
$coupon->min_order_amount = $min_order_amount;
$coupon->max_discount_amount = $max_discount_amount;
$coupon->status = $status;
$coupon->start_date = date('Y-m-d', $start_date);
$coupon->end_date = date('Y-m-d', $end_date);
$coupon->updated_at = date('Y-m-d H:i:s');
try {
$coupon->save();
r2(getUrl('coupons'), 's', Lang::T('Coupon has been updated successfully'));
} catch (Exception $e) {
_log(Lang::T('Error updating coupon: ') . $e->getMessage());
r2(getUrl('coupons/edit/') . $coupon_id, 'e', Lang::T('Error updating coupon: ') . $e->getMessage());
}
break;
case 'delete':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$couponIds = json_decode($_POST['couponIds'], true);
if (is_array($couponIds) && !empty($couponIds)) {
// Delete coupons from the database
ORM::for_table('tbl_coupons')
->where_in('id', $couponIds)
->delete_many();
// Return success response
echo json_encode(['status' => 'success', 'message' => Lang::T("Coupons Deleted Successfully.")]);
exit;
} else {
echo json_encode(['status' => 'error', 'message' => Lang::T("Invalid or missing coupon IDs.")]);
exit;
}
} else {
echo json_encode(['status' => 'error', 'message' => Lang::T("Invalid request method.")]);
}
break;
case 'status':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$couponId = $_GET['coupon_id'] ?? '';
$csrf_token = $_GET['csrf_token'] ?? '';
$status = $_GET['status'] ?? '';
if (empty($couponId) || empty($csrf_token) || !Csrf::check($csrf_token) || empty($status)) {
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Invalid request"));
exit;
}
$coupon = ORM::for_table('tbl_coupons')->where('id', $couponId)->find_one();
if (!$coupon) {
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon not found."));
exit;
}
$coupon->status = $status;
$coupon->save();
r2($_SERVER['HTTP_REFERER'], 's', Lang::T("Coupon status updated successfully."));
} else {
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Invalid request method"));
}
break;
default:
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]);
exit;
}
$ui->assign('_title', Lang::T('Coupons'));
$ui->assign('_system_menu', 'crm');
$search = _post('search');
$filter = _post('filter', 'none');
$couponsData = ORM::for_table('tbl_coupons')
->table_alias('c')
->select_many(
'c.id',
'c.code',
'c.type',
'c.value',
'c.description',
'c.max_usage',
'c.usage_count',
'c.status',
'c.min_order_amount',
'c.max_discount_amount',
'c.start_date',
'c.end_date',
'c.created_at',
'c.updated_at'
);
// Apply filters
if ($search != '') {
$searchLike = "%$search%";
$couponsData->whereRaw(
"code LIKE ? OR type LIKE ? OR value LIKE ? OR max_usage LIKE ? OR usage_count LIKE ? OR status LIKE ? OR min_order_amount LIKE ? OR max_discount_amount LIKE ?",
[$searchLike, $searchLike, $searchLike, $searchLike, $searchLike, $searchLike, $searchLike, $searchLike]
);
}
$couponsData->order_by_asc('c.id');
$coupons = Paginator::findMany($couponsData, ['search' => $search], 5, '');
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->assign('coupons', $coupons);
$ui->display('coupons.tpl');
break;
}

View File

@ -25,6 +25,10 @@ switch ($action) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('customers'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$cs = ORM::for_table('tbl_customers')
->select('tbl_customers.id', 'id')
@ -153,6 +157,7 @@ switch ($action) {
}
$ui->assign('xheader', $leafletpickerHeader);
run_hook('view_add_customer'); #HOOK
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('customers-add.tpl');
break;
case 'recharge':
@ -161,22 +166,39 @@ switch ($action) {
}
$id_customer = $routes['2'];
$plan_id = $routes['3'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('customers/view/') . $id_customer, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$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']);
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
} else {
$tax_rate = $tax_rate_setting;
}
if ($tax_enable === 'yes') {
$tax = Package::tax($plan['price'], $tax_rate);
} else {
$tax = 0;
}
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'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Customer not found'));
}
if (!$plan) {
r2(U . 'plan/recharge', 'e', Lang::T('Plan not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Plan not found'));
}
if ($cust['balance'] < ($plan['price'] + $add_cost)) {
r2(U . 'plan/recharge', 'e', Lang::T('insufficient balance'));
if ($cust['balance'] < ($plan['price'] + $add_cost + $tax)) {
r2(getUrl('plan/recharge'), 'e', Lang::T('insufficient balance'));
}
$gateway = 'Recharge Balance';
}
@ -189,7 +211,12 @@ switch ($action) {
if (count($usings) == 0) {
$usings[] = Lang::T('Cash');
}
$abills = User::getAttributes("Bill");
if ($tax_enable === 'yes') {
$ui->assign('tax', $tax);
}
$ui->assign('usings', $usings);
$ui->assign('abills', $abills);
$ui->assign('bills', $bills);
$ui->assign('add_cost', $add_cost);
$ui->assign('cust', $cust);
@ -197,9 +224,10 @@ switch ($action) {
$ui->assign('channel', $channel);
$ui->assign('server', $b['routers']);
$ui->assign('plan', $plan);
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('recharge-confirm.tpl');
} else {
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
r2(getUrl('customers/view/') . $id_customer, 'e', 'Cannot find active plan');
}
break;
case 'deactivate':
@ -208,6 +236,10 @@ switch ($action) {
}
$id_customer = $routes['2'];
$plan_id = $routes['3'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('customers/view/') . $id_customer, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$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();
@ -229,13 +261,17 @@ switch ($action) {
$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(getUrl('customers/view/') . $id_customer, 's', 'Success deactivate customer to Mikrotik');
}
}
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
r2(getUrl('customers/view/') . $id_customer, 'e', 'Cannot find active plan');
break;
case 'sync':
$id_customer = $routes['2'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('customers/view/') . $id_customer, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$bs = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('status', 'on')->findMany();
if ($bs) {
$routers = [];
@ -248,16 +284,37 @@ switch ($action) {
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
(new $p['device'])->add_customer($c, $p);
if (method_exists($dvc, 'sync_customer')) {
(new $p['device'])->sync_customer($c, $p);
}else{
(new $p['device'])->add_customer($c, $p);
}
} else {
new Exception(Lang::T("Devices Not Found"));
}
}
}
}
r2(U . 'customers/view/' . $id_customer, 's', 'Sync success to ' . implode(", ", $routers));
r2(getUrl('customers/view/') . $id_customer, 's', 'Sync success to ' . implode(", ", $routers));
}
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
r2(getUrl('customers/view/') . $id_customer, 'e', 'Cannot find active plan');
break;
case 'login':
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'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('customers/view/') . $id, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$customer = ORM::for_table('tbl_customers')->find_one($id);
if ($customer) {
$_SESSION['uid'] = $id;
User::setCookie($id);
_alert("You are logging in as $customer[fullname],<br>don't logout just close tab.", 'info', "home", 10);
}
_alert(Lang::T('Customer not found'), 'danger', "customers");
break;
case 'viewu':
$customer = ORM::for_table('tbl_customers')->where('username', $routes['2'])->find_one();
@ -268,8 +325,6 @@ switch ($action) {
$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'])
@ -278,28 +333,44 @@ switch ($action) {
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);
switch ($v) {
case 'order':
$v = 'order';
$query = ORM::for_table('tbl_payment_gateway')->where('user_id', $customer['id'])->order_by_desc('id');
$order = Paginator::findMany($query);
if (empty($order) || $order < 5) {
$query = ORM::for_table('tbl_payment_gateway')->where('username', $customer['username'])->order_by_desc('id');
$order = Paginator::findMany($query);
}
$ui->assign('order', $order);
break;
case 'activation':
$query = ORM::for_table('tbl_transactions')->where('user_id', $customer['id'])->order_by_desc('id');
$activation = Paginator::findMany($query);
if (empty($activation) || $activation < 5) {
$query = ORM::for_table('tbl_transactions')->where('username', $customer['username'])->order_by_desc('id');
$activation = Paginator::findMany($query);
}
$ui->assign('activation', $activation);
break;
}
$ui->assign('packages', User::_billing($customer['id']));
$ui->assign('v', $v);
$ui->assign('d', $customer);
$ui->assign('customFields', $customFields);
$ui->assign('xheader', $leafletpickerHeader);
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('customers-view.tpl');
} else {
r2(U . 'customers/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('customers/list'), 'e', Lang::T('Account Not Found'));
}
break;
case 'edit':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent'])) {
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'];
@ -310,13 +381,31 @@ switch ($action) {
->where('customer_id', $id)
->find_many();
if ($d) {
if (isset($routes['3']) && $routes['3'] == 'deletePhoto') {
if ($d['photo'] != '' && strpos($d['photo'], 'default') === false) {
if (file_exists($UPLOAD_PATH . $d['photo']) && strpos($d['photo'], 'default') === false) {
unlink($UPLOAD_PATH . $d['photo']);
if (file_exists($UPLOAD_PATH . $d['photo'] . '.thumb.jpg')) {
unlink($UPLOAD_PATH . $d['photo'] . '.thumb.jpg');
}
}
$d->photo = '/user.default.jpg';
$d->save();
$ui->assign('notify_t', 's');
$ui->assign('notify', 'You have successfully deleted the photo');
} else {
$ui->assign('notify_t', 'e');
$ui->assign('notify', 'No photo found to delete');
}
}
$ui->assign('d', $d);
$ui->assign('statuses', ORM::for_table('tbl_customers')->getEnum("status"));
$ui->assign('customFields', $customFields);
$ui->assign('xheader', $leafletpickerHeader);
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('customers-edit.tpl');
} else {
r2(U . 'customers/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('customers/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -325,6 +414,10 @@ switch ($action) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$id = $routes['2'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('customers/view/') . $id, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
run_hook('delete_customer'); #HOOK
$c = ORM::for_table('tbl_customers')->find_one($id);
if ($c) {
@ -355,15 +448,22 @@ switch ($action) {
$c->delete();
} catch (Exception $e) {
}
r2(U . 'customers/list', 's', Lang::T('User deleted Successfully'));
r2(getUrl('customers/list'), 's', Lang::T('User deleted Successfully'));
}
break;
case 'add-post':
$username = alphanumeric(_post('username'), "+_.@-");
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('customers/add'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$username = alphanumeric(_post('username'), ":+_.@-");
$fullname = _post('fullname');
$password = trim(_post('password'));
$pppoe_username = trim(_post('pppoe_username'));
$pppoe_password = trim(_post('pppoe_password'));
$pppoe_ip = trim(_post('pppoe_ip'));
$email = _post('email');
$address = _post('address');
$phonenumber = _post('phonenumber');
@ -399,7 +499,9 @@ switch ($action) {
$d = ORM::for_table('tbl_customers')->create();
$d->username = $username;
$d->password = $password;
$d->pppoe_username = $pppoe_username;
$d->pppoe_password = $pppoe_password;
$d->pppoe_ip = $pppoe_ip;
$d->email = $email;
$d->account_type = $account_type;
$d->fullname = $fullname;
@ -432,18 +534,66 @@ switch ($action) {
}
}
}
r2(U . 'customers/list', 's', Lang::T('Account Created Successfully'));
// Send welcome message
if (isset($_POST['send_welcome_message']) && $_POST['send_welcome_message'] == true) {
$welcomeMessage = Lang::getNotifText('welcome_message');
$welcomeMessage = str_replace('[[company]]', $config['CompanyName'], $welcomeMessage);
$welcomeMessage = str_replace('[[name]]', $d['fullname'], $welcomeMessage);
$welcomeMessage = str_replace('[[username]]', $d['username'], $welcomeMessage);
$welcomeMessage = str_replace('[[password]]', $d['password'], $welcomeMessage);
$welcomeMessage = str_replace('[[url]]', APP_URL . '/?_route=login', $welcomeMessage);
$emailSubject = "Welcome to " . $config['CompanyName'];
$channels = [
'sms' => [
'enabled' => isset($_POST['sms']),
'method' => 'sendSMS',
'args' => [$d['phonenumber'], $welcomeMessage]
],
'whatsapp' => [
'enabled' => isset($_POST['wa']),
'method' => 'sendWhatsapp',
'args' => [$d['phonenumber'], $welcomeMessage]
],
'email' => [
'enabled' => isset($_POST['mail']),
'method' => 'Message::sendEmail',
'args' => [$d['email'], $emailSubject, $welcomeMessage, $d['email']]
]
];
foreach ($channels as $channel => $message) {
if ($message['enabled']) {
try {
call_user_func_array($message['method'], $message['args']);
} catch (Exception $e) {
// Log the error and handle the failure
_log("Failed to send welcome message via $channel: " . $e->getMessage());
}
}
}
}
r2(getUrl('customers/list'), 's', Lang::T('Account Created Successfully'));
} else {
r2(U . 'customers/add', 'e', $msg);
r2(getUrl('customers/add'), 'e', $msg);
}
break;
case 'edit-post':
$username = alphanumeric(_post('username'), "+_.@-");
$id = _post('id');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('customers/edit/') . $id, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$username = alphanumeric(_post('username'), ":+_.@-");
$fullname = _post('fullname');
$account_type = _post('account_type');
$password = trim(_post('password'));
$pppoe_username = trim(_post('pppoe_username'));
$pppoe_password = trim(_post('pppoe_password'));
$pppoe_ip = trim(_post('pppoe_ip'));
$email = _post('email');
$address = _post('address');
$phonenumber = Lang::phoneFormat(_post('phonenumber'));
@ -464,7 +614,6 @@ switch ($action) {
$msg .= 'Full Name should be between 2 to 25 characters' . '<br>';
}
$id = _post('id');
$c = ORM::for_table('tbl_customers')->find_one($id);
if (!$c) {
@ -477,33 +626,100 @@ switch ($action) {
->find_many();
$oldusername = $c['username'];
$oldPppoePassword = $c['password'];
$oldPassPassword = $c['pppoe_password'];
$oldPppoeUsername = $c['pppoe_username'];
$oldPppoePassword = $c['pppoe_password'];
$oldPppoeIp = $c['pppoe_ip'];
$oldPassPassword = $c['password'];
$userDiff = false;
$pppoeDiff = false;
$passDiff = false;
$pppoeIpDiff = false;
if ($oldusername != $username) {
$cx = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
if ($cx) {
$msg .= Lang::T('Account already exist') . '<br>';
if (ORM::for_table('tbl_customers')->where('username', $username)->find_one()) {
$msg .= Lang::T('Username already used by another customer') . '<br>';
}
if (ORM::for_table('tbl_customers')->where('pppoe_username', $username)->find_one()) {
$msg .= Lang::T('Username already used by another pppoe username customer') . '<br>';
}
$userDiff = true;
}
if ($oldPppoePassword != $pppoe_password) {
if ($oldPppoeUsername != $pppoe_username) {
// if(!empty($pppoe_username)){
// if(ORM::for_table('tbl_customers')->where('pppoe_username', $pppoe_username)->find_one()){
// $msg.= Lang::T('PPPoE Username already used by another customer') . '<br>';
// }
// if(ORM::for_table('tbl_customers')->where('username', $pppoe_username)->find_one()){
// $msg.= Lang::T('PPPoE Username already used by another customer') . '<br>';
// }
// }
$pppoeDiff = true;
}
if ($oldPppoeIp != $pppoe_ip) {
$pppoeIpDiff = true;
}
if ($password != '' && $oldPassPassword != $password) {
$passDiff = true;
}
if ($msg == '') {
if (!empty($_FILES['photo']['name']) && file_exists($_FILES['photo']['tmp_name'])) {
if (function_exists('imagecreatetruecolor')) {
$hash = md5_file($_FILES['photo']['tmp_name']);
$subfolder = substr($hash, 0, 2);
$folder = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'photos' . DIRECTORY_SEPARATOR;
if (!file_exists($folder)) {
mkdir($folder);
}
$folder = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'photos' . DIRECTORY_SEPARATOR . $subfolder . DIRECTORY_SEPARATOR;
if (!file_exists($folder)) {
mkdir($folder);
}
$imgPath = $folder . $hash . '.jpg';
if (!file_exists($imgPath)) {
File::resizeCropImage($_FILES['photo']['tmp_name'], $imgPath, 1600, 1600, 100);
}
if (!file_exists($imgPath . '.thumb.jpg')) {
if (_post('faceDetect') == 'yes') {
try {
$detector = new svay\FaceDetector();
$detector->setTimeout(5000);
$detector->faceDetect($imgPath);
$detector->cropFaceToJpeg($imgPath . '.thumb.jpg', false);
} catch (Exception $e) {
File::makeThumb($imgPath, $imgPath . '.thumb.jpg', 200);
} catch (Throwable $e) {
File::makeThumb($imgPath, $imgPath . '.thumb.jpg', 200);
}
} else {
File::makeThumb($imgPath, $imgPath . '.thumb.jpg', 200);
}
}
if (file_exists($imgPath)) {
if ($c['photo'] != '' && strpos($c['photo'], 'default') === false) {
if (file_exists($UPLOAD_PATH . $c['photo'])) {
unlink($UPLOAD_PATH . $c['photo']);
if (file_exists($UPLOAD_PATH . $c['photo'] . '.thumb.jpg')) {
unlink($UPLOAD_PATH . $c['photo'] . '.thumb.jpg');
}
}
}
$c->photo = '/photos/' . $subfolder . '/' . $hash . '.jpg';
}
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
} else {
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
}
if ($userDiff) {
$c->username = $username;
}
if ($password != '') {
$c->password = $password;
}
$c->pppoe_username = $pppoe_username;
$c->pppoe_password = $pppoe_password;
$c->pppoe_ip = $pppoe_ip;
$c->fullname = $fullname;
$c->email = $email;
$c->account_type = $account_type;
@ -564,11 +780,9 @@ switch ($action) {
}
}
if ($userDiff || $pppoeDiff || $passDiff) {
if ($userDiff || $pppoeDiff || $pppoeIpDiff || $passDiff) {
$turs = ORM::for_table('tbl_user_recharges')->where('customer_id', $c['id'])->findMany();
foreach ($turs as $tur) {
$tur->username = $username;
$tur->save();
$p = ORM::for_table('tbl_plans')->find_one($tur['plan_id']);
$dvc = Package::getDevice($p);
if ($_app_stage != 'demo') {
@ -579,26 +793,40 @@ switch ($action) {
if ($userDiff) {
(new $p['device'])->change_username($p, $oldusername, $username);
}
if ($pppoeDiff && $tur['type'] == 'PPPOE') {
if (empty($oldPppoeUsername) && !empty($pppoe_username)) {
// admin just add pppoe username
(new $p['device'])->change_username($p, $username, $pppoe_username);
} else if (empty($pppoe_username) && !empty($oldPppoeUsername)) {
// admin want to use customer username
(new $p['device'])->change_username($p, $oldPppoeUsername, $username);
} else {
// regular change pppoe username
(new $p['device'])->change_username($p, $oldPppoeUsername, $pppoe_username);
}
}
(new $p['device'])->add_customer($c, $p);
} else {
new Exception(Lang::T("Devices Not Found"));
}
}
}
$tur->username = $username;
$tur->save();
}
}
r2(U . 'customers/view/' . $id, 's', 'User Updated Successfully');
r2(getUrl('customers/view/') . $id, 's', 'User Updated Successfully');
} else {
r2(U . 'customers/edit/' . $id, 'e', $msg);
r2(getUrl('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');
$search = _req('search');
$order = _req('order', 'username');
$filter = _req('filter', 'Active');
$orderby = _req('orderby', 'asc');
$order_pos = [
'username' => 0,
'created_at' => 8,
@ -606,6 +834,8 @@ switch ($action) {
'status' => 7
];
$append_url = "&order=" . urlencode($order) . "&filter=" . urlencode($filter) . "&orderby=" . urlencode($orderby);
if ($search != '') {
$query = ORM::for_table('tbl_customers')
->whereRaw("username LIKE '%$search%' OR fullname LIKE '%$search%' OR address LIKE '%$search%' " .
@ -614,13 +844,21 @@ switch ($action) {
$query = ORM::for_table('tbl_customers');
$query->where("status", $filter);
}
if ($orderby == 'asc') {
$query->order_by_asc($order);
if ($order == 'lastname') {
$query->order_by_expr("SUBSTR(fullname, INSTR(fullname, ' ')) $orderby");
} else {
$query->order_by_desc($order);
if ($orderby == 'asc') {
$query->order_by_asc($order);
} else {
$query->order_by_desc($order);
}
}
$d = $query->findMany();
if (_post('export', '') == 'csv') {
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('customers'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$d = $query->findMany();
$h = false;
set_time_limit(-1);
header('Pragma: public');
@ -661,7 +899,7 @@ switch ($action) {
fclose($fp);
die();
}
$ui->assign('xheader', '<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">');
$d = Paginator::findMany($query, ['search' => $search], 30, $append_url);
$ui->assign('d', $d);
$ui->assign('statuses', ORM::for_table('tbl_customers')->getEnum("status"));
$ui->assign('filter', $filter);
@ -669,6 +907,7 @@ switch ($action) {
$ui->assign('order', $order);
$ui->assign('order_pos', $order_pos[$order]);
$ui->assign('orderby', $orderby);
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('customers.tpl');
break;
}

View File

@ -0,0 +1,53 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
_admin();
$ui->assign('_title', Lang::T('Custom Fields'));
$ui->assign('_system_menu', 'settings');
$action = $routes['1'];
$ui->assign('_admin', $admin);
$fieldPath = $UPLOAD_PATH . DIRECTORY_SEPARATOR . "customer_field.json";
switch ($action) {
case 'save':
print_r($_POST);
$datas = [];
$count = count($_POST['name']);
for($n=0;$n<$count;$n++){
if(!empty($_POST['name'][$n])){
$datas[] = [
'order' => $_POST['order'][$n],
'name' => Text::alphanumeric(strtolower(str_replace(" ", "_", $_POST['name'][$n])), "_"),
'type' => $_POST['type'][$n],
'placeholder' => $_POST['placeholder'][$n],
'value' => $_POST['value'][$n],
'register' => $_POST['register'][$n],
'required' => $_POST['required'][$n]
];
}
}
if(count($datas)>1){
usort($datas, function ($item1, $item2) {
return $item1['order'] <=> $item2['order'];
});
}
if(file_put_contents($fieldPath, json_encode($datas))){
r2(getUrl('customfield'), 's', 'Successfully saved custom fields!');
}else{
r2(getUrl('customfield'), 'e', 'Failed to save custom fields!');
}
default:
$fields = [];
if(file_exists($fieldPath)){
$fields = json_decode(file_get_contents($fieldPath), true);
}
$ui->assign('fields', $fields);
$ui->display('customfield.tpl');
break;
}

View File

@ -17,7 +17,7 @@ if (isset($_GET['refresh'])) {
unlink($CACHE_PATH . DIRECTORY_SEPARATOR . $file);
}
}
r2(U . 'dashboard', 's', 'Data Refreshed');
r2(getUrl('dashboard'), 's', 'Data Refreshed');
}
$reset_day = $config['reset_day'];
@ -55,6 +55,11 @@ if ($imonth == '') {
}
$ui->assign('imonth', $imonth);
if ($config['enable_balance'] == 'yes'){
$cb = ORM::for_table('tbl_customers')->whereGte('balance', 0)->sum('balance');
$ui->assign('cb', $cb);
}
$u_act = ORM::for_table('tbl_user_recharges')->where('status', 'on')->count();
if (empty($u_act)) {
$u_act = '0';
@ -204,6 +209,17 @@ if (file_exists($cacheMSfile) && time() - filemtime($cacheMSfile) < 43200) {
file_put_contents($cacheMSfile, json_encode($monthlySales));
}
if ($config['router_check']) {
$routeroffs = ORM::for_table('tbl_routers')->selects(['id', 'name', 'last_seen'])->where('status', 'Offline')->where('enabled', '1')->order_by_desc('name')->find_array();
$ui->assign('routeroffs', $routeroffs);
}
$timestampFile = "$UPLOAD_PATH/cron_last_run.txt";
if (file_exists($timestampFile)) {
$lastRunTime = file_get_contents($timestampFile);
$ui->assign('run_date', date('Y-m-d h:i:s A', $lastRunTime));
}
// Assign the monthly sales data to Smarty
$ui->assign('start_date', $start_date);
$ui->assign('current_date', $current_date);

View File

@ -5,9 +5,9 @@
**/
if(Admin::getID()){
r2(U.'dashboard');
r2(getUrl('dashboard'));
}if(User::getID()){
r2(U.'home');
r2(getUrl('home'));
}else{
r2(U.'login');
r2(getUrl('login'));
}

View File

@ -0,0 +1,169 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
$step = _req('step', 0);
$otpPath = $CACHE_PATH . File::pathFixer('/forgot/');
if ($step == '-1') {
$_COOKIE['forgot_username'] = '';
setcookie('forgot_username', '', time() - 3600, '/');
$step = 0;
}
if (!empty($_COOKIE['forgot_username']) && in_array($step, [0, 1])) {
$step = 1;
$_POST['username'] = $_COOKIE['forgot_username'];
}
if ($step == 1) {
$username = _post('username');
if (!empty($username)) {
$ui->assign('username', $username);
if (!file_exists($otpPath)) {
mkdir($otpPath);
}
setcookie('forgot_username', $username, time() + 3600, '/');
$user = ORM::for_table('tbl_customers')->selects(['phonenumber', 'email'])->where('username', $username)->find_one();
if ($user) {
$otpPath .= sha1($username . $db_pass) . ".txt";
if (file_exists($otpPath) && time() - filemtime($otpPath) < 600) {
$sec = time() - filemtime($otpPath);
$ui->assign('notify_t', 's');
$ui->assign('notify', Lang::T("Verification Code already Sent to Your Phone/Email/Whatsapp, please wait")." $sec seconds.");
} else {
$via = $config['user_notification_reminder'];
if ($via == 'email') {
$via = 'sms';
}
$otp = mt_rand(100000, 999999);
file_put_contents($otpPath, $otp);
if ($via == 'sms') {
Message::sendSMS($user['phonenumber'], $config['CompanyName'] . " C0de: $otp");
} else {
Message::sendWhatsapp($user['phonenumber'], $config['CompanyName'] . " C0de: $otp");
}
Message::sendEmail(
$user['email'],
$config['CompanyName'] . Lang::T("Your Verification Code") . ' : ' . $otp,
Lang::T("Your Verification Code") . ' : <b>' . $otp . '</b>'
);
$ui->assign('notify_t', 's');
$ui->assign('notify', Lang::T("If your Username is found, Verification Code has been Sent to Your Phone/Email/Whatsapp"));
}
} else {
// Username not found
$ui->assign('notify_t', 's');
$ui->assign('notify', Lang::T("If your Username is found, Verification Code has been Sent to Your Phone/Email/Whatsapp") . ".");
}
} else {
$step = 0;
}
} else if ($step == 2) {
$username = _post('username');
$otp_code = _post('otp_code');
if (!empty($username) && !empty($otp_code)) {
$otpPath .= sha1($username . $db_pass) . ".txt";
if (file_exists($otpPath) && time() - filemtime($otpPath) <= 600) {
$otp = file_get_contents($otpPath);
if ($otp == $otp_code) {
$pass = mt_rand(10000, 99999);
$user = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
$user->password = $pass;
$user->save();
$ui->assign('username', $username);
$ui->assign('passsword', $pass);
$ui->assign('notify_t', 's');
$ui->assign('notify', Lang::T("Verification Code Valid"));
if (file_exists($otpPath)) {
unlink($otpPath);
}
setcookie('forgot_username', '', time() - 3600, '/');
} else {
r2(getUrl('forgot&step=1'), 'e', Lang::T('Invalid Username or Verification Code'));
}
} else {
if (file_exists($otpPath)) {
unlink($otpPath);
}
r2(getUrl('forgot&step=1'), 'e', Lang::T('Invalid Username or Verification Code'));
}
} else {
r2(getUrl('forgot&step=1'), 'e', Lang::T('Invalid Username or Verification Code'));
}
} else if ($step == 7) {
$find = _post('find');
$step = 6;
if (!empty($find)) {
$via = $config['user_notification_reminder'];
if ($via == 'email') {
$via = 'sms';
}
if (!file_exists($otpPath)) {
mkdir($otpPath);
}
$otpPath .= sha1($find . $db_pass) . ".txt";
$users = ORM::for_table('tbl_customers')->selects(['username', 'phonenumber', 'email'])->where('phonenumber', $find)->find_array();
if ($users) {
// prevent flooding only can request every 10 minutes
if (!file_exists($otpPath) || (file_exists($otpPath) && time() - filemtime($otpPath) >= 600)) {
$usernames = implode(", ", array_column($users, 'username'));
if ($via == 'sms') {
Message::sendSMS($find, Lang::T("Your username for") . ' ' . $config['CompanyName'] . "\n" . $usernames);
} else {
Message::sendWhatsapp($find, Lang::T("Your username for") . ' ' . $config['CompanyName'] . "\n" . $usernames);
}
file_put_contents($otpPath, time());
}
$ui->assign('notify_t', 's');
$ui->assign('notify', Lang::T("Usernames have been sent to your phone/Whatsapp") . " $find");
$step = 0;
} else {
$users = ORM::for_table('tbl_customers')->selects(['username', 'phonenumber', 'email'])->where('email', $find)->find_array();
if ($users) {
// prevent flooding only can request every 10 minutes
if (!file_exists($otpPath) || (file_exists($otpPath) && time() - filemtime($otpPath) >= 600)) {
$usernames = implode(", ", array_column($users, 'username'));
$phones = [];
foreach ($users as $user) {
if (!in_array($user['phonenumber'], $phones)) {
if ($via == 'sms') {
Message::sendSMS($user['phonenumber'], Lang::T("Your username for") . ' ' . $config['CompanyName'] . "\n" . $usernames);
} else {
Message::sendWhatsapp($user['phonenumber'], Lang::T("Your username for") . ' ' . $config['CompanyName'] . "\n" . $usernames);
}
$phones[] = $user['phonenumber'];
}
}
Message::sendEmail(
$user['email'],
Lang::T("Your username for") . ' ' . $config['CompanyName'],
Lang::T("Your username for") . ' ' . $config['CompanyName'] . "\n" . $usernames
);
file_put_contents($otpPath, time());
}
$ui->assign('notify_t', 's');
$ui->assign('notify', Lang::T("Usernames have been sent to your phone/Whatsapp/Email"));
$step = 0;
} else {
$ui->assign('notify_t', 'e');
$ui->assign('notify', Lang::T("No data found"));
}
}
}
}
// delete old files
$pth = $CACHE_PATH . File::pathFixer('/forgot/');
$fs = scandir($pth);
foreach ($fs as $file) {
if(is_file($pth.$file) && time() - filemtime($pth.$file) > 3600) {
unlink($pth.$file);
}
}
$ui->assign('step', $step);
$ui->assign('_title', Lang::T('Forgot Password'));
$ui->display('customer/forgot.tpl');

View File

@ -23,18 +23,18 @@ if (_post('send') == 'balance') {
}
$target = ORM::for_table('tbl_customers')->where('username', _post('username'))->find_one();
if (!$target) {
r2(U . 'home', 'd', Lang::T('Username not found'));
r2(getUrl('home'), 'd', Lang::T('Username not found'));
}
$username = _post('username');
$balance = _post('balance');
if ($user['balance'] < $balance) {
r2(U . 'home', 'd', Lang::T('insufficient balance'));
r2(getUrl('home'), 'd', Lang::T('insufficient balance'));
}
if (!empty($config['minimum_transfer']) && intval($balance) < intval($config['minimum_transfer'])) {
r2(U . 'home', 'd', Lang::T('Minimum Transfer') . ' ' . Lang::moneyFormat($config['minimum_transfer']));
r2(getUrl('home'), 'd', Lang::T('Minimum Transfer') . ' ' . Lang::moneyFormat($config['minimum_transfer']));
}
if ($user['username'] == $target['username']) {
r2(U . 'home', 'd', Lang::T('Cannot send to yourself'));
r2(getUrl('home'), 'd', Lang::T('Cannot send to yourself'));
}
if (Balance::transfer($user['id'], $username, $balance)) {
//sender
@ -71,13 +71,14 @@ if (_post('send') == 'balance') {
$d->pg_url_payment = 'balance';
$d->status = 2;
$d->save();
Message::sendBalanceNotification($user, $balance, ($user['balance'] - $balance), Lang::getNotifText('balance_send'), $config['user_notification_payment']);
Message::sendBalanceNotification($target, $balance, ($target['balance'] + $balance), Lang::getNotifText('balance_received'), $config['user_notification_payment']);
//
Message::sendBalanceNotification($user, $target, $balance, ($user['balance'] - $balance), Lang::getNotifText('balance_send'), $config['user_notification_payment']);
Message::sendBalanceNotification($target, $user, $balance, ($target['balance'] + $balance), Lang::getNotifText('balance_received'), $config['user_notification_payment']);
Message::sendTelegram("#u$user[username] send balance to #u$target[username] \n" . Lang::moneyFormat($balance));
r2(U . 'home', 's', Lang::T('Sending balance success'));
r2(getUrl('home'), 's', Lang::T('Sending balance success'));
}
} else {
r2(U . 'home', 'd', Lang::T('Failed, balance is not available'));
r2(getUrl('home'), 'd', Lang::T('Failed, balance is not available'));
}
} else if (_post('send') == 'plan') {
if ($user['status'] != 'Active') {
@ -89,20 +90,53 @@ if (_post('send') == 'balance') {
foreach ($actives as $active) {
$router = ORM::for_table('tbl_routers')->where('name', $active['routers'])->find_one();
if ($router) {
r2(U . "order/send/$router[id]/$active[plan_id]&u=" . trim(_post('username')), 's', Lang::T('Review package before recharge'));
r2(getUrl('order/send/$router[id]/$active[plan_id]&u=') . trim(_post('username')), 's', Lang::T('Review package before recharge'));
}
}
r2(U . 'home', 'w', Lang::T('Your friend do not have active package'));
r2(getUrl('home'), 'w', Lang::T('Your friend do not have active package'));
}
$_bill = User::_billing();
$ui->assign('_bills', $_bill);
$ui->assign('_bills', User::_billing());
// Sync plan to router
if (isset($_GET['sync']) && !empty($_GET['sync'])) {
foreach ($_bill as $tur) {
if ($tur['status'] == 'on') {
$p = ORM::for_table('tbl_plans')->findOne($tur['plan_id']);
if ($p) {
$c = ORM::for_table('tbl_customers')->findOne($tur['customer_id']);
if ($c) {
$dvc = Package::getDevice($p);
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
if (method_exists($dvc, 'sync_customer')) {
(new $p['device'])->sync_customer($c, $p);
}else{
(new $p['device'])->add_customer($c, $p);
}
} else {
new Exception(Lang::T("Devices Not Found"));
}
}
$log .= "DONE : $ptur[namebp], $tur[type], $tur[routers]<br>";
} else {
$log .= "Customer NOT FOUND : $tur[namebp], $tur[type], $tur[routers]<br>";
}
} else {
$log .= "PLAN NOT FOUND : $tur[namebp], $tur[type], $tur[routers]<br>";
}
}
}
r2(getUrl('home'), 's', $log);
}
if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
if ($user['status'] != 'Active') {
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
}
if (!empty(App::getTokenValue(_get('stoken')))) {
r2(U . "voucher/invoice/");
r2(getUrl('voucher/invoice/'));
die();
}
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['recharge'])->where('username', $user['username'])->findOne();
@ -113,29 +147,17 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
$routers = ORM::for_table('tbl_routers')->where('name', $bill['routers'])->find_one();
$router = $routers['id'];
}
if ($config['enable_balance'] == 'yes') {
$plan = ORM::for_table('tbl_plans')->find_one($bill['plan_id']);
if (!$plan['enabled']) {
r2(U . "home", 'e', 'Plan is not exists');
}
if ($user['balance'] > $plan['price']) {
r2(U . "order/pay/$router/$bill[plan_id]&stoken=" . _get('stoken'), 'e', 'Order Plan');
} else {
r2(U . "order/buy/$router/$bill[plan_id]", 'e', 'Order Plan');
}
} else {
r2(U . "order/buy/$router/$bill[plan_id]", 'e', 'Order Plan');
}
r2(getUrl('order/gateway/$router/$bill[plan_id]'));
}
} else if (!empty(_get('extend'))) {
if ($user['status'] != 'Active') {
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
}
if (!$config['extend_expired']) {
r2(U . 'home', 'e', "cannot extend");
r2(getUrl('home'), 'e', "cannot extend");
}
if (!empty(App::getTokenValue(_get('stoken')))) {
r2(U . 'home', 'e', "You already extend");
r2(getUrl('home'), 'e', "You already extend");
}
$id = _get('extend');
$tur = ORM::for_table('tbl_user_recharges')->where('customer_id', $user['id'])->where('id', $id)->find_one();
@ -150,7 +172,7 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
// is already extend
$last = file_get_contents($path);
if ($last == $m) {
r2(U . 'home', 'e', "You already extend for this month");
r2(getUrl('home'), 'e', "You already extend for this month");
}
}
if ($tur['status'] != 'on') {
@ -159,6 +181,8 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
global $isChangePlan;
$isChangePlan = true;
(new $p['device'])->add_customer($user, $p);
} else {
new Exception(Lang::T("Devices Not Found"));
@ -178,12 +202,12 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
"\nLocation: " . $p['routers'] .
"\nCustomer: " . $user['fullname'] .
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
r2(U . 'home', 's', "Extend until $expiration");
r2(getUrl('home'), 's', "Extend until $expiration");
} else {
r2(U . 'home', 'e', "Plan is not expired");
r2(getUrl('home'), 'e', "Plan is not expired");
}
} else {
r2(U . 'home', 'e', "Plan Not Found or Not Active");
r2(getUrl('home'), 'e', "Plan Not Found or Not Active");
}
} else if (isset($_GET['deactivate']) && !empty($_GET['deactivate'])) {
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['deactivate'])->where('username', $user['username'])->findOne();
@ -204,9 +228,9 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
$bill->save();
_log('User ' . $bill['username'] . ' Deactivate ' . $bill['namebp'], 'Customer', $bill['customer_id']);
Message::sendTelegram('User u' . $bill['username'] . ' Deactivate ' . $bill['namebp']);
r2(U . 'home', 's', 'Success deactivate ' . $bill['namebp']);
r2(getUrl('home'), 's', 'Success deactivate ' . $bill['namebp']);
} else {
r2(U . 'home', 'e', 'No Active Plan');
r2(getUrl('home'), 'e', 'No Active Plan');
}
}
@ -221,10 +245,10 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && $_c['hs_auth_m
require_once $dvc;
if ($_GET['mikrotik'] == 'login') {
(new $p['device'])->connect_customer($user, $_SESSION['nux-ip'], $_SESSION['nux-mac'], $bill['routers']);
r2(U . 'home', 's', Lang::T('Login Request successfully'));
r2(getUrl('home'), 's', Lang::T('Login Request successfully'));
} else if ($_GET['mikrotik'] == 'logout') {
(new $p['device'])->disconnect_customer($user, $bill['routers']);
r2(U . 'home', 's', Lang::T('Logout Request successfully'));
r2(getUrl('home'), 's', Lang::T('Logout Request successfully'));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -233,7 +257,7 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && $_c['hs_auth_m
}
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSION['nux-hostname']) && $_c['hs_auth_method'] == 'hchap')) {
$apkurl = (((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'onoff')|| $_SERVER['SERVER_PORT'] == 443)?'https':'http').'://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$apkurl = (((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'onoff') || $_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$ui->assign('nux_mac', $_SESSION['nux-mac']);
$ui->assign('nux_ip', $_SESSION['nux-ip']);
$keys = explode('-', $_SESSION['nux-key']);
@ -244,23 +268,23 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSI
$ui->assign('hchap', $_GET['hchap']);
$ui->assign('logged', $_GET['logged']);
if ($_app_stage != 'demo') {
if ($_GET['mikrotik'] == 'login') {
r2(U . 'home&hchap=true', 's', Lang::T('Login Request successfully'));
}
$getmsg = $_GET['msg'];
///get auth notification from mikrotik
if($getmsg == 'Connected') {
$msg .= Lang::T($getmsg);
r2(U . 'home&logged=1', 's', $msg);
} else if($getmsg){
$msg .= Lang::T($getmsg);
r2(U . 'home', 's', $msg);
}
if ($_GET['mikrotik'] == 'login') {
r2(getUrl('home&hchap=true'), 's', Lang::T('Login Request successfully'));
}
$getmsg = $_GET['msg'];
///get auth notification from mikrotik
if ($getmsg == 'Connected') {
$msg .= Lang::T($getmsg);
r2(getUrl('home&logged=1'), 's', $msg);
} else if ($getmsg) {
$msg .= Lang::T($getmsg);
r2(getUrl('home'), 's', $msg);
}
}
}
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSION['nux-hostname']) && $_c['hs_auth_method'] == 'hchap')) {
$apkurl = (((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'onoff')|| $_SERVER['SERVER_PORT'] == 443)?'https':'http').'://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$apkurl = (((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'onoff') || $_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$ui->assign('nux_mac', $_SESSION['nux-mac']);
$ui->assign('nux_ip', $_SESSION['nux-ip']);
$keys = explode('-', $_SESSION['nux-key']);
@ -271,29 +295,61 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSI
$ui->assign('hchap', $_GET['hchap']);
$ui->assign('logged', $_GET['logged']);
if ($_app_stage != 'demo') {
if ($_GET['mikrotik'] == 'login') {
r2(U . 'home&hchap=true', 's', Lang::T('Login Request successfully'));
}
$getmsg = $_GET['msg'];
///get auth notification from mikrotik
if($getmsg == 'Connected') {
$msg .= Lang::T($getmsg);
r2(U . 'home&logged=1', 's', $msg);
} else if($getmsg){
$msg .= Lang::T($getmsg);
r2(U . 'home', 's', $msg);
}
if ($_GET['mikrotik'] == 'login') {
r2(getUrl('home&hchap=true'), 's', Lang::T('Login Request successfully'));
}
$getmsg = $_GET['msg'];
///get auth notification from mikrotik
if ($getmsg == 'Connected') {
$msg .= Lang::T($getmsg);
r2(getUrl('home&logged=1'), 's', $msg);
} else if ($getmsg) {
$msg .= Lang::T($getmsg);
r2(getUrl('home'), 's', $msg);
}
}
}
$ui->assign('unpaid', ORM::for_table('tbl_payment_gateway')
$tcf = ORM::for_table('tbl_customers_fields')
->where('customer_id', $user['id'])
->find_many();
$vpn = ORM::for_table('tbl_port_pool')
->find_one();
$ui->assign('cf', $tcf);
$ui->assign('vpn', $vpn);
$unpaid = ORM::for_table('tbl_payment_gateway')
->where('username', $user['username'])
->where('status', 1)
->find_one());
->find_one();
// check expired payments
if ($unpaid) {
try {
if (strtotime($unpaid['expired_date']) < time()) {
$unpaid->status = 4;
$unpaid->save();
$unpaid = [];
}
} catch (Throwable $e) {
} catch (Exception $e) {
}
try {
if (strtotime($unpaid['created_date'], "+24 HOUR") < time()) {
$unpaid->status = 4;
$unpaid->save();
$unpaid = [];
}
} catch (Throwable $e) {
} catch (Exception $e) {
}
}
$ui->assign('unpaid', $unpaid);
$ui->assign('code', alphanumeric(_get('code'), "-"));
$abills = User::getAttributes("Bill");
$ui->assign('abills', $abills);
run_hook('view_customer_dashboard'); #HOOK
$ui->display('user-dashboard.tpl');
$ui->display('customer/dashboard.tpl');

View File

@ -11,7 +11,7 @@ if ($maintenance_mode == true) {
}
if (User::getID()) {
r2(U . 'home');
r2(getUrl('home'));
}
if (isset($routes['1'])) {
@ -24,6 +24,11 @@ switch ($do) {
case 'post':
$username = _post('username');
$password = _post('password');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
_msglog('e', Lang::T('Invalid or Expired CSRF Token'));
r2(getUrl('login'));
}
run_hook('customer_login'); #HOOK
if ($username != '' and $password != '') {
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
@ -34,30 +39,42 @@ switch ($do) {
}
if (Password::_uverify($password, $d_pass) == true) {
$_SESSION['uid'] = $d['id'];
User::setCookie($d['id']);
$token = User::setCookie($d['id']);
$d->last_login = date('Y-m-d H:i:s');
$d->save();
_log($username . ' ' . Lang::T('Login Successful'), 'User', $d['id']);
if ($isApi) {
if ($token) {
showResult(true, Lang::T('Login Successful'), ['token' => "u." . $token]);
} else {
showResult(false, Lang::T('Invalid Username or Password'));
}
}
_alert(Lang::T('Login Successful'), 'success', "home");
} else {
_msglog('e', Lang::T('Invalid Username or Password'));
_log($username . ' ' . Lang::T('Failed Login'), 'User');
r2(U . 'login');
r2(getUrl('login'));
}
} else {
_msglog('e', Lang::T('Invalid Username or Password'));
r2(U . 'login');
r2(getUrl('login'));
}
} else {
_msglog('e', Lang::T('Invalid Username or Password'));
r2(U . 'login');
r2(getUrl('login'));
}
break;
case 'activation':
if (!empty(_post('voucher_only'))) {
$voucher = _post('voucher_only');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
_msglog('e', Lang::T('Invalid or Expired CSRF Token'));
r2(getUrl('login'));
}
$voucher = Text::alphanumeric(_post('voucher_only'), "-_.,");
$tur = ORM::for_table('tbl_user_recharges')
->where('username', $voucher)
->where('customer_id', '0') // Voucher Only will make customer ID as 0
@ -86,7 +103,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -95,13 +112,13 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
_alert(Lang::T("Voucher activation success, now you can login"), 'danger', $config['voucher_redirect']);
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, you are connected to internet"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, you are connected to internet"));
}
} else {
_alert(Lang::T('Internet Plan Expired'), 'danger', "login");
}
} else {
$v = ORM::for_table('tbl_voucher')->where('code', $voucher)->find_one();
$v = ORM::for_table('tbl_voucher')->whereRaw("BINARY code = '$voucher'")->find_one();
if (!$v) {
_alert(Lang::T('Voucher invalid'), 'danger', "login");
}
@ -131,7 +148,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -140,7 +157,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
_alert(Lang::T("Voucher activation success, now you can login"), 'danger', $config['voucher_redirect']);
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, you are connected to internet"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, you are connected to internet"));
}
} else {
_alert(Lang::T('Internet Plan Expired'), 'danger', "login");
@ -156,9 +173,9 @@ switch ($do) {
}
}
} else {
$voucher = _post('voucher');
$voucher = Text::alphanumeric(_post('voucher'), "-_.,");
$username = _post('username');
$v1 = ORM::for_table('tbl_voucher')->where('code', $voucher)->find_one();
$v1 = ORM::for_table('tbl_voucher')->whereRaw("BINARY code = '$voucher'")->find_one();
if ($v1) {
// voucher exists, check customer exists or not
$user = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
@ -173,11 +190,11 @@ switch ($do) {
if ($d->save()) {
$user = ORM::for_table('tbl_customers')->where('username', $username)->find_one($d->id());
if (!$user) {
r2(U . 'login', 'e', Lang::T('Voucher activation failed'));
r2(getUrl('login'), 'e', Lang::T('Voucher activation failed'));
}
} else {
_alert(Lang::T('Login Successful'), 'success', "dashboard");
r2(U . 'login', 'e', Lang::T('Voucher activation failed') . '.');
r2(getUrl('login'), 'e', Lang::T('Voucher activation failed') . '.');
}
}
if ($v1['status'] == 0) {
@ -205,7 +222,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -214,26 +231,26 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, you are connected to internet"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, you are connected to internet"));
}
} catch (Exception $e) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
}
}
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
// if failed to recharge, restore old password
$user->password = $oldPass;
$user->save();
r2(U . 'login', 'e', Lang::T("Failed to activate voucher"));
r2(getUrl('login'), 'e', Lang::T("Failed to activate voucher"));
}
} else {
// used voucher
@ -252,7 +269,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -261,39 +278,80 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} catch (Exception $e) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
}
} else {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
}
} else {
// voucher used by other customer
r2(U . 'login', 'e', Lang::T('Voucher Not Valid'));
r2(getUrl('login'), 'e', Lang::T('Voucher Not Valid'));
}
}
} else {
_msglog('e', Lang::T('Invalid Username or Password'));
r2(U . 'login');
r2(getUrl('login'));
}
}
default:
run_hook('customer_view_login'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
if ($config['disable_registration'] == 'yes') {
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_title', Lang::T('Activation'));
$ui->assign('code', alphanumeric(_get('code'), "-"));
$ui->display('user-login-noreg.tpl');
$ui->display('customer/login-noreg.tpl');
} else {
$ui->display('user-login.tpl');
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
if (!empty($config['login_page_logo']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'])) {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png')) {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png';
} else {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.default.png';
}
if (!empty($config['login_page_wallpaper']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'])) {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png')) {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png';
} else {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.default.png';
}
if (!empty($config['login_page_favicon']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'])) {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png')) {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png';
} else {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.default.png';
}
$ui->assign('login_logo', $login_logo);
$ui->assign('wallpaper', $wallpaper);
$ui->assign('favicon', $favicon);
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_title', Lang::T('Login'));
switch ($config['login_page_type']) {
case 'custom':
$ui->display('customer/login-custom-' . $config['login_Page_template'] . '.tpl');
break;
default:
$ui->display('customer/login.tpl');
break;
}
}
break;
}

View File

@ -1,12 +1,17 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Expires: Tue, 01 Jan 2000 00:00:00 GMT");
header("Pragma: no-cache");
run_hook('customer_logout'); #HOOK
if (session_status() == PHP_SESSION_NONE) session_start();
Admin::removeCookie();
User::removeCookie();
session_destroy();
_alert(Lang::T('Logout Successful'),'warning', "login");
_alert(Lang::T('Logout Successful'), 'warning', "login");

View File

@ -85,7 +85,7 @@ switch ($action) {
$keep = _post('keep');
if (!empty($keep)) {
ORM::raw_execute("DELETE FROM tbl_logs WHERE UNIX_TIMESTAMP(date) < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
r2(U . "logs/list/", 's', "Delete logs older than $keep days");
r2(getUrl('logs/list/'), 's', "Delete logs older than $keep days");
}
if ($q != '') {
$query = ORM::for_table('tbl_logs')->where_like('description', '%' . $q . '%')->order_by_desc('id');
@ -104,7 +104,7 @@ switch ($action) {
$keep = _post('keep');
if (!empty($keep)) {
ORM::raw_execute("DELETE FROM radpostauth WHERE UNIX_TIMESTAMP(authdate) < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))", [], 'radius');
r2(U . "logs/radius/", 's', "Delete logs older than $keep days");
r2(getUrl('logs/radius/'), 's', "Delete logs older than $keep days");
}
if ($q != '') {
$query = ORM::for_table('radpostauth', 'radius')->where_like('username', '%' . $q . '%')->order_by_desc('id');
@ -121,5 +121,5 @@ switch ($action) {
default:
r2(U . 'logs/list/', 's', '');
r2(getUrl('logs/list/'), 's', '');
}

View File

@ -0,0 +1,61 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
_auth();
$action = $routes['1'];
$user = User::_info();
$ui->assign('_user', $user);
switch ($action) {
case 'view':
$mail = ORM::for_table('tbl_customers_inbox')->where('customer_id', $user['id'])->find_one($routes['2']);
if(!$mail){
r2(getUrl('mail'), 'e', Lang::T('Message Not Found'));
}
if($mail['date_read'] == null){
$mail->date_read = date('Y-m-d H:i:s');
$mail->save();
}
$next = ORM::for_table('tbl_customers_inbox')->select("id")->where('customer_id', $user['id'])->where_gt("id", $routes['2'])->order_by_asc("id")->find_one();
$prev = ORM::for_table('tbl_customers_inbox')->select("id")->where('customer_id', $user['id'])->where_lt("id", $routes['2'])->order_by_desc("id")->find_one();
$ui->assign('next', $next['id']);
$ui->assign('prev', $prev['id']);
$ui->assign('mail', $mail);
$ui->assign('tipe', 'view');
$ui->assign('_system_menu', 'inbox');
$ui->assign('_title', Lang::T('Inbox'));
$ui->display('customer/inbox.tpl');
break;
case 'delete':
if($routes['2']){
if(ORM::for_table('tbl_customers_inbox')->where('customer_id', $user['id'])->where('id', $routes['2'])->find_one()->delete()){
r2(getUrl('mail'), 's', Lang::T('Mail Deleted Successfully'));
}else{
r2(getUrl('home'), 'e', Lang::T('Failed to Delete Message'));
}
break;
}
default:
$q = _req('q');
$limit = 40;
$p = (int) _req('p', 0);
$offset = $p * $limit;
$query = ORM::for_table('tbl_customers_inbox')->where('customer_id', $user['id'])->order_by_desc('date_created');
$query->limit($limit)->offset($offset);
if(!empty($q)){
$query->whereRaw("(subject like '%$q%' or body like '%$q%')");
}
$mails = $query->find_array();
$ui->assign('tipe', '');
$ui->assign('q', $q);
$ui->assign('p', $p);
$ui->assign('mails', $mails);
$ui->assign('_system_menu', 'inbox');
$ui->assign('_title', Lang::T('Inbox'));
$ui->display('customer/inbox.tpl');
}

View File

@ -49,6 +49,6 @@ switch ($action) {
break;
default:
r2(U . 'map/customer', 'e', 'action not defined');
r2(getUrl('map/customer'), 'e', 'action not defined');
break;
}

View File

@ -30,9 +30,9 @@ document.addEventListener("DOMContentLoaded", function(event) {
ajax: {
url: function(params) {
if(params.term != undefined){
return './index.php?_route=autoload/customer_select2&s='+params.term;
return './?_route=autoload/customer_select2&s='+params.term;
}else{
return './index.php?_route=autoload/customer_select2';
return './?_route=autoload/customer_select2';
}
}
}
@ -62,7 +62,7 @@ EOT;
// Check if fields are empty
if ($id_customer == '' or $message == '' or $via == '') {
r2(U . 'message/send', 'e', Lang::T('All field is required'));
r2(getUrl('message/send'), 'e', Lang::T('All field is required'));
} else {
// Get customer details from the database
$c = ORM::for_table('tbl_customers')->find_one($id_customer);
@ -84,9 +84,9 @@ EOT;
}
if (isset($smsSent) || isset($waSent)) {
r2(U . 'message/send', 's', Lang::T('Message Sent Successfully'));
r2(getUrl('message/send'), 's', Lang::T('Message Sent Successfully'));
} else {
r2(U . 'message/send', 'e', Lang::T('Failed to send message'));
r2(getUrl('message/send'), 'e', Lang::T('Failed to send message'));
}
}
break;
@ -114,7 +114,7 @@ EOT;
if (_req('send') == 'now') {
// Check if fields are empty
if ($group == '' || $message == '' || $via == '') {
r2(U . 'message/send_bulk', 'e', Lang::T('All fields are required'));
r2(getUrl('message/send_bulk'), 'e', Lang::T('All fields are required'));
} else {
// Get customer details from the database based on the selected group
if ($group == 'all') {
@ -234,5 +234,5 @@ EOT;
break;
default:
r2(U . 'message/send_sms', 'e', 'action not defined');
r2(getUrl('message/send_sms'), 'e', 'action not defined');
}

View File

@ -15,30 +15,36 @@ switch ($action) {
$ui->assign('_system_menu', 'voucher');
$ui->assign('_title', Lang::T('Order Voucher'));
run_hook('customer_view_order'); #HOOK
$ui->display('user-order.tpl');
$ui->display('customer/order.tpl');
break;
case 'history':
$ui->assign('_system_menu', 'history');
$query = ORM::for_table('tbl_payment_gateway')->where('username', $user['username'])->order_by_desc('id');
$query = ORM::for_table('tbl_payment_gateway')->where('user_id', $user['id'])->order_by_desc('id');
$d = Paginator::findMany($query);
if (empty($order) || $order < 5) {
$query = ORM::for_table('tbl_payment_gateway')->where('username', $user['username'])->order_by_desc('id');
$d = Paginator::findMany($query);
}
$ui->assign('d', $d);
$ui->assign('_title', Lang::T('Order History'));
run_hook('customer_view_order_history'); #HOOK
$ui->display('user-orderHistory.tpl');
$ui->display('customer/orderHistory.tpl');
break;
case 'balance':
if (strpos($user['email'], '@') === false) {
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
r2(getUrl('accounts/profile'), 'e', Lang::T("Please enter your email address"));
}
$ui->assign('_title', 'Top Up');
$ui->assign('_system_menu', 'balance');
$plans_balance = ORM::for_table('tbl_plans')->where('enabled', '1')->where('type', 'Balance')->where('prepaid', 'yes')->find_many();
$ui->assign('plans_balance', $plans_balance);
$ui->display('user-orderBalance.tpl');
$ui->display('customer/orderBalance.tpl');
break;
case 'package':
if (strpos($user['email'], '@') === false) {
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
r2(getUrl('accounts/profile'), 'e', Lang::T("Please enter your email address"));
}
$ui->assign('_title', 'Order Plan');
$ui->assign('_system_menu', 'package');
@ -48,32 +54,86 @@ switch ($action) {
}
if (!empty($_SESSION['nux-router'])) {
if ($_SESSION['nux-router'] == 'radius') {
$radius_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
$radius_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
$radius_pppoe = ORM::for_table('tbl_plans')
->where('plan_type', $account_type)
->where('enabled', '1')
->where('is_radius', 1)
->where('type', 'PPPOE')
->where('prepaid', 'yes')->find_many();
$radius_hotspot = ORM::for_table('tbl_plans')
->where('plan_type', $account_type)
->where('enabled', '1')
->where('is_radius', 1)
->where('type', 'Hotspot')
->where('prepaid', 'yes')->find_many();
} else {
$routers = ORM::for_table('tbl_routers')->where('id', $_SESSION['nux-router'])->find_many();
$rs = [];
foreach ($routers as $r) {
$rs[] = $r['name'];
}
$plans_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where_in('routers', $rs)->where('is_radius', 0)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
$plans_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where_in('routers', $rs)->where('is_radius', 0)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
$plans_pppoe = ORM::for_table('tbl_plans')
->where('plan_type', $account_type)
->where('enabled', '1')
->where_in('routers', $rs)
->where('is_radius', 0)
->where('type', 'PPPOE')
->where('prepaid', 'yes')
->find_many();
$plans_hotspot = ORM::for_table('tbl_plans')
->where('plan_type', $account_type)
->where('enabled', '1')
->where_in('routers', $rs)
->where('is_radius', 0)
->where('type', 'Hotspot')
->where('prepaid', 'yes')
->find_many();
}
} else {
$radius_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
$radius_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 1)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
$radius_pppoe = ORM::for_table('tbl_plans')
->where('plan_type', $account_type)
->where('enabled', '1')
->where('is_radius', 1)
->where('type', 'PPPOE')
->where('prepaid', 'yes')
->find_many();
$radius_hotspot = ORM::for_table('tbl_plans')
->where('plan_type', $account_type)
->where('enabled', '1')
->where('is_radius', 1)
->where('type', 'Hotspot')
->where('prepaid', 'yes')
->find_many();
$routers = ORM::for_table('tbl_routers')->find_many();
$plans_pppoe = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 0)->where('type', 'PPPOE')->where('prepaid', 'yes')->find_many();
$plans_hotspot = ORM::for_table('tbl_plans')->where('plan_type', $account_type)->where('enabled', '1')->where('is_radius', 0)->where('type', 'Hotspot')->where('prepaid', 'yes')->find_many();
$plans_pppoe = ORM::for_table('tbl_plans')
->where('plan_type', $account_type)
->where('enabled', '1')
->where('is_radius', 0)
->where('type', 'PPPOE')
->where('prepaid', 'yes')
->find_many();
$plans_hotspot = ORM::for_table('tbl_plans')
->where('plan_type', $account_type)
->where('enabled', '1')->where('is_radius', 0)
->where('type', 'Hotspot')
->where('prepaid', 'yes')
->find_many();
$plans_vpn = ORM::for_table('tbl_plans')
->where('plan_type', $account_type)
->where('enabled', '1')->where('is_radius', 0)
->where('type', 'VPN')
->where('prepaid', 'yes')
->find_many();
}
$ui->assign('routers', $routers);
$ui->assign('radius_pppoe', $radius_pppoe);
$ui->assign('radius_hotspot', $radius_hotspot);
$ui->assign('plans_pppoe', $plans_pppoe);
$ui->assign('plans_hotspot', $plans_hotspot);
$ui->assign('plans_vpn', $plans_vpn);
run_hook('customer_view_order_plan'); #HOOK
$ui->display('user-orderPlan.tpl');
$ui->display('customer/orderPlan.tpl');
break;
case 'unpaid':
$d = ORM::for_table('tbl_payment_gateway')
@ -84,12 +144,12 @@ switch ($action) {
r_find_unpaid'); #HOOK
if ($d) {
if (empty($d['pg_url_payment'])) {
r2(U . "order/buy/" . $trx['routers_id'] . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
r2(getUrl('order/buy/') . $trx['routers_id'] . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
} else {
r2(U . "order/view/" . $d['id'] . '/check/', 's', Lang::T("You have unpaid transaction"));
r2(getUrl('order/view/') . $d['id'] . '/check/', 's', Lang::T("You have unpaid transaction"));
}
} else {
r2(U . "order/package/", 's', Lang::T("You have no unpaid transaction"));
r2(getUrl('order/package/'), 's', Lang::T("You have no unpaid transaction"));
}
break;
case 'view':
@ -100,15 +160,15 @@ switch ($action) {
run_hook('customer_view_payment'); #HOOK
// jika tidak ditemukan, berarti punya orang lain
if (empty($trx)) {
r2(U . "order/package", 'w', Lang::T("Payment not found"));
r2(getUrl('order/package'), 'w', Lang::T("Payment not found"));
}
// jika url kosong, balikin ke buy, kecuali cancel
if (empty($trx['pg_url_payment']) && $routes['3'] != 'cancel') {
r2(U . "order/buy/" . (($trx['routers_id'] == 0) ? $trx['routers'] : $trx['routers_id']) . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
if ($trx['status'] == 1 && empty($trx['pg_url_payment']) && $routes['3'] != 'cancel') {
r2(getUrl('order/buy/') . (($trx['routers_id'] == 0) ? $trx['routers'] : $trx['routers_id']) . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
}
if ($routes['3'] == 'check') {
if (!file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $trx['gateway'] . '.php')) {
r2(U . 'order/view/' . $trxid, 'e', Lang::T("No Payment Gateway Available"));
r2(getUrl('order/view/') . $trxid, 'e', Lang::T("No Payment Gateway Available"));
}
run_hook('customer_check_payment_status'); #HOOK
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $trx['gateway'] . '.php';
@ -125,7 +185,7 @@ switch ($action) {
->find_one($trxid);
}
if (empty($trx)) {
r2(U . "order/package", 'e', Lang::T("Transaction Not found"));
r2(getUrl('order/package'), 'e', Lang::T("Transaction Not found"));
}
$router = ORM::for_table('tbl_routers')->where('name', $trx['routers'])->find_one();
@ -138,28 +198,26 @@ switch ($action) {
$ui->assign('plan', $plan);
$ui->assign('bandw', $bandw);
$ui->assign('_title', 'TRX #' . $trxid);
$ui->display('user-orderView.tpl');
$ui->display('customer/orderView.tpl');
break;
case 'pay':
if ($config['enable_balance'] != 'yes') {
r2(U . "order/package", 'e', Lang::T("Balance not enabled"));
r2(getUrl('order/package'), 'e', Lang::T("Balance not enabled"));
}
if (!empty(App::getTokenValue($_GET['stoken']))) {
r2(U . "voucher/invoice/");
r2(getUrl('voucher/invoice/'));
die();
}
if ($user['status'] != 'Active') {
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
}
$plan = ORM::for_table('tbl_plans')->where('enabled', '1')->find_one($routes['3']);
if (empty($plan)) {
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
$plan = ORM::for_table('tbl_plans')->find_one($routes[3]);
if (!$plan) {
r2(getUrl('order/package'), 'e', Lang::T("Plan Not found"));
}
if (!$plan['enabled']) {
r2(U . "home", 'e', 'Plan is not exists');
}
if ($routes['2'] == 'radius') {
if ($plan['is_radius'] == '1') {
$router_name = 'radius';
$router = 'radius';
} else {
$router_name = $plan['routers'];
}
@ -183,27 +241,27 @@ switch ($action) {
$tax = 0;
}
// Tax calculation stop
if ($plan && $plan['enabled'] && $user['balance'] >= $plan['price'] + $tax) {
$total_cost = $plan['price'] + $add_cost + $tax;
if ($plan && $plan['enabled'] && $user['balance'] >= $total_cost) {
if (Package::rechargeUser($user['id'], $router_name, $plan['id'], 'Customer', 'Balance')) {
// if success, then get the balance
Balance::min($user['id'], $plan['price'] + $add_cost + $tax);
Balance::min($user['id'], $total_cost);
App::setToken($_GET['stoken'], "success");
r2(U . "voucher/invoice/", 's', Lang::T("Success to buy package"));
r2(getUrl('voucher/invoice/'), 's', Lang::T("Success to buy package"));
} else {
r2(U . "order/package", 'e', Lang::T("Failed to buy package"));
r2(getUrl('order/package'), 'e', Lang::T("Failed to buy package"));
Message::sendTelegram("Buy Package with Balance Failed\n\n#u$c[username] #buy \n" . $plan['name_plan'] .
"\nRouter: " . $router_name .
"\nPrice: " . $plan['price'] + $tax);
"\nPrice: " . $total_cost);
}
} else {
r2(U . "home", 'e', 'Plan is not exists');
r2(getUrl('order/gateway/$routes[2]/$routes[3]'), 'e', Lang::T("Insufficient balance"));
}
break;
case 'send':
if ($config['enable_balance'] != 'yes') {
r2(U . "order/package", 'e', Lang::T("Balance not enabled"));
r2(getUrl('order/package'), 'e', Lang::T("Balance not enabled"));
}
if ($user['status'] != 'Active') {
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
@ -212,12 +270,13 @@ switch ($action) {
$ui->assign('_system_menu', 'package');
$plan = ORM::for_table('tbl_plans')->find_one($routes['3']);
if (empty($plan)) {
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
r2(getUrl('order/package'), 'e', Lang::T("Plan Not found"));
}
if (!$plan['enabled']) {
r2(U . "home", 'e', 'Plan is not exists');
r2(getUrl('home'), 'e', 'Plan is not exists');
}
if ($routes['2'] == 'radius') {
if ($plan['is_radius'] == '1') {
$routes['2'] = 0;
$router_name = 'radius';
} else {
$router_name = $plan['routers'];
@ -253,13 +312,13 @@ switch ($action) {
}
if (!$target) {
r2(U . 'home', 'd', Lang::T('Username not found'));
r2(getUrl('home'), 'd', Lang::T('Username not found'));
}
if ($user['balance'] < $plan['price']) {
r2(U . 'home', 'd', Lang::T('insufficient balance'));
r2(getUrl('home'), 'd', Lang::T('insufficient balance'));
}
if ($user['username'] == $target['username']) {
r2(U . "order/pay/$routes[2]/$routes[3]", 's', '^_^ v');
r2(getUrl('order/pay/$routes[2]/$routes[3]'), 's', '^_^ v');
}
$active = ORM::for_table('tbl_user_recharges')
->where('username', _post('username'))
@ -267,7 +326,7 @@ switch ($action) {
->find_one();
if ($active && $active['plan_id'] != $plan['id']) {
r2(U . "order/package", 'e', Lang::T("Target has active plan, different with current plant.") . " [ <b>$active[namebp]</b> ]");
r2(getUrl('order/package'), 'e', Lang::T("Target has active plan, different with current plant.") . " [ <b>$active[namebp]</b> ]");
}
$result = Package::rechargeUser($target['id'], $router_name, $plan['id'], $user['username'], 'Balance');
if (!empty($result)) {
@ -276,6 +335,7 @@ switch ($action) {
//sender
$d = ORM::for_table('tbl_payment_gateway')->create();
$d->username = $user['username'];
$d->user_id = $user['id'];
$d->gateway = $target['username'];
$d->plan_id = $plan['id'];
$d->plan_name = $plan['name_plan'];
@ -295,6 +355,7 @@ switch ($action) {
//receiver
$d = ORM::for_table('tbl_payment_gateway')->create();
$d->username = $target['username'];
$d->user_id = $target['id'];
$d->gateway = $user['username'];
$d->plan_id = $plan['id'];
$d->plan_name = $plan['name_plan'];
@ -310,7 +371,7 @@ switch ($action) {
$d->trx_invoice = $result;
$d->status = 2;
$d->save();
r2(U . "order/view/$trx_id", 's', Lang::T("Success to send package"));
r2(getUrl('order/view/$trx_id'), 's', Lang::T("Success to send package"));
} else {
$errorMessage = "Send Package with Balance Failed\n\n#u$user[username] #send \n" . $plan['name_plan'] .
"\nRouter: " . $router_name .
@ -320,7 +381,7 @@ switch ($action) {
$errorMessage .= "\nTax: " . $tax;
}
r2(U . "order/package", 'e', Lang::T("Failed to Send package"));
r2(getUrl('order/package'), 'e', Lang::T("Failed to Send package"));
Message::sendTelegram($errorMessage);
}
}
@ -328,13 +389,13 @@ switch ($action) {
$ui->assign('router', $router_name);
$ui->assign('plan', $plan);
$ui->assign('tax', $tax);
$ui->display('user-sendPlan.tpl');
$ui->display('customer/sendPlan.tpl');
break;
case 'gateway':
$ui->assign('_title', Lang::T('Select Payment Gateway'));
$ui->assign('_system_menu', 'package');
if (strpos($user['email'], '@') === false) {
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
r2(getUrl('accounts/profile'), 'e', Lang::T("Please enter your email address"));
}
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
@ -345,34 +406,139 @@ switch ($action) {
$tax_rate = $tax_rate_setting;
}
$plan = ORM::for_table('tbl_plans')->find_one($routes['3']);
$tax = Package::tax($plan['price'], $tax_rate);
$add_cost = 0;
if ($router['name'] != 'balance') {
list($bills, $add_cost) = User::getBills($id_customer);
}
if($config['enable_coupons']){
if (!isset($_SESSION['coupon_attempts'])) {
$_SESSION['coupon_attempts'] = 0;
$_SESSION['last_attempt_time'] = time();
}
if ($_SESSION['coupon_attempts'] >= 5) {
$timeout = 10 * 60; // 10 minutes in seconds
$time_diff = time() - $_SESSION['last_attempt_time'];
if ($time_diff >= $timeout) {
$_SESSION['coupon_attempts'] = 0;
$_SESSION['last_attempt_time'] = time();
} else {
$remaining_time = ceil(($timeout - $time_diff) / 60);
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Too many invalid attempts. Please try again after $remaining_time minutes."));
}
}
if (_post('coupon')) {
if ($plan['routers'] === 'balance') {
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon not available for Balance"));
}
$coupon = ORM::for_table('tbl_coupons')->where('code', _post('coupon'))->find_one();
if (!$coupon) {
$_SESSION['coupon_attempts']++;
$_SESSION['last_attempt_time'] = time();
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon not found"));
}
if ($coupon['status'] != 'active') {
$_SESSION['coupon_attempts']++;
$_SESSION['last_attempt_time'] = time();
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon is not active"));
}
// Reset attempts after a successful coupon validation
$_SESSION['coupon_attempts'] = 0;
$_SESSION['last_attempt_time'] = time();
$today = date('Y-m-d');
if ($today < $coupon['start_date'] || $today > $coupon['end_date']) {
$_SESSION['coupon_attempts']++;
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon is not valid for today"));
}
if ($coupon['max_usage'] > 0 && $coupon['usage_count'] >= $coupon['max_usage']) {
$_SESSION['coupon_attempts']++;
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon usage limit reached"));
}
if ($plan['price'] < $coupon['min_order_amount']) {
$_SESSION['coupon_attempts']++;
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("The order amount does not meet the minimum requirement for this coupon"));
}
// Calculate discount value
$discount = 0;
switch ($coupon['type']) {
case 'percent':
$discount = ($coupon['value'] / 100) * $plan['price'];
if ($discount > $coupon['max_discount_amount']) {
$discount = $coupon['max_discount_amount'];
}
break;
case 'fixed':
$discount = $coupon['value'];
break;
}
// Ensure discount does not exceed the plan price
if ($discount >= $plan['price']) {
r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Discount value exceeds the plan price"));
}
$plan['price'] -= $discount;
$coupon->usage_count = $coupon['usage_count'] + 1;
$coupon->save();
$ui->assign('discount', $discount);
$ui->assign('notify', Lang::T("Coupon applied successfully. You saved " . Lang::moneyFormat($discount)));
$ui->assign('notify_t', 's');
}
}
$tax = Package::tax($plan['price'] + $add_cost, $tax_rate);
$pgs = array_values(explode(',', $config['payment_gateway']));
if (count($pgs) == 0) {
sendTelegram("Payment Gateway not set, please set it in Settings");
_log(Lang::T("Payment Gateway not set, please set it in Settings"));
r2(U . "home", 'e', Lang::T("Failed to create Transaction.."));
r2(getUrl('home'), 'e', Lang::T("Failed to create Transaction.."));
}
if (count($pgs) > 1) {
if (count($pgs) > 0) {
$ui->assign('pgs', $pgs);
if ($tax_enable === 'yes') {
$ui->assign('tax', $tax);
}
if (_post('custom') == '1') {
if (_post('amount') > 0) {
$ui->assign('custom', '1');
$ui->assign('amount', _post('amount'));
} else {
r2(getUrl('order/balance'), 'e', Lang::T("Please enter amount"));
}
}
$ui->assign('route2', $routes[2]);
$ui->assign('route3', $routes[3]);
$ui->assign('add_cost', $add_cost);
$ui->assign('bills', $bills);
$ui->assign('plan', $plan);
$ui->display('user-selectGateway.tpl');
$ui->display('customer/selectGateway.tpl');
break;
} else {
if (empty($pgs[0])) {
sendTelegram("Payment Gateway not set, please set it in Settings");
_log(Lang::T("Payment Gateway not set, please set it in Settings"));
r2(U . "home", 'e', Lang::T("Failed to create Transaction.."));
} else {
$_POST['gateway'] = $pgs[0];
}
sendTelegram("Payment Gateway not set, please set it in Settings");
_log(Lang::T("Payment Gateway not set, please set it in Settings"));
r2(getUrl('home'), 'e', Lang::T("Failed to create Transaction.."));
}
case 'buy':
$gateway = _post('gateway');
$discount = _post('discount') ?: 0;
if ($gateway == 'balance') {
unset($_SESSION['gateway']);
r2(getUrl('order/pay/') . $routes[2] . '/' . $routes[3]);
}
if (empty($gateway) && !empty($_SESSION['gateway'])) {
$gateway = $_SESSION['gateway'];
} else if (!empty($gateway)) {
@ -382,112 +548,156 @@ switch ($action) {
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
}
if (empty($gateway)) {
r2(U . 'order/gateway/' . $routes[2] . '/' . $routes[3], 'w', Lang::T("Please select Payment Gateway"));
r2(getUrl('order/gateway/') . $routes[2] . '/' . $routes[3], 'w', Lang::T("Please select Payment Gateway"));
}
run_hook('customer_buy_plan'); #HOOK
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $gateway . '.php';
call_user_func($gateway . '_validate_config');
if ($routes['2'] == 'radius') {
$router['id'] = 0;
$router['name'] = 'radius';
} else if ($routes['2'] > 0) {
$router = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routes['2']);
} else {
$router['id'] = 0;
$router['name'] = 'balance';
}
$plan = ORM::for_table('tbl_plans')->where('enabled', '1')->find_one($routes['3']);
if (empty($router) || empty($plan)) {
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
}
$d = ORM::for_table('tbl_payment_gateway')
->where('username', $user['username'])
->where('status', 1)
->find_one();
if ($d) {
if ($d['pg_url_payment']) {
r2(U . "order/view/" . $d['id'], 'w', Lang::T("You already have unpaid transaction, cancel it or pay it."));
} else {
if ($gateway == $d['gateway']) {
$id = $d['id'];
switch (_post('custom')) {
case '1':
$amount = _post('amount');
$amount = (float) $amount;
if ($amount <= 0) {
r2(getUrl('order/gateway/') . $routes[2] . '/' . $routes[3], 'w', Lang::T("Please enter amount"));
}
$d = ORM::for_table('tbl_payment_gateway')
->where('username', $user['username'])
->where('status', 1)
->find_one();
if ($d) {
if ($d['pg_url_payment']) {
r2(getUrl('order/view/') . $d['id'], 'w', Lang::T("You already have unpaid transaction, cancel it or pay it."));
} else {
if ($gateway == $d['gateway']) {
$id = $d['id'];
} else {
$d->status = 4;
$d->save();
}
}
}
$d = ORM::for_table('tbl_payment_gateway')->create();
$d->username = $user['username'];
$d->user_id = $user['id'];
$d->gateway = $gateway;
$d->plan_id = 0;
$d->plan_name = 'Custom';
$d->routers_id = '0';
$d->routers = 'Custom Balance';
$d->price = $amount;
$d->created_date = date('Y-m-d H:i:s');
$d->status = 1;
$d->save();
$id = $d->id;
break;
default:
$plan = ORM::for_table('tbl_plans')->where('enabled', '1')->find_one($routes['3']);
if ($plan['is_radius'] == '1') {
$router['id'] = 0;
$router['name'] = 'radius';
} else if ($routes['2'] > 0) {
$router = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routes['2']);
} else {
$d->status = 4;
$router['id'] = 0;
$router['name'] = 'balance';
}
if (empty($router) || empty($plan)) {
r2(getUrl('order/package'), 'e', Lang::T("Plan Not found"));
}
$d = ORM::for_table('tbl_payment_gateway')
->where('username', $user['username'])
->where('status', 1)
->find_one();
if ($d) {
if ($d['pg_url_payment']) {
r2(getUrl('order/view/') . $d['id'], 'w', Lang::T("You already have unpaid transaction, cancel it or pay it."));
} else {
if ($gateway == $d['gateway']) {
$id = $d['id'];
} else {
$d->status = 4;
$d->save();
}
}
}
$add_cost = 0;
$tax = 0;
if ($router['name'] != 'balance') {
list($bills, $add_cost) = User::getBills($id_customer);
}
// Tax calculation start
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
} else {
$tax_rate = $tax_rate_setting;
}
if ($tax_enable === 'yes') {
$tax = Package::tax($plan['price'], $tax_rate);
}
// Tax calculation stop
if (empty($id)) {
$d = ORM::for_table('tbl_payment_gateway')->create();
$d->username = $user['username'];
$d->user_id = $user['id'];
$d->gateway = $gateway;
$d->plan_id = $plan['id'];
$d->plan_name = $plan['name_plan'];
$d->routers_id = $router['id'];
$d->routers = $router['name'];
if ($plan['validity_unit'] == 'Period') {
// Postpaid price from field
$add_inv = User::getAttribute("Invoice", $id_customer);
if (empty($add_inv) or $add_inv == 0) {
$d->price = $plan['price'] + $add_cost + $tax - $discount;
} else {
$d->price = $add_inv + $add_cost + $tax - $discount;
}
} else {
$d->price = $plan['price'] + $add_cost + $tax - $discount;
}
$d->created_date = date('Y-m-d H:i:s');
$d->status = 1;
$d->save();
$id = $d->id();
} else {
$d->username = $user['username'];
$d->user_id = $user['id'];
$d->gateway = $gateway;
$d->plan_id = $plan['id'];
$d->plan_name = $plan['name_plan'];
$d->routers_id = $router['id'];
$d->routers = $router['name'];
if ($plan['validity_unit'] == 'Period') {
// Postpaid price from field
$add_inv = User::getAttribute("Invoice", $id_customer);
if (empty($add_inv) or $add_inv == 0) {
$d->price = ($plan['price'] + $add_cost + $tax - $discount);
} else {
$d->price = ($add_inv + $add_cost + $tax - $discount);
}
} else {
$d->price = ($plan['price'] + $add_cost + $tax - $discount);
}
//$d->price = ($plan['price'] + $add_cost);
$d->created_date = date('Y-m-d H:i:s');
$d->status = 1;
$d->save();
}
}
}
$add_cost = 0;
$tax = 0;
if ($router['name'] != 'balance') {
list($bills, $add_cost) = User::getBills($id_customer);
}
// Tax calculation start
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
} else {
$tax_rate = $tax_rate_setting;
}
if ($tax_enable === 'yes') {
$tax = Package::tax($plan['price'], $tax_rate);
}
// Tax calculation stop
if (empty($id)) {
$d = ORM::for_table('tbl_payment_gateway')->create();
$d->username = $user['username'];
$d->gateway = $gateway;
$d->plan_id = $plan['id'];
$d->plan_name = $plan['name_plan'];
$d->routers_id = $router['id'];
$d->routers = $router['name'];
if ($plan['validity_unit'] == 'Period') {
// Postpaid price from field
$add_inv = User::getAttribute("Invoice", $id_customer);
if (empty($add_inv) or $add_inv == 0) {
$d->price = ($plan['price'] + $add_cost + $tax);
} else {
$d->price = ($add_inv + $add_cost + $tax);
}
} else {
$d->price = ($plan['price'] + $add_cost + $tax);
}
//$d->price = ($plan['price'] + $add_cost);
$d->created_date = date('Y-m-d H:i:s');
$d->status = 1;
$d->save();
$id = $d->id();
} else {
$d->username = $user['username'];
$d->gateway = $gateway;
$d->plan_id = $plan['id'];
$d->plan_name = $plan['name_plan'];
$d->routers_id = $router['id'];
$d->routers = $router['name'];
if ($plan['validity_unit'] == 'Period') {
// Postpaid price from field
$add_inv = User::getAttribute("Invoice", $id_customer);
if (empty($add_inv) or $add_inv == 0) {
$d->price = ($plan['price'] + $add_cost + $tax);
} else {
$d->price = ($add_inv + $add_cost + $tax);
}
} else {
$d->price = ($plan['price'] + $add_cost + $tax);
}
//$d->price = ($plan['price'] + $add_cost);
$d->created_date = date('Y-m-d H:i:s');
$d->status = 1;
$d->save();
break;
}
if (!$id) {
r2(U . "order/package/" . $d['id'], 'e', Lang::T("Failed to create Transaction.."));
r2(getUrl('order/package/') . $d['id'], 'e', Lang::T("Failed to create Transaction.."));
} else {
call_user_func($gateway . '_create_transaction', $d, $user);
}
break;
default:
r2(U . "order/package/", 's', '');
r2(getUrl('order/package/'), 's', '');
}

View File

@ -16,6 +16,6 @@ if(file_exists(__DIR__."/../../pages/".str_replace(".","",$action).".html")){
$ui->assign("PageFile",$action);
$ui->assign("pageHeader",$action);
run_hook('customer_view_page'); #HOOK
$ui->display('user-pages.tpl');
$ui->display('customer/pages.tpl');
}else
$ui->display('404.tpl');
$ui->display('customer/404.tpl');

View File

@ -1,4 +1,5 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
@ -11,61 +12,76 @@ $ui->assign('_system_menu', 'pages');
$action = $routes['1'];
$ui->assign('_admin', $admin);
if(strpos($action,"-reset")!==false){
if (strpos($action, "-reset") !== false) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$action = str_replace("-reset","",$action);
$path = "pages/".str_replace(".","",$action).".html";
$temp = "pages_template/".str_replace(".","",$action).".html";
if(file_exists($temp)){
if(!copy($temp, $path)){
file_put_contents($path, Http::getData('https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/pages_template/'.$action.'.html'));
$action = str_replace("-reset", "", $action);
$path = "$PAGES_PATH/" . str_replace(".", "", $action) . ".html";
$temp = "pages_template/" . str_replace(".", "", $action) . ".html";
if (file_exists($temp)) {
if (!copy($temp, $path)) {
file_put_contents($path, Http::getData('https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/pages_template/' . $action . '.html'));
}
}else{
file_put_contents($path, Http::getData('https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/pages_template/'.$action.'.html'));
} else {
file_put_contents($path, Http::getData('https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/pages_template/' . $action . '.html'));
}
r2(U . 'pages/'.$action);
}else if(strpos($action,"-post")===false){
r2(getUrl('pages/') . $action);
} else if (strpos($action, "-post") === false) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$path = "pages/".str_replace(".","",$action).".html";
$path = "$PAGES_PATH/" . str_replace(".", "", $action) . ".html";
$ui->assign("action", $action);
//echo $path;
run_hook('view_edit_pages'); #HOOK
if(!file_exists($path)){
$temp = "pages_template/".str_replace(".","",$action).".html";
if(file_exists($temp)){
if(!copy($temp, $path)){
if (!file_exists($path)) {
$temp = "pages_template/" . str_replace(".", "", $action) . ".html";
if (file_exists($temp)) {
if (!copy($temp, $path)) {
touch($path);
}
}else{
} else {
touch($path);
}
}
if(file_exists($path)){
if (file_exists($path)) {
if ($action == 'Voucher') {
if (!file_exists("$PAGES_PATH/vouchers/")) {
mkdir("$PAGES_PATH/vouchers/");
if (file_exists("pages_template/vouchers/")) {
File::copyFolder("pages_template/vouchers/", "$PAGES_PATH/vouchers/");
}
}
$ui->assign("vouchers", scandir("$PAGES_PATH/vouchers/"));
}
$html = file_get_contents($path);
$ui->assign("htmls",str_replace(["<div","</div>"],"",$html));
$ui->assign("writeable",is_writable($path));
$ui->assign("pageHeader",str_replace('_', ' ', $action));
$ui->assign("PageFile",$action);
$ui->assign("htmls", str_replace(["<div", "</div>"], "", $html));
$ui->assign("writeable", is_writable($path));
$ui->assign("pageHeader", str_replace('_', ' ', $action));
$ui->assign("PageFile", $action);
$ui->display('page-edit.tpl');
}else
} else
$ui->display('a404.tpl');
}else{
} else {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'),'danger', "dashboard");
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$action = str_replace("-post","",$action);
$path = "pages/".str_replace(".","",$action).".html";
if(file_exists($path)){
$action = str_replace("-post", "", $action);
$path = "$PAGES_PATH/" . str_replace(".", "", $action) . ".html";
if (file_exists($path)) {
$html = _post("html");
run_hook('save_pages'); #HOOK
if(file_put_contents($path, str_replace(["<div","</div>"],"",$html))){
r2(U . 'pages/'.$action, 's', Lang::T("Saving page success"));
}else{
r2(U . 'pages/'.$action, 'e', Lang::T("Failed to save page, make sure i can write to folder pages, <i>chmod 664 pages/*.html<i>"));
if (file_put_contents($path, $html)) {
if (_post('template_save') == 'yes') {
if (!empty(_post('template_name'))) {
file_put_contents("$PAGES_PATH/vouchers/" . _post('template_name') . '.html', $html);
}
}
r2(getUrl('pages/') . $action, 's', Lang::T("Saving page success"));
} else {
r2(getUrl('pages/') . $action, 'e', Lang::T("Failed to save page, make sure i can write to folder pages, <i>chmod 664 pages/*.html<i>"));
}
}else
} else
$ui->display('a404.tpl');
}
}

View File

@ -10,63 +10,91 @@ $ui->assign('_system_menu', 'paymentgateway');
$action = alphanumeric($routes[1]);
$ui->assign('_admin', $admin);
if ($action == 'delete') {
$pg = alphanumeric($routes[2]);
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $pg . '.php')) {
deleteFile($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR, $pg);
}
r2(U . 'paymentgateway', 's', Lang::T('Payment Gateway Deleted'));
}
if (_post('save') == 'actives') {
$pgs = '';
if(is_array($_POST['pgs'])){
$pgs = implode(',', $_POST['pgs']);
}
$d = ORM::for_table('tbl_appconfig')->where('setting', 'payment_gateway')->find_one();
if ($d) {
$d->value = $pgs;
$d->save();
} else {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = 'payment_gateway';
$d->value = $pgs;
$d->save();
}
r2(U . 'paymentgateway', 's', Lang::T('Payment Gateway saved successfully'));
}
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php')) {
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (function_exists($action . '_save_config')) {
call_user_func($action . '_save_config');
} else {
$ui->display('a404.tpl');
switch ($action) {
case 'delete':
$pg = alphanumeric($routes[2]);
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $pg . '.php')) {
deleteFile($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR, $pg);
}
} else {
if (function_exists($action . '_show_config')) {
call_user_func($action . '_show_config');
} else {
$ui->display('a404.tpl');
r2(getUrl('paymentgateway'), 's', Lang::T('Payment Gateway Deleted'));
case 'audit':
$pg = alphanumeric($routes[2]);
$q = alphanumeric(_req('q'), '-._ ');
$query = ORM::for_table('tbl_payment_gateway')->order_by_desc("id");
$query->selects('id', 'username', 'gateway', 'gateway_trx_id', 'plan_id', 'plan_name', 'routers_id', 'routers', 'price', 'pg_url_payment', 'payment_method', 'payment_channel', 'expired_date', 'created_date', 'paid_date', 'trx_invoice', 'status');
$query->where('gateway', $pg);
if (!empty($q)) {
$query->whereRaw("(gateway_trx_id LIKE '%$q%' OR username LIKE '%$q%' OR routers LIKE '%$q%' OR plan_name LIKE '%$q%')");
$append_url = 'q=' . urlencode($q);
}
}
} else {
if (!empty($action)) {
r2(U . 'paymentgateway', 'w', Lang::T('Payment Gateway Not Found'));
} else {
$files = scandir($PAYMENTGATEWAY_PATH);
foreach ($files as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) == 'php') {
$pgs[] = str_replace('.php', '', $file);
$pgs = Paginator::findMany($query, ["search" => $search], 50, $append_url);
$ui->assign('_title', 'Payment Gateway Audit');
$ui->assign('pgs', $pgs);
$ui->assign('pg', $pg);
$ui->assign('q', $q);
$ui->display('paymentgateway-audit.tpl');
break;
case 'auditview':
$pg = alphanumeric($routes[2]);
$d = ORM::for_table('tbl_payment_gateway')->find_one($pg);
$d['pg_request'] = (!empty($d['pg_request']))? Text::jsonArray21Array(json_decode($d['pg_request'], true)) : [];
$d['pg_paid_response'] = (!empty($d['pg_paid_response']))? Text::jsonArray21Array(json_decode($d['pg_paid_response'], true)) : [];
$ui->assign('_title', 'Payment Gateway Audit View');
$ui->assign('pg', $d);
$ui->display('paymentgateway-audit-view.tpl');
break;
default:
if (_post('save') == 'actives') {
$pgs = '';
if (is_array($_POST['pgs'])) {
$pgs = implode(',', $_POST['pgs']);
}
$d = ORM::for_table('tbl_appconfig')->where('setting', 'payment_gateway')->find_one();
if ($d) {
$d->value = $pgs;
$d->save();
} else {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = 'payment_gateway';
$d->value = $pgs;
$d->save();
}
r2(getUrl('paymentgateway'), 's', Lang::T('Payment Gateway saved successfully'));
}
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php')) {
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (function_exists($action . '_save_config')) {
call_user_func($action . '_save_config');
} else {
$ui->display('a404.tpl');
}
} else {
if (function_exists($action . '_show_config')) {
call_user_func($action . '_show_config');
} else {
$ui->display('a404.tpl');
}
}
} else {
if (!empty($action)) {
r2(getUrl('paymentgateway'), 'w', Lang::T('Payment Gateway Not Found'));
} else {
$files = scandir($PAYMENTGATEWAY_PATH);
foreach ($files as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) == 'php') {
$pgs[] = str_replace('.php', '', $file);
}
}
$ui->assign('_title', 'Payment Gateway Settings');
$ui->assign('pgs', $pgs);
$ui->assign('actives', explode(',', $config['payment_gateway']));
$ui->display('paymentgateway.tpl');
}
}
$ui->assign('_title', 'Payment Gateway Settings');
$ui->assign('pgs', $pgs);
$ui->assign('actives', explode(',', $config['payment_gateway']));
$ui->display('paymentgateway.tpl');
}
}

View File

@ -20,9 +20,9 @@ document.addEventListener("DOMContentLoaded", function(event) {
ajax: {
url: function(params) {
if(params.term != undefined){
return './index.php?_route=autoload/customer_select2&s='+params.term;
return './?_route=autoload/customer_select2&s='+params.term;
}else{
return './index.php?_route=autoload/customer_select2';
return './?_route=autoload/customer_select2';
}
}
}
@ -49,7 +49,11 @@ switch ($action) {
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
(new $p['device'])->add_customer($c, $p);
if (method_exists($dvc, 'sync_customer')) {
(new $p['device'])->sync_customer($c, $p);
} else {
(new $p['device'])->add_customer($c, $p);
}
} else {
new Exception(Lang::T("Devices Not Found"));
}
@ -62,7 +66,7 @@ switch ($action) {
$log .= "PLAN NOT FOUND : $tur[username], $tur[namebp], $tur[type], $tur[routers]<br>";
}
}
r2(U . 'plan/list', 's', $log);
r2(getUrl('plan/list'), 's', $log);
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");
@ -101,15 +105,35 @@ switch ($action) {
$cust = User::_info($id_customer);
$plan = ORM::for_table('tbl_plans')->find_one($planId);
list($bills, $add_cost) = User::getBills($id_customer);
// Tax calculation start
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
} else {
$tax_rate = $tax_rate_setting;
}
if ($tax_enable === 'yes') {
$tax = Package::tax($plan['price'], $tax_rate);
} else {
$tax = 0;
}
// Tax calculation stop
$total_cost = $plan['price'] + $add_cost + $tax;
if ($using == 'balance' && $config['enable_balance'] == 'yes') {
if (!$cust) {
r2(U . 'plan/recharge', 'e', Lang::T('Customer not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Customer not found'));
}
if (!$plan) {
r2(U . 'plan/recharge', 'e', Lang::T('Plan not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Plan not found'));
}
if ($cust['balance'] < ($plan['price'] + $add_cost)) {
r2(U . 'plan/recharge', 'e', Lang::T('insufficient balance'));
if ($cust['balance'] < $total_cost) {
r2(getUrl('plan/recharge'), 'e', Lang::T('insufficient balance'));
}
$gateway = 'Recharge Balance';
}
@ -122,6 +146,9 @@ switch ($action) {
if (count($usings) == 0) {
$usings[] = Lang::T('Cash');
}
if ($tax_enable === 'yes') {
$ui->assign('tax', $tax);
}
$ui->assign('usings', $usings);
$ui->assign('bills', $bills);
$ui->assign('add_cost', $add_cost);
@ -133,7 +160,7 @@ switch ($action) {
$ui->assign('plan', $plan);
$ui->display('recharge-confirm.tpl');
} else {
r2(U . 'plan/recharge', 'e', $msg);
r2(getUrl('plan/recharge'), 'e', $msg);
}
break;
@ -145,10 +172,12 @@ switch ($action) {
$server = _post('server');
$planId = _post('plan');
$using = _post('using');
$stoken = _post('stoken');
$svoucher = _post('svoucher');
if (!empty(App::getTokenValue($stoken))) {
$username = App::getTokenValue($stoken);
$plan = ORM::for_table('tbl_plans')->find_one($planId);
if (!empty(App::getVoucherValue($svoucher))) {
$username = App::getVoucherValue($svoucher);
$in = ORM::for_table('tbl_transactions')->where('username', $username)->order_by_desc('id')->find_one();
Package::createInvoice($in);
$ui->display('invoice.tpl');
@ -165,16 +194,36 @@ switch ($action) {
$channel = $admin['fullname'];
$cust = User::_info($id_customer);
list($bills, $add_cost) = User::getBills($id_customer);
// Tax calculation start
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
} else {
$tax_rate = $tax_rate_setting;
}
if ($tax_enable === 'yes') {
$tax = Package::tax($plan['price'], $tax_rate);
} else {
$tax = 0;
}
// Tax calculation stop
$total_cost = $plan['price'] + $add_cost + $tax;
if ($using == 'balance' && $config['enable_balance'] == 'yes') {
$plan = ORM::for_table('tbl_plans')->find_one($planId);
//$plan = ORM::for_table('tbl_plans')->find_one($planId);
if (!$cust) {
r2(U . 'plan/recharge', 'e', Lang::T('Customer not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Customer not found'));
}
if (!$plan) {
r2(U . 'plan/recharge', 'e', Lang::T('Plan not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Plan not found'));
}
if ($cust['balance'] < ($plan['price'] + $add_cost)) {
r2(U . 'plan/recharge', 'e', Lang::T('insufficient balance'));
if ($cust['balance'] < $total_cost) {
r2(getUrl('plan/recharge'), 'e', Lang::T('insufficient balance'));
}
$gateway = 'Recharge Balance';
}
@ -185,18 +234,18 @@ switch ($action) {
}
if (Package::rechargeUser($id_customer, $server, $planId, $gateway, $channel)) {
if ($using == 'balance') {
Balance::min($cust['id'], $plan['price'] + $add_cost);
Balance::min($cust['id'], $total_cost);
}
$in = ORM::for_table('tbl_transactions')->where('username', $cust['username'])->order_by_desc('id')->find_one();
Package::createInvoice($in);
App::setToken($stoken, $cust['username']);
App::setVoucher($svoucher, $cust['username']);
$ui->display('invoice.tpl');
_log('[' . $admin['username'] . ']: ' . 'Recharge ' . $cust['username'] . ' [' . $in['plan_name'] . '][' . Lang::moneyFormat($in['price']) . ']', $admin['user_type'], $admin['id']);
} else {
r2(U . 'plan/recharge', 'e', "Failed to recharge account");
r2(getUrl('plan/recharge'), 'e', "Failed to recharge account");
}
} else {
r2(U . 'plan/recharge', 'e', $msg);
r2(getUrl('plan/recharge'), 'e', $msg);
}
break;
@ -208,9 +257,9 @@ switch ($action) {
$c = ORM::for_table('tbl_customers')->where('username', $in['username'])->find_one();
if ($c) {
Message::sendInvoice($c, $in);
r2(U . 'plan/view/' . $id, 's', "Success send to customer");
r2(getUrl('plan/view/') . $id, 's', "Success send to customer");
}
r2(U . 'plan/view/' . $id, 'd', "Customer not found");
r2(getUrl('plan/view/') . $id, 'd', "Customer not found");
}
Package::createInvoice($in);
$ui->assign('_title', 'View Invoice');
@ -228,11 +277,13 @@ switch ($action) {
$ui->assign('content', $content);
} else {
$id = _post('id');
if (empty($id)) {
$id = $routes['2'];
}
$d = ORM::for_table('tbl_transactions')->where('id', $id)->find_one();
$ui->assign('in', $d);
$ui->assign('date', Lang::dateAndTimeFormat($d['recharged_on'], $d['recharged_time']));
}
run_hook('print_invoice'); #HOOK
$ui->display('invoice-print.tpl');
break;
@ -263,7 +314,7 @@ switch ($action) {
$ui->assign('_title', 'Edit Plan');
$ui->display('plan-edit.tpl');
} else {
r2(U . 'plan/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('plan/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -288,7 +339,7 @@ switch ($action) {
}
$d->delete();
_log('[' . $admin['username'] . ']: ' . 'Delete Plan for Customer ' . $c['username'] . ' [' . $in['plan_name'] . '][' . Lang::moneyFormat($in['price']) . ']', $admin['user_type'], $admin['id']);
r2(U . 'plan/list', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('plan/list'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -354,14 +405,14 @@ switch ($action) {
}
$d->save();
_log('[' . $admin['username'] . ']: ' . 'Edit Plan for Customer ' . $d['username'] . ' to [' . $d['namebp'] . '][' . Lang::moneyFormat($p['price']) . ']', $admin['user_type'], $admin['id']);
r2(U . 'plan/list', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('plan/list'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'plan/edit/' . $id, 'e', $msg);
r2(getUrl('plan/edit/') . $id, 'e', $msg);
}
break;
case 'voucher':
$ui->assign('_title', Lang::T('Vouchers'));
$ui->assign('_title', Lang::T('Voucher Cards'));
$search = _req('search');
$router = _req('router');
$customer = _req('customer');
@ -371,9 +422,10 @@ switch ($action) {
$ui->assign('customer', $customer);
$ui->assign('status', $status);
$ui->assign('plan', $plan);
$ui->assign('_system_menu', 'cards');
$query = ORM::for_table('tbl_plans')
->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'));
->inner_join('tbl_voucher', ['tbl_plans.id', '=', 'tbl_voucher.id_plan']);
if (!empty($router)) {
$query->where('tbl_voucher.routers', $router);
@ -482,8 +534,8 @@ switch ($action) {
}
$time3months = strtotime('-3 months');
$d = ORM::for_table('tbl_voucher')->where_equal('status', '1')
->where_raw("UNIX_TIMESTAMP(used_date) < $time3months")
->findMany();
->where_raw("UNIX_TIMESTAMP(used_date) < $time3months")
->findMany();
if ($d) {
$jml = 0;
foreach ($d as $v) {
@ -492,7 +544,7 @@ switch ($action) {
$jml++;
}
}
r2(U . 'plan/voucher', 's', "$jml " . Lang::T('Data Deleted Successfully'));
r2(getUrl('plan/voucher'), 's', "$jml " . Lang::T('Data Deleted Successfully'));
}
case 'print-voucher':
$from_id = _post('from_id');
@ -500,6 +552,7 @@ switch ($action) {
$pagebreak = _post('pagebreak');
$limit = _post('limit');
$vpl = _post('vpl');
$selected_datetime = _post('selected_datetime');
if (empty($vpl)) {
$vpl = 3;
}
@ -542,6 +595,17 @@ switch ($action) {
->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'))
->where('tbl_voucher.status', '0')
->where_gt('tbl_voucher.id', $from_id);
} else if ($from_id > 0 && $planid == 0 && $selected_datetime != '') {
$v = ORM::for_table('tbl_plans')
->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'))
->where('tbl_voucher.status', '0')
->where_raw("DATE(created_at) = ?", [$selected_datetime])
->where_gt('tbl_voucher.id', $from_id)
->limit($limit);
$vc = ORM::for_table('tbl_plans')
->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'))
->where('tbl_voucher.status', '0')
->where_gt('tbl_voucher.id', $from_id);
} else {
$v = ORM::for_table('tbl_plans')
->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'))
@ -551,6 +615,16 @@ switch ($action) {
->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'))
->where('tbl_voucher.status', '0');
}
if (!empty($selected_datetime)) {
$v = ORM::for_table('tbl_plans')
->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'))
->where('tbl_voucher.status', '0')
->where('tbl_voucher.created_at', $selected_datetime)
->limit($limit);
$vc = ORM::for_table('tbl_plans')
->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'))
->where('tbl_voucher.status', '0');
}
if (in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
$v = $v->find_many();
$vc = $vc->count();
@ -577,6 +651,19 @@ switch ($action) {
$ui->assign('limit', $limit);
$ui->assign('planid', $planid);
$createdate = ORM::for_table('tbl_voucher')
->select_expr(
"CASE WHEN DATE(created_at) = CURDATE() THEN 'Today' ELSE DATE(created_at) END",
'created_datetime'
)
->where_not_equal('created_at', '0')
->select_expr('COUNT(*)', 'voucher_count')
->group_by('created_datetime')
->order_by_desc('created_datetime')
->find_array();
$ui->assign('createdate', $createdate);
$voucher = [];
$n = 1;
foreach ($v as $vs) {
@ -592,6 +679,7 @@ switch ($action) {
$ui->assign('voucher', $voucher);
$ui->assign('vc', $vc);
$ui->assign('selected_datetime', $selected_datetime);
//for counting pagebreak
$ui->assign('jml', 0);
@ -602,6 +690,7 @@ switch ($action) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$type = _post('type');
$plan = _post('plan');
$voucher_format = _post('voucher_format');
@ -609,18 +698,22 @@ switch ($action) {
$server = _post('server');
$numbervoucher = _post('numbervoucher');
$lengthcode = _post('lengthcode');
$printNow = _post('print_now', 'no');
$voucherPerPage = _post('voucher_per_page', '36');
$msg = '';
if ($type == '' or $plan == '' or $server == '' or $numbervoucher == '' or $lengthcode == '') {
$msg .= Lang::T('All field is required') . '<br>';
if (empty($type) || empty($plan) || empty($server) || empty($numbervoucher) || empty($lengthcode)) {
$msg .= Lang::T('All fields are required') . '<br>';
}
if (Validator::UnsignedNumber($numbervoucher) == false) {
if (!Validator::UnsignedNumber($numbervoucher)) {
$msg .= 'The Number of Vouchers must be a number' . '<br>';
}
if (Validator::UnsignedNumber($lengthcode) == false) {
if (!Validator::UnsignedNumber($lengthcode)) {
$msg .= 'The Length Code must be a number' . '<br>';
}
if ($msg == '') {
// Update or create voucher prefix
if (!empty($prefix)) {
$d = ORM::for_table('tbl_appconfig')->where('setting', 'voucher_prefix')->find_one();
if ($d) {
@ -633,11 +726,14 @@ switch ($action) {
$d->save();
}
}
run_hook('create_voucher'); #HOOK
run_hook('create_voucher'); // HOOK
$vouchers = [];
$newVoucherIds = [];
if ($voucher_format == 'numbers') {
if (strlen($lengthcode) < 6) {
$msg .= 'The Length Code must be a more than 6 for numbers' . '<br>';
if ($lengthcode < 6) {
$msg .= 'The Length Code must be more than 6 for numbers' . '<br>';
}
$vouchers = generateUniqueNumericVouchers($numbervoucher, $lengthcode);
} else {
@ -657,19 +753,91 @@ switch ($action) {
$d->type = $type;
$d->routers = $server;
$d->id_plan = $plan;
$d->code = $prefix . $code;
$d->code = "$prefix$code";
$d->user = '0';
$d->status = '0';
$d->generated_by = $admin['id'];
$d->save();
}
if ($numbervoucher == 1) {
r2(U . 'plan/voucher-view/' . $d->id(), 's', Lang::T('Create Vouchers Successfully'));
$newVoucherIds[] = $d->id();
}
r2(U . 'plan/voucher', 's', Lang::T('Create Vouchers Successfully'));
if ($printNow == 'yes' && count($newVoucherIds) > 0) {
$template = file_get_contents("pages/Voucher.html");
$template = str_replace('[[company_name]]', $config['CompanyName'], $template);
$vouchersToPrint = ORM::for_table('tbl_voucher')
->left_outer_join('tbl_plans', ['tbl_plans.id', '=', 'tbl_voucher.id_plan'])
->where_in('tbl_voucher.id', $newVoucherIds)
->find_many();
$voucherHtmls = [];
$n = 1;
foreach ($vouchersToPrint as $vs) {
$temp = $template;
$temp = str_replace('[[qrcode]]', '<img src="qrcode/?data=' . $vs['code'] . '">', $temp);
$temp = str_replace('[[price]]', Lang::moneyFormat($vs['price']), $temp);
$temp = str_replace('[[voucher_code]]', $vs['code'], $temp);
$temp = str_replace('[[plan]]', $vs['name_plan'], $temp);
$temp = str_replace('[[counter]]', $n, $temp);
$voucherHtmls[] = $temp;
$n++;
}
$vc = count($voucherHtmls);
$ui->assign('voucher', $voucherHtmls);
$ui->assign('vc', $vc);
$ui->assign('jml', 0);
$ui->assign('from_id', 0);
$ui->assign('vpl', '3');
$ui->assign('pagebreak', $voucherPerPage);
$ui->display('print-voucher.tpl');
}
if ($numbervoucher == 1) {
r2(getUrl('plan/voucher-view/') . $d->id(), 's', Lang::T('Create Vouchers Successfully'));
}
r2(getUrl('plan/voucher'), 's', Lang::T('Create Vouchers Successfully'));
} else {
r2(U . 'plan/add-voucher/' . $id, 'e', $msg);
r2(getUrl('plan/add-voucher/') . $id, 'e', $msg);
}
break;
case 'voucher-delete-many':
header('Content-Type: application/json');
$admin = Admin::_info();
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$voucherIds = json_decode($_POST['voucherIds'], true);
if (is_array($voucherIds) && !empty($voucherIds)) {
$voucherIds = array_map('intval', $voucherIds);
try {
ORM::for_table('tbl_voucher')
->where_in('id', $voucherIds)
->delete_many();
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => Lang::T('Failed to delete vouchers.')]);
exit;
}
// Return success response
echo json_encode(['status' => 'success', 'message' => Lang::T("Vouchers Deleted Successfully.")]);
exit;
} else {
echo json_encode(['status' => 'error', 'message' => Lang::T("Invalid or missing voucher IDs.")]);
exit;
}
} else {
echo json_encode(['status' => 'error', 'message' => Lang::T("Invalid request method.")]);
}
break;
@ -687,11 +855,11 @@ switch ($action) {
$voucher = ORM::for_table('tbl_voucher')
->find_one($id);
if (!in_array($voucher['generated_by'], $sales)) {
r2(U . 'plan/voucher/', 'e', Lang::T('Voucher Not Found'));
r2(getUrl('plan/voucher/'), 'e', Lang::T('Voucher Not Found'));
}
}
if (!$voucher) {
r2(U . 'plan/voucher/', 'e', Lang::T('Voucher Not Found'));
r2(getUrl('plan/voucher/'), 'e', Lang::T('Voucher Not Found'));
}
$plan = ORM::for_table('tbl_plans')->find_one($voucher['id_plan']);
if ($voucher && $plan) {
@ -725,7 +893,7 @@ switch ($action) {
$ui->assign('whatsapp', urlencode("```$content```"));
$ui->display('voucher-view.tpl');
} else {
r2(U . 'plan/voucher/', 'e', Lang::T('Voucher Not Found'));
r2(getUrl('plan/voucher/'), 'e', Lang::T('Voucher Not Found'));
}
break;
case 'voucher-delete':
@ -737,7 +905,7 @@ switch ($action) {
$d = ORM::for_table('tbl_voucher')->find_one($id);
if ($d) {
$d->delete();
r2(U . 'plan/voucher', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('plan/voucher'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -756,9 +924,9 @@ switch ($action) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$code = _post('code');
$code = Text::alphanumeric(_post('code'), "-_.,");
$user = ORM::for_table('tbl_customers')->where('id', _post('id_customer'))->find_one();
$v1 = ORM::for_table('tbl_voucher')->where('code', $code)->where('status', 0)->find_one();
$v1 = ORM::for_table('tbl_voucher')->whereRaw("BINARY code = '$code'")->where('status', 0)->find_one();
run_hook('refill_customer'); #HOOK
if ($v1) {
@ -770,10 +938,10 @@ switch ($action) {
Package::createInvoice($in);
$ui->display('invoice.tpl');
} else {
r2(U . 'plan/refill', 'e', "Failed to refill account");
r2(getUrl('plan/refill'), 'e', "Failed to refill account");
}
} else {
r2(U . 'plan/refill', 'e', Lang::T('Voucher Not Valid'));
r2(getUrl('plan/refill'), 'e', Lang::T('Voucher Not Valid'));
}
break;
case 'deposit':
@ -795,39 +963,57 @@ switch ($action) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$user = _post('id_customer');
$amount = _post('amount');
$plan = _post('id_plan');
$stoken = _req('stoken');
if (App::getTokenValue($stoken)) {
$c = ORM::for_table('tbl_customers')->where('id', $user)->find_one();
$in = ORM::for_table('tbl_transactions')->where('username', $c['username'])->order_by_desc('id')->find_one();
$note = _post('note');
$svoucher = _req('svoucher');
$c = ORM::for_table('tbl_customers')->find_one($user);
if (App::getVoucherValue($svoucher)) {
$in = ORM::for_table('tbl_transactions')->find_one(App::getVoucherValue($svoucher));
Package::createInvoice($in);
$ui->display('invoice.tpl');
die();
}
run_hook('deposit_customer'); #HOOK
if (!empty($user) && !empty($plan)) {
if (Package::rechargeUser($user, 'balance', $plan, "Deposit", $admin['fullname'])) {
$c = ORM::for_table('tbl_customers')->where('id', $user)->find_one();
$in = ORM::for_table('tbl_transactions')->where('username', $c['username'])->order_by_desc('id')->find_one();
if (!empty($user) && strlen($amount) > 0 && $amount != 0) {
$plan = [];
$plan['name_plan'] = Lang::T('Balance');
$plan['price'] = $amount;
$trxId = Package::rechargeBalance($c, $plan, "Deposit", $admin['fullname'], $note);
if ($trxId > 0) {
$in = ORM::for_table('tbl_transactions')->find_one($trxId);
Package::createInvoice($in);
if (!empty($stoken)) {
App::setToken($stoken, $in['id']);
if (!empty($svoucher)) {
App::setVoucher($svoucher, $trxId);
}
$ui->display('invoice.tpl');
} else {
r2(U . 'plan/refill', 'e', "Failed to refill account");
r2(getUrl('plan/refill'), 'e', "Failed to refill account");
}
} else if (!empty($user) && !empty($plan)) {
$p = ORM::for_table('tbl_plans')->find_one($plan);
$trxId = Package::rechargeBalance($c, $p, "Deposit", $admin['fullname'], $note);
if ($trxId > 0) {
$in = ORM::for_table('tbl_transactions')->find_one($trxId);
Package::createInvoice($in);
if (!empty($svoucher)) {
App::setVoucher($svoucher, $trxId);
}
$ui->display('invoice.tpl');
} else {
r2(getUrl('plan/refill'), 'e', "Failed to refill account");
}
} else {
r2(U . 'plan/refill', 'e', "All field is required");
r2(getUrl('plan/refill'), 'e', "All field is required");
}
break;
case 'extend':
$id = $routes[2];
$days = $routes[3];
$stoken = $_GET['stoken'];
if (App::getTokenValue($stoken)) {
r2(U . 'plan', 's', "Extend already done");
$svoucher = $_GET['svoucher'];
if (App::getVoucherValue($svoucher)) {
r2(getUrl('plan'), 's', "Extend already done");
}
$tur = ORM::for_table('tbl_user_recharges')->find_one($id);
$status = $tur['status'];
@ -839,7 +1025,7 @@ switch ($action) {
//expired
$expiration = date('Y-m-d', strtotime(" +$days day"));
}
App::setToken($stoken, $id);
App::setVoucher($svoucher, $id);
$c = ORM::for_table('tbl_customers')->findOne($tur['customer_id']);
if ($c) {
$p = ORM::for_table('tbl_plans')->find_one($tur['plan_id']);
@ -848,6 +1034,8 @@ switch ($action) {
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
global $isChangePlan;
$isChangePlan = true;
(new $p['device'])->add_customer($c, $p);
} else {
new Exception(Lang::T("Devices Not Found"));
@ -857,23 +1045,23 @@ switch ($action) {
$tur->status = "on";
$tur->save();
} else {
r2(U . 'plan', 's', "Plan not found");
r2(getUrl('plan'), 's', "Plan not found");
}
} else {
r2(U . 'plan', 's', "Customer not found");
r2(getUrl('plan'), 's', "Customer not found");
}
Message::sendTelegram("#u$tur[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
"\nLocation: " . $p['routers'] .
"\nCustomer: " . $c['fullname'] .
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
_log("$admin[fullname] extend Customer $tur[customer_id] $tur[username] for $days days", $admin['user_type'], $admin['id']);
r2(U . 'plan', 's', "Extend until $expiration");
r2(getUrl('plan'), 's', "Extend until $expiration");
} else {
r2(U . 'plan', 's', "Customer is not expired yet");
r2(getUrl('plan'), 's', "Customer is not expired yet");
}
break;
default:
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/plan.js"></script>');
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/plan.js"></script>');
$ui->assign('_title', Lang::T('Customer'));
$search = _post('search');
$status = _req('status');

View File

@ -7,5 +7,5 @@
if(function_exists($routes[1])){
call_user_func($routes[1]);
}else{
r2(U.'dashboard', 'e', 'Function not found');
r2(getUrl('dashboard'), 'e', 'Function not found');
}

View File

@ -25,7 +25,7 @@ if (file_exists($cache) && time() - filemtime($cache) < (24 * 60 * 60)) {
$json = json_decode($txt, true);
if (empty($json['plugins']) && empty($json['payment_gateway'])) {
unlink($cache);
r2(U . 'pluginmanager');
r2(getUrl('pluginmanager'));
}
} else {
$data = Http::getData($plugin_repository);
@ -35,23 +35,23 @@ if (file_exists($cache) && time() - filemtime($cache) < (24 * 60 * 60)) {
switch ($action) {
case 'refresh':
if (file_exists($cache)) unlink($cache);
r2(U . "pluginmanager", 's', 'Refresh success');
r2(getUrl('pluginmanager'), 's', 'Refresh success');
break;
case 'dlinstall':
if($_app_stage == 'demo'){
r2(U . "pluginmanager", 'e', 'Demo Mode cannot install as it Security risk');
if ($_app_stage == 'demo') {
r2(getUrl('pluginmanager'), 'e', 'Demo Mode cannot install as it Security risk');
}
if (!is_writeable($CACHE_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder cache/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder cache/ is not writable');
}
if (!is_writeable($PLUGIN_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder plugin/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder plugin/ is not writable');
}
if (!is_writeable($DEVICE_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder devices/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder devices/ is not writable');
}
if (!is_writeable($UI_PATH . DIRECTORY_SEPARATOR . 'themes')) {
r2(U . "pluginmanager", 'e', 'Folder themes/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder themes/ is not writable');
}
$cache = $CACHE_PATH . DIRECTORY_SEPARATOR . 'installer' . DIRECTORY_SEPARATOR;
if (!file_exists($cache)) {
@ -62,25 +62,47 @@ switch ($action) {
$zip->open($_FILES['zip_plugin']['tmp_name']);
$zip->extractTo($cache);
$zip->close();
$plugin = basename($_FILES['zip_plugin']['name']);
unlink($_FILES['zip_plugin']['tmp_name']);
$success = 0;
//moving
if (file_exists($cache . 'plugin')) {
File::copyFolder($cache . 'plugin' . DIRECTORY_SEPARATOR, $PLUGIN_PATH . DIRECTORY_SEPARATOR);
$success++;
}
if (file_exists($cache . 'paymentgateway')) {
File::copyFolder($cache . 'paymentgateway' . DIRECTORY_SEPARATOR, $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR);
$success++;
}
if (file_exists($cache . 'theme')) {
File::copyFolder($cache . 'theme' . DIRECTORY_SEPARATOR, $UI_PATH . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR);
$success++;
}
if (file_exists($cache . 'device')) {
File::copyFolder($cache . 'device' . DIRECTORY_SEPARATOR, $DEVICE_PATH . DIRECTORY_SEPARATOR);
$success++;
}
if ($success == 0) {
// old plugin and theme using this
$check = strtolower($ghUrl);
if (strpos($check, 'plugin') !== false) {
File::copyFolder($folder, $PLUGIN_PATH . DIRECTORY_SEPARATOR);
} else if (strpos($check, 'payment') !== false) {
File::copyFolder($folder, $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR);
} else if (strpos($check, 'theme') !== false) {
rename($folder, $UI_PATH . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $plugin);
} else if (strpos($check, 'device') !== false) {
File::copyFolder($folder, $DEVICE_PATH . DIRECTORY_SEPARATOR);
}
}
//Cleaning
File::deleteFolder($cache);
r2(U . "pluginmanager", 's', 'Installation success');
r2(getUrl('pluginmanager'), 's', 'Installation success');
} else if (_post('gh_url', '') != '') {
$ghUrl = _post('gh_url', '');
if (!empty($config['github_token']) && !empty($config['github_username'])) {
$ghUrl = str_replace('https://github.com', 'https://' . $config['github_username'] . ':' . $config['github_token'] . '@github.com', $ghUrl);
}
$plugin = basename($ghUrl);
$file = $cache . $plugin . '.zip';
$fp = fopen($file, 'w+');
@ -99,30 +121,51 @@ switch ($action) {
$zip->extractTo($cache);
$zip->close();
$folder = $cache . DIRECTORY_SEPARATOR . $plugin . '-main' . DIRECTORY_SEPARATOR;
if(!file_exists($folder)) {
$folder = $cache . DIRECTORY_SEPARATOR . $plugin . '-master' . DIRECTORY_SEPARATOR;
}
$success = 0;
if (file_exists($folder . 'plugin')) {
File::copyFolder($folder . 'plugin' . DIRECTORY_SEPARATOR, $PLUGIN_PATH . DIRECTORY_SEPARATOR);
$success++;
}
if (file_exists($folder . 'paymentgateway')) {
File::copyFolder($folder . 'paymentgateway' . DIRECTORY_SEPARATOR, $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR);
$success++;
}
if (file_exists($folder . 'theme')) {
File::copyFolder($folder . 'theme' . DIRECTORY_SEPARATOR, $UI_PATH . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR);
$success++;
}
if (file_exists($folder . 'device')) {
File::copyFolder($folder . 'device' . DIRECTORY_SEPARATOR, $DEVICE_PATH . DIRECTORY_SEPARATOR);
$success++;
}
if ($success == 0) {
// old plugin and theme using this
$check = strtolower($ghUrl);
if (strpos($check, 'plugin') !== false) {
File::copyFolder($folder, $PLUGIN_PATH . DIRECTORY_SEPARATOR);
} else if (strpos($check, 'payment') !== false) {
File::copyFolder($folder, $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR);
} else if (strpos($check, 'theme') !== false) {
rename($folder, $UI_PATH . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $plugin);
} else if (strpos($check, 'device') !== false) {
File::copyFolder($folder, $DEVICE_PATH . DIRECTORY_SEPARATOR);
}
}
File::deleteFolder($cache);
r2(U . "pluginmanager", 's', 'Installation success');
r2(getUrl('pluginmanager'), 's', 'Installation success');
} else {
r2(U . 'pluginmanager', 'e', 'Nothing Installed');
r2(getUrl('pluginmanager'), 'e', 'Nothing Installed');
}
break;
case 'delete':
if (!is_writeable($CACHE_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder cache/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder cache/ is not writable');
}
if (!is_writeable($PLUGIN_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder plugin/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder plugin/ is not writable');
}
set_time_limit(-1);
$tipe = $routes['2'];
@ -132,6 +175,9 @@ switch ($action) {
if ($tipe == 'plugin') {
foreach ($json['plugins'] as $plg) {
if ($plg['id'] == $plugin) {
if (!empty($config['github_token']) && !empty($config['github_username'])) {
$plg['github'] = str_replace('https://github.com', 'https://' . $config['github_username'] . ':' . $config['github_token'] . '@github.com', $plg['github']);
}
$fp = fopen($file, 'w+');
$ch = curl_init($plg['github'] . '/archive/refs/heads/master.zip');
curl_setopt($ch, CURLOPT_POST, 0);
@ -153,12 +199,12 @@ switch ($action) {
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
}
if (!file_exists($folder)) {
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
r2(getUrl('pluginmanager'), 'e', 'Extracted Folder is unknown');
}
scanAndRemovePath($folder, $PLUGIN_PATH . DIRECTORY_SEPARATOR);
File::deleteFolder($folder);
unlink($file);
r2(U . "pluginmanager", 's', 'Plugin ' . $plugin . ' has been deleted');
r2(getUrl('pluginmanager'), 's', 'Plugin ' . $plugin . ' has been deleted');
break;
}
}
@ -167,10 +213,10 @@ switch ($action) {
break;
case 'install':
if (!is_writeable($CACHE_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder cache/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder cache/ is not writable');
}
if (!is_writeable($PLUGIN_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder plugin/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder plugin/ is not writable');
}
set_time_limit(-1);
$tipe = $routes['2'];
@ -180,6 +226,9 @@ switch ($action) {
if ($tipe == 'plugin') {
foreach ($json['plugins'] as $plg) {
if ($plg['id'] == $plugin) {
if (!empty($config['github_token']) && !empty($config['github_username'])) {
$plg['github'] = str_replace('https://github.com', 'https://' . $config['github_username'] . ':' . $config['github_token'] . '@github.com', $plg['github']);
}
$fp = fopen($file, 'w+');
$ch = curl_init($plg['github'] . '/archive/refs/heads/master.zip');
curl_setopt($ch, CURLOPT_POST, 0);
@ -201,12 +250,12 @@ switch ($action) {
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
}
if (!file_exists($folder)) {
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
r2(getUrl('pluginmanager'), 'e', 'Extracted Folder is unknown');
}
File::copyFolder($folder, $PLUGIN_PATH . DIRECTORY_SEPARATOR, ['README.md', 'LICENSE']);
File::deleteFolder($folder);
unlink($file);
r2(U . "pluginmanager", 's', 'Plugin ' . $plugin . ' has been installed');
r2(getUrl('pluginmanager'), 's', 'Plugin ' . $plugin . ' has been installed');
break;
}
}
@ -214,6 +263,9 @@ switch ($action) {
} else if ($tipe == 'payment') {
foreach ($json['payment_gateway'] as $plg) {
if ($plg['id'] == $plugin) {
if (!empty($config['github_token']) && !empty($config['github_username'])) {
$plg['github'] = str_replace('https://github.com', 'https://' . $config['github_username'] . ':' . $config['github_token'] . '@github.com', $plg['github']);
}
$fp = fopen($file, 'w+');
$ch = curl_init($plg['github'] . '/archive/refs/heads/master.zip');
curl_setopt($ch, CURLOPT_POST, 0);
@ -235,12 +287,49 @@ switch ($action) {
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
}
if (!file_exists($folder)) {
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
r2(getUrl('pluginmanager'), 'e', 'Extracted Folder is unknown');
}
File::copyFolder($folder, $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR, ['README.md', 'LICENSE']);
File::deleteFolder($folder);
unlink($file);
r2(U . "paymentgateway", 's', 'Payment Gateway ' . $plugin . ' has been installed');
r2(getUrl('paymentgateway'), 's', 'Payment Gateway ' . $plugin . ' has been installed');
break;
}
}
break;
} else if ($tipe == 'device') {
foreach ($json['devices'] as $d) {
if ($d['id'] == $plugin) {
if (!empty($config['github_token']) && !empty($config['github_username'])) {
$d['github'] = str_replace('https://github.com', 'https://' . $config['github_username'] . ':' . $config['github_token'] . '@github.com', $d['github']);
}
$fp = fopen($file, 'w+');
$ch = curl_init($d['github'] . '/archive/refs/heads/master.zip');
curl_setopt($ch, CURLOPT_POST, 0);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_exec($ch);
curl_close($ch);
fclose($fp);
$zip = new ZipArchive();
$zip->open($file);
$zip->extractTo($CACHE_PATH);
$zip->close();
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-main/');
if (!file_exists($folder)) {
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
}
if (!file_exists($folder)) {
r2(getUrl('pluginmanager'), 'e', 'Extracted Folder is unknown');
}
File::copyFolder($folder, $DEVICE_PATH . DIRECTORY_SEPARATOR, ['README.md', 'LICENSE']);
File::deleteFolder($folder);
unlink($file);
r2(getUrl('settings/devices'), 's', 'Device ' . $plugin . ' has been installed');
break;
}
}
@ -255,6 +344,7 @@ switch ($action) {
$ui->assign('zipExt', $zipExt);
$ui->assign('plugins', $json['plugins']);
$ui->assign('pgs', $json['payment_gateway']);
$ui->assign('dvcs', $json['devices']);
$ui->display('plugin-manager.tpl');
}

View File

@ -20,7 +20,7 @@ require_once $DEVICE_PATH . DIRECTORY_SEPARATOR . 'MikrotikPppoe' . '.php';
switch ($action) {
case 'list':
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/pool.js"></script>');
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/pool.js"></script>');
$name = _post('name');
if ($name != '') {
@ -51,7 +51,7 @@ switch ($action) {
run_hook('view_edit_pool'); #HOOK
$ui->display('pool-edit.tpl');
} else {
r2(U . 'pool/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('pool/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -65,7 +65,7 @@ switch ($action) {
}
$d->delete();
r2(U . 'pool/list', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('pool/list'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -78,7 +78,7 @@ switch ($action) {
$log .= 'DONE: ' . $pool['pool_name'] . ': ' . $pool['range_ip'] . '<br>';
}
}
r2(U . 'pool/list', 's', $log);
r2(getUrl('pool/list'), 's', $log);
break;
case 'add-post':
$name = _post('name');
@ -108,9 +108,9 @@ switch ($action) {
(new MikrotikPppoe())->add_pool($b);
}
$b->save();
r2(U . 'pool/list', 's', Lang::T('Data Created Successfully'));
r2(getUrl('pool/list'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'pool/add', 'e', $msg);
r2(getUrl('pool/add'), 'e', $msg);
}
break;
@ -143,12 +143,136 @@ switch ($action) {
(new MikrotikPppoe())->update_pool($old, $d);
}
r2(U . 'pool/list', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('pool/list'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'pool/edit/' . $id, 'e', $msg);
r2(getUrl('pool/edit/') . $id, 'e', $msg);
}
case 'port':
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/pool.js"></script>');
$name = _post('name');
if ($name != '') {
$query = ORM::for_table('tbl_port_pool')->where_like('pool_name', '%' . $name . '%')->order_by_desc('id');
$d = Paginator::findMany($query, ['name' => $name]);
} else {
$query = ORM::for_table('tbl_port_pool')->order_by_desc('id');
$d = Paginator::findMany($query);
}
$ui->assign('d', $d);
run_hook('view_port'); #HOOK
$ui->display('port.tpl');
break;
case 'add-port':
$r = ORM::for_table('tbl_routers')->find_many();
$ui->assign('r', $r);
run_hook('view_add_port'); #HOOK
$ui->display('port-add.tpl');
break;
case 'edit-port':
$id = $routes['2'];
$d = ORM::for_table('tbl_port_pool')->find_one($id);
if ($d) {
$ui->assign('d', $d);
run_hook('view_edit_port'); #HOOK
$ui->display('port-edit.tpl');
} else {
r2(getUrl('pool/port'), 'e', Lang::T('Account Not Found'));
}
break;
case 'delete-port':
$id = $routes['2'];
run_hook('delete_port'); #HOOK
$d = ORM::for_table('tbl_port_pool')->find_one($id);
if ($d) {
$d->delete();
r2(getUrl('pool/port'), 's', Lang::T('Data Deleted Successfully'));
}
break;
case 'sync':
$pools = ORM::for_table('tbl_port_pool')->find_many();
$log = '';
foreach ($pools as $pool) {
if ($pool['routers'] != 'radius') {
(new MikrotikPppoe())->update_pool($pool, $pool);
$log .= 'DONE: ' . $pool['port_name'] . ': ' . $pool['range_port'] . '<br>';
}
}
r2(getUrl('pool/list'), 's', $log);
break;
case 'add-port-post':
$name = _post('name');
$port_range = _post('port_range');
$public_ip = _post('public_ip');
$routers = _post('routers');
run_hook('add_pool'); #HOOK
$msg = '';
if (Validator::Length($name, 30, 2) == false) {
$msg .= 'Name should be between 3 to 30 characters' . '<br>';
}
if ($port_range == '' or $routers == '') {
$msg .= Lang::T('All field is required') . '<br>';
}
$d = ORM::for_table('tbl_port_pool')->where('routers', $routers)->find_one();
if ($d) {
$msg .= Lang::T('Routers already have ports, each router can only have 1 port range!') . '<br>';
}
if ($msg == '') {
$b = ORM::for_table('tbl_port_pool')->create();
$b->public_ip = $public_ip;
$b->port_name = $name;
$b->range_port = $port_range;
$b->routers = $routers;
$b->save();
r2(getUrl('pool/port'), 's', Lang::T('Data Created Successfully'));
} else {
r2(getUrl('pool/add-port'), 'e', $msg);
}
break;
case 'edit-port-post':
$name = _post('name');
$public_ip = _post('public_ip');
$range_port = _post('range_port');
$routers = _post('routers');
run_hook('edit_port'); #HOOK
$msg = '';
$msg = '';
if (Validator::Length($name, 30, 2) == false) {
$msg .= 'Name should be between 3 to 30 characters' . '<br>';
}
if ($range_port == '' or $routers == '') {
$msg .= Lang::T('All field is required') . '<br>';
}
$id = _post('id');
$d = ORM::for_table('tbl_port_pool')->find_one($id);
$old = ORM::for_table('tbl_port_pool')->find_one($id);
if (!$d) {
$msg .= Lang::T('Data Not Found') . '<br>';
}
if ($msg == '') {
$d->port_name = $name;
$d->public_ip = $public_ip;
$d->range_port = $range_port;
$d->routers = $routers;
$d->save();
r2(getUrl('pool/port'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(getUrl('pool/edit-port/') . $id, 'e', $msg);
}
break;
default:
r2(U . 'pool/list/', 's', '');
r2(getUrl('pool/list/'), 's', '');
}

View File

@ -58,12 +58,12 @@ switch ($action) {
if ($msg == '') {
require_once $DEVICE_PATH . DIRECTORY_SEPARATOR . "Radius.php";
if ((new Radius())->nasAdd($shortname, $nasname, $ports, $secret, $routers, $description, $type, $server, $community) > 0) {
r2(U . 'radius/nas-list/', 's', "NAS Added");
r2(getUrl('radius/nas-list/'), 's', "NAS Added");
} else {
r2(U . 'radius/nas-add/', 'e', "NAS Added Failed");
r2(getUrl('radius/nas-add/'), 'e', "NAS Added Failed");
}
} else {
r2(U . 'radius/nas-add', 'e', $msg);
r2(getUrl('radius/nas-add'), 'e', $msg);
}
break;
case 'nas-edit':
@ -80,7 +80,7 @@ switch ($action) {
$ui->assign('d', $d);
$ui->display('radius-nas-edit.tpl');
} else {
r2(U . 'radius/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('radius/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -115,12 +115,12 @@ switch ($action) {
if ($msg == '') {
require_once $DEVICE_PATH . DIRECTORY_SEPARATOR . "Radius.php";
if ((new Radius())->nasUpdate($id, $shortname, $nasname, $ports, $secret, $routers, $description, $type, $server, $community)) {
r2(U . 'radius/list/', 's', "NAS Saved");
r2(getUrl('radius/list/'), 's', "NAS Saved");
} else {
r2(U . 'radius/nas-add', 'e', 'NAS NOT Exists');
r2(getUrl('radius/nas-add'), 'e', 'NAS NOT Exists');
}
} else {
r2(U . 'radius/nas-add', 'e', $msg);
r2(getUrl('radius/nas-add'), 'e', $msg);
}
break;
case 'nas-delete':
@ -129,7 +129,7 @@ switch ($action) {
if ($d) {
$d->delete();
} else {
r2(U . 'radius/nas-list', 'e', 'NAS Not found');
r2(getUrl('radius/nas-list'), 'e', 'NAS Not found');
}
default:
$ui->assign('_system_menu', 'radius');

View File

@ -5,6 +5,9 @@
* by https://t.me/ibnux
**/
if ($_c['disable_registration'] == 'noreg') {
_alert(Lang::T('Registration Disabled'), 'danger', "login");
}
if (isset($routes['1'])) {
$do = $routes['1'];
} else {
@ -22,36 +25,39 @@ switch ($do) {
$password = _post('password');
$cpassword = _post('cpassword');
$address = _post('address');
if (!empty($config['sms_url'])) {
$phonenumber = Lang::phoneFormat($username);
$username = $phonenumber;
} else if (strlen($username) < 21) {
$phonenumber = $username;
}
// Separate phone number input if OTP is required
$phone_number = ($config['sms_otp_registration'] == 'yes') ? alphanumeric(_post('phone_number'), "+_.@-") : $username;
$msg = '';
if (Validator::Length($username, 35, 2) == false) {
$msg .= 'Username should be between 3 to 55 characters' . '<br>';
$msg .= "Username should be between 3 to 55 characters<br>";
}
if (Validator::Length($fullname, 36, 2) == false) {
$msg .= 'Full Name should be between 3 to 25 characters' . '<br>';
if ($config['man_fields_fname'] == 'yes') {
if (Validator::Length($fullname, 36, 2) == false) {
$msg .= "Full Name should be between 3 to 25 characters<br>";
}
}
if (!Validator::Length($password, 35, 2)) {
$msg .= 'Password should be between 3 to 35 characters' . '<br>';
$msg .= "Password should be between 3 to 35 characters<br>";
}
if (!Validator::Email($email)) {
$msg .= 'Email is not Valid<br>';
if ($config['man_fields_email'] == 'yes') {
if (!Validator::Email($email)) {
$msg .= 'Email is not Valid<br>';
}
}
if ($password != $cpassword) {
$msg .= Lang::T('Passwords does not match') . '<br>';
}
if (!empty($config['sms_url'])) {
$otpPath .= sha1($username . $db_password) . ".txt";
// OTP verification if OTP is enabled
if ($_c['sms_otp_registration'] == 'yes') {
$otpPath .= sha1("$phone_number$db_pass") . ".txt";
run_hook('validate_otp'); #HOOK
//expired 10 minutes
// Expire after 10 minutes
if (file_exists($otpPath) && time() - filemtime($otpPath) > 1200) {
unlink($otpPath);
r2(U . 'register', 's', 'Verification code expired');
r2(getUrl('register'), 's', 'Verification code expired');
} else if (file_exists($otpPath)) {
$code = file_get_contents($otpPath);
if ($code != $otp_code) {
@ -59,97 +65,146 @@ switch ($do) {
$ui->assign('fullname', $fullname);
$ui->assign('address', $address);
$ui->assign('email', $email);
$ui->assign('phonenumber', $phonenumber);
$ui->assign('phone_number', $phone_number);
$ui->assign('notify', 'Wrong Verification code');
$ui->assign('notify_t', 'd');
$ui->display('register-otp.tpl');
$ui->assign('_title', Lang::T('Register'));
$ui->display('customer/register-otp.tpl');
exit();
} else {
unlink($otpPath);
}
} else {
r2(U . 'register', 's', 'No Verification code');
r2(getUrl('register'), 's', 'No Verification code');
}
}
// Check if username already exists
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
if ($d) {
$msg .= Lang::T('Account already axist') . '<br>';
$msg .= Lang::T('Account already exists') . '<br>';
}
if ($msg == '') {
run_hook('register_user'); #HOOK
$d = ORM::for_table('tbl_customers')->create();
$d->username = alphanumeric($username, "+_.@-");
$d->password = $password;
$d->fullname = $fullname;
$d->address = $address;
$d->email = $email;
$d->phonenumber = $phonenumber;
$d->phonenumber = $phone_number;
if ($d->save()) {
$user = $d->id();
r2(U . 'login', 's', Lang::T('Register Success! You can login now'));
if ($config['photo_register'] == 'yes' && !empty($_FILES['photo']['name']) && file_exists($_FILES['photo']['tmp_name'])) {
if (function_exists('imagecreatetruecolor')) {
$hash = md5_file($_FILES['photo']['tmp_name']);
$subfolder = substr($hash, 0, 2);
$folder = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'photos' . DIRECTORY_SEPARATOR;
if (!file_exists($folder)) {
mkdir($folder);
}
$folder = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'photos' . DIRECTORY_SEPARATOR . $subfolder . DIRECTORY_SEPARATOR;
if (!file_exists($folder)) {
mkdir($folder);
}
$imgPath = $folder . $hash . '.jpg';
File::resizeCropImage($_FILES['photo']['tmp_name'], $imgPath, 1600, 1600, 100);
$d->photo = '/photos/' . $subfolder . '/' . $hash . '.jpg';
$d->save();
}
}
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
User::setFormCustomField($user);
run_hook('register_user'); #HOOK
$msg .= Lang::T('Registration successful') . '<br>';
if ($config['reg_nofify_admin'] == 'yes') {
sendTelegram($config['CompanyName'] . ' - ' . Lang::T('New User Registration') . "\n\nFull Name: " . $fullname . "\nUsername: " . $username . "\nEmail: " . $email . "\nPhone Number: " . $phone_number . "\nAddress: " . $address);
}
r2(getUrl('login'), 's', Lang::T('Register Success! You can login now'));
} else {
$ui->assign('username', $username);
$ui->assign('fullname', $fullname);
$ui->assign('address', $address);
$ui->assign('email', $email);
$ui->assign('phonenumber', $phonenumber);
$ui->assign('phone_number', $phone_number);
$ui->assign('notify', 'Failed to register');
$ui->assign('notify_t', 'd');
$ui->assign('_title', Lang::T('Register'));
run_hook('view_otp_register'); #HOOK
$ui->display('register-rotp.tpl');
$ui->display('customer/register-rotp.tpl');
}
} else {
$ui->assign('username', $username);
$ui->assign('fullname', $fullname);
$ui->assign('address', $address);
$ui->assign('email', $email);
$ui->assign('phonenumber', $phonenumber);
$ui->assign('phone_number', $phone_number);
$ui->assign('notify', $msg);
$ui->assign('notify_t', 'd');
$ui->display('register.tpl');
$ui->assign('_title', Lang::T('Register'));
// Check if OTP is enabled
if (!empty($config['sms_url']) && $_c['sms_otp_registration'] == 'yes') {
// Display register-otp.tpl if OTP is enabled
$ui->display('customer/register-otp.tpl');
} else {
// Display register.tpl if OTP is not enabled
$ui->display('customer/register.tpl');
}
}
break;
default:
if (!empty($config['sms_url'])) {
$username = _post('username');
if (!empty($username)) {
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
if ($_c['sms_otp_registration'] == 'yes') {
$phone_number = _post('phone_number');
if (!empty($phone_number)) {
$d = ORM::for_table('tbl_customers')->where('username', $phone_number)->find_one();
if ($d) {
r2(U . 'register', 's', Lang::T('Account already axist'));
r2(getUrl('register'), 's', Lang::T('Account already exists'));
}
if (!file_exists($otpPath)) {
mkdir($otpPath);
touch($otpPath . 'index.html');
}
$otpPath .= sha1($username . $db_password) . ".txt";
//expired 10 minutes
if (file_exists($otpPath) && time() - filemtime($otpPath) < 1200) {
$ui->assign('username', $username);
$ui->assign('notify', 'Please wait ' . (1200 - (time() - filemtime($otpPath))) . ' seconds before sending another SMS');
$otpPath .= sha1($phone_number . $db_pass) . ".txt";
if (file_exists($otpPath) && time() - filemtime($otpPath) < 600) {
$ui->assign('phone_number', $phone_number);
$ui->assign('notify', 'Please wait ' . (600 - (time() - filemtime($otpPath))) . ' seconds before sending another SMS');
$ui->assign('notify_t', 'd');
$ui->display('register-otp.tpl');
$ui->assign('_title', Lang::T('Register'));
$ui->display('customer/register-otp.tpl');
} else {
$otp = rand(100000, 999999);
file_put_contents($otpPath, $otp);
Message::sendSMS($username, $config['CompanyName'] . "\nYour Verification code are: $otp");
$ui->assign('username', $username);
$ui->assign('notify', 'Verification code has been sent to your phone');
if ($config['phone_otp_type'] == 'whatsapp') {
Message::sendWhatsapp($phone_number, $config['CompanyName'] . "\n\n" . Lang::T("Registration code") . "\n$otp");
} else if ($config['phone_otp_type'] == 'both') {
Message::sendWhatsapp($phone_number, $config['CompanyName'] . "\n\n" . Lang::T("Registration code") . "\n$otp");
Message::sendSMS($phone_number, $config['CompanyName'] . "\n\n" . Lang::T("Registration code") . "\n$otp");
} else {
Message::sendSMS($phone_number, $config['CompanyName'] . "\n\n" . Lang::T("Registration code") . "\n$otp");
}
$ui->assign('phone_number', $phone_number);
$ui->assign('notify', 'Registration code has been sent to your phone');
$ui->assign('notify_t', 's');
$ui->display('register-otp.tpl');
$ui->assign('_title', Lang::T('Register'));
$ui->assign('customFields', User::getFormCustomField($ui, true));
$ui->display('customer/register-otp.tpl');
}
} else {
$ui->assign('_title', Lang::T('Register'));
run_hook('view_otp_register'); #HOOK
$ui->display('register-rotp.tpl');
$ui->display('customer/register-rotp.tpl');
}
} else {
$ui->assign('customFields', User::getFormCustomField($ui, true));
$ui->assign('username', "");
$ui->assign('fullname', "");
$ui->assign('address', "");
$ui->assign('email', "");
$ui->assign('otp', false);
$ui->assign('_title', Lang::T('Register'));
run_hook('view_register'); #HOOK
$ui->display('register.tpl');
$ui->display('customer/register.tpl');
}
break;
}

View File

@ -21,13 +21,243 @@ $before_30_days = date('Y-m-d', strtotime('today - 30 days'));
$month_n = date('n');
switch ($action) {
case 'ajax':
$data = $routes['2'];
$reset_day = $config['reset_day'];
if (empty($reset_day)) {
$reset_day = 1;
}
//first day of month
if (date("d") >= $reset_day) {
$start_date = date('Y-m-' . $reset_day);
} else {
$start_date = date('Y-m-' . $reset_day, strtotime("-1 MONTH"));
}
$sd = _req('sd', $start_date);
$ed = _req('ed', $mdate);
$ts = _req('ts', '00:00:00');
$te = _req('te', '23:59:59');
$types = ORM::for_table('tbl_transactions')->getEnum('type');
$tps = ($_GET['tps']) ? $_GET['tps'] : $types;
$plans = array_column(ORM::for_table('tbl_transactions')->select('plan_name')->distinct('plan_name')->find_array(), 'plan_name');
$plns = ($_GET['plns']) ? $_GET['plns'] : $plans;
$methods = array_column(ORM::for_table('tbl_transactions')->rawQuery("SELECT DISTINCT SUBSTRING_INDEX(`method`, ' - ', 1) as method FROM tbl_transactions;")->findArray(), 'method');
$mts = ($_GET['mts']) ? $_GET['mts'] : $methods;
$routers = array_column(ORM::for_table('tbl_transactions')->select('routers')->distinct('routers')->find_array(), 'routers');
$rts = ($_GET['rts']) ? $_GET['rts'] : $routers;
$result = [];
switch ($data) {
case 'type':
foreach ($tps as $tp) {
$query = ORM::for_table('tbl_transactions')
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
->where('type', $tp);
if (count($mts) > 0) {
if (count($mts) != count($methods)) {
foreach ($mts as $mt) {
$query->where_like('method', "$mt - %");
}
}
}
if (count($rts) > 0) {
$query->where_in('routers', $rts);
}
if (count($plns) > 0) {
$query->where_in('plan_name', $plns);
}
$count = $query->count();
if ($count > 0) {
$result['datas'][] = $count;
$result['labels'][] = "$tp ($count)";
}
}
break;
case 'plan':
foreach ($plns as $pln) {
$query = ORM::for_table('tbl_transactions')
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
->where('plan_name', $pln);
if (count($tps) > 0) {
$query->where_in('type', $tps);
}
if (count($mts) > 0) {
if (count($mts) != count($methods)) {
foreach ($mts as $mt) {
$query->where_like('method', "$mt - %");
}
}
}
if (count($rts) > 0) {
$query->where_in('routers', $rts);
}
$count = $query->count();
if ($count > 0) {
$result['datas'][] = $count;
$result['labels'][] = "$pln ($count)";
}
}
break;
case 'method':
foreach ($mts as $mt) {
$query = ORM::for_table('tbl_transactions')
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
->where_like('method', "$mt - %");
if (count($tps) > 0) {
$query->where_in('type', $tps);
}
if (count($rts) > 0) {
$query->where_in('routers', $rts);
}
if (count($plns) > 0) {
$query->where_in('plan_name', $plns);
}
if (count($mts) > 0) {
if (count($mts) != count($methods)) {
foreach ($mts as $mt) {
$query->where_like('method', "$mt - %");
}
}
}
$count = $query->count();
if ($count > 0) {
$result['datas'][] = $count;
$result['labels'][] = "$mt ($count)";
}
}
break;
case 'router':
foreach ($rts as $rt) {
$query = ORM::for_table('tbl_transactions')
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
->where('routers', $rt);
if (count($tps) > 0) {
$query->where_in('type', $tps);
}
if (count($plns) > 0) {
$query->where_in('plan_name', $plns);
}
$count = $query->count();
if ($count > 0) {
$result['datas'][] = $count;
$result['labels'][] = "$rt ($count)";
}
}
break;
case 'line':
$query = ORM::for_table('tbl_transactions')
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
->order_by_desc('id');
if (count($tps) > 0) {
$query->where_in('type', $tps);
}
if (count($mts) > 0) {
if (count($mts) != count($methods)) {
foreach ($mts as $mt) {
$query->where_like('method', "$mt - %");
}
}
}
if (count($rts) > 0) {
$query->where_in('routers', $rts);
}
if (count($plns) > 0) {
$query->where_in('plan_name', $plns);
}
$datas = $query->find_array();
$period = new DatePeriod(
new DateTime($sd),
new DateInterval('P1D'),
new DateTime($ed)
);
$pos = 0;
$dates = [];
foreach ($period as $key => $value) {
$dates[] = $value->format('Y-m-d');
}
$dates = array_reverse($dates);
$result = [];
$temp;
foreach ($dates as $date) {
$result['labels'][] = $date;
// type
foreach ($tps as $key) {
if (!isset($temp[$key][$date])) {
$temp[$key][$date] = 0;
}
foreach ($datas as $data) {
if ($data['recharged_on'] == date('Y-m-d', strtotime($date)) && $data['type'] == $key) {
$temp[$key][$date] += 1;
}
}
}
//plan
foreach ($plns as $key) {
if (!isset($temp[$key][$date])) {
$temp[$key][$date] = 0;
}
foreach ($datas as $data) {
if ($data['recharged_on'] == date('Y-m-d', strtotime($date)) && $data['plan_name'] == $key) {
$temp[$key][$date] += 1;
}
}
}
//method
foreach ($mts as $key) {
if (!isset($temp[$key][$date])) {
$temp[$key][$date] = 0;
}
foreach ($datas as $data) {
if ($data['recharged_on'] == date('Y-m-d', strtotime($date)) && strpos($data['method'], $key) !== false) {
$temp[$key][$date] += 1;
}
}
}
foreach ($rts as $key) {
if (!isset($temp[$key][$date])) {
$temp[$key][$date] = 0;
}
foreach ($datas as $data) {
if ($data['recharged_on'] == date('Y-m-d', strtotime($date)) && $data['routers'] == $key) {
$temp[$key][$date] += 1;
}
}
}
$pos++;
if ($pos > 29) {
// only 30days
break;
}
}
foreach ($temp as $key => $value) {
$array = ['label' => $key];
$total = 0;
foreach ($value as $k => $v) {
$total += $v;
$array['data'][] = $v;
}
if($total>0){
$result['datas'][] = $array;
}
}
break;
default:
$result = ['labels' => [], 'datas' => []];
}
echo json_encode($result);
die();
case 'by-date':
case 'activation':
$q = (_post('q') ? _post('q') : _get('q'));
$keep = _post('keep');
if (!empty($keep)) {
ORM::raw_execute("DELETE FROM tbl_transactions WHERE date < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
r2(U . "logs/list/", 's', "Delete logs older than $keep days");
r2(getUrl('logs/list/'), 's', "Delete logs older than $keep days");
}
if ($q != '') {
$query = ORM::for_table('tbl_transactions')->where_like('invoice', '%' . $q . '%')->order_by_desc('id');
@ -107,7 +337,7 @@ switch ($action) {
$ed = _req('ed', $mdate);
$ts = _req('ts', '00:00:00');
$te = _req('te', '23:59:59');
$urlquery = str_replace('_route=reports&', '', $_SERVER['QUERY_STRING']);
$urlquery = str_replace('_route=reports', '', $_SERVER['QUERY_STRING']);
$query = ORM::for_table('tbl_transactions')
@ -154,6 +384,6 @@ switch ($action) {
$ui->assign('dr', $dr);
$ui->assign('mdate', $mdate);
run_hook('view_daily_reports'); #HOOK
$ui->display('reports-daily.tpl');
$ui->display('reports.tpl');
break;
}

View File

@ -18,24 +18,26 @@ if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$leafletpickerHeader = <<<EOT
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css">
EOT;
switch ($action) {
case 'list':
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/routers.js"></script>');
case 'maps':
$name = _post('name');
$query = ORM::for_table('tbl_routers')->where_not_equal('coordinates', '')->order_by_desc('id');
$query->selects(['id', 'name', 'coordinates', 'description', 'coverage', 'enabled']);
if ($name != '') {
$query = ORM::for_table('tbl_routers')->where_like('name', '%' . $name . '%')->order_by_desc('id');
$d = Paginator::findMany($query, ['name' => $name]);
} else {
$query = ORM::for_table('tbl_routers')->order_by_desc('id');
$d = Paginator::findMany($query);
$query->where_like('name', '%' . $name . '%');
}
$d = Paginator::findMany($query, ['name' => $name], '20', '', true);
$ui->assign('name', $name);
$ui->assign('d', $d);
run_hook('view_list_routers'); #HOOK
$ui->display('routers.tpl');
$ui->assign('_title', Lang::T('Routers Geo Location Information'));
$ui->assign('xheader', $leafletpickerHeader);
$ui->assign('xfooter', '<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>');
$ui->display('routers-maps.tpl');
break;
case 'add':
run_hook('view_add_routers'); #HOOK
$ui->display('routers-add.tpl');
@ -47,12 +49,13 @@ switch ($action) {
if (!$d) {
$d = ORM::for_table('tbl_routers')->where_equal('name', _get('name'))->find_one();
}
$ui->assign('xheader', $leafletpickerHeader);
if ($d) {
$ui->assign('d', $d);
run_hook('view_router_edit'); #HOOK
$ui->display('routers-edit.tpl');
} else {
r2(U . 'routers/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('routers/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -62,7 +65,7 @@ switch ($action) {
$d = ORM::for_table('tbl_routers')->find_one($id);
if ($d) {
$d->delete();
r2(U . 'routers/list', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('routers/list'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -75,16 +78,18 @@ switch ($action) {
$enabled = _post('enabled');
$msg = '';
if (Validator::Length($name, 30, 4) == false) {
$msg .= 'Name should be between 5 to 30 characters' . '<br>';
}
if ($ip_address == '' or $username == '') {
$msg .= Lang::T('All field is required') . '<br>';
if (Validator::Length($name, 30, 1) == false) {
$msg .= 'Name should be between 1 to 30 characters' . '<br>';
}
if($enabled || _post("testIt")){
if ($ip_address == '' or $username == '') {
$msg .= Lang::T('All field is required') . '<br>';
}
$d = ORM::for_table('tbl_routers')->where('ip_address', $ip_address)->find_one();
if ($d) {
$msg .= Lang::T('IP Router Already Exist') . '<br>';
$d = ORM::for_table('tbl_routers')->where('ip_address', $ip_address)->find_one();
if ($d) {
$msg .= Lang::T('IP Router Already Exist') . '<br>';
}
}
if (strtolower($name) == 'radius') {
$msg .= '<b>Radius</b> name is reserved<br>';
@ -92,7 +97,7 @@ switch ($action) {
if ($msg == '') {
run_hook('add_router'); #HOOK
if(_post("testIt")){
if (_post("testIt")) {
(new MikrotikHotspot())->getClient($ip_address, $username, $password);
}
$d = ORM::for_table('tbl_routers')->create();
@ -104,9 +109,9 @@ switch ($action) {
$d->enabled = $enabled;
$d->save();
r2(U . 'routers/list', 's', Lang::T('Data Created Successfully'));
r2(getUrl('routers/edit/') . $d->id(), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'routers/add', 'e', $msg);
r2(getUrl('routers/add'), 'e', $msg);
}
break;
@ -117,13 +122,17 @@ switch ($action) {
$username = _post('username');
$password = _post('password');
$description = _post('description');
$coordinates = _post('coordinates');
$coverage = _post('coverage');
$enabled = $_POST['enabled'];
$msg = '';
if (Validator::Length($name, 30, 4) == false) {
$msg .= 'Name should be between 5 to 30 characters' . '<br>';
}
if ($ip_address == '' or $username == '') {
$msg .= Lang::T('All field is required') . '<br>';
if($enabled || _post("testIt")){
if ($ip_address == '' or $username == '') {
$msg .= Lang::T('All field is required') . '<br>';
}
}
$id = _post('id');
@ -141,10 +150,12 @@ switch ($action) {
}
$oldname = $d['name'];
if ($d['ip_address'] != $ip_address) {
$c = ORM::for_table('tbl_routers')->where('ip_address', $ip_address)->where_not_equal('id', $id)->find_one();
if ($c) {
$msg .= 'IP Already Exists<br>';
if($enabled || _post("testIt")){
if ($d['ip_address'] != $ip_address) {
$c = ORM::for_table('tbl_routers')->where('ip_address', $ip_address)->where_not_equal('id', $id)->find_one();
if ($c) {
$msg .= 'IP Already Exists<br>';
}
}
}
@ -154,7 +165,7 @@ switch ($action) {
if ($msg == '') {
run_hook('router_edit'); #HOOK
if(_post("testIt")){
if (_post("testIt")) {
(new MikrotikHotspot())->getClient($ip_address, $username, $password);
}
$d->name = $name;
@ -162,6 +173,8 @@ switch ($action) {
$d->username = $username;
$d->password = $password;
$d->description = $description;
$d->coordinates = $coordinates;
$d->coverage = $coverage;
$d->enabled = $enabled;
$d->save();
if ($name != $oldname) {
@ -184,12 +197,24 @@ switch ($action) {
$p->set('routers', $name);
$p->save();
}
r2(U . 'routers/list', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('routers/list'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'routers/edit/' . $id, 'e', $msg);
r2(getUrl('routers/edit/') . $id, 'e', $msg);
}
break;
default:
r2(U . 'routers/list/', 's', '');
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/routers.js"></script>');
$name = _post('name');
$name = _post('name');
$query = ORM::for_table('tbl_routers')->order_by_desc('id');
if ($name != '') {
$query->where_like('name', '%' . $name . '%');
}
$d = Paginator::findMany($query, ['name' => $name]);
$ui->assign('d', $d);
run_hook('view_list_routers'); #HOOK
$ui->display('routers.tpl');
break;
}

View File

@ -0,0 +1,21 @@
<?php
$query = isset($_GET['query']) ? trim($_GET['query']) : '';
if (!empty($query)) {
$results = ORM::for_table('tbl_customers')
->where_like('username', "%$query%")
->find_many();
if ($results) {
echo '<ul>';
foreach ($results as $user) {
echo '<li><a href="'.$_url.'?_route=customers/view/'.$user->id.'">' . htmlspecialchars($user->username, ENT_QUOTES, 'UTF-8') . '</a></li>';
}
echo '</ul>';
} else {
echo '<p>' . Lang::T('No users found.') . '</p>';
}
} else {
echo '<p>' . Lang::T('Please enter a search term.') . '</p>';
}

View File

@ -33,7 +33,7 @@ switch ($action) {
}
}
}
r2(U . 'services/hotspot', 's', $log);
r2(getUrl('services/hotspot'), 's', $log);
} else if ($routes['2'] == 'pppoe') {
$plans = ORM::for_table('tbl_plans')->where('type', 'PPPOE')->find_many();
$log = '';
@ -49,11 +49,11 @@ switch ($action) {
}
}
}
r2(U . 'services/pppoe', 's', $log);
r2(getUrl('services/pppoe'), 's', $log);
}
r2(U . 'services/hotspot', 'w', 'Unknown command');
r2(getUrl('services/hotspot'), 'w', 'Unknown command');
case 'hotspot':
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/hotspot.js"></script>');
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/hotspot.js"></script>');
$name = _req('name');
$type1 = _req('type1');
$type2 = _req('type2');
@ -83,9 +83,9 @@ switch ($action) {
$bws = ORM::for_table('tbl_plans')->distinct()->select("id_bw")->where('tbl_plans.type', 'Hotspot')->findArray();
$ids = array_column($bws, 'id_bw');
if(count($ids)){
if (count($ids)) {
$ui->assign('bws', ORM::for_table('tbl_bandwidth')->select("id")->select('name_bw')->where_id_in($ids)->findArray());
}else{
} else {
$ui->assign('bws', []);
}
$ui->assign('type2s', ORM::for_table('tbl_plans')->getEnum("plan_type"));
@ -193,7 +193,7 @@ switch ($action) {
run_hook('view_edit_plan'); #HOOK
$ui->display('hotspot-edit.tpl');
} else {
r2(U . 'services/hotspot', 'e', Lang::T('Account Not Found'));
r2(getUrl('services/hotspot'), 'e', Lang::T('Account Not Found'));
}
break;
@ -214,7 +214,7 @@ switch ($action) {
}
$d->delete();
r2(U . 'services/hotspot', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('services/hotspot'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -307,9 +307,9 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/edit/' . $d->id(), 's', Lang::T('Data Created Successfully'));
r2(getUrl('services/edit/') . $d->id(), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'services/add', 'e', $msg);
r2(getUrl('services/add'), 'e', $msg);
}
break;
@ -321,6 +321,7 @@ switch ($action) {
$id_bw = _post('id_bw');
$typebp = _post('typebp');
$price = _post('price');
$price_old = _post('price_old');
$limit_type = _post('limit_type');
$time_limit = _post('time_limit');
$time_unit = _post('time_unit');
@ -353,6 +354,11 @@ switch ($action) {
} else {
$msg .= Lang::T('Data Not Found') . '<br>';
}
if ($price_old <= $price) {
$price_old = '';
}
run_hook('edit_plan'); #HOOK
if ($msg == '') {
$b = ORM::for_table('tbl_bandwidth')->where('id', $id_bw)->find_one();
@ -378,6 +384,7 @@ switch ($action) {
$d->name_plan = $name;
$d->id_bw = $id_bw;
$d->price = $price; // Set price with or without tax based on configuration
$d->price_old = $price_old;
$d->typebp = $typebp;
$d->limit_type = $limit_type;
$d->time_limit = $time_limit;
@ -413,15 +420,15 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/hotspot', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('services/hotspot'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'services/edit/' . $id, 'e', $msg);
r2(getUrl('services/edit/') . $id, 'e', $msg);
}
break;
case 'pppoe':
$ui->assign('_title', Lang::T('PPPOE Plans'));
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/pppoe.js"></script>');
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/pppoe.js"></script>');
$name = _post('name');
$name = _req('name');
@ -453,9 +460,9 @@ switch ($action) {
$bws = ORM::for_table('tbl_plans')->distinct()->select("id_bw")->where('tbl_plans.type', 'PPPOE')->findArray();
$ids = array_column($bws, 'id_bw');
if(count($ids)){
if (count($ids)) {
$ui->assign('bws', ORM::for_table('tbl_bandwidth')->select("id")->select('name_bw')->where_id_in($ids)->findArray());
}else{
} else {
$ui->assign('bws', []);
}
$ui->assign('type2s', ORM::for_table('tbl_plans')->getEnum("plan_type"));
@ -573,7 +580,7 @@ switch ($action) {
run_hook('view_edit_ppoe'); #HOOK
$ui->display('pppoe-edit.tpl');
} else {
r2(U . 'services/pppoe', 'e', Lang::T('Account Not Found'));
r2(getUrl('services/pppoe'), 'e', Lang::T('Account Not Found'));
}
break;
@ -595,7 +602,7 @@ switch ($action) {
}
$d->delete();
r2(U . 'services/pppoe', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('services/pppoe'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -693,9 +700,9 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/pppoe', 's', Lang::T('Data Created Successfully'));
r2(getUrl('services/pppoe'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'services/pppoe-add', 'e', $msg);
r2(getUrl('services/pppoe-add'), 'e', $msg);
}
break;
@ -705,6 +712,7 @@ switch ($action) {
$name = _post('name_plan');
$id_bw = _post('id_bw');
$price = _post('price');
$price_old = _post('price_old');
$validity = _post('validity');
$validity_unit = _post('validity_unit');
$routers = _post('routers');
@ -728,6 +736,10 @@ switch ($action) {
$msg .= Lang::T('All field is required') . '<br>';
}
if ($price_old <= $price) {
$price_old = '';
}
$d = ORM::for_table('tbl_plans')->where('id', $id)->find_one();
$old = ORM::for_table('tbl_plans')->where('id', $id)->find_one();
if ($d) {
@ -758,6 +770,7 @@ switch ($action) {
$d->name_plan = $name;
$d->id_bw = $id_bw;
$d->price = $price;
$d->price_old = $price_old;
$d->plan_type = $plan_type;
$d->validity = $validity;
$d->validity_unit = $validity_unit;
@ -788,9 +801,9 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/pppoe', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('services/pppoe'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'services/pppoe-edit/' . $id, 'e', $msg);
r2(getUrl('services/pppoe-edit/') . $id, 'e', $msg);
}
break;
case 'balance':
@ -828,13 +841,14 @@ switch ($action) {
if ($d) {
run_hook('delete_balance'); #HOOK
$d->delete();
r2(U . 'services/balance', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('services/balance'), 's', Lang::T('Data Deleted Successfully'));
}
break;
case 'balance-edit-post':
$id = _post('id');
$name = _post('name');
$price = _post('price');
$price_old = _post('price_old');
$enabled = _post('enabled');
$prepaid = _post('prepaid');
@ -851,17 +865,21 @@ switch ($action) {
} else {
$msg .= Lang::T('Data Not Found') . '<br>';
}
if ($price_old <= $price) {
$price_old = '';
}
run_hook('edit_ppoe'); #HOOK
if ($msg == '') {
$d->name_plan = $name;
$d->price = $price;
$d->enabled = $enabled;
$d->price_old = $price_old;
$d->prepaid = 'yes';
$d->save();
r2(U . 'services/balance', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('services/balance'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'services/balance-edit/' . $id, 'e', $msg);
r2(getUrl('services/balance-edit/') . $id, 'e', $msg);
}
break;
case 'balance-add-post':
@ -896,9 +914,389 @@ switch ($action) {
$d->prepaid = 'yes';
$d->save();
r2(U . 'services/balance', 's', Lang::T('Data Created Successfully'));
r2(getUrl('services/balance'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'services/balance-add', 'e', $msg);
r2(getUrl('services/balance-add'), 'e', $msg);
}
break;
case 'vpn':
$ui->assign('_title', Lang::T('VPN Plans'));
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/pppoe.js"></script>');
$name = _post('name');
$name = _req('name');
$type1 = _req('type1');
$type2 = _req('type2');
$type3 = _req('type3');
$bandwidth = _req('bandwidth');
$valid = _req('valid');
$device = _req('device');
$status = _req('status');
$router = _req('router');
$ui->assign('type1', $type1);
$ui->assign('type2', $type2);
$ui->assign('type3', $type3);
$ui->assign('bandwidth', $bandwidth);
$ui->assign('valid', $valid);
$ui->assign('device', $device);
$ui->assign('status', $status);
$ui->assign('router', $router);
$append_url = "&type1=" . urlencode($type1)
. "&type2=" . urlencode($type2)
. "&type3=" . urlencode($type3)
. "&bandwidth=" . urlencode($bandwidth)
. "&valid=" . urlencode($valid)
. "&device=" . urlencode($device)
. "&status=" . urlencode($status)
. "&router=" . urlencode($router);
$bws = ORM::for_table('tbl_plans')->distinct()->select("id_bw")->where('tbl_plans.type', 'VPN')->findArray();
$ids = array_column($bws, 'id_bw');
if (count($ids)) {
$ui->assign('bws', ORM::for_table('tbl_bandwidth')->select("id")->select('name_bw')->where_id_in($ids)->findArray());
} else {
$ui->assign('bws', []);
}
$ui->assign('type2s', ORM::for_table('tbl_plans')->getEnum("plan_type"));
$ui->assign('type3s', ORM::for_table('tbl_plans')->getEnum("typebp"));
$ui->assign('valids', ORM::for_table('tbl_plans')->getEnum("validity_unit"));
$ui->assign('routers', array_column(ORM::for_table('tbl_plans')->distinct()->select("routers")->whereNotEqual('routers', '')->findArray(), 'routers'));
$devices = [];
$files = scandir($DEVICE_PATH);
foreach ($files as $file) {
$ext = pathinfo($file, PATHINFO_EXTENSION);
if ($ext == 'php') {
$devices[] = pathinfo($file, PATHINFO_FILENAME);
}
}
$ui->assign('devices', $devices);
$query = ORM::for_table('tbl_bandwidth')
->left_outer_join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))
->where('tbl_plans.type', 'VPN');
if (!empty($type1)) {
$query->where('tbl_plans.prepaid', $type1);
}
if (!empty($type2)) {
$query->where('tbl_plans.plan_type', $type2);
}
if (!empty($type3)) {
$query->where('tbl_plans.typebp', $type3);
}
if (!empty($bandwidth)) {
$query->where('tbl_plans.id_bw', $bandwidth);
}
if (!empty($valid)) {
$query->where('tbl_plans.validity_unit', $valid);
}
if (!empty($router)) {
if ($router == 'radius') {
$query->where('tbl_plans.is_radius', '1');
} else {
$query->where('tbl_plans.routers', $router);
}
}
if (!empty($device)) {
$query->where('tbl_plans.device', $device);
}
if (in_array($status, ['0', '1'])) {
$query->where('tbl_plans.enabled', $status);
}
if ($name != '') {
$query->where_like('tbl_plans.name_plan', '%' . $name . '%');
}
$d = Paginator::findMany($query, ['name' => $name], 20, $append_url);
$ui->assign('d', $d);
run_hook('view_list_vpn'); #HOOK
$ui->display('vpn.tpl');
break;
case 'vpn-add':
$ui->assign('_title', Lang::T('VPN Plans'));
$d = ORM::for_table('tbl_bandwidth')->find_many();
$ui->assign('d', $d);
$r = ORM::for_table('tbl_routers')->find_many();
$ui->assign('r', $r);
$devices = [];
$files = scandir($DEVICE_PATH);
foreach ($files as $file) {
$ext = pathinfo($file, PATHINFO_EXTENSION);
if ($ext == 'php') {
$devices[] = pathinfo($file, PATHINFO_FILENAME);
}
}
$ui->assign('devices', $devices);
run_hook('view_add_vpn'); #HOOK
$ui->display('vpn-add.tpl');
break;
case 'vpn-edit':
$ui->assign('_title', Lang::T('VPN Plans'));
$id = $routes['2'];
$d = ORM::for_table('tbl_plans')->find_one($id);
if ($d) {
if (empty($d['device'])) {
if ($d['is_radius']) {
$d->device = 'Radius';
} else {
$d->device = 'MikrotikVpn';
}
$d->save();
}
$ui->assign('d', $d);
$p = ORM::for_table('tbl_pool')->where('routers', ($d['is_radius']) ? 'radius' : $d['routers'])->find_many();
$ui->assign('p', $p);
$b = ORM::for_table('tbl_bandwidth')->find_many();
$ui->assign('b', $b);
$r = [];
if ($d['is_radius']) {
$r = ORM::for_table('tbl_routers')->find_many();
}
$ui->assign('r', $r);
$devices = [];
$files = scandir($DEVICE_PATH);
foreach ($files as $file) {
$ext = pathinfo($file, PATHINFO_EXTENSION);
if ($ext == 'php') {
$devices[] = pathinfo($file, PATHINFO_FILENAME);
}
}
$ui->assign('devices', $devices);
//select expired plan
if ($d['is_radius']) {
$exps = ORM::for_table('tbl_plans')->selects('id', 'name_plan')->where('type', 'VPN')->where("is_radius", 1)->findArray();
} else {
$exps = ORM::for_table('tbl_plans')->selects('id', 'name_plan')->where('type', 'VPN')->where("routers", $d['routers'])->findArray();
}
$ui->assign('exps', $exps);
run_hook('view_edit_vpn'); #HOOK
$ui->display('vpn-edit.tpl');
} else {
r2(getUrl('services/vpn'), 'e', Lang::T('Account Not Found'));
}
break;
case 'vpn-delete':
$id = $routes['2'];
$d = ORM::for_table('tbl_plans')->find_one($id);
if ($d) {
run_hook('delete_vpn'); #HOOK
$dvc = Package::getDevice($d);
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
(new $d['device'])->remove_plan($d);
} else {
new Exception(Lang::T("Devices Not Found"));
}
}
$d->delete();
r2(getUrl('services/vpn'), 's', Lang::T('Data Deleted Successfully'));
}
break;
case 'vpn-add-post':
$name = _post('name_plan');
$plan_type = _post('plan_type');
$radius = _post('radius');
$id_bw = _post('id_bw');
$price = _post('price');
$validity = _post('validity');
$validity_unit = _post('validity_unit');
$routers = _post('routers');
$device = _post('device');
$pool = _post('pool_name');
$enabled = _post('enabled');
$prepaid = _post('prepaid');
$expired_date = _post('expired_date');
$msg = '';
if (Validator::UnsignedNumber($validity) == false) {
$msg .= 'The validity must be a number' . '<br>';
}
if (Validator::UnsignedNumber($price) == false) {
$msg .= 'The price must be a number' . '<br>';
}
if ($name == '' or $id_bw == '' or $price == '' or $validity == '' or $pool == '') {
$msg .= Lang::T('All field is required') . '<br>';
}
if (empty($radius)) {
if ($routers == '') {
$msg .= Lang::T('All field is required') . '<br>';
}
}
$d = ORM::for_table('tbl_plans')->where('name_plan', $name)->find_one();
if ($d) {
$msg .= Lang::T('Name Plan Already Exist') . '<br>';
}
run_hook('add_vpn'); #HOOK
if ($msg == '') {
$b = ORM::for_table('tbl_bandwidth')->where('id', $id_bw)->find_one();
if ($b['rate_down_unit'] == 'Kbps') {
$unitdown = 'K';
$raddown = '000';
} else {
$unitdown = 'M';
$raddown = '000000';
}
if ($b['rate_up_unit'] == 'Kbps') {
$unitup = 'K';
$radup = '000';
} else {
$unitup = 'M';
$radup = '000000';
}
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
$radiusRate = $b['rate_up'] . $radup . '/' . $b['rate_down'] . $raddown . '/' . $b['burst'];
$rate = trim($rate . " " . $b['burst']);
$d = ORM::for_table('tbl_plans')->create();
$d->type = 'VPN';
$d->name_plan = $name;
$d->id_bw = $id_bw;
$d->price = $price;
$d->plan_type = $plan_type;
$d->validity = $validity;
$d->validity_unit = $validity_unit;
$d->pool = $pool;
if (!empty($radius)) {
$d->is_radius = 1;
$d->routers = '';
} else {
$d->is_radius = 0;
$d->routers = $routers;
}
if ($prepaid == 'no') {
if ($expired_date > 28 && $expired_date < 1) {
$expired_date = 20;
}
$d->expired_date = $expired_date;
} else {
$d->expired_date = 0;
}
$d->enabled = $enabled;
$d->prepaid = $prepaid;
$d->device = $device;
$d->save();
$dvc = Package::getDevice($d);
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
(new $d['device'])->add_plan($d);
} else {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(getUrl('services/vpn'), 's', Lang::T('Data Created Successfully'));
} else {
r2(getUrl('services/vpn-add'), 'e', $msg);
}
break;
case 'edit-vpn-post':
$id = _post('id');
$plan_type = _post('plan_type');
$name = _post('name_plan');
$id_bw = _post('id_bw');
$price = _post('price');
$price_old = _post('price_old');
$validity = _post('validity');
$validity_unit = _post('validity_unit');
$routers = _post('routers');
$device = _post('device');
$pool = _post('pool_name');
$plan_expired = _post('plan_expired');
$enabled = _post('enabled');
$prepaid = _post('prepaid');
$expired_date = _post('expired_date');
$on_login = _post('on_login');
$on_logout = _post('on_logout');
$msg = '';
if (Validator::UnsignedNumber($validity) == false) {
$msg .= 'The validity must be a number' . '<br>';
}
if (Validator::UnsignedNumber($price) == false) {
$msg .= 'The price must be a number' . '<br>';
}
if ($name == '' or $id_bw == '' or $price == '' or $validity == '' or $pool == '') {
$msg .= Lang::T('All field is required') . '<br>';
}
if($price_old<=$price){
$price_old = '';
}
$d = ORM::for_table('tbl_plans')->where('id', $id)->find_one();
$old = ORM::for_table('tbl_plans')->where('id', $id)->find_one();
if ($d) {
} else {
$msg .= Lang::T('Data Not Found') . '<br>';
}
run_hook('edit_vpn'); #HOOK
if ($msg == '') {
$b = ORM::for_table('tbl_bandwidth')->where('id', $id_bw)->find_one();
if ($b['rate_down_unit'] == 'Kbps') {
$unitdown = 'K';
$raddown = '000';
} else {
$unitdown = 'M';
$raddown = '000000';
}
if ($b['rate_up_unit'] == 'Kbps') {
$unitup = 'K';
$radup = '000';
} else {
$unitup = 'M';
$radup = '000000';
}
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
$radiusRate = $b['rate_up'] . $radup . '/' . $b['rate_down'] . $raddown . '/' . $b['burst'];
$rate = trim($rate . " " . $b['burst']);
$d->name_plan = $name;
$d->id_bw = $id_bw;
$d->price = $price;
$d->price_old = $price_old;
$d->plan_type = $plan_type;
$d->validity = $validity;
$d->validity_unit = $validity_unit;
$d->routers = $routers;
$d->pool = $pool;
$d->plan_expired = $plan_expired;
$d->enabled = $enabled;
$d->prepaid = $prepaid;
$d->device = $device;
$d->on_login = $on_login;
$d->on_logout = $on_logout;
if ($prepaid == 'no') {
if ($expired_date > 28 && $expired_date < 1) {
$expired_date = 20;
}
$d->expired_date = $expired_date;
} else {
$d->expired_date = 0;
}
$d->save();
$dvc = Package::getDevice($d);
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
(new $d['device'])->update_plan($old, $d);
} else {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(getUrl('services/vpn'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(getUrl('services/vpn-edit/') . $id, 'e', $msg);
}
break;
default:

View File

@ -34,11 +34,11 @@ switch ($action) {
$dev = pathinfo($file, PATHINFO_FILENAME);
require_once $DEVICE_PATH . DIRECTORY_SEPARATOR . $file;
$dvc = new $dev;
if(method_exists($dvc, 'description')){
if (method_exists($dvc, 'description')) {
$arr = $dvc->description();
$arr['file'] = $dev;
$devices[] = $arr;
}else{
} else {
$devices[] = [
'title' => $dev,
'description' => '',
@ -59,19 +59,19 @@ switch ($action) {
if (!empty(_get('testWa'))) {
$result = Message::sendWhatsapp(_get('testWa'), 'PHPNuxBill Test Whatsapp');
r2(U . "settings/app", 's', 'Test Whatsapp has been send<br>Result: ' . $result);
r2(getUrl('settings/app'), 's', 'Test Whatsapp has been send<br>Result: ' . $result);
}
if (!empty(_get('testSms'))) {
$result = Message::sendSMS(_get('testSms'), 'PHPNuxBill Test SMS');
r2(U . "settings/app", 's', 'Test SMS has been send<br>Result: ' . $result);
r2(getUrl('settings/app'), 's', 'Test SMS has been send<br>Result: ' . $result);
}
if (!empty(_get('testEmail'))) {
Message::sendEmail(_get('testEmail'), 'PHPNuxBill Test Email', 'PHPNuxBill Test Email Body');
r2(U . "settings/app", 's', 'Test Email has been send');
r2(getUrl('settings/app'), 's', 'Test Email has been send');
}
if (!empty(_get('testTg'))) {
$result = Message::sendTelegram('PHPNuxBill Test Telegram');
r2(U . "settings/app", 's', 'Test Telegram has been send<br>Result: ' . $result);
r2(getUrl('settings/app'), 's', 'Test Telegram has been send<br>Result: ' . $result);
}
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
@ -81,6 +81,35 @@ switch ($action) {
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.default.png';
}
$ui->assign('logo', $logo);
if (!empty($config['login_page_logo']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'])) {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_logo'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png')) {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.png';
} else {
$login_logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'login-logo.default.png';
}
if (!empty($config['login_page_wallpaper']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'])) {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_wallpaper'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png')) {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.png';
} else {
$wallpaper = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'wallpaper.default.png';
}
if (!empty($config['login_page_favicon']) && file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'])) {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . $config['login_page_favicon'];
} elseif (file_exists($UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png')) {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.png';
} else {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.default.png';
}
$ui->assign('login_logo', $login_logo);
$ui->assign('wallpaper', $wallpaper);
$ui->assign('favicon', $favicon);
$themes = [];
$files = scandir('ui/themes/');
foreach ($files as $file) {
@ -88,6 +117,20 @@ switch ($action) {
$themes[] = $file;
}
}
$template_files = glob('ui/ui/customer/login-custom-*.tpl');
$templates = [];
foreach ($template_files as $file) {
$parts = explode('-', basename($file, '.tpl'));
$template_identifier = $parts[2] ?? 'unknown';
$templates[] = [
'filename' => basename($file),
'value' => $template_identifier,
'name' => str_replace('_', ' ', ucfirst($template_identifier))
];
}
$r = ORM::for_table('tbl_routers')->find_many();
$ui->assign('r', $r);
if (function_exists("shell_exec")) {
@ -111,11 +154,17 @@ switch ($action) {
$d->save();
}
}
if (empty($config['mikrotik_sms_command'])) {
$config['mikrotik_sms_command'] = "/tool sms send";
}
$ui->assign('template_files', $templates);
$ui->assign('_c', $config);
$ui->assign('php', $php);
$ui->assign('dir', str_replace('controllers', '', __DIR__));
$ui->assign('themes', $themes);
run_hook('view_app_settings'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('app-settings.tpl');
break;
@ -123,10 +172,15 @@ switch ($action) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/app'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$company = _post('CompanyName');
$custom_tax_rate = filter_var(_post('custom_tax_rate'), FILTER_SANITIZE_SPECIAL_CHARS);
if (preg_match('/[^0-9.]/', $custom_tax_rate)) {
r2(U . 'settings/app', 'e', 'Special characters are not allowed in tax rate');
r2(getUrl('settings/app'), 'e', 'Special characters are not allowed in tax rate');
die();
}
run_hook('save_settings'); #HOOK
@ -136,11 +190,11 @@ switch ($action) {
File::resizeCropImage($_FILES['logo']['tmp_name'], $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png', 1078, 200, 100);
if (file_exists($_FILES['logo']['tmp_name'])) unlink($_FILES['logo']['tmp_name']);
} else {
r2(U . 'settings/app', 'e', 'PHP GD is not installed');
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
}
if ($company == '') {
r2(U . 'settings/app', 'e', Lang::T('All field is required'));
if ($_POST['general'] && $company == '') {
r2(getUrl('settings/app'), 'e', Lang::T('All field is required'));
} else {
if ($radius_enable) {
try {
@ -151,11 +205,17 @@ switch ($action) {
$ui->assign("error_message", "Radius table not found.<br><br>" .
$e->getMessage() .
"<br><br>Download <a href=\"https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/Development/install/radius.sql\">here</a> or <a href=\"https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/install/radius.sql\">here</a> and import it to database.<br><br>Check config.php for radius connection details");
$ui->display('router-error.tpl');
$ui->display('error.tpl');
die();
}
}
// Save all settings including tax system
$_POST['man_fields_email'] = isset($_POST['man_fields_email']) ? 'yes' : 'no';
$_POST['man_fields_fname'] = isset($_POST['man_fields_fname']) ? 'yes' : 'no';
$_POST['man_fields_address'] = isset($_POST['man_fields_address']) ? 'yes' : 'no';
$_POST['man_fields_custom'] = isset($_POST['man_fields_custom']) ? 'yes' : 'no';
$enable_session_timeout = isset($_POST['enable_session_timeout']) ? 1 : 0;
$_POST['enable_session_timeout'] = $enable_session_timeout;
foreach ($_POST as $key => $value) {
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
if ($d) {
@ -168,29 +228,104 @@ switch ($action) {
$d->save();
}
}
//checkbox
$checks = ['hide_mrc', 'hide_tms', 'hide_aui', 'hide_al', 'hide_uet', 'hide_vs', 'hide_pg'];
foreach ($checks as $check) {
if (!isset($_POST[$check])) {
$d = ORM::for_table('tbl_appconfig')->where('setting', $check)->find_one();
if ($d) {
$d->value = 'no';
$d->save();
} else {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = $check;
$d->value = 'no';
$d->save();
}
}
}
_log('[' . $admin['username'] . ']: ' . Lang::T('Settings Saved Successfully'), $admin['user_type'], $admin['id']);
r2(U . 'settings/app', 's', Lang::T('Settings Saved Successfully'));
r2(getUrl('settings/app'), 's', Lang::T('Settings Saved Successfully'));
}
break;
case 'login-page-post':
// Login page post
$login_page_title = _post('login_page_head');
$login_page_description = _post('login_page_description');
$login_Page_template = _post('login_Page_template');
$login_page_type = _post('login_page_type');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/app'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
if ($login_page_type == 'custom' && (empty($login_Page_template) || empty($login_page_title) || empty($login_page_description))) {
r2(getUrl('settings/app'), 'e', 'Please fill all required fields');
return;
}
if (strlen($login_page_title) > 25) {
r2(getUrl('settings/app'), 'e', 'Login page title must not exceed 25 characters');
return;
}
if (strlen($login_page_description) > 100) {
r2(getUrl('settings/app'), 'e', 'Login page description must not exceed 50 characters');
return;
}
$settings = [
'login_page_head' => $login_page_title,
'login_page_description' => $login_page_description,
'login_Page_template' => $login_Page_template,
'login_page_type' => $login_page_type,
];
$image_paths = [];
$allowed_types = ['image/jpeg', 'image/png'];
if ($_FILES['login_page_favicon']['name'] != '') {
$favicon_type = $_FILES['login_page_favicon']['type'];
if (in_array($favicon_type, $allowed_types) && preg_match('/\.(jpg|jpeg|png)$/i', $_FILES['login_page_favicon']['name'])) {
$extension = pathinfo($_FILES['login_page_favicon']['name'], PATHINFO_EXTENSION);
$favicon_path = $UPLOAD_PATH . DIRECTORY_SEPARATOR . uniqid('favicon_') . '.' . $extension;
File::resizeCropImage($_FILES['login_page_favicon']['tmp_name'], $favicon_path, 16, 16, 100);
$settings['login_page_favicon'] = basename($favicon_path); // Save dynamic file name
if (file_exists($_FILES['login_page_favicon']['tmp_name'])) unlink($_FILES['login_page_favicon']['tmp_name']);
} else {
r2(getUrl('settings/app'), 'e', 'Favicon must be a JPG, JPEG, or PNG image.');
}
}
if ($_FILES['login_page_wallpaper']['name'] != '') {
$wallpaper_type = $_FILES['login_page_wallpaper']['type'];
if (in_array($wallpaper_type, $allowed_types) && preg_match('/\.(jpg|jpeg|png)$/i', $_FILES['login_page_wallpaper']['name'])) {
$extension = pathinfo($_FILES['login_page_wallpaper']['name'], PATHINFO_EXTENSION);
$wallpaper_path = $UPLOAD_PATH . DIRECTORY_SEPARATOR . uniqid('wallpaper_') . '.' . $extension;
File::resizeCropImage($_FILES['login_page_wallpaper']['tmp_name'], $wallpaper_path, 1920, 1080, 100);
$settings['login_page_wallpaper'] = basename($wallpaper_path); // Save dynamic file name
if (file_exists($_FILES['login_page_wallpaper']['tmp_name'])) unlink($_FILES['login_page_wallpaper']['tmp_name']);
} else {
r2(getUrl('settings/app'), 'e', 'Wallpaper must be a JPG, JPEG, or PNG image.');
}
}
if ($_FILES['login_page_logo']['name'] != '') {
$logo_type = $_FILES['login_page_logo']['type'];
if (in_array($logo_type, $allowed_types) && preg_match('/\.(jpg|jpeg|png)$/i', $_FILES['login_page_logo']['name'])) {
$extension = pathinfo($_FILES['login_page_logo']['name'], PATHINFO_EXTENSION);
$logo_path = $UPLOAD_PATH . DIRECTORY_SEPARATOR . uniqid('logo_') . '.' . $extension;
File::resizeCropImage($_FILES['login_page_logo']['tmp_name'], $logo_path, 300, 60, 100);
$settings['login_page_logo'] = basename($logo_path); // Save dynamic file name
if (file_exists($_FILES['login_page_logo']['tmp_name'])) unlink($_FILES['login_page_logo']['tmp_name']);
} else {
r2(getUrl('settings/app'), 'e', 'Logo must be a JPG, JPEG, or PNG image.');
}
}
foreach ($settings as $key => $value) {
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
if ($d) {
$d->value = $value;
$d->save();
} else {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = $key;
$d->value = $value;
$d->save();
}
}
_log('[' . $admin['username'] . ']: ' . Lang::T('Login Page Settings Saved Successfully'), $admin['user_type'], $admin['id']);
r2(getUrl('settings/app'), 's', Lang::T('Login Page Settings Saved Successfully'));
break;
case 'localisation':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
@ -215,6 +350,8 @@ switch ($action) {
$ui->assign('tlist', $timezonelist);
$ui->assign('xjq', ' $("#tzone").select2(); ');
run_hook('view_localisation'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('app-localisation.tpl');
break;
@ -222,13 +359,17 @@ switch ($action) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/app'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$tzone = _post('tzone');
$date_format = _post('date_format');
$country_code_phone = _post('country_code_phone');
$lan = _post('lan');
run_hook('save_localisation'); #HOOK
if ($tzone == '' or $date_format == '' or $lan == '') {
r2(U . 'settings/app', 'e', Lang::T('All field is required'));
r2(getUrl('settings/app'), 'e', Lang::T('All field is required'));
} else {
$d = ORM::for_table('tbl_appconfig')->where('setting', 'timezone')->find_one();
$d->value = $tzone;
@ -293,6 +434,16 @@ switch ($action) {
$d->value = _post('pppoe_plan');
$d->save();
}
$d = ORM::for_table('tbl_appconfig')->where('setting', 'vpn_plan')->find_one();
if ($d) {
$d->value = _post('vpn_plan');
$d->save();
} else {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = 'vpn_plan';
$d->value = _post('vpn_plan');
$d->save();
}
$currency_code = $_POST['currency_code'];
$d = ORM::for_table('tbl_appconfig')->where('setting', 'currency_code')->find_one();
@ -302,9 +453,8 @@ switch ($action) {
$d = ORM::for_table('tbl_appconfig')->where('setting', 'language')->find_one();
$d->value = $lan;
$d->save();
unset($_SESSION['Lang']);
_log('[' . $admin['username'] . ']: ' . 'Settings Saved Successfully', $admin['user_type'], $admin['id']);
r2(U . 'settings/localisation', 's', 'Settings Saved Successfully');
r2(getUrl('settings/localisation'), 's', 'Settings Saved Successfully');
}
break;
@ -375,16 +525,20 @@ switch ($action) {
$ui->assign('d', $d);
$ui->assign('search', $search);
run_hook('view_list_admin'); #HOOK
$ui->display('users.tpl');
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('admin.tpl');
break;
case 'users-add':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_title', Lang::T('Add User'));
$ui->assign('agents', ORM::for_table('tbl_users')->where('user_type', 'Agent')->find_many());
$ui->display('users-add.tpl');
$ui->display('admin-add.tpl');
break;
case 'users-view':
$ui->assign('_title', Lang::T('Edit User'));
@ -411,9 +565,11 @@ switch ($action) {
}
$ui->assign('d', $d);
$ui->assign('_title', $d['username']);
$ui->display('users-view.tpl');
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('admin-view.tpl');
} else {
r2(U . 'settings/users', 'e', Lang::T('Account Not Found'));
r2(getUrl('settings/users'), 'e', Lang::T('Account Not Found'));
}
break;
case 'users-edit':
@ -445,12 +601,31 @@ switch ($action) {
}
}
if ($d) {
if (isset($routes['3']) && $routes['3'] == 'deletePhoto') {
if ($d['photo'] != '' && strpos($d['photo'], 'default') === false) {
if (file_exists($UPLOAD_PATH . $d['photo']) && strpos($d['photo'], 'default') === false) {
unlink($UPLOAD_PATH . $d['photo']);
if (file_exists($UPLOAD_PATH . $d['photo'] . '.thumb.jpg')) {
unlink($UPLOAD_PATH . $d['photo'] . '.thumb.jpg');
}
}
$d->photo = '/admin.default.png';
$d->save();
$ui->assign('notify_t', 's');
$ui->assign('notify', 'You have successfully deleted the photo');
} else {
$ui->assign('notify_t', 'e');
$ui->assign('notify', 'No photo found to delete');
}
}
$ui->assign('id', $id);
$ui->assign('d', $d);
run_hook('view_edit_admin'); #HOOK
$ui->display('users-edit.tpl');
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('admin-edit.tpl');
} else {
r2(U . 'settings/users', 'e', Lang::T('Account Not Found'));
r2(getUrl('settings/users'), 'e', Lang::T('Account Not Found'));
}
break;
@ -461,15 +636,15 @@ switch ($action) {
$id = $routes['2'];
if (($admin['id']) == $id) {
r2(U . 'settings/users', 'e', 'Sorry You can\'t delete yourself');
r2(getUrl('settings/users'), 'e', 'Sorry You can\'t delete yourself');
}
$d = ORM::for_table('tbl_users')->find_one($id);
if ($d) {
run_hook('delete_admin'); #HOOK
$d->delete();
r2(U . 'settings/users', 's', Lang::T('User deleted Successfully'));
r2(getUrl('settings/users'), 's', Lang::T('User deleted Successfully'));
} else {
r2(U . 'settings/users', 'e', Lang::T('Account Not Found'));
r2(getUrl('settings/users'), 'e', Lang::T('Account Not Found'));
}
break;
@ -477,6 +652,10 @@ switch ($action) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/users-add'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$username = _post('username');
$fullname = _post('fullname');
$password = _post('password');
@ -534,13 +713,17 @@ switch ($action) {
}
_log('[' . $admin['username'] . ']: ' . "Created $user_type <b>$username</b>", $admin['user_type'], $admin['id']);
r2(U . 'settings/users', 's', Lang::T('Account Created Successfully'));
r2(getUrl('settings/users'), 's', Lang::T('Account Created Successfully'));
} else {
r2(U . 'settings/users-add', 'e', $msg);
r2(getUrl('settings/users-add'), 'e', $msg);
}
break;
case 'users-edit-post':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/users-edit/'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$username = _post('username');
$fullname = _post('fullname');
$password = _post('password');
@ -597,6 +780,55 @@ switch ($action) {
}
run_hook('edit_admin'); #HOOK
if ($msg == '') {
if (!empty($_FILES['photo']['name']) && file_exists($_FILES['photo']['tmp_name'])) {
if (function_exists('imagecreatetruecolor')) {
$hash = md5_file($_FILES['photo']['tmp_name']);
$subfolder = substr($hash, 0, 2);
$folder = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'photos' . DIRECTORY_SEPARATOR;
if (!file_exists($folder)) {
mkdir($folder);
}
$folder = $UPLOAD_PATH . DIRECTORY_SEPARATOR . 'photos' . DIRECTORY_SEPARATOR . $subfolder . DIRECTORY_SEPARATOR;
if (!file_exists($folder)) {
mkdir($folder);
}
$imgPath = $folder . $hash . '.jpg';
if (!file_exists($imgPath)) {
File::resizeCropImage($_FILES['photo']['tmp_name'], $imgPath, 1600, 1600, 100);
}
if (!file_exists($imgPath . '.thumb.jpg')) {
if (_post('faceDetect') == 'yes') {
try {
$detector = new svay\FaceDetector();
$detector->setTimeout(5000);
$detector->faceDetect($imgPath);
$detector->cropFaceToJpeg($imgPath . '.thumb.jpg', false);
} catch (Exception $e) {
File::makeThumb($imgPath, $imgPath . '.thumb.jpg', 200);
} catch (Throwable $e) {
File::makeThumb($imgPath, $imgPath . '.thumb.jpg', 200);
}
} else {
File::makeThumb($imgPath, $imgPath . '.thumb.jpg', 200);
}
}
if (file_exists($imgPath)) {
if ($d['photo'] != '' && strpos($d['photo'], 'default') === false) {
if (file_exists($UPLOAD_PATH . $d['photo'])) {
unlink($UPLOAD_PATH . $d['photo']);
if (file_exists($UPLOAD_PATH . $d['photo'] . '.thumb.jpg')) {
unlink($UPLOAD_PATH . $d['photo'] . '.thumb.jpg');
}
}
}
$d->photo = '/photos/' . $subfolder . '/' . $hash . '.jpg';
}
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
} else {
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
}
$d->username = $username;
if ($password != '') {
$password = Password::_crypt($password);
@ -627,19 +859,25 @@ switch ($action) {
$d->save();
_log('[' . $admin['username'] . ']: $username ' . Lang::T('User Updated Successfully'), $admin['user_type'], $admin['id']);
r2(U . 'settings/users', 's', 'User Updated Successfully');
r2(getUrl('settings/users-view/') . $id, 's', 'User Updated Successfully');
} else {
r2(U . 'settings/users-edit/' . $id, 'e', $msg);
r2(getUrl('settings/users-edit/') . $id, 'e', $msg);
}
break;
case 'change-password':
run_hook('view_change_password'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('change-password.tpl');
break;
case 'change-password-post':
$password = _post('password');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/change-password'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
if ($password != '') {
$d = ORM::for_table('tbl_users')->where('username', $admin['username'])->find_one();
run_hook('change_password'); #HOOK
@ -649,10 +887,10 @@ switch ($action) {
$npass = _post('npass');
$cnpass = _post('cnpass');
if (!Validator::Length($npass, 15, 5)) {
r2(U . 'settings/change-password', 'e', 'New Password must be 6 to 14 character');
r2(getUrl('settings/change-password'), 'e', 'New Password must be 6 to 14 character');
}
if ($npass != $cnpass) {
r2(U . 'settings/change-password', 'e', 'Both Password should be same');
r2(getUrl('settings/change-password'), 'e', 'Both Password should be same');
}
$npass = Password::_crypt($npass);
@ -662,15 +900,15 @@ switch ($action) {
_msglog('s', Lang::T('Password changed successfully, Please login again'));
_log('[' . $admin['username'] . ']: Password changed successfully', $admin['user_type'], $admin['id']);
r2(U . 'admin');
r2(getUrl('admin'));
} else {
r2(U . 'settings/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('settings/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
} else {
r2(U . 'settings/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('settings/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
} else {
r2(U . 'settings/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('settings/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
break;
@ -684,6 +922,9 @@ switch ($action) {
} else {
$ui->assign('_json', json_decode(file_get_contents($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'notifications.default.json'), true));
}
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_default', json_decode(file_get_contents($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'notifications.default.json'), true));
$ui->display('app-notifications.tpl');
break;
@ -691,15 +932,19 @@ switch ($action) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/notifications'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
file_put_contents($UPLOAD_PATH . "/notifications.json", json_encode($_POST));
r2(U . 'settings/notifications', 's', Lang::T('Settings Saved Successfully'));
r2(getUrl('settings/notifications'), 's', Lang::T('Settings Saved Successfully'));
break;
case 'dbstatus':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$dbc = new mysqli($db_host, $db_user, $db_password, $db_name);
$dbc = new mysqli($db_host, $db_user, $db_pass, $db_name);
if ($result = $dbc->query('SHOW TABLE STATUS')) {
$tables = array();
while ($row = $result->fetch_array()) {
@ -781,9 +1026,9 @@ switch ($action) {
} catch (Exception $e) {
}
if (file_exists($_FILES['json']['tmp_name'])) unlink($_FILES['json']['tmp_name']);
r2(U . "settings/dbstatus", 's', "Restored $suc success $fal failed");
r2(getUrl('settings/dbstatus'), 's', "Restored $suc success $fal failed");
} else {
r2(U . "settings/dbstatus", 'e', 'Upload failed');
r2(getUrl('settings/dbstatus'), 'e', 'Upload failed');
}
break;
case 'language':
@ -796,12 +1041,18 @@ switch ($action) {
} else {
$ui->assign('langs', []);
}
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('language-add.tpl');
break;
case 'lang-post':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/language'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
file_put_contents($lan_file, json_encode($_POST, JSON_PRETTY_PRINT));
r2(U . 'settings/language', 's', Lang::T('Translation saved Successfully'));
r2(getUrl('settings/language'), 's', Lang::T('Translation saved Successfully'));
break;
case 'maintenance':
@ -809,7 +1060,12 @@ switch ($action) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
exit;
}
if (_post('save') == 'save') {
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/maintenance'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$status = isset($_POST['maintenance_mode']) ? 1 : 0; // Checkbox returns 1 if checked, otherwise 0
$force_logout = isset($_POST['maintenance_mode_logout']) ? 1 : 0; // Checkbox returns 1 if checked, otherwise 0
$date = isset($_POST['maintenance_date']) ? $_POST['maintenance_date'] : null;
@ -833,13 +1089,47 @@ switch ($action) {
}
}
r2(U . "settings/maintenance", 's', Lang::T('Settings Saved Successfully'));
r2(getUrl('settings/maintenance'), 's', Lang::T('Settings Saved Successfully'));
}
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_c', $config);
$ui->assign('_title', Lang::T('Maintenance Mode Settings'));
$ui->display('maintenance-mode.tpl');
break;
case 'miscellaneous':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
exit;
}
if (_post('save') == 'save') {
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/miscellaneous'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
foreach ($_POST as $key => $value) {
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
if ($d) {
$d->value = $value;
$d->save();
} else {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = $key;
$d->value = $value;
$d->save();
}
}
r2(getUrl('settings/miscellaneous'), 's', Lang::T('Settings Saved Successfully'));
}
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_c', $config);
$ui->assign('_title', Lang::T('Miscellaneous Settings'));
$ui->display('app-miscellaneous.tpl');
break;
default:
$ui->display('a404.tpl');
}

View File

@ -1,4 +1,5 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
@ -11,19 +12,17 @@ $action = $routes['1'];
$user = User::_info();
$ui->assign('_user', $user);
require_once 'system/autoload/PEAR2/Autoload.php';
switch ($action) {
case 'activation':
run_hook('view_activate_voucher'); #HOOK
$ui->assign('code', alphanumeric(_get('code'),"-"));
$ui->display('user-activation.tpl');
$ui->assign('code', alphanumeric(_get('code'), "-_.,"));
$ui->display('customer/activation.tpl');
break;
case 'activation-post':
$code = _post('code');
$v1 = ORM::for_table('tbl_voucher')->where('code', $code)->where('status', 0)->find_one();
$code = alphanumeric(_post('code'), "-_.,");
$v1 = ORM::for_table('tbl_voucher')->whereRaw("BINARY code = '$code'")->where('status', 0)->find_one();
run_hook('customer_activate_voucher'); #HOOK
if ($v1) {
if (Package::rechargeUser($user['id'], $v1['routers'], $v1['id_plan'], "Voucher", $code)) {
@ -31,37 +30,43 @@ switch ($action) {
$v1->used_date = date('Y-m-d H:i:s');
$v1->user = $user['username'];
$v1->save();
r2(U . "voucher/list-activated", 's', Lang::T('Activation Vouchers Successfully'));
r2(getUrl('voucher/list-activated'), 's', Lang::T('Activation Vouchers Successfully'));
} else {
r2(U . 'voucher/activation', 'e', "Failed to refill account");
r2(getUrl('voucher/activation'), 'e', "Failed to refill account");
}
} else {
r2(U . 'voucher/activation', 'e', Lang::T('Voucher Not Valid'));
r2(getUrl('voucher/activation'), 'e', Lang::T('Voucher Not Valid'));
}
break;
case 'list-activated':
$ui->assign('_system_menu', 'list-activated');
$query = ORM::for_table('tbl_transactions')->where('username', $user['username'])->order_by_desc('id');
$query = ORM::for_table('tbl_transactions')->where('user_id', $user['id'])->order_by_desc('id');
$d = Paginator::findMany($query);
if (empty($d) || $d < 5) {
$query = ORM::for_table('tbl_transactions')->where('username', $user['username'])->order_by_desc('id');
$d = Paginator::findMany($query);
}
$ui->assign('d', $d);
$ui->assign('_title', Lang::T('Activation History'));
run_hook('customer_view_activation_list'); #HOOK
$ui->display('user-activation-list.tpl');
$ui->display('customer/activation-list.tpl');
break;
case 'invoice':
$id = $routes[2];
if(empty($id)){
if (empty($id)) {
$in = ORM::for_table('tbl_transactions')->where('username', $user['username'])->order_by_desc('id')->find_one();
}else{
} else {
$in = ORM::for_table('tbl_transactions')->where('username', $user['username'])->where('id', $id)->find_one();
}
if($in){
if ($in) {
Package::createInvoice($in);
$ui->display('invoice-customer.tpl');
}else{
r2(U . 'voucher/list-activated', 'e', Lang::T('Not Found'));
$ui->display('customer/invoice-customer.tpl');
} else {
r2(getUrl('voucher/list-activated'), 'e', Lang::T('Not Found'));
}
break;
default:

View File

@ -1,6 +1,27 @@
<?php
include "../init.php";
$lockFile = "$CACHE_PATH/router_monitor.lock";
if (!is_dir($CACHE_PATH)) {
echo "Directory '$CACHE_PATH' does not exist. Exiting...\n";
exit;
}
$lock = fopen($lockFile, 'c');
if ($lock === false) {
echo "Failed to open lock file. Exiting...\n";
exit;
}
if (!flock($lock, LOCK_EX | LOCK_NB)) {
echo "Script is already running. Exiting...\n";
fclose($lock);
exit;
}
$isCli = true;
if (php_sapi_name() !== 'cli') {
$isCli = false;
@ -32,8 +53,11 @@ foreach ($d as $ds) {
$u = ORM::for_table('tbl_user_recharges')->where('id', $ds['id'])->find_one();
$c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
if (empty($c)) {
$c = $u;
}
$dvc = Package::getDevice($p);
if($_app_stage != 'demo'){
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
(new $p['device'])->remove_customer($c, $p);
@ -50,7 +74,7 @@ foreach ($d as $ds) {
// autorenewal from deposit
if ($config['enable_balance'] == 'yes' && $c['auto_renewal']) {
list($bills, $add_cost) = User::getBills($ds['customer_id']);
if ($add_cost > 0) {
if ($add_cost != 0) {
if (!empty($add_cost)) {
$p['price'] += $add_cost;
}
@ -78,3 +102,127 @@ foreach ($d as $ds) {
echo " : ACTIVE \r\n";
}
}
//Cek interim-update radiusrest
if ($config['frrest_interim_update'] != 0) {
$r_a = ORM::for_table('rad_acct')
->whereRaw("BINARY acctstatustype = 'Start' OR acctstatustype = 'Interim-Update'")
->where_lte('dateAdded', date("Y-m-d H:i:s"))->find_many();
foreach ($r_a as $ra) {
$interval = $_c['frrest_interim_update']*60;
$timeUpdate = strtotime($ra['dateAdded'])+$interval;
$timeNow = strtotime(date("Y-m-d H:i:s"));
if ($timeNow >= $timeUpdate) {
$ra->acctstatustype = 'Stop';
$ra->save();
}
}
}
if ($config['router_check']) {
echo "Checking router status...\n";
$routers = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
if (!$routers) {
echo "No active routers found in the database.\n";
flock($lock, LOCK_UN);
fclose($lock);
unlink($lockFile);
exit;
}
$offlineRouters = [];
$errors = [];
foreach ($routers as $router) {
// check if custom port
if (strpos($router->ip_address, ':') === false){
$ip = $router->ip_address;
$port = 8728;
} else {
[$ip, $port] = explode(':', $router->ip_address);
}
$isOnline = false;
try {
$timeout = 5;
if (is_callable('fsockopen') && false === stripos(ini_get('disable_functions'), 'fsockopen')) {
$fsock = @fsockopen($ip, $port, $errno, $errstr, $timeout);
if ($fsock) {
fclose($fsock);
$isOnline = true;
} else {
throw new Exception("Unable to connect to $ip on port $port using fsockopen: $errstr ($errno)");
}
} elseif (is_callable('stream_socket_client') && false === stripos(ini_get('disable_functions'), 'stream_socket_client')) {
$connection = @stream_socket_client("$ip:$port", $errno, $errstr, $timeout);
if ($connection) {
fclose($connection);
$isOnline = true;
} else {
throw new Exception("Unable to connect to $ip on port $port using stream_socket_client: $errstr ($errno)");
}
} else {
throw new Exception("Neither fsockopen nor stream_socket_client are enabled on the server.");
}
} catch (Exception $e) {
_log($e->getMessage());
$errors[] = "Error with router $ip: " . $e->getMessage();
}
if ($isOnline) {
$router->last_seen = date('Y-m-d H:i:s');
$router->status = 'Online';
} else {
$router->status = 'Offline';
$offlineRouters[] = $router;
}
$router->save();
}
if (!empty($offlineRouters)) {
$message = "Dear Administrator,\n";
$message .= "The following routers are offline:\n";
foreach ($offlineRouters as $router) {
$message .= "Name: {$router->name}, IP: {$router->ip_address}, Last Seen: {$router->last_seen}\n";
}
$message .= "\nPlease check the router's status and take appropriate action.\n\nBest regards,\nRouter Monitoring System";
$adminEmail = $config['mail_from'];
$subject = "Router Offline Alert";
Message::SendEmail($adminEmail, $subject, $message);
sendTelegram($message);
}
if (!empty($errors)) {
$message = "The following errors occurred during router monitoring:\n";
foreach ($errors as $error) {
$message .= "$error\n";
}
$adminEmail = $config['mail_from'];
$subject = "Router Monitoring Error Alert";
Message::SendEmail($adminEmail, $subject, $message);
sendTelegram($message);
}
echo "Router monitoring finished\n";
}
if (defined('PHP_SAPI') && PHP_SAPI === 'cli') {
echo "Cronjob finished\n";
} else {
echo "</pre>";
}
flock($lock, LOCK_UN);
fclose($lock);
unlink($lockFile);
$timestampFile = "$UPLOAD_PATH/cron_last_run.txt";
file_put_contents($timestampFile, time());
run_hook('cronjob_end'); #HOOK

View File

@ -33,10 +33,38 @@ class MikrotikHotspot
{
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$isExp = ORM::for_table('tbl_plans')->select("id")->where('plan_expired', $plan['id'])->find_one();
$this->removeHotspotUser($client, $customer['username']);
if ($isExp){
$this->removeHotspotActiveUser($client, $customer['username']);
}
$this->addHotspotUser($client, $plan, $customer);
}
function sync_customer($customer, $plan)
{
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$t = ORM::for_table('tbl_user_recharges')->where('username', $customer['username'])->where('status', 'on')->find_one();
if ($t) {
$printRequest = new RouterOS\Request('/ip/hotspot/user/print');
$printRequest->setArgument('.proplist', '.id,limit-uptime,limit-bytes-total');
$printRequest->setQuery(RouterOS\Query::where('name', $customer['username']));
$userInfo = $client->sendSync($printRequest);
$id = $userInfo->getProperty('.id');
$uptime = $userInfo->getProperty('limit-uptime');
$data = $userInfo->getProperty('limit-bytes-total');
if (!empty($id) && (!empty($uptime) || !empty($data))) {
$setRequest = new RouterOS\Request('/ip/hotspot/user/set');
$setRequest->setArgument('numbers', $id);
$setRequest->setArgument('profile', $t['namebp']);
$client->sendSync($setRequest);
} else {
$this->add_customer($customer, $plan);
}
}
}
function remove_customer($customer, $plan)
{
@ -44,10 +72,13 @@ class MikrotikHotspot
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
if (!empty($plan['plan_expired'])) {
$p = ORM::for_table("tbl_plans")->find_one($plan['plan_expired']);
$this->add_customer($customer, $p);
} else {
$this->removeHotspotUser($client, $customer['username']);
if($p){
$this->add_customer($customer, $p);
$this->removeHotspotActiveUser($client, $customer['username']);
return;
}
}
$this->removeHotspotUser($client, $customer['username']);
$this->removeHotspotActiveUser($client, $customer['username']);
}
@ -106,9 +137,10 @@ class MikrotikHotspot
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$printRequest = new RouterOS\Request(
'/ip hotspot active print',
RouterOS\Query::where('name', $customer['username'])
RouterOS\Query::where('user', $customer['username'])
);
return $client->sendSync($printRequest)->getProperty('.id');
$id = $client->sendSync($printRequest)->getProperty('.id');
return $id;
}
function connect_customer($customer, $ip, $mac_address, $router_name)
@ -207,7 +239,7 @@ class MikrotikHotspot
function getClient($ip, $user, $pass)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$iport = explode(":", $ip);
@ -217,7 +249,7 @@ class MikrotikHotspot
function removeHotspotUser($client, $username)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$printRequest = new RouterOS\Request(
@ -235,7 +267,7 @@ class MikrotikHotspot
function addHotspotUser($client, $plan, $customer)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$addRequest = new RouterOS\Request('/ip/hotspot/user/add');
@ -303,7 +335,7 @@ class MikrotikHotspot
function setHotspotUser($client, $user, $pass)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$printRequest = new RouterOS\Request('/ip/hotspot/user/print');
@ -320,7 +352,7 @@ class MikrotikHotspot
function setHotspotUserPackage($client, $username, $plan_name)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$printRequest = new RouterOS\Request('/ip/hotspot/user/print');
@ -337,7 +369,7 @@ class MikrotikHotspot
function removeHotspotActiveUser($client, $username)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$onlineRequest = new RouterOS\Request('/ip/hotspot/active/print');
@ -353,7 +385,7 @@ class MikrotikHotspot
function getIpHotspotUser($client, $username)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$printRequest = new RouterOS\Request(

View File

@ -29,43 +29,83 @@ class MikrotikPppoe
function add_customer($customer, $plan)
{
global $isChangePlan;
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
//check if customer exists
$printRequest = new RouterOS\Request('/ppp/secret/print');
$printRequest->setQuery(RouterOS\Query::where('name', $customer['username']));
$cid = $client->sendSync($printRequest)->getProperty('.id');
$cid = self::getIdByCustomer($customer, $client);
$isExp = ORM::for_table('tbl_plans')->select("id")->where('plan_expired', $plan['id'])->find_one();
if (empty($cid)) {
//customer not exists, add it
$this->addPpoeUser($client, $plan, $customer);
$this->addPpoeUser($client, $plan, $customer, $isExp);
}else{
if (!empty($customer['pppoe_password'])) {
$pass = $customer['pppoe_password'];
} else {
$pass = $customer['password'];
}
$setRequest = new RouterOS\Request('/ppp/secret/set');
$setRequest->setArgument('numbers', $cid);
if (!empty($customer['pppoe_password'])) {
$setRequest->setArgument('password', $customer['pppoe_password']);
} else {
$setRequest->setArgument('password', $customer['password']);
}
if (!empty($customer['pppoe_username'])) {
$setRequest->setArgument('name', $customer['pppoe_username']);
} else {
$setRequest->setArgument('name', $customer['username']);
}
$unsetIP = false;
if (!empty($customer['pppoe_ip']) && !$isExp){
$setRequest->setArgument('remote-address', $customer['pppoe_ip']);
} else {
$unsetIP = true;
}
$setRequest->setArgument('profile', $plan['name_plan']);
$setRequest->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email'] . ' | ' . implode(', ', User::getBillNames($customer['id'])));
$setRequest->setArgument('password', $pass);
$client->sendSync($setRequest);
if($unsetIP){
$unsetRequest = new RouterOS\Request('/ppp/secret/unset');
$unsetRequest->setArgument('.id', $cid);
$unsetRequest->setArgument('value-name','remote-address');
$client->sendSync($unsetRequest);
}
//disconnect then
//$this->removePpoeActive($client, $customer['username']);
if(isset($isChangePlan) && $isChangePlan){
$this->removePpoeActive($client, $customer['username']);
if (!empty($customer['pppoe_username'])) {
$this->removePpoeActive($client, $customer['pppoe_username']);
}
}
}
}
function sync_customer($customer, $plan)
{
$this->add_customer($customer, $plan);
}
function remove_customer($customer, $plan)
{
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
if (!empty($plan['plan_expired'])) {
$p = ORM::for_table("tbl_plans")->find_one($plan['plan_expired']);
$this->add_customer($customer, $p);
} else {
$this->removePpoeUser($client, $customer['username']);
if($p){
$this->add_customer($customer, $p);
$this->removePpoeActive($client, $customer['username']);
if (!empty($customer['pppoe_username'])) {
$this->removePpoeActive($client, $customer['pppoe_username']);
}
return;
}
}
$this->removePpoeUser($client, $customer['username']);
if (!empty($customer['pppoe_username'])) {
$this->removePpoeUser($client, $customer['pppoe_username']);
}
$this->removePpoeActive($client, $customer['username']);
if (!empty($customer['pppoe_username'])) {
$this->removePpoeActive($client, $customer['pppoe_username']);
}
}
// customer change username
@ -120,6 +160,23 @@ class MikrotikPppoe
);
}
/**
* Function to ID by username from Mikrotik
*/
function getIdByCustomer($customer, $client){
$printRequest = new RouterOS\Request('/ppp/secret/print');
$printRequest->setQuery(RouterOS\Query::where('name', $customer['username']));
$id = $client->sendSync($printRequest)->getProperty('.id');
if(empty($id)){
if (!empty($customer['pppoe_username'])) {
$printRequest = new RouterOS\Request('/ppp/secret/print');
$printRequest->setQuery(RouterOS\Query::where('name', $customer['pppoe_username']));
$id = $client->sendSync($printRequest)->getProperty('.id');
}
}
return $id;
}
function update_plan($old_name, $new_plan)
{
$mikrotik = $this->info($new_plan['routers']);
@ -245,9 +302,17 @@ class MikrotikPppoe
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$printRequest = new RouterOS\Request(
'/ppp active print',
RouterOS\Query::where('user', $customer['username'])
RouterOS\Query::where('name', $customer['username'])
);
return $client->sendSync($printRequest)->getProperty('.id');
$id = $client->sendSync($printRequest)->getProperty('.id');
if(empty($id)){
$printRequest = new RouterOS\Request(
'/ppp active print',
RouterOS\Query::where('name', $customer['pppoe_username'])
);
$id = $client->sendSync($printRequest)->getProperty('.id');
}
return $id;
}
function info($name)
@ -280,53 +345,25 @@ class MikrotikPppoe
$client->sendSync($removeRequest);
}
function addPpoeUser($client, $plan, $customer)
function addPpoeUser($client, $plan, $customer, $isExp = false)
{
global $_app_stage;
$addRequest = new RouterOS\Request('/ppp/secret/add');
$setRequest = new RouterOS\Request('/ppp/secret/add');
$setRequest->setArgument('service', 'pppoe');
$setRequest->setArgument('profile', $plan['name_plan']);
$setRequest->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email'] . ' | ' . implode(', ', User::getBillNames($customer['id'])));
if (!empty($customer['pppoe_password'])) {
$pass = $customer['pppoe_password'];
$setRequest->setArgument('password', $customer['pppoe_password']);
} else {
$pass = $customer['password'];
$setRequest->setArgument('password', $customer['password']);
}
$client->sendSync(
$addRequest
->setArgument('name', $customer['username'])
->setArgument('service', 'pppoe')
->setArgument('profile', $plan['name_plan'])
->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email'] . ' | ' . implode(', ', User::getBillNames($customer['id'])))
->setArgument('password', $pass)
);
}
function setPpoeUser($client, $user, $pass)
{
global $_app_stage;
$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);
}
function setPpoeUserPlan($client, $user, $plan)
{
global $_app_stage;
if ($_app_stage == 'demo') {
return null;
if (!empty($customer['pppoe_username'])) {
$setRequest->setArgument('name', $customer['pppoe_username']);
} else {
$setRequest->setArgument('name', $customer['username']);
}
if (!empty($customer['pppoe_ip']) && !$isExp) {
$setRequest->setArgument('remote-address', $customer['pppoe_ip']);
}
$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);
}

View File

@ -0,0 +1,512 @@
<?php
use PEAR2\Net\RouterOS;
class MikrotikVpn
{
function description()
{
return [
'title' => 'Mikrotik Vpn',
'description' => 'To handle connection between PHPNuxBill with Mikrotik VPN',
'author' => 'agstr',
'url' => [
'Github' => 'https://github.com/agstrxyz',
'Telegram' => 'https://t.me/agstrxyz',
'Youtube' => 'https://www.youtube.com/@agstrxyz',
'Donate' => 'https://paypal.me/ibnux'
]
];
}
function add_customer($customer, $plan)
{
global $isChangePlan;
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$cid = self::getIdByCustomer($customer, $client);
if (empty($cid)) {
$this->addVpnUser($client, $plan, $customer);
} else {
$setRequest = new RouterOS\Request('/ppp/secret/set');
$setRequest->setArgument('numbers', $cid);
if (!empty($customer['pppoe_password'])) {
$setRequest->setArgument('password', $customer['pppoe_password']);
} else {
$setRequest->setArgument('password', $customer['password']);
}
if (!empty($customer['pppoe_username'])) {
$setRequest->setArgument('name', $customer['pppoe_username']);
} else {
$setRequest->setArgument('name', $customer['username']);
}
if (!empty($customer['pppoe_ip'])) {
$setRequest->setArgument('remote-address', $customer['pppoe_ip']);
} else {
$setRequest->setArgument('remote-address', '0.0.0.0');
}
$setRequest->setArgument('profile', $plan['name_plan']);
$setRequest->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email'] . ' | ' . implode(', ', User::getBillNames($customer['id'])));
$client->sendSync($setRequest);
if (isset($isChangePlan) && $isChangePlan) {
$this->removeVpnActive($client, $customer['username']);
if (!empty($customer['pppoe_username'])) {
$this->removeVpnActive($client, $customer['pppoe_username']);
}
}
}
}
function sync_customer($customer, $plan)
{
$this->add_customer($customer, $plan);
}
function remove_customer($customer, $plan)
{
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
if (!empty($plan['plan_expired'])) {
$p = ORM::for_table("tbl_plans")->find_one($plan['plan_expired']);
if ($p) {
$this->add_customer($customer, $p);
$this->removeVpnActive($client, $customer['username']);
if (!empty($customer['pppoe_username'])) {
$this->removeVpnActive($client, $customer['pppoe_username']);
}
return;
}
}
$this->removeVpnUser($client, $customer['username'], $customer['id']);
if (!empty($customer['pppoe_username'])) {
$this->removeVpnUser($client, $customer['pppoe_username'], $customer['id']);
}
$this->removeVpnActive($client, $customer['username']);
if (!empty($customer['pppoe_username'])) {
$this->removeVpnActive($client, $customer['pppoe_username']);
}
}
public function change_username($plan, $from, $to)
{
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$printRequest = new RouterOS\Request('/ppp/secret/print');
$printRequest->setQuery(RouterOS\Query::where('name', $from));
$cid = $client->sendSync($printRequest)->getProperty('.id');
if (!empty($cid)) {
$setRequest = new RouterOS\Request('/ppp/secret/set');
$setRequest->setArgument('numbers', $cid);
$setRequest->setArgument('name', $to);
$client->sendSync($setRequest);
$this->removeVpnActive($client, $from);
}
}
function add_plan($plan)
{
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$bw = ORM::for_table("tbl_bandwidth")->find_one($plan['id_bw']);
if ($bw['rate_down_unit'] == 'Kbps') {
$unitdown = 'K';
} else {
$unitdown = 'M';
}
if ($bw['rate_up_unit'] == 'Kbps') {
$unitup = 'K';
} else {
$unitup = 'M';
}
$rate = $bw['rate_up'] . $unitup . "/" . $bw['rate_down'] . $unitdown;
if (!empty(trim($bw['burst']))) {
$rate .= ' ' . $bw['burst'];
}
$pool = ORM::for_table("tbl_pool")->where("pool_name", $plan['pool'])->find_one();
$addRequest = new RouterOS\Request('/ppp/profile/add');
$client->sendSync(
$addRequest
->setArgument('name', $plan['name_plan'])
->setArgument('local-address', (!empty($pool['local_ip'])) ? $pool['local_ip'] : $pool['pool_name'])
->setArgument('remote-address', $pool['pool_name'])
->setArgument('rate-limit', $rate)
);
}
function getIdByCustomer($customer, $client)
{
$printRequest = new RouterOS\Request('/ppp/secret/print');
$printRequest->setQuery(RouterOS\Query::where('name', $customer['username']));
$id = $client->sendSync($printRequest)->getProperty('.id');
if (empty($id)) {
if (!empty($customer['pppoe_username'])) {
$printRequest = new RouterOS\Request('/ppp/secret/print');
$printRequest->setQuery(RouterOS\Query::where('name', $customer['pppoe_username']));
$id = $client->sendSync($printRequest)->getProperty('.id');
}
}
return $id;
}
function update_plan($old_name, $new_plan)
{
$mikrotik = $this->info($new_plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$printRequest = new RouterOS\Request(
'/ppp profile print .proplist=.id',
RouterOS\Query::where('name', $old_name['name_plan'])
);
$profileID = $client->sendSync($printRequest)->getProperty('.id');
if (empty($profileID)) {
$this->add_plan($new_plan);
} else {
$bw = ORM::for_table("tbl_bandwidth")->find_one($new_plan['id_bw']);
if ($bw['rate_down_unit'] == 'Kbps') {
$unitdown = 'K';
} else {
$unitdown = 'M';
}
if ($bw['rate_up_unit'] == 'Kbps') {
$unitup = 'K';
} else {
$unitup = 'M';
}
$rate = $bw['rate_up'] . $unitup . "/" . $bw['rate_down'] . $unitdown;
if (!empty(trim($bw['burst']))) {
$rate .= ' ' . $bw['burst'];
}
$pool = ORM::for_table("tbl_pool")->where("pool_name", $new_plan['pool'])->find_one();
$setRequest = new RouterOS\Request('/ppp/profile/set');
$client->sendSync(
$setRequest
->setArgument('numbers', $profileID)
->setArgument('local-address', (!empty($pool['local_ip'])) ? $pool['local_ip'] : $pool['pool_name'])
->setArgument('remote-address', $pool['pool_name'])
->setArgument('rate-limit', $rate)
->setArgument('on-up', $new_plan['on_login'])
->setArgument('on-down', $new_plan['on_logout'])
);
}
}
function remove_plan($plan)
{
$mikrotik = $this->info($plan['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$printRequest = new RouterOS\Request(
'/ppp profile print .proplist=.id',
RouterOS\Query::where('name', $plan['name_plan'])
);
$profileID = $client->sendSync($printRequest)->getProperty('.id');
$removeRequest = new RouterOS\Request('/ppp/profile/remove');
$client->sendSync(
$removeRequest
->setArgument('numbers', $profileID)
);
}
function add_pool($pool)
{
global $_app_stage;
if ($_app_stage == 'demo') {
return null;
}
$mikrotik = $this->info($pool['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$addRequest = new RouterOS\Request('/ip/pool/add');
$client->sendSync(
$addRequest
->setArgument('name', $pool['pool_name'])
->setArgument('ranges', $pool['range_ip'])
);
}
function update_pool($old_pool, $new_pool)
{
global $_app_stage;
if ($_app_stage == 'demo') {
return null;
}
$mikrotik = $this->info($new_pool['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$printRequest = new RouterOS\Request(
'/ip pool print .proplist=.id',
RouterOS\Query::where('name', $old_pool['pool_name'])
);
$poolID = $client->sendSync($printRequest)->getProperty('.id');
if (empty($poolID)) {
$this->add_pool($new_pool);
} else {
$setRequest = new RouterOS\Request('/ip/pool/set');
$client->sendSync(
$setRequest
->setArgument('numbers', $poolID)
->setArgument('name', $new_pool['pool_name'])
->setArgument('ranges', $new_pool['range_ip'])
);
}
}
function remove_pool($pool)
{
global $_app_stage;
if ($_app_stage == 'demo') {
return null;
}
$mikrotik = $this->info($pool['routers']);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$printRequest = new RouterOS\Request(
'/ip pool print .proplist=.id',
RouterOS\Query::where('name', $pool['pool_name'])
);
$poolID = $client->sendSync($printRequest)->getProperty('.id');
$removeRequest = new RouterOS\Request('/ip/pool/remove');
$client->sendSync(
$removeRequest
->setArgument('numbers', $poolID)
);
}
function online_customer($customer, $router_name)
{
$mikrotik = $this->info($router_name);
$client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
$printRequest = new RouterOS\Request(
'/ppp active print',
RouterOS\Query::where('user', $customer['username'])
);
return $client->sendSync($printRequest)->getProperty('.id');
}
function info($name)
{
return ORM::for_table('tbl_routers')->where('name', $name)->find_one();
}
function getClient($ip, $user, $pass)
{
global $_app_stage;
if ($_app_stage == 'demo') {
return null;
}
$iport = explode(":", $ip);
return new RouterOS\Client($iport[0], $user, $pass, ($iport[1]) ? $iport[1] : null);
}
function removeVpnUser($client, $username, $cstid)
{
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);
$this->rmNat($client, $cstid);
}
function addVpnUser($client, $plan, $customer)
{
$setRequest = new RouterOS\Request('/ppp/secret/add');
$setRequest->setArgument('service', 'any');
$setRequest->setArgument('profile', $plan['name_plan']);
$setRequest->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email'] . ' | ' . implode(', ', User::getBillNames($customer['id'])));
if (!empty($customer['pppoe_password'])) {
$setRequest->setArgument('password', $customer['pppoe_password']);
} else {
$setRequest->setArgument('password', $customer['password']);
}
if (!empty($customer['pppoe_username'])) {
$setRequest->setArgument('name', $customer['pppoe_username']);
} else {
$setRequest->setArgument('name', $customer['username']);
}
if (!empty($customer['pppoe_ip'])) {
$ips = $customer['pppoe_ip'];
$setRequest->setArgument('remote-address', $customer['pppoe_ip']);
} else {
$ips = $this->checkIpAddr($plan['pool'], $customer['id']);
$setRequest->setArgument('remote-address', $ips);
}
$this->addNat($client, $plan, $customer, $ips);
$client->sendSync($setRequest);
$customer->service_type = 'VPN';
$customer->pppoe_ip = $ips;
$customer->save();
}
function removeVpnActive($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);
}
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)
);
}
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)
);
}
function addNat($client, $plan, $cust, $ips)
{
global $_app_stage;
if ($_app_stage == 'demo') {
return null;
}
$this->checkPort($cust['id'], 'Winbox', $plan['routers']);
$this->checkPort($cust['id'], 'Api', $plan['routers']);
$this->checkPort($cust['id'], 'Web', $plan['routers']);
$tcf = ORM::for_table('tbl_customers_fields')
->where('customer_id', $cust['id'])
->find_many();
$ip = ORM::for_table('tbl_port_pool')
->where('routers', $plan['routers'])
->find_one();
foreach ($tcf as $cf) {
$dst = $cf['field_value'];
$cmnt = $cf['field_name'];
if ($cmnt == 'Winbox') {
$tp = '8291';
}
if ($cmnt == 'Web') {
$tp = '80';
}
if ($cmnt == 'Api') {
$tp = '8728';
}
if ($cmnt == 'Winbox' || $cmnt == 'Web' || $cmnt == 'Api') {
$addRequest = new RouterOS\Request('/ip/firewall/nat/add');
$client->sendSync(
$addRequest
->setArgument('chain', 'dstnat')
->setArgument('protocol', 'tcp')
->setArgument('dst-port', $dst)
->setArgument('action', 'dst-nat')
->setArgument('to-addresses', $ips)
->setArgument('to-ports', $tp)
->setArgument('dst-address', $ip['public_ip'])
->setArgument('comment', $cmnt . ' || ' . $cust['username'])
);
}
}
}
function rmNat($client, $cstid)
{
global $_app_stage;
if ($_app_stage == 'demo') {
return null;
}
$cst = ORM::for_table('tbl_customers')->find_one($cstid);
$printRequest = new RouterOS\Request('/ip/firewall/nat/print');
$printRequest->setQuery(RouterOS\Query::where('to-addresses', $cst['pppoe_ip']));
$nats = $client->sendSync($printRequest);
foreach ($nats as $nat) {
$id = $client->sendSync($printRequest)->getProperty('.id');
$removeRequest = new RouterOS\Request('/ip/firewall/nat/remove');
$removeRequest->setArgument('numbers', $id);
$client->sendSync($removeRequest);
}
}
function checkPort($id, $portn, $router)
{
$tcf = ORM::for_table('tbl_customers_fields')
->where('customer_id', $id)
->where('field_name', $portn)
->find_one();
$ports = ORM::for_table('tbl_port_pool')
->where('routers', $router)
->find_one();
$port = explode('-', $ports['range_port']);
if (empty($tcf) && !empty($ports)) {
repeat:
$portr = rand($port['0'], $port['1']);
if (ORM::for_table('tbl_customers_fields')->where('field_value', $portr)->find_one()) {
if ($portr == $port['1']) {
return;
}
goto repeat;
}
$cf = ORM::for_table('tbl_customers_fields')->create();
$cf->customer_id = $id;
$cf->field_name = $portn;
$cf->field_value = $portr;
$cf->save();
}
}
function checkIpAddr($pname, $id)
{
$c = ORM::for_table('tbl_customers')->find_one($id);
$ipp = ORM::for_table('tbl_pool')
->where('pool_name', $pname)
->find_one();
$ip_r = explode('-', $ipp['range_ip']);
$ip_1 = explode('.', $ip_r['0']);
$ip_2 = explode('.', $ip_r['1']);
repeat:
$ipt = rand($ip_1['3'], $ip_2['3']);
$ips = $ip_1['0'] . '.' . $ip_1['1'] . '.' . $ip_1['2'] . '.' . $ipt;
if (empty($c['pppoe_ip'])) {
if (ORM::for_table('tbl_customers')->where('pppoe_ip', $ips)->find_one()) {
if ($ip_2['3'] == $ipt) {
return;
}
goto repeat;
}
return $ips;
}
}
}

View File

@ -28,28 +28,104 @@ class Radius
function add_customer($customer, $plan)
{
$b = ORM::for_table('tbl_user_recharges')
->where('customer_id', $customer['id'])
->where('plan_id', $plan['id'])
->where('status', 'on')
->findMany();
$p = ORM::for_table('tbl_plans')
->where('id', $plan['id'])
->findOne();
if($b){
$this->customerAddPlan($customer, $plan, $b['expiration'] . ' ' . $b['time']);
$date_only = date("Y-m-d");
if ($p['validity_unit'] == 'Period') {
// if customer has attribute Expired Date use it
$day_exp = User::getAttribute("Expired Date", $customer['id']);
if (!$day_exp) {
// if customer no attribute Expired Date use plan expired date
$day_exp = 20;
if ($p['prepaid'] == 'no') {
$day_exp = $p['expired_date'];
}
if (empty($day_exp)) {
$day_exp = 20;
}
}
}
if ($p['validity_unit'] == 'Months') {
$date_exp = date("Y-m-d", strtotime('+' . $p['validity'] . ' month'));
$time = date("H:i:s");
} else if ($p['validity_unit'] == 'Period') {
$current_date = new DateTime($date_only);
$exp_date = clone $current_date;
$exp_date->modify('first day of next month');
$exp_date->setDate($exp_date->format('Y'), $exp_date->format('m'), $day_exp);
$min_days = 7 * $p['validity'];
$max_days = 35 * $p['validity'];
$days_until_exp = $exp_date->diff($current_date)->days;
// If less than min_days away, move to the next period
while ($days_until_exp < $min_days) {
$exp_date->modify('+1 month');
$days_until_exp = $exp_date->diff($current_date)->days;
}
// If more than max_days away, move to the previous period
while ($days_until_exp > $max_days) {
$exp_date->modify('-1 month');
$days_until_exp = $exp_date->diff($current_date)->days;
}
// Final check to ensure we're not less than min_days or in the past
if ($days_until_exp < $min_days || $exp_date <= $current_date) {
$exp_date->modify('+1 month');
}
// Adjust for multiple periods
if ($p['validity'] > 1) {
$exp_date->modify('+' . ($p['validity'] - 1) . ' months');
}
$date_exp = $exp_date->format('Y-m-d');
$time = "23:59:59";
} 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];
}
if ($p) {
$this->customerAddPlan($customer, $plan, $date_exp . ' ' . $time);
}
}
function sync_customer($customer, $plan)
{
$t = ORM::for_table('tbl_user_recharges')->where('username', $customer['username'])->where('status', 'on')->findOne();
$date_exp = $t['expiration'];
$time = $t['time'];
$this->customerAddPlan($customer, $plan, $date_exp . ' ' . $time);
}
function remove_customer($customer, $plan)
{
if (!empty($plan['plan_expired'])) {
$p = ORM::for_table("tbl_plans")->find_one($plan['plan_expired']);
$this->customerAddPlan($customer, $p);
if ($p) {
$this->customerAddPlan($customer, $p);
}
} else {
$this->customerDeactivate($customer['username'], true);
}
$this->customerDeactivate($customer['username'], true);
}
}
public function change_username($from, $to)
public function change_username($plan, $from, $to)
{
$c = $this->getTableCustomer()->where_equal('username', $from)->findMany();
if ($c) {
@ -108,7 +184,7 @@ class Radius
function remove_plan($plan)
{
// Delete Plan
$this->getTablePackage()->where_equal('plan_id', "plan_" . $plan['id'])->delete_many();
$this->getTablePackage()->where_equal('plan_id', $plan['id'])->delete_many();
// Reset User Plan
$c = $this->getTableUserPackage()->where_equal('groupname', "plan_" . $plan['id'])->findMany();
if ($c) {
@ -214,21 +290,29 @@ class Radius
{
$this->getTableCustomer()->where_equal('username', $username)->delete_many();
$this->getTableUserPackage()->where_equal('username', $username)->delete_many();
$this->getTableCustomerAttr()->where_equal('username', $username)->delete_many();
}
/**
* When add a plan to Customer, use this
*/
public function customerAddPlan($customer, $plan, $expired = null)
public function customerAddPlan($customer, $plan, $expired = '')
{
global $config;
if ($this->customerUpsert($customer, $plan)) {
$p = $this->getTableUserPackage()->where_equal('username', $customer['username'])->findOne();
if ($p) {
// if exists
// session timeout [it reset everyday, am still making my research] we can use it for something like 1H/Day - since it reset daily, and Max-All-Session clear everything
//$this->delAtribute($customer['username'], 'Session-Timeout', 3600); // 3600 = 1 hour
$this->delAtribute($this->getTableCustomer(), 'Max-All-Session', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'Max-Volume', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'Max-Data', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'Mikrotik-Rate-Limit', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'WISPr-Session-Terminate-Time', 'username', $customer['username']);
//$this->delAtribute($this->getTableCustomer(), 'Ascend-Data-Rate', 'username', $customer['username']);
//we are removing the below in the next two updates, some users may have that attribute, it will remove them before we remove it
$this->delAtribute($this->getTableCustomer(), 'access-period', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'Max-Volume', 'username', $customer['username']);
$p->groupname = "plan_" . $plan['id'];
$p->save();
} else {
@ -238,53 +322,62 @@ class Radius
$p->priority = 1;
$p->save();
}
$this->addBandwidth($customer, $plan);
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;
// session timeout [it reset everyday, am still making my research] we can use it for something like 1H/Day - since it reset daily, and Max-All-Session clear everything
//$this->upsertCustomer($customer['username'], 'Session-Timeout', 3600); // 3600 = 1 hour
$this->upsertCustomer($customer['username'], 'Max-All-Session', $timelimit);
$this->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";
//$this->upsertCustomer($customer['username'], 'Max-Volume', $datalimit);
// Mikrotik Spesific
$this->upsertCustomer($customer['username'], 'Max-Data', $datalimit);
//$this->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;
// session timeout [it reset everyday, am still making my research] we can use it for something like 1H/Day - since it reset daily, and Max-All-Session clear everything
//$this->upsertCustomer($customer['username'], 'Session-Timeout', 3600); // 3600 = 1 hour
$this->upsertCustomer($customer['username'], 'Max-All-Session', $timelimit);
if ($plan['data_unit'] == 'GB')
$datalimit = $plan['data_limit'] . "000000000";
else
$datalimit = $plan['data_limit'] . "000000";
//$this->upsertCustomer($customer['username'], 'Max-Volume', $datalimit);
$this->upsertCustomer($customer['username'], 'Max-All-Session', $timelimit);
// Mikrotik Spesific
$this->upsertCustomer($customer['username'], 'Max-Data', $datalimit);
//$this->upsertCustomer($customer['username'], 'Mikrotik-Total-Limit', $datalimit);
}
} else {
//$this->delAtribute($this->getTableCustomer(), 'Max-Volume', 'username', $customer['username']);
// session timeout [it reset everyday, am still making my research] we can use it for something like 1H/Day - since it reset daily, and Max-All-Session clear everything
//$this->delAtribute($customer['username'], 'Session-Timeout', 3600); // 3600 = 1 hour
$this->delAtribute($this->getTableCustomer(), 'Max-All-Session', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'Max-Data', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'Mikrotik-Rate-Limit', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'WISPr-Session-Terminate-Time', 'username', $customer['username']);
//we are removing the below in the next two updates, some users may have that attribute, it will remove them before we remove it
$this->delAtribute($this->getTableCustomer(), 'access-period', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'Max-Volume', 'username', $customer['username']);
}
$this->disconnectCustomer($customer['username']);
$this->getTableAcct()->where_equal('username', $customer['username'])->delete_many();
// expired user
if ($expired != null) {
//$this->upsertCustomer($customer['username'], 'access-period', strtotime($expired) - time());
if ($expired != '') {
//extend session time only if the plan are the same
// session timeout [it reset everyday, am still making my research] we can use it for something like 1H/Day - since it reset daily, and Max-All-Session clear everything
//$this->upsertCustomer($customer['username'], 'Session-Timeout', 3600); // 3600 = 1 hour
$this->upsertCustomer($customer['username'], 'Max-All-Session', strtotime($expired) - time());
$this->upsertCustomer($customer['username'], 'expiration', date('d M Y H:i:s', strtotime($expired)));
$this->upsertCustomer($customer['username'], 'Expiration', date('d M Y H:i:s', strtotime($expired)));
// Mikrotik Spesific
$this->upsertCustomer(
$customer['username'],
@ -293,12 +386,23 @@ class Radius
);
} else {
$this->delAtribute($this->getTableCustomer(), 'Max-All-Session', 'username', $customer['username']);
//$this->delAtribute($this->getTableCustomer(), 'access-period', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'expiration', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'Expiration', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'Mikrotik-Rate-Limit', 'username', $customer['username']);
$this->delAtribute($this->getTableCustomer(), 'WISPr-Session-Terminate-Time', 'username', $customer['username']);
//we are removing the below in the next two updates, some users may have that attribute, it will remove them before we remove it
$this->delAtribute($this->getTableCustomer(), 'access-period', 'username', $customer['username']);
}
if ($plan['type'] == 'PPPOE') {
if (!empty($customer['pppoe_ip']) && $expired != '') {
$this->upsertCustomerAttr($customer['username'], 'Framed-Pool', $plan['pool'], ':=');
$this->upsertCustomerAttr($customer['username'], 'Framed-IP-Address', $customer['pppoe_ip'], ':=');
$this->upsertCustomerAttr($customer['username'], 'Framed-IP-Netmask', '255.255.255.0', ':=');
}else{
$this->upsertCustomerAttr($customer['username'], 'Framed-Pool', $plan['pool'], ':=');
$this->upsertCustomerAttr($customer['username'], 'Framed-IP-Address', '0.0.0.0', ':=');
$this->upsertCustomerAttr($customer['username'], 'Framed-IP-Netmask', '255.255.255.0', ':=');
}
}
@ -307,7 +411,8 @@ class Radius
return false;
}
public function customerUpsert($customer, $plan)
public function customerUpsert($customer, $plan) //Update or Insert customer plan
{
if ($plan['type'] == 'PPPOE') {
$this->upsertCustomer($customer['username'], 'Cleartext-Password', (empty($customer['pppoe_password'])) ? $customer['password'] : $customer['pppoe_password']);
@ -321,9 +426,9 @@ class Radius
return true;
}
private function delAtribute($tabel, $attribute, $key, $value)
private function delAtribute($table, $attribute, $key, $value)
{
$r = $tabel->where_equal($key, $value)->whereEqual('attribute', $attribute)->findOne();
$r = $table->where_equal($key, $value)->whereEqual('attribute', $attribute)->findOne();
if ($r) $r->delete();
}
@ -357,7 +462,8 @@ class Radius
$r->attribute = $attr;
$r->op = $op;
$r->value = $value;
return $r->save();
$r->save();
return true;
}
/**
* To insert or update existing customer Attribute
@ -398,4 +504,31 @@ class Radius
}
return $result;
}
public function addBandwidth($customer, $plan)
{
$bw = ORM::for_table("tbl_bandwidth")->find_one($plan['id_bw']);
$unitdown = ($bw['rate_down_unit'] == 'Kbps') ? 'K' : 'M';
$unitup = ($bw['rate_up_unit'] == 'Kbps') ? 'K' : 'M';
// TODO Burst mode [ 2M/1M 256K/128K 128K/64K 1s 1 64K/32K]
if (!empty(trim($bw['burst']))) {
// burst format: 2M/1M 256K/128K 128K/64K 1s 1 64K/32K
$pattern = '/(\d+[KM])\/(\d+[KM]) (\d+[KM])\/(\d+[KM]) (\d+) (\d+) (\d+[KM])\/(\d+[KM])/';
preg_match($pattern, $bw['burst'], $matches);
if (count($matches) == 9) {
$burst = $bw['rate_up'] . $unitup . "/" . $bw['rate_down'] . $unitdown . ' ' . $matches[1] . '/' . $matches[2] . ' ' . $matches[3] . '/' . $matches[4] . ' ' . $matches[5] . ' ' . $matches[6] . ' ' . $matches[7] . '/' . $matches[8];
$this->upsertCustomer($customer['username'], 'Mikrotik-Rate-Limit', $burst);
} else {
_log("Unexpected burst format for customer " . $customer['username']);
}
} else {
//$this->upsertCustomer($customer['username'], 'Ascend-Data-Rate', $this->stringToInteger($bw['rate_up'] . $unitup) . "/" . $this->stringToInteger($bw['rate_down'] . $unitdown));
$this->upsertCustomer($customer['username'], 'Mikrotik-Rate-Limit', $bw['rate_up'] . $unitup . "/" . $bw['rate_down'] . $unitdown);
}
return true;
}
}

View File

@ -21,14 +21,28 @@ class RadiusRest {
function add_customer($customer, $plan)
{
}
function sync_customer($customer, $plan)
{
$this->add_customer($customer, $plan);
}
// Remove Customer to Mikrotik/Device
function remove_customer($customer, $plan)
{
// set zero data usage
if ($plan['typebp'] == "Limited" && ($plan['limit_type'] == "Data_Limit" || $plan['limit_type'] == "Both_Limit")) {
$cs = ORM::for_table("rad_acct")->where('username', $customer['username'])->findMany();
foreach ($cs as $c) {
$c->acctOutputOctets = 0;
$c->acctInputOctets = 0;
$c->save();
}
}
}
// customer change username
public function change_username($from, $to)
public function change_username($plan, $from, $to)
{
}

View File

@ -34,7 +34,7 @@ class FileName {
}
// customer change username
public function change_username($from, $to)
public function change_username($plan, $from, $to)
{
}

719
system/lan/arabic.json Normal file
View File

@ -0,0 +1,719 @@
{
"Log_in": "تسجيل الدخول",
"Register": "تسجيل",
"Announcement": "إعلان",
"Registration_Info": "معلومات التسجيل",
"Voucher_not_found__please_buy_voucher_befor_register": "القسيمة غير موجودة، يرجى شراء القسيمة قبل التسجيل",
"Register_Success__You_can_login_now": "تم التسجيل بنجاح! يمكنك تسجيل الدخول الآن",
"Log_in_to_Member_Panel": "تسجيل الدخول إلى لوحة الأعضاء",
"Register_as_Member": "التسجيل كعضو",
"Enter_Admin_Area": "دخول منطقة الإدارة",
"PHPNuxBill": "PHPNuxBill",
"Username": "اسم المستخدم",
"Password": "كلمة المرور",
"Passwords_does_not_match": "كلمات المرور غير متطابقة",
"Account_already_axist": "الحساب موجود بالفعل",
"Manage": "إدارة",
"Submit": "إرسال",
"Save_Changes": "حفظ التغييرات",
"Cancel": "إلغاء",
"Edit": "تعديل",
"Delete": "حذف",
"Welcome": "مرحباً",
"Data_Created_Successfully": "تم إنشاء البيانات بنجاح",
"Data_Updated_Successfully": "تم تحديث البيانات بنجاح",
"Data_Deleted_Successfully": "تم حذف البيانات بنجاح",
"Static_Pages": "صفحات ثابتة",
"Failed_to_save_page__make_sure_i_can_write_to_folder_pages___i_chmod_664_pages___html_i_": "فشل في حفظ الصفحة، تأكد من أنني أستطيع الكتابة إلى مجلد الصفحات، <i>chmod 664 pages/*.html<i>",
"Saving_page_success": "تم حفظ الصفحة بنجاح",
"Sometimes_you_need_to_refresh_3_times_until_content_change": "أحيانًا تحتاج إلى التحديث ثلاث مرات حتى يتغير المحتوى",
"Dashboard": "لوحة التحكم",
"Search_Customers___": "بحث العملاء...",
"My_Account": "حسابي",
"My_Profile": "ملفي الشخصي",
"Settings": "الإعدادات",
"Edit_Profile": "تعديل الملف الشخصي",
"Change_Password": "تغيير كلمة المرور",
"Logout": "تسجيل الخروج",
"Services": "الخدمات",
"Bandwidth_Plans": "خطط عرض النطاق الترددي",
"Bandwidth_Name": "اسم عرض النطاق الترددي",
"New_Bandwidth": "عرض نطاق ترددي جديد",
"Edit_Bandwidth": "تعديل عرض النطاق الترددي",
"Add_New_Bandwidth": "إضافة عرض نطاق ترددي جديد",
"Rate_Download": "معدل التنزيل",
"Rate_Upload": "معدل الرفع",
"Name_Bandwidth_Already_Exist": "اسم عرض النطاق الترددي موجود بالفعل",
"Hotspot_Plans": "خطط هوتسبوت",
"PPPOE_Plans": "خطط PPPOE",
"Plan_Name": "اسم الخطة",
"New_Service_Plan": "خطة خدمة جديدة",
"Add_Service_Plan": "إضافة خطة خدمة",
"Edit_Service_Plan": "تعديل خطة الخدمة",
"Name_Plan_Already_Exist": "اسم الخطة موجود بالفعل",
"Plan_Type": "نوع الخطة",
"Plan_Price": "سعر الخطة",
"Limit_Type": "نوع الحد",
"Unlimited": "غير محدود",
"Limited": "محدود",
"Time_Limit": "حد زمني",
"Data_Limit": "حد البيانات",
"Both_Limit": "كلاهما",
"Plan_Validity": "صلاحية الخطة",
"Select_Bandwidth": "اختر عرض النطاق الترددي",
"Shared_Users": "المستخدمون المشتركين",
"Choose_User_Type_Sales_to_disable_access_to_Settings": "اختر نوع المستخدم المبيعات لتعطيل الوصول إلى الإعدادات",
"Current_Password": "كلمة المرور الحالية",
"New_Password": "كلمة المرور الجديدة",
"Administrator": "مدير",
"Sales": "مبيعات",
"Member": "عضو",
"Confirm_New_Password": "تأكيد كلمة المرور الجديدة",
"Confirm_Password": "تأكيد كلمة المرور",
"Full_Name": "الاسم الكامل",
"User_Type": "نوع المستخدم",
"Address": "العنوان",
"Created_On": "تم الإنشاء في",
"Expires_On": "ينتهي في",
"Phone_Number": "رقم الهاتف",
"User_deleted_Successfully": "تم حذف المستخدم بنجاح",
"Full_Administrator": "مدير كامل",
"Keep_Blank_to_do_not_change_Password": "اتركه فارغًا لعدم تغيير كلمة المرور",
"Keep_it_blank_if_you_do_not_want_to_show_currency_code": "اتركه فارغًا إذا كنت لا تريد عرض رمز العملة",
"Theme_Style": "نمط السمة",
"Theme_Color": "لون السمة",
"Default_Language": "اللغة الافتراضية",
"Network": "الشبكة",
"Routers": "الموجهات",
"IP_Pool": "مجمع IP",
"New_Router": "موجه جديد",
"Add_Router": "إضافة موجه",
"Edit_Router": "تعديل الموجه",
"Router_Name": "اسم الموجه",
"IP_Address": "عنوان IP",
"Router_Secret": "سر الموجه",
"Description": "الوصف",
"IP_Router_Already_Exist": "عنوان IP للموجه موجود بالفعل",
"Name_Pool": "اسم المجمع",
"Range_IP": "نطاق IP",
"New_Pool": "مجمع جديد",
"Add_Pool": "إضافة مجمع",
"Edit_Pool": "تعديل المجمع",
"Pool_Name_Already_Exist": "اسم المجمع موجود بالفعل",
"Refill_Account": "إعادة شحن الحساب",
"Recharge_Account": "إعادة شحن الحساب",
"Select_Account": "اختر الحساب",
"Service_Plan": "خطة الخدمة",
"Recharge": "إعادة شحن",
"Method": "طريقة",
"Account_Created_Successfully": "تم إنشاء الحساب بنجاح",
"Database_Status": "حالة قاعدة البيانات",
"Total_Database_Size": "إجمالي حجم قاعدة البيانات",
"Download_Database_Backup": "تحميل نسخة احتياطية من قاعدة البيانات",
"Table_Name": "اسم الجدول",
"Rows": "الصفوف",
"Size": "الحجم",
"Customer": "العميل",
"Add_New_Contact": "إضافة جهة اتصال جديدة",
"Edit_Contact": "تعديل جهة الاتصال",
"List_Contact": "قائمة جهات الاتصال",
"Manage_Contact": "إدارة جهات الاتصال",
"Reports": "التقارير",
"Daily_Reports": "التقارير اليومية",
"Period_Reports": "تقارير الفترة",
"All_Transactions": "جميع المعاملات",
"Total_Income": "إجمالي الدخل",
"All_Transactions_at_Date": "جميع المعاملات في التاريخ",
"Export_for_Print": "تصدير للطباعة",
"Print": "طباعة",
"Export_to_PDF": "تصدير إلى PDF",
"Click_Here_to_Print": "انقر هنا للطباعة",
"You_can_use_html_tag": "يمكنك استخدام وسم HTML",
"Date_Format": "تنسيق التاريخ",
"Income_Today": "الدخل اليوم",
"Income_This_Month": "الدخل هذا الشهر",
"Users_Active": "المستخدمون النشطون",
"Total_Users": "إجمالي المستخدمين",
"Users": "المستخدمون",
"Edit_User": "تعديل المستخدم",
"Last_Login": "آخر تسجيل دخول",
"Administrator_Users": "مستخدمو المدير",
"Manage_Administrator": "إدارة المدير",
"Add_New_Administrator": "إضافة مدير جديد",
"Localisation": "الموقع",
"Backup_Restore": "النسخ الاحتياطي / الاستعادة",
"General_Settings": "الإعدادات العامة",
"Date": "التاريخ",
"Login_Successful": "تم تسجيل الدخول بنجاح",
"Failed_Login": "فشل تسجيل الدخول",
"Settings_Saved_Successfully": "تم حفظ الإعدادات بنجاح",
"User_Updated_Successfully": "تم تحديث المستخدم بنجاح",
"User_Expired__Today": "انتهت صلاحية المستخدم، اليوم",
"Activity_Log": "سجل النشاطات",
"View_Reports": "عرض التقارير",
"View_All": "عرض الكل",
"Number_of_Vouchers": "عدد القسائم",
"Length_Code": "طول الكود",
"Code_Voucher": "كود القسيمة",
"Voucher": "قسيمة",
"Hotspot_Voucher": "قسيمة هوتسبوت",
"Status_Voucher": "حالة القسيمة",
"Add_Vouchers": "إضافة قسائم",
"Create_Vouchers_Successfully": "تم إنشاء القسائم بنجاح",
"Generate": "توليد",
"Print_side_by_side__it_will_easy_to_cut": "اطبع جنبًا إلى جنب، سيكون من السهل قصها",
"From_Date": "من التاريخ",
"To_Date": "إلى التاريخ",
"New_Service": "خدمة جديدة",
"Type": "نوع",
"Finish": "إنهاء",
"Application_Name__Company_Name": "اسم التطبيق / اسم الشركة",
"This_Name_will_be_shown_on_the_Title": "سيتم عرض هذا الاسم في العنوان",
"Next": "التالي",
"Last": "الأخير",
"Timezone": "المنطقة الزمنية",
"Decimal_Point": "الفاصلة العشرية",
"Thousands_Separator": "فاصل الآلاف",
"Currency_Code": "رمز العملة",
"Order_Voucher": "طلب قسيمة",
"Voucher_Activation": "تفعيل القسيمة",
"List_Activated_Voucher": "قائمة القسائم المفعلة",
"Enter_voucher_code_here": "أدخل رمز القسيمة هنا",
"Private_Message": "رسالة خاصة",
"Inbox": "البريد الوارد",
"Outbox": "البريد الصادر",
"Compose": "إنشاء",
"Send_to": "إرسال إلى",
"Title": "العنوان",
"Message": "الرسالة",
"Your_Account_Information": "معلومات حسابك",
"Welcome_to_the_Panel_Members_page__on_this_page_you_can_": "مرحبًا بك في صفحة أعضاء اللوحة، في هذه الصفحة يمكنك:",
"Invalid_Username_or_Password": "اسم المستخدم أو كلمة المرور غير صالحة",
"You_do_not_have_permission_to_access_this_page": "ليس لديك إذن للوصول إلى هذه الصفحة",
"Incorrect_Current_Password": "كلمة المرور الحالية غير صحيحة",
"Password_changed_successfully__Please_login_again": "تم تغيير كلمة المرور بنجاح، يرجى تسجيل الدخول مرة أخرى",
"All_field_is_required": "جميع الحقول مطلوبة",
"Voucher_Not_Valid": "القسيمة غير صالحة",
"Activation_Vouchers_Successfully": "تم تفعيل القسائم بنجاح",
"Data_Not_Found": "لم يتم العثور على البيانات",
"Search_by_Username": "البحث حسب اسم المستخدم",
"Search_by_Name": "البحث حسب الاسم",
"Search_by_Code_Voucher": "البحث حسب كود القسيمة",
"Search": "بحث",
"Select_a_customer": "اختر عميلاً",
"Select_Routers": "اختر الموجهات",
"Select_Plans": "اختر الخطط",
"Select_Pool": "اختر المجمع",
"Hrs": "ساعات",
"Mins": "دقائق",
"Days": "أيام",
"Months": "أشهر",
"Add_Language": "إضافة لغة",
"Language_Name": "اسم اللغة",
"Folder_Name": "اسم المجلد",
"Translator": "المترجم",
"Language_Name_Already_Exist": "اسم اللغة موجود بالفعل",
"Payment_Gateway": "بوابة الدفع",
"Community": "المجتمع",
"1_user_can_be_used_for_many_devices_": "هل يمكن استخدام مستخدم واحد للعديد من الأجهزة؟",
"Cannot_be_change_after_saved": "لا يمكن تغييرها بعد الحفظ",
"Explain_Coverage_of_router": "شرح تغطية الموجه",
"Name_of_Area_that_router_operated": "اسم المنطقة التي يعمل بها الموجه",
"Payment_Notification_URL__Recurring_Notification_URL__Pay_Account_Notification_URL": "عنوان URL لإشعار الدفع، عنوان URL لإشعار المتكرر، عنوان URL لإشعار دفع الحساب",
"Finish_Redirect_URL__Unfinish_Redirect_URL__Error_Redirect_URL": "عنوان URL لإعادة التوجيه بعد الانتهاء، عنوان URL لإعادة التوجيه بعد عدم الانتهاء، عنوان URL لإعادة التوجيه عند حدوث خطأ",
"Status": "الحالة",
"Plan_Not_found": "الخطة غير موجودة",
"Failed_to_create_transaction_": "فشل في إنشاء المعاملة.",
"Seller_has_not_yet_setup_Xendit_payment_gateway": "البائع لم يقم بعد بإعداد بوابة الدفع Xendit",
"Admin_has_not_yet_setup_Xendit_payment_gateway__please_tell_admin": "المدير لم يقم بعد بإعداد بوابة الدفع Xendit، يرجى إخبار المدير",
"You_already_have_unpaid_transaction__cancel_it_or_pay_it_": "لديك معاملة غير مدفوعة، قم بإلغائها أو دفعها.",
"Transaction_Not_found": "المعاملة غير موجودة",
"Cancel_it_": "هل تريد إلغاءها؟",
"expired": "منتهية الصلاحية",
"Check_for_Payment": "التحقق من الدفع",
"Transaction_still_unpaid_": "المعاملة لا تزال غير مدفوعة.",
"Paid_Date": "تاريخ الدفع",
"Transaction_has_been_paid_": "تم دفع المعاملة.",
"PAID": "مدفوع",
"CANCELED": "ملغي",
"UNPAID": "غير مدفوع",
"PAY_NOW": "ادفع الآن",
"Buy_Hotspot_Plan": "شراء خطة هوتسبوت",
"Buy_PPOE_Plan": "شراء خطة PPOE",
"Package": "الحزمة",
"Order_Internet_Package": "طلب حزمة إنترنت",
"Unknown_Command_": "أمر غير معروف.",
"Checking_payment": "التحقق من الدفع",
"Create_Transaction_Success": "تم إنشاء المعاملة بنجاح",
"You_have_unpaid_transaction": "لديك معاملة غير مدفوعة",
"TripayPayment_Channel": "قناة الدفع Tripay",
"Payment_Channel": "قناة الدفع",
"Payment_check_failed_": "فشل التحقق من الدفع.",
"Order_Package": "طلب الحزمة",
"Transactions": "المعاملات",
"Payments": "المدفوعات",
"History": "التاريخ",
"Order_History": "تاريخ الطلبات",
"Gateway": "البوابة",
"Date_Done": "تاريخ الإنجاز",
"Unpaid_Order": "طلب غير مدفوع",
"Payment_Gateway_Not_Found": "لم يتم العثور على بوابة الدفع",
"Payment_Gateway_saved_successfully": "تم حفظ بوابة الدفع بنجاح",
"ORDER": "طلب",
"Package_History": "تاريخ الحزم",
"Buy_History": "تاريخ الشراء",
"Activation_History": "تاريخ التفعيل",
"Buy_Package": "شراء حزمة",
"Email": "البريد الإلكتروني",
"Company_Footer": "تذييل الشركة",
"Will_show_below_user_pages": "سيظهر أسفل صفحات المستخدم",
"Request_OTP": "طلب رمز التحقق",
"Verification_Code": "رمز التحقق",
"SMS_Verification_Code": "رمز التحقق عبر الرسائل القصيرة",
"Please_enter_your_email_address": "يرجى إدخال عنوان بريدك الإلكتروني",
"Failed_to_create_Paypal_transaction_": "فشل في إنشاء معاملة Paypal.",
"Plugin": "الإضافة",
"Plugin_Manager": "مدير الإضافات",
"User_Notification": "إشعار المستخدم",
"Expired_Notification": "إشعار انتهاء الصلاحية",
"User_will_get_notification_when_package_expired": "سيحصل المستخدم على إشعار عند انتهاء صلاحية الحزمة",
"Expired_Notification_Message": "رسالة إشعار انتهاء الصلاحية",
"Payment_Notification": "إشعار الدفع",
"User_will_get_invoice_notification_when_buy_package_or_package_refilled": "سيحصل المستخدم على إشعار بالفاتورة عند شراء حزمة أو إعادة تعبئة الحزمة",
"Current_IP": "عنوان IP الحالي",
"Current_MAC": "عنوان MAC الحالي",
"Login_Status": "حالة تسجيل الدخول",
"Login_Request_successfully": "تم طلب تسجيل الدخول بنجاح",
"Logout_Request_successfully": "تم طلب تسجيل الخروج بنجاح",
"Disconnect_Internet_": "قطع الاتصال بالإنترنت؟",
"Not_Online__Login_now_": "غير متصل، تسجيل الدخول الآن؟",
"You_are_Online__Logout_": "أنت متصل، تسجيل الخروج؟",
"Connect_to_Internet_": "الاتصال بالإنترنت؟",
"Your_account_not_connected_to_internet": "حسابك غير متصل بالإنترنت",
"Failed_to_create_transaction__": "فشل في إنشاء المعاملة.",
"Failed_to_check_status_transaction__": "فشل في التحقق من حالة المعاملة.",
"Disable_Voucher": "تعطيل القسيمة",
"Balance": "الرصيد",
"Balance_System": "نظام الرصيد",
"Enable_System": "تمكين النظام",
"Allow_Transfer": "السماح بالنقل",
"Telegram_Notification": "إشعار تليغرام",
"SMS_OTP_Registration": "تسجيل رمز التحقق عبر الرسائل القصيرة",
"Whatsapp_Notification": "إشعار واتساب",
"Tawk_to_Chat_Widget": "أداة الدردشة Tawk.to",
"Invoice": "الفاتورة",
"Country_Code_Phone": "رمز الهاتف للبلد",
"Voucher_activation_menu_will_be_hidden": "سيتم إخفاء قائمة تفعيل القسيمة",
"Customer_can_deposit_money_to_buy_voucher": "يمكن للعميل إيداع الأموال لشراء القسيمة",
"Allow_balance_transfer_between_customers": "السماح بنقل الرصيد بين العملاء",
"Reminder_Notification": "إشعار التذكير",
"Reminder_Notification_Message": "رسالة إشعار التذكير",
"Reminder_7_days": "تذكير 7 أيام",
"Reminder_3_days": "تذكير 3 أيام",
"Reminder_1_day": "تذكير يوم واحد",
"PPPOE_Password": "كلمة مرور PPPOE",
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_user_password": "لا يمكن للمستخدم تغيير هذا، فقط المدير. إذا كان فارغًا، سيتم استخدام كلمة مرور المستخدم",
"Invoice_Balance_Message": "رسالة رصيد الفاتورة",
"Invoice_Notification_Payment": "إشعار دفع الفاتورة",
"Balance_Notification_Payment": "إشعار دفع الرصيد",
"Balance_Plans": "خطط الرصيد",
"Buy_Balance": "شراء رصيد",
"Price": "السعر",
"Validity": "الصلاحية",
"Disable_auto_renewal_": "تعطيل التجديد التلقائي؟",
"Auto_Renewal_On": "التجديد التلقائي مفعل",
"Enable_auto_renewal_": "تمكين التجديد التلقائي؟",
"Auto_Renewal_Off": "التجديد التلقائي معطل",
"Refill_Balance": "إعادة شحن الرصيد",
"Invoice_Footer": "تذييل الفاتورة",
"Pay_With_Balance": "الدفع بالرصيد",
"Pay_this_with_Balance__your_active_package_will_be_overwrite": "ادفع هذا بالرصيد؟ سيتم الكتابة فوق الحزمة النشطة الخاصة بك",
"Success_to_buy_package": "تم شراء الحزمة بنجاح",
"Auto_Renewal": "التجديد التلقائي",
"View": "عرض",
"Back": "العودة",
"Active": "نشط",
"Transfer_Balance": "نقل الرصيد",
"Send_your_balance_": "إرسال رصيدك؟",
"Send": "إرسال",
"Cannot_send_to_yourself": "لا يمكن الإرسال إلى نفسك",
"Sending_balance_success": "تم إرسال الرصيد بنجاح",
"From": "من",
"To": "إلى",
"insufficient_balance": "رصيد غير كافٍ",
"Send_Balance": "إرسال الرصيد",
"Received_Balance": "الرصيد المستلم",
"Minimum_Balance_Transfer": "الحد الأدنى لنقل الرصيد",
"Minimum_Transfer": "الحد الأدنى للنقل",
"Company_Logo": "شعار الشركة",
"Expired_IP_Pool": "مجمع IP منتهي الصلاحية",
"Proxy": "الوكيل",
"Proxy_Server": "خادم الوكيل",
"Proxy_Server_Login": "تسجيل دخول خادم الوكيل",
"Hotspot_Plan": "خطة هوتسبوت",
"PPPOE_Plan": "خطة PPPOE",
"UNKNOWN": "غير معروف",
"Are_You_Sure_": "هل أنت متأكد؟",
"Success_to_send_package": "تم إرسال الحزمة بنجاح",
"Target_has_active_plan__different_with_current_plant_": "الهدف لديه خطة نشطة، تختلف عن الخطة الحالية.",
"Recharge_a_friend": "إعادة شحن صديق",
"Buy_for_friend": "شراء لصديق",
"Buy_this_for_friend_account_": "شراء هذا لحساب صديقك؟",
"Review_package_before_recharge": "مراجعة الحزمة قبل الشحن",
"Activate": "تفعيل",
"Deactivate": "تعطيل",
"Sync": "مزامنة",
"Failed_to_create_PaymeTrust_transaction_": "فشل في إنشاء معاملة PaymeTrust.",
"Location": "الموقع",
"Radius_Plans": "خطط Radius",
"Change_title_in_user_Plan_order": "تغيير العنوان في طلب خطة المستخدم",
"Logs": "السجلات",
"Voucher_Format": "تنسيق القسيمة",
"Resend_To_Customer": "إعادة الإرسال للعميل",
"Your_friend_do_not_have_active_package": "صديقك لا يملك حزمة نشطة",
"Service_Type": "نوع الخدمة",
"Others": "أخرى",
"PPPoE": "PPPoE",
"Hotspot": "هوتسبوت",
"Disable_Registration": "تعطيل التسجيل",
"Customer_just_Login_with_Phone_number_and_Voucher_Code__Voucher_will_be_password": "العميل يسجل الدخول فقط باستخدام رقم الهاتف وكود القسيمة، ستكون القسيمة كلمة المرور",
"Login___Activate_Voucher": "تسجيل الدخول / تفعيل القسيمة",
"After_Customer_activate_voucher_or_login__customer_will_be_redirected_to_this_url": "بعد أن يقوم العميل بتفعيل القسيمة أو تسجيل الدخول، سيتم إعادة توجيه العميل إلى هذا الرابط",
"Voucher_Prefix": "بادئة القسيمة",
"Voucher_activation_success__now_you_can_login": "تم تفعيل القسيمة بنجاح، الآن يمكنك تسجيل الدخول",
"Buy_this__your_active_package_will_be_overwritten": "شراء هذا؟ سيتم الكتابة فوق الحزمة النشطة الخاصة بك",
"Pay_this_with_Balance__your_active_package_will_be_overwritten": "ادفع هذا بالرصيد؟ سيتم الكتابة فوق الحزمة النشطة الخاصة بك",
"Buy_this__your_active_package_will_be_overwrite": "شراء هذا؟ سيتم الكتابة فوق الحزمة النشطة الخاصة بك",
"Monthly_Registered_Customers": "العملاء المسجلين شهريًا",
"Total_Monthly_Sales": "إجمالي المبيعات الشهرية",
"Active_Users": "المستخدمون النشطون",
"All_Users_Insights": "رؤى جميع المستخدمين",
"SuperAdmin": "مدير النظام",
"Radius": "Radius",
"Radius_NAS": "Radius NAS",
"Translation": "الترجمة",
"Translation_saved_Successfully": "تم حفظ الترجمة بنجاح",
"Language_Editor": "محرر اللغة",
"year": "سنة",
"month": "شهر",
"week": "أسبوع",
"day": "يوم",
"hour": "ساعة",
"minute": "دقيقة",
"second": "ثانية",
"Attributes": "السمات",
"Profile": "الملف الشخصي",
"Phone": "الهاتف",
"City": "المدينة",
"Sub_District": "الحي",
"Ward": "الحي",
"Credentials": "بيانات الاعتماد",
"Agent": "الوكيل",
"This_Token_will_act_as_SuperAdmin_Admin": "سيعمل هذا الرمز المميز كمدير النظام / المدير",
"Login": "تسجيل الدخول",
"Expired_Action": "إجراء منتهي الصلاحية",
"Expired_Address_List_Name": "اسم قائمة العناوين المنتهية الصلاحية",
"Address_List": "قائمة العناوين",
"Optional": "اختياري",
"Generated_By": "تم إنشاؤه بواسطة",
"Admin": "مدير",
"Password_should_be_minimum_6_characters": "يجب أن تكون كلمة المرور مكونة من 6 أحرف على الأقل",
"Add_User": "إضافة مستخدم",
"Send_Notification": "إرسال إشعار",
"Code": "الكود",
"Send_To_Customer": "إرسال للعميل",
"Prev": "السابق",
"Voucher_Not_Found": "القسيمة غير موجودة",
"Miscellaneous": "متفرقات",
"OTP_Required": "رمز التحقق مطلوب",
"Change": "تغيير",
"Change_Phone_Number": "تغيير رقم الهاتف",
"Current_Number": "الرقم الحالي",
"New_Number": "الرقم الجديد",
"Input_your_phone_number": "أدخل رقم هاتفك",
"OTP": "رمز التحقق",
"Enter_OTP_that_was_sent_to_your_phone": "أدخل رمز التحقق الذي تم إرساله إلى هاتفك",
"Update": "تحديث",
"OTP_is_required_when_user_want_to_change_phone_number": "رمز التحقق مطلوب عندما يريد المستخدم تغيير رقم الهاتف",
"Rate": "المعدل",
"Burst": "الانفجار",
"Editing_Bandwidth_will_not_automatically_update_the_plan__you_need_to_edit_the_plan_then_save_again": "تحرير عرض النطاق الترددي لن يحدث تحديثًا تلقائيًا للخطة، تحتاج إلى تحرير الخطة ثم الحفظ مرة أخرى",
"OTP_Method": "طريقة رمز التحقق",
"SMS": "الرسائل القصيرة",
"WhatsApp": "واتساب",
"SMS_and_WhatsApp": "الرسائل القصيرة وواتساب",
"The_method_which_OTP_will_be_sent_to_user": "الطريقة التي سيتم إرسال رمز التحقق بها إلى المستخدم",
"Report_Viewer": "عارض التقارير",
"Super_Administrator": "مدير النظام الأعلى",
"Send_To": "إرسال إلى",
"Resend": "إعادة الإرسال",
"Alert": "تنبيه",
"success": "نجاح",
"Click_Here": "انقر هنا",
"danger": "خطر",
"Logout_Successful": "تم تسجيل الخروج بنجاح",
"warning": "تحذير",
"Users_Announcement": "إعلان للمستخدمين",
"Customer_Announcement": "إعلان للعملاء",
"1_Period___1_Month__Expires_the_20th_of_each_month": "1 فترة = 1 شهر، تنتهي في 20 من كل شهر",
"Period": "الفترة",
"Add": "إضافة",
"Select_Payment_Gateway": "اختر بوابة الدفع",
"Available_Payment_Gateway": "بوابة الدفع المتاحة",
"Pay_Now": "ادفع الآن",
"Please_select_Payment_Gateway": "يرجى اختيار بوابة الدفع",
"Payment_Gateway_Deleted": "تم حذف بوابة الدفع",
"Payment_Gateway_not_set__please_set_it_in_Settings": "بوابة الدفع غير محددة، يرجى إعدادها في الإعدادات",
"Failed_to_create_Transaction__": "فشل في إنشاء المعاملة.",
"Show_To_Customer": "عرض للعميل",
"Using": "باستخدام",
"Default": "افتراضي",
"Customer_Balance": "رصيد العميل",
"Vouchers": "القسائم",
"Refill_Customer": "إعادة شحن العميل",
"Recharge_Customer": "إعادة شحن العميل",
"Plans": "الخطط",
"PPPOE": "PPPOE",
"Bandwidth": "عرض النطاق الترددي",
"Customers": "العملاء",
"Actives": "النشطون",
"Name": "الاسم",
"Confirm": "تأكيد",
"Plan": "الخطة",
"Total": "الإجمالي",
"Current_Cycle": "الدورة الحالية",
"Additional_Cost": "التكلفة الإضافية",
"Remaining": "المتبقي",
"Not_Found": "غير موجود",
"Cash": "نقدًا",
"Payment_not_found": "لم يتم العثور على الدفع",
"If_your_friend_have_Additional_Cost__you_will_pay_for_that_too": "إذا كان لدى صديقك تكلفة إضافية، ستدفعها أيضًا",
"Cache_cleared_successfully_": "تم مسح الذاكرة المؤقتة بنجاح!",
"Paid": "مدفوع",
"Send_Message": "إرسال رسالة",
"Send_Personal_Message": "إرسال رسالة شخصية",
"Send_Via": "إرسال عبر",
"Compose_your_message___": "أنشئ رسالتك...",
"Use_placeholders_": "استخدم العناصر النائبة:",
"Customer_Name": "اسم العميل",
"Customer_Username": "اسم المستخدم للعميل",
"Customer_Phone": "هاتف العميل",
"Your_Company_Name": "اسم شركتك",
"Message_Sent_Successfully": "تم إرسال الرسالة بنجاح",
"Send_Bulk_Message": "إرسال رسالة مجمعة",
"Group": "المجموعة",
"All_Customers": "جميع العملاء",
"New_Customers": "عملاء جدد",
"Expired_Customers": "عملاء منتهية صلاحيتهم",
"Active_Customers": "العملاء النشطون",
"Map": "الخريطة",
"Customer_Location": "موقع العميل",
"Account_Type": "نوع الحساب",
"Coordinates": "الإحداثيات",
"Latitude_and_Longitude_coordinates_for_map_must_be_separate_with_comma____": "يجب أن تكون إحداثيات خطوط العرض والطول للخريطة مفصولة بفاصلة",
"Customer_Geo_Location_Information": "معلومات الموقع الجغرافي للعميل",
"List": "القائمة",
"Lists": "القوائم",
"Single_Customer": "عميل واحد",
"Bulk_Customers": "عملاء مجمعين",
"Message_per_time": "رسالة لكل مرة",
"5_Messages": "5 رسائل",
"10_Messages": "10 رسائل",
"15_Messages": "15 رسالة",
"20_Messages": "20 رسالة",
"30_Messages": "30 رسالة",
"40_Messages": "40 رسالة",
"50_Messages": "50 رسالة",
"60_Messages": "60 رسالة",
"Use_20_and_above_if_you_are_sending_to_all_customers_to_avoid_server_time_out": "استخدم 20 وما فوق إذا كنت ترسل لجميع العملاء لتجنب انتهاء مهلة الخادم",
"Delay": "التأخير",
"No_Delay": "بدون تأخير",
"5_Seconds": "5 ثوانٍ",
"10_Seconds": "10 ثوانٍ",
"15_Seconds": "15 ثانية",
"20_Seconds": "20 ثانية",
"Use_at_least_5_secs_if_you_are_sending_to_all_customers_to_avoid_being_banned_by_your_message_provider": "استخدم 5 ثوانٍ على الأقل إذا كنت ترسل لجميع العملاء لتجنب الحظر من مزود الرسائل الخاص بك",
"Testing__if_checked_no_real_message_is_sent_": "اختبار [إذا تم التحقق منه، لن يتم إرسال رسالة حقيقية]",
"All_fields_are_required": "جميع الحقول مطلوبة",
"Personal": "شخصي",
"Email_Notification": "إشعار البريد الإلكتروني",
"Router_Name___Location": "اسم الموجه / الموقع",
"Plan_Category": "فئة الخطة",
"ID": "المعرف",
"Internet_Plan": "خطة الإنترنت",
"Privacy_Policy": "سياسة الخصوصية",
"Terms_and_Conditions": "الشروط والأحكام",
"Contact": "الاتصال",
"will_be_replaced_with_Customer_Name": "سيتم استبداله باسم العميل",
"will_be_replaced_with_Customer_username": "سيتم استبداله باسم المستخدم للعميل",
"will_be_replaced_with_Package_name": "سيتم استبداله باسم الحزمة",
"will_be_replaced_with_Package_price": "سيتم استبداله بسعر الحزمة",
"additional_bills_for_customers": "فواتير إضافية للعملاء",
"will_be_replaced_with_Expiration_date": "سيتم استبداله بتاريخ انتهاء الصلاحية",
"Your_Company_Name_at_Settings": "اسم شركتك في الإعدادات",
"Your_Company_Address_at_Settings": "عنوان شركتك في الإعدادات",
"Your_Company_Phone_at_Settings": "هاتف شركتك في الإعدادات",
"Invoice_number": "رقم الفاتورة",
"Date_invoice_created": "تاريخ إنشاء الفاتورة",
"Payment_gateway_user_paid_from": "بوابة الدفع التي دفع منها المستخدم",
"Payment_channel_user_paid_from": "قناة الدفع التي دفع منها المستخدم",
"is_Hotspot_or_PPPOE": "هل هو هوتسبوت أو PPPOE",
"Internet_Package": "حزمة الإنترنت",
"Internet_Package_Prices": "أسعار حزمة الإنترنت",
"Receiver_name": "اسم المستلم",
"Username_internet": "اسم المستخدم للإنترنت",
"User_password": "كلمة مرور المستخدم",
"Expired_datetime": "تاريخ ووقت انتهاء الصلاحية",
"For_Notes_by_admin": "للملاحظات من قبل المدير",
"Transaction_datetime": "تاريخ ووقت المعاملة",
"Balance_Before": "الرصيد قبل",
"Balance_After": "الرصيد بعد",
"how_much_balance_have_been_send": "كم من الرصيد تم إرساله",
"Current_Balance": "الرصيد الحالي",
"Sender_name": "اسم المرسل",
"how_much_balance_have_been_received": "كم من الرصيد تم استلامه",
"Extend_Postpaid_Expiration": "تمديد انتهاء الدفع المسبق",
"Allow_Extend": "السماح بالتمديد",
"Extend_Days": "تمديد الأيام",
"Confirmation_Message": "رسالة التأكيد",
"You_are_already_logged_in": "أنت مسجل الدخول بالفعل",
"Extend": "تمديد",
"Created___Expired": "تم الإنشاء / منتهي الصلاحية",
"Bank_Transfer": "تحويل بنكي",
"Recharge_Using": "إعادة الشحن باستخدام",
"ago": "منذ",
"Disabled": "معطل",
"Banned": "محظور",
"Customer_cannot_login_again": "لا يمكن للعميل تسجيل الدخول مرة أخرى",
"Customer_can_login_but_cannot_buy_internet_plan__Admin_cannot_recharge_customer": "يمكن للعميل تسجيل الدخول ولكن لا يمكنه شراء خطة الإنترنت، لا يمكن للمدير إعادة شحن العميل",
"Don_t_forget_to_deactivate_all_active_plan_too": "لا تنسى تعطيل جميع الخطط النشطة أيضًا",
"Ascending": "تصاعدي",
"Descending": "تنازلي",
"Created_Date": "تاريخ الإنشاء",
"Inactive": "غير نشط",
"Suspended": "معلق",
"Query": "استعلام",
"Notes": "ملاحظات",
"This_account_status": "حالة هذا الحساب",
"Maintenance_Mode": "وضع الصيانة",
"Maintenance_Mode_Settings": "إعدادات وضع الصيانة",
"Status_": "الحالة:",
"End_Date_": "تاريخ الانتهاء:",
"Save": "حفظ",
"Site_is_temporarily_unavailable_": "الموقع غير متاح مؤقتًا.",
"Scheduled_maintenance_is_currently_in_progress__Please_check_back_soon_": "الصيانة المجدولة جارية حاليًا. يرجى التحقق مرة أخرى قريبًا.",
"We_apologize_for_any_inconvenience_": "نعتذر عن أي إزعاج.",
"The": "الـ",
"Team": "الفريق",
"Extend_Package_Expiry": "تمديد انتهاء الحزمة",
"No": "لا",
"Yes": "نعم",
"If_user_buy_same_internet_plan__expiry_date_will_extend": "إذا اشترى المستخدم نفس خطة الإنترنت، سيتم تمديد تاريخ انتهاء الصلاحية",
"Tax_System": "نظام الضرائب",
"Enable_Tax_System": "تمكين نظام الضرائب",
"Tax_will_be_calculated_in_Internet_Plan_Price": "سيتم حساب الضريبة في سعر خطة الإنترنت",
"Tax_Rate": "معدل الضريبة",
"0_5_": "0.5%",
"1_": "1%",
"1_5_": "1.5%",
"2_": "2%",
"5_": "5%",
"10_": "10%",
"Custom": "مخصص",
"Tax_Rates_in_percentage": "معدلات الضريبة بالنسبة المئوية",
"Custom_Tax_Rate": "معدل الضريبة المخصص",
"Enter_Custom_Tax_Rate": "أدخل معدل الضريبة المخصص",
"Enter_the_custom_tax_rate__e_g___3_75_for_3_75__": "أدخل معدل الضريبة المخصص (مثال: 3.75 لـ 3.75%)",
"Additional_Information": "معلومات إضافية",
"City_of_Resident": "مدينة الإقامة",
"District": "المنطقة",
"State": "الولاية",
"State_of_Resident": "ولاية الإقامة",
"Zip": "الرمز البريدي",
"Zip_Code": "الرمز البريدي",
"Local_IP": "IP المحلي",
"Device": "الجهاز",
"Expired_Internet_Plan": "خطة الإنترنت المنتهية الصلاحية",
"When_Expired__customer_will_be_move_to_selected_internet_plan": "عند انتهاء الصلاحية، سيتم نقل العميل إلى خطة الإنترنت المحددة",
"Plugin_Installer": "مثبت الإضافة",
"Expired_Date": "تاريخ الانتهاء",
"Expired": "منتهية الصلاحية",
"Time": "الوقت",
"Data": "البيانات",
"Category": "الفئة",
"later": "لاحقًا",
"Package_Details": "تفاصيل الحزمة",
"Summary": "الملخص",
"Devices_Not_Found": "لم يتم العثور على الأجهزة",
"Income_reset_date": "تاريخ إعادة ضبط الدخل",
"Devices": "الأجهزة",
"Documentation": "التوثيق",
"Hotspot_Auth_Method": "طريقة المصادقة لهوتسبوت",
"Api": "API",
"Http_Chap": "Http-Chap",
"Hotspot_Authentication_Method__Make_sure_you_have_changed_your_hotspot_login_page_": "طريقة المصادقة لهوتسبوت. تأكد من تغيير صفحة تسجيل الدخول لهوتسبوت.",
"Business": "الأعمال",
"Source": "المصدر",
"Destination": "الوجهة",
"Activate_Voucher": "تفعيل القسيمة",
"Voucher_invalid": "القسيمة غير صالحة",
"Account_Not_Found": "الحساب غير موجود",
"Internet_Voucher_Expired": "القسيمة الإنترنت منتهية الصلاحية",
"Additional_Billing": "الفواتير الإضافية",
"Used_Date": "تاريخ الاستخدام",
"Filter": "التصفية",
"Start_time": "وقت البدء",
"End_Time": "وقت الانتهاء",
"Internet_Plans": "خطط الإنترنت",
"Methods": "الطرق",
"Hap_Lite": "Hap Lite",
"balance": "الرصيد",
"radius": "Radius",
"Start_Date": "تاريخ البدء",
"End_Date": "تاريخ الانتهاء",
"New_Version_Notification": "إشعار الإصدار الجديد",
"Enabled": "مفعل",
"This_is_to_notify_you_when_new_updates_is_available": "هذا لإخطارك عندما تكون هناك تحديثات جديدة متاحة",
"Number": "الرقم",
"NAS": "NAS",
"MAC_Address": "عنوان MAC",
"Uptime": "وقت التشغيل",
"Upload": "الرفع",
"Download": "التنزيل",
"CPU_Load": "حمل المعالج",
"Temperature": "درجة الحرارة",
"Voltage": "الجهد الكهربائي",
"Wireless_Status": "حالة اللاسلكي",
"Interface_Status": "حالة الواجهة",
"Hotspot_Online_Users": "مستخدمو الهوتسبوت المتصلون",
"PPPoE_Online_Users": "مستخدمو PPPoE المتصلون",
"Traffic_Monitor": "مراقبة الحركة",
"Interface_Name": "اسم الواجهة",
"Tx__bytes_Out_": "Tx (البيانات المرسلة)",
"Rx__bytes_In_": "Rx (البيانات المستلمة)",
"Total_Usage": "إجمالي الاستخدام",
"Server": "الخادم",
"Mac_Address": "عنوان MAC",
"Session_Time_Left": "الوقت المتبقي للجلسة",
"Upload__RX_": "الرفع (RX)",
"Download__TX_": "التنزيل (TX)",
"Service": "الخدمة",
"Caller_ID": "معرف المتصل",
"Interface": "الواجهة",
"Last_Ip": "آخر IP",
"Last_Activity": "آخر نشاط",
"Signal_Strength": "قوة الإشارة",
"Tx___Rx_CCQ": "Tx / Rx CCQ",
"Rx_Rate": "معدل الاستلام",
"Tx_Rate": "معدل الإرسال",
"TX": "الإرسال",
"RX": "الاستلام",
"Date_Time": "التاريخ / الوقت",
"Topic": "الموضوع",
"Info": "المعلومات",
"Success": "نجاح",
"Error": "خطأ",
"Warning": "تحذير",
"Force_Logout_": "تسجيل الخروج القسري:",
"Disconnect": "قطع الاتصال"
}

View File

@ -312,7 +312,6 @@
"Reminder_3_days": "Reminder 3 days",
"Reminder_1_day": "Reminder 1 day",
"PPPOE_Password": "PPPOE Password",
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_user_password": "User Cannot change this, only admin. if it Empty it will use user password",
"Invoice_Balance_Message": "Invoice Balance Message",
"Invoice_Notification_Payment": "Invoice Notification Payment",
"Balance_Notification_Payment": "Balance Notification Payment",
@ -659,9 +658,7 @@
"Voucher_invalid": "Voucher invalid",
"Account_Not_Found": "Account Not Found",
"Internet_Voucher_Expired": "Internet Voucher Expired",
"": "",
"Additional_Billing": "Additional Billing",
"_": "-",
"Used_Date": "Used Date",
"Filter": "Filter",
"Start_time": "Start time",
@ -672,5 +669,299 @@
"balance": "balance",
"radius": "radius",
"Start_Date": "Start Date",
"End_Date": "End Date"
"End_Date": "End Date",
"New_Version_Notification": "New Version Notification",
"Enabled": "Enabled",
"This_is_to_notify_you_when_new_updates_is_available": "This is to notify you when new updates is available",
"Enable_Session_Timeout": "Enable Session Timeout",
"Logout_Admin_if_not_Available_Online_a_period_of_time": "Logout Admin if not Available\/Online a period of time",
"Timeout_Duration": "Timeout Duration",
"Enter_the_session_timeout_duration__minutes_": "Enter the session timeout duration (minutes)",
"Idle_Timeout__Logout_Admin_if_Idle_for_xx_minutes": "Idle Timeout, Logout Admin if Idle for xx minutes",
"Failed_to_create_transaction__please_tell_seller_": "Failed to create transaction, please tell seller.",
"paid_off": "paid off",
"Sync_account_if_you_failed_login_to_internet": "Sync account if you failed login to internet",
"Channel": "Channel",
"Payment_Link": "Payment Link",
"Created": "Created",
"Previous": "Previous",
"Share": "Share",
"Mail_Deleted_Successfully": "Mail Deleted Successfully",
"Message_Not_Found": "Message Not Found",
"Send_Welcome_Message": "Send Welcome Message",
"WA": "WA",
"_": "-",
"Routers_Maps": "Routers Maps",
"Routers_Geo_Location_Information": "Routers Geo Location Information",
"Coverage": "Coverage",
"PPPoE_Username_already_used_by_another_customer": "PPPoE Username already used by another customer",
"just_now": "just now",
"Not_Working_for_freeradius": "Not Working for freeradius",
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_Customer_Credentials": "User Cannot change this, only admin. if it Empty it will use Customer Credentials",
"Failed_to_buy_package": "Failed to buy package",
"New_Voucher_Created": "New Voucher Created",
"New_Voucher_for_10mbps_Created": "New Voucher for 10mbps Created",
"Show_Chart": "Show Chart",
"For_PDF_Reports___Best_size_1078_x_200___uploaded_image_will_be_autosize": "For PDF Reports | Best size 1078 x 200 | uploaded image will be autosize",
"For_invoice_print_using_Thermal_Printer": "For invoice print using Thermal Printer",
"Theme": "Theme",
"Theme_Info": "Theme Info",
"This_used_for_admin_to_select_payment_in_recharge__using_comma_for_every_new_options": "This used for admin to select payment in recharge, using comma for every new options",
"Income_will_reset_every_this_day": "Income will reset every this day",
"edit_at_config_php": "edit at config.php",
"Hide_Dashboard_Content": "Hide Dashboard Content",
"Radius_Instructions": "Radius Instructions",
"Customer_can_request_to_extend_expirations": "Customer can request to extend expirations",
"i_agree_to_extends_and_will_paid_full_after_this": "i agree to extends and will paid full after this",
"Telegram_Bot_Token": "Telegram Bot Token",
"You_will_get_Payment_and_Error_notification_": "You will get Payment and Error notification&lt;",
"Must_include": "Must include",
"it_will_be_replaced_": "it will be replaced.",
"Or_use_Mikrotik_SMS": "Or use Mikrotik SMS",
"You_can_use": "You can use",
"in_here_too_": "in here too.",
"Empty_this_to_use_internal_mail___PHP": "Empty this to use internal mail() PHP",
"Mail_Reply_To": "Mail Reply To",
"Customer_will_reply_email_to_this_address__empty_if_you_want_to_use_From_Address": "Customer will reply email to this address, empty if you want to use From Address",
"You_will_get_Payment_and_Error_notification": "You will get Payment and Error notification",
"Languge_set_to_english": "Bahasa diatur ke bahasa Inggris",
"Forgot_Password": "Forgot Password",
"_Are_You_Sure_": "Are You Sure?",
"Send_your_balance___": "Send your balance ?",
"Search_Users": "Search Users",
"Theme_Voucher": "Theme Voucher",
"Payment_Info": "Payment Info",
"Radius_Package": "Radius Package",
"Hotspot_Package": "Hotspot Package",
"PPPOE_Package": "PPPOE Package",
"VPN_Package": "VPN Package",
"Application_Name___Company_Name": "Application Name \/ Company Name",
"Print_Max_Char": "Print Max Char",
"Redirect_URL_after_Activation": "Redirect URL after Activation",
"Enable_Radius": "Enable Radius",
"Customer_Balance_System": "Customer Balance System",
"Telegram_User_Channel_Group_ID": "Telegram User\/Channel\/Group ID",
"Test_SMS": "Test SMS",
"SMS_Server_URL": "SMS Server URL",
"Select_Router": "Select Router",
"Free_Server": "Free Server",
"WhatsApp_Server_URL": "WhatsApp Server URL",
"SMTP_Username": "SMTP Username",
"SMTP_Password": "SMTP Password",
"SMTP_Security": "SMTP Security",
"None": "None",
"By_WhatsApp": "By WhatsApp",
"By_SMS": "By SMS",
"By_Email": "By Email",
"From_Direct_Chat_Link_": "From Direct Chat Link.",
"Access_Token": "Access Token",
"Empty_this_to_randomly_created_API_key": "Empty this to randomly created API key",
"Router_Check": "Router Check",
"If_enabled__the_system_will_notify_Admin_when_router_goes_Offline__If_admin_have_10_or_more_router_and_many_customers__it_will_get_overlapping__you_can_disabled": "If enabled, the system will notify Admin when router goes Offline, If admin have 10 or more router and many customers, it will get overlapping, you can disabled",
"Phone_OTP_Required": "Phone OTP Required",
"OTP_is_required_when_user_want_to_change_phone_number_and_registration": "OTP is required when user want to change phone number and registration",
"by_WhatsApp": "by WhatsApp",
"By_WhatsApp_and_SMS": "By WhatsApp and SMS",
"Email_OTP_Required": "Email OTP Required",
"OTP_is_required_when_user_want_to_change_Email_Address": "OTP is required when user want to change Email Address",
"Show_Bandwidth_Plan": "Show Bandwidth Plan",
"_for_Customer": "for Customer",
"Custome": "Custom",
"Custome_Tax_Rate": "Custome Tax Rate",
"Enter_Custome_Tax_Rate": "Enter Custome Tax Rate",
"Authentication": "Authentication",
"Github_Username": "Github Username",
"Github_Token": "Github Token",
"Create_GitHub_personal_access_token": "Create GitHub personal access token",
"only_need_repo_scope": "only need repo scope",
"This_will_allow_you_to_download_plugin_from_private_paid_repository": "This will allow you to download plugin from private\/paid repository",
"Expired_Cronjob_Every_5_Minutes": "Expired Cronjob Every 5 Minutes",
"Expired_Cronjob_Every_1_Hour": "Expired Cronjob Every 1 Hour",
"Reminder_Cronjob_Every_7_AM": "Reminder Cronjob Every 7 AM",
"Force_Logout_": "Force Logout:",
"Activation": "Activation",
"Package_Name": "Package Name",
"Routers_Offline": "Routers Offline",
"Cron_appear_not_been_setup__please_check_your_cron_setup_": "Cron appear not been setup, please check your cron setup.",
"3_Months": "3 Months",
"Invalid_or_Expired_CSRF_Token": "Invalid or Expired CSRF Token",
"Miscellaneous_Settings": "Miscellaneous Settings",
"Check_if_Customer_Online": "Check if Customer Online",
"This_will_show_is_Customer_currently_is_online_or_not": "This will show is Customer currently is online or not",
"General": "General",
"Tax_Rates_by_percentage": "Tax Rates by percentage",
"Settings_For_Mikrotik": "Mikrotik Settings",
"Settings_For_Cron_Expired": "Settings For Cron Expired",
"Choose_one__above_or_below": "Choose one, above or below",
"Settings_For_Cron_Reminder": "Settings For Cron Reminder",
"Upload_Zip_Plugin_Theme_Device": "Upload Zip Plugin\/Theme\/Device",
"Install": "Install",
"To_download_from_private_paid_repository": "To download from private\/paid repository",
"Set_your_Github_Authentication_first": "Set your Github Authentication first",
"SMS_Notification": "SMS Notification",
"Customer_Registration_need_to_validate_using_OTP": "Customer Registration need to validate using OTP",
"Registration_Username": "Registration Username",
"Registration": "Registration",
"For_Registration_and_Update_Phone_Number": "For Registration and Update Phone Number",
"Voucher_Only": "Voucher Only",
"No_Registration": "No Registration",
"Allow_Registration": "Allow Registration",
"Prepaid": "Prepaid",
"Postpaid": "Postpaid",
"Not_Active": "Not Active",
"New_Service_Package": "New Service Package",
"Limit": "Limit",
"Create_expired_Internet_Package": "Create expired Internet Package",
"When_customer_expired__you_can_move_it_to_Expired_Internet_Package": "When customer expired, you can move it to Expired Internet Package",
"Disable": "Disable",
"Create_expired_Internet_Plan": "Create expired Internet Plan",
"When_customer_expired__you_can_move_it_to_Expired_Internet_Plan": "When customer expired, you can move it to Expired Internet Plan",
"Payment_Method": "Payment Method",
"Created_on": "Created on",
"Expires_on": "Expires on",
"Edit_Service_Package": "Edit Service Package",
"Package_Type": "Package Type",
"Package_Price": "Package Price",
"Price_Before_Discount": "Price Before Discount",
"For_Discount_Rate__this_is_price_before_get_discount__must_be_more_expensive_with_real_price": "For Discount Rate, this is price before get discount, must be more expensive with real price",
"Package_Validity": "Package Validity",
"Expired_Internet_Package": "Expired Internet Package",
"Default___Remove_Customer": "Default - Remove Customer",
"When_Expired__customer_will_be_move_to_selected_internet_package": "When Expired, customer will be move to selected internet package",
"on_login___on_up": "on-login \/ on-up",
"on_logout___on_down": "on-logout \/ on-down",
"Online_Status": "Online Status",
"Last_Seen": "Last Seen",
"Online": "Online",
"Offline": "Offline",
"Check_if_Mikrotik_Online_": "Check if Mikrotik is Online?",
"To_check_if_Mikrotik_is_Online_or_not__go_to_Settings__set_Router_Check_Enabled": "To check if Mikrotik is Online or not, go to Settings, set Router Check Enabled",
"via_SMS": "via SMS",
"Via_WhatsApp": "Via WhatsApp",
"Via_WhatsApp_and_SMS": "Via WhatsApp and SMS",
"Make_sure_you_use_API_Port__Default_8728": "Make sure you use API Port, Default 8728",
"Make_sure_Username_and_Password_are_correct": "Make sure Username and Password are correct",
"Make_sure_your_hosting_not_blocking_port_to_external": "Make sure your hosting not blocking port to external",
"Make_sure_your_Mikrotik_accessible_from_PHPNuxBill": "Make sure your Mikrotik accessible from PHPNuxBill",
"If_you_just_update_PHPNuxBill_from_upload_files__try_click_Update": "If you just update PHPNuxBill from upload files, try click Update",
"Update_PHPNuxBill": "Update PHPNuxBill",
"Ask_Github_Community": "Ask Github Community",
"Ask_Telegram_Community": "Ask Telegram Community",
"Transaction_History_List": "Transaction History List",
"Login_as_Customer": "Login as Customer",
"info": "info",
"Registration_code": "Registration code",
"Admin_can_only_have_single_session_login__it_will_logout_another_session": "Admin can only have single session login, it will logout another session",
"Single_session_Admin": "Single session Admin",
"Get_Directions": "Get Directions",
"Buy_Balance_Plans": "Buy Balance Plans",
"Buy": "Buy",
"Cron_Job_last_ran_on": "Cron Job last ran on",
"VPN_Plans": "VPN Plans",
"Postpaid_Recharge_for_the_first_time_use": "Postpaid Recharge for the first time use",
"Or": "Or",
"Balance_Package": "Balance Package",
"Balance_Custom": "Balance Custom",
"Balance_Amount": "Balance Amount",
"Select_Balance_Package_or_Custom_Amount": "Select Balance Package or Custom Amount",
"Note": "Note",
"Or_custom_balance_amount_below": "Or custom balance amount below",
"Input_custom_balance__will_ignore_plan_above": "Input custom balance, will ignore plan above",
"Cron_has_not_run_for_over_1_hour__Please_check_your_setup_": "Cron has not run for over 1 hour. Please check your setup.",
"Hello": "Hello",
"your_internet_package": "your internet package",
"has_been_expired": "has been expired",
"Welcome_Message": "Welcome Message",
"will_be_replaced_with_Customer_password": "will be replaced with Customer password",
"will_be_replaced_with_Customer_Portal_URL": "will be replaced with Customer Portal URL",
"will_be_replaced_with_Company_Name": "will be replaced with Company Name",
"Token_has_expired__Please_log_in_again_": "Token has expired. Please log in again.",
"Minute": "Minute",
"Hour": "Hour",
"Failed_to_connect_to_device": "Failed to connect to device",
"Custom_Balance": "Custom Balance",
"Input_Desired_Amount": "Input Desired Amount",
"Security": "Security",
"Enable_CSRF_Validation": "Enable CSRF Validation",
"Cross_site_request_forgery": "Cross-site request forgery",
"Validity_Periode": "Validity Periode",
"Insufficient_balance": "Insufficient balance",
"Display_bandwidth_plan_for_customer": "Display bandwidth plan for customer",
"Allow_Balance_Custom_Amount": "Allow Balance Custom Amount",
"Allow_Customer_buy_balance_with_any_amount": "Allow Customer buy balance with any amount",
"Customer_Login_Page_Settings": "Customer Login Page Settings",
"Choose_Template": "Choose Template",
"Select_your_login_template_type": "Select your login template type",
"Select_Login_Page": "Select Login Page",
"Select_your_preferred_login_template": "Select your preferred login template",
"Page_Heading___Company_Name": "Page Heading \/ Company Name",
"This_Name_will_be_shown_on_the_login_wallpaper": "This Name will be shown on the login wallpaper",
"Page_Description": "Page Description",
"This_will_also_display_on_wallpaper__You_can_use_html_tag": "This will also display on wallpaper, You can use html tag",
"Favicon": "Favicon",
"Best_size_30_x_30___uploaded_image_will_be_autosize": "Best size 30 x 30 | uploaded image will be autosize",
"Login_Page_Logo": "Login Page Logo",
"Best_size_300_x_60___uploaded_image_will_be_autosize": "Best size 300 x 60 | uploaded image will be autosize",
"Login_Page_Wallpaper": "Login Page Wallpaper",
"Best_size_1920_x_1080___uploaded_image_will_be_autosize": "Best size 1920 x 1080 | uploaded image will be autosize",
"Single_Admin_Session": "Single Admin Session",
"Expired_Cronjob_Every_5_Minutes__Recommended_": "Expired Cronjob Every 5 Minutes [Recommended]",
"Login_Page_Settings_Saved_Successfully": "Login Page Settings Saved Successfully",
"Sign_in_into_your_account": "Sign in into your account",
"Don_t_have_an_account_": "Don&#39;t have an account?",
"You_do_not_have_permission_to_access_this_page_in_demo_mode": "You do not have permission to access this page in demo mode",
"Custom_Fields": "Custom Fields",
"New_Field": "New Field",
"Data_Change": "Data Change",
"Photo": "Photo",
"Home_Address": "Home Address",
"Email_Address": "Email Address",
"Photo_Required": "Photo Required",
"Customer_Registration_need_to_upload_their_photo": "Customer Registration need to upload their photo",
"Account_already_exists": "Account already exists",
"Notify_Admin": "Notify Admin",
"Notify_Admin_upon_self_registration": "Notify Admin upon self registration",
"Registration_Mandatory_Fields": "Registration Mandatory Fields",
"Mikrotik_SMS_Command": "Mikrotik SMS Command",
"Mandatory_Fields": "Mandatory Fields",
"Usernames": "Usernames",
"Yours_Balance": "Yours Balance",
"Friend_Usernames": "Friend Usernames",
"Send_yours_balance___": "Send yours balance ? ",
"Cards": "Cards",
"CRM": "CRM",
"Coupons": "Coupons",
"Search_Coupons": "Search Coupons",
"Add_Coupon": "Add Coupon",
"Value": "Value",
"Max_Usage": "Max Usage",
"Usage_Count": "Usage Count",
"Min_Order": "Min Order",
"Max_Discount": "Max Discount",
"Updated_Date": "Updated Date",
"Action": "Action",
"No_coupons_found_": "No coupons found.",
"Delete_Selected": "Delete Selected",
"Voucher_Cards": "Voucher Cards",
"Create_Date": "Create Date",
"Message_Results": "Message Results",
"Total_SMS_Sent": "Total SMS Sent",
"Total_SMS_Failed": "Total SMS Failed",
"Total_WhatsApp_Sent": "Total WhatsApp Sent",
"Total_WhatsApp_Failed": "Total WhatsApp Failed",
"First_Name": "First Name",
"Last_Name": "Last Name",
"Not_Working_with_Freeradius_Mysql": "Not Working with Freeradius Mysql",
"Radius_Rest_Interim_Update": "Radius Rest Interim-Update",
"in_minutes__leave_0_to_disable_this_feature_": "in minutes, leave 0 to disable this feature.",
"Check_if_Router_Online_": "Check if Router Online?",
"To_check_whether_the_Router_is_online_or_not__please_visit_the_following_page": "To check whether the Router is online or not, please visit the following page",
"Cek_Now": "Cek Now",
"Pretty_URL": "Pretty URL",
"rename__htaccess_firewall_to__htaccess": "rename .htaccess_firewall to .htaccess",
"Show_chart": "Show chart",
"Max_30_days": "Max 30 days",
"Information": "Information",
"Export_and_Print_will_show_all_data_without_pagination": "Export and Print will show all data without pagination",
"Free_Internet_Plan_updated_successfully_": "Free Internet Plan updated successfully!"
}

View File

@ -381,8 +381,8 @@
"SuperAdmin": "Super Admin",
"Lists": "Daftar",
"Vouchers": "Voucher",
"Refill_Customer": "Isi Ulang Pelanggan",
"Recharge_Customer": "Isi Ulang Pelanggan",
"Refill_Customer": "Isi Ulang Voucher",
"Recharge_Customer": "Isi Ulang Paket",
"Plans": "Paket",
"PPPOE": "PPPOE",
"Bandwidth": "Bandwidth",
@ -484,13 +484,13 @@
"Income_reset_date": "Tanggal pengaturan ulang pendapatan",
"Extend_Package_Expiry": "Perpanjang Masa Kedaluwarsa Paket",
"Yes": "Ya",
"No": "TIDAK",
"No": "Tidak",
"If_user_buy_same_internet_plan__expiry_date_will_extend": "Jika pengguna membeli paket internet yang sama, tanggal kedaluwarsa akan diperpanjang",
"Tax_System": "Sistem pajak",
"Enable_Tax_System": "Aktifkan Sistem Pajak",
"Tax_will_be_calculated_in_Internet_Plan_Price": "Pajak akan dihitung dalam Harga Paket Internet",
"Tax_Rate": "Persentase pajak",
"Custom": "Kebiasaan",
"Custom": "Kustom",
"Tax_Rates_in_percentage": "Tarif Pajak dalam persentase",
"Custom_Tax_Rate": "Tarif Pajak Khusus",
"Enter_Custom_Tax_Rate": "Masukkan Tarif Pajak Khusus",
@ -510,7 +510,7 @@
"When_Expired__customer_will_be_move_to_selected_internet_plan": "Ketika Expired, pelanggan akan dipindahkan ke paket internet yang dipilih",
"Period": "Periode",
"Rate": "Kecepatan",
"Burst": "Meletus",
"Burst": "Burst",
"Router_Name___Location": "Nama\/Lokasi Router",
"Extend": "Memperpanjang",
"City": "Kota",
@ -523,7 +523,7 @@
"Descending": "Menurun",
"Query": "Query",
"Add": "Menambahkan",
"Logout_Successful": "Logout Berhasil",
"Logout_Successful": "Berhasil Keluar",
"warning": "peringatan",
"Created___Expired": "Dibuat \/ Kedaluwarsa",
"Login___Activate_Voucher": "Masuk \/ Aktifkan Voucher",
@ -538,7 +538,6 @@
"Zip_Code": "Kode Pos",
"Phone": "Telepon",
"Customer_Geo_Location_Information": "Informasi Lokasi Geo Pelanggan",
"": "",
"Code": "Kode",
"Send_Personal_Message": "Kirim Pesan Pribadi",
"Send_Via": "Kirim melalui",
@ -547,7 +546,7 @@
"Customer_Name": "Nama Pelanggan",
"Customer_Username": "Nama Pengguna Pelanggan",
"Customer_Phone": "Telepon Pelanggan",
"Your_Company_Name": "Nama perusahaan Anda",
"Your_Company_Name": "Nama Perusahaan Anda",
"Change": "Mengubah",
"Change_Phone_Number": "Ubah Nomor Telepon",
"Current_Number": "Nomor Saat Ini",
@ -566,6 +565,349 @@
"This_account_status": "Status akun ini",
"Hotspot_Auth_Method": "Metode Otentikasi Hotspot",
"Api": "Api",
"Http_Chap": "Http-Bab",
"Hotspot_Authentication_Method__Make_sure_you_have_changed_your_hotspot_login_page_": "Metode Otentikasi Hotspot. Pastikan Anda telah mengubah halaman login hotspot Anda."
"Http_Chap": "Http-Chap",
"Hotspot_Authentication_Method__Make_sure_you_have_changed_your_hotspot_login_page_": "Metode Otentikasi Hotspot. Pastikan Anda telah mengubah halaman login hotspot Anda.",
"Languge_set_to_indonesia": "Language set to indonesia",
"Enable": "Aktifkan",
"Diable": "Nonaktifkan",
"Verification_code": "Kod3 V3r1fik@s1",
"Registration_code": "Kod3 R3g1str@s1",
"TX": "TX",
"RX": "RX",
"Database": "Database",
"Additional_Billing": "Penagihan Tambahan",
"paid_off": "terbayar lunas",
"Sync_account_if_you_failed_login_to_internet": "Sinkronkan akun jika Anda gagal masuk ke internet",
"_Are_You_Sure_": "Apa kamu yakin?",
"Send_your_balance___": "Kirim saldo Anda?",
"_": "-",
"Search_Users": "Pencarian Pengguna",
"Routers_Maps": "Peta Router",
"Theme_Voucher": "Voucher Tema",
"Payment_Info": "Info Pembayaran",
"Documentation": "Dokumentasi",
"Customers": "Pelanggan",
"Package_Name": "Nama Paket",
"Routers_Offline": "Router Offline",
"Cron_appear_not_been_setup__please_check_your_cron_setup_": "Cron tampaknya belum disiapkan, silakan periksa pengaturan cron Anda.",
"Buy": "Membeli",
"You_are_already_logged_in": "Anda sudah masuk",
"PPPOE_Package": "Paket PPPoE",
"Prepaid": "Prabayar",
"Postpaid": "Pascabayar",
"Enabled": "Diaktifkan",
"Disable": "Nonaktifkan",
"Create_expired_Internet_Plan": "Buat Paket Internet yang Kedaluwarsa",
"When_customer_expired__you_can_move_it_to_Expired_Internet_Plan": "Ketika pelanggan kedaluwarsa, Anda dapat memindahkannya ke Paket Internet Kedaluwarsa",
"Price_Before_Discount": "Harga Sebelum Diskon",
"For_Discount_Rate__this_is_price_before_get_discount__must_be_more_expensive_with_real_price": "Untuk Discount Rate, ini adalah harga sebelum mendapat diskon, pasti lebih mahal dari harga sebenarnya",
"on_login___on_up": "saat masuk \/ saat mendaftar",
"on_logout___on_down": "saat keluar \/ saat turun",
"Get_Directions": "Dapatkan Petunjuk Arah",
"Not_Working_with_Freeradius_Mysql": "Tidak Bekerja dengan Freeradius Mysql",
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_Customer_Credentials": "Pengguna tidak dapat mengubah ini, hanya admin. Jika Kosong, maka akan menggunakan Kredensial Pelanggan",
"Buy_this__your_active_package_will_be_overwritten": "Beli ini? Paket aktif Anda akan ditimpa",
"Pay_this_with_Balance__your_active_package_will_be_overwritten": "Bayar ini dengan Saldo? Paket aktif Anda akan ditimpa",
"Error": "Kesalahan",
"Internal_Error": "Kesalahan Internal",
"Sorry__the_software_failed_to_process_the_request__if_it_still_happening__please_tell": "Maaf, perangkat lunak gagal memproses permintaan, jika masih terjadi, mohon beri tahu",
"Try_Again": "Coba Lagi",
"Make_sure_you_use_API_Port__Default_8728": "Pastikan Anda menggunakan Port API, Default 8728",
"Make_sure_Username_and_Password_are_correct": "Pastikan Username dan Password sudah benar",
"Make_sure_your_hosting_not_blocking_port_to_external": "Pastikan hosting Anda tidak memblokir port ke eksternal",
"Make_sure_your_Mikrotik_accessible_from_PHPNuxBill": "Pastikan Mikrotik Anda dapat diakses dari PHPNuxBill",
"If_you_just_update_PHPNuxBill_from_upload_files__try_click_Update": "Jika Anda baru saja memperbarui PHPNuxBill dari mengunggah file, coba klik Perbarui",
"Update_PHPNuxBill": "Perbarui PHPNuxBill",
"Ask_Github_Community": "Tanya Komunitas Github",
"Ask_Telegram_Community": "Tanya Komunitas Telegram",
"Token_has_expired__Please_log_in_again_": "Token telah kedaluwarsa. Silakan masuk lagi.",
"danger": "bahaya",
"Application_Name___Company_Name": "Nama Aplikasi \/ Nama Perusahaan",
"For_PDF_Reports___Best_size_1078_x_200___uploaded_image_will_be_autosize": "Untuk Laporan PDF | Ukuran terbaik 1078 x 200 | gambar yang diunggah akan berukuran otomatis",
"Print_Max_Char": "Cetak Max Char",
"For_invoice_print_using_Thermal_Printer": "Untuk cetak faktur menggunakan Printer Thermal",
"Theme": "Tema",
"Default": "Bawaan",
"Theme_Info": "Info Tema",
"This_used_for_admin_to_select_payment_in_recharge__using_comma_for_every_new_options": "Ini digunakan untuk admin untuk memilih pembayaran dalam pengisian ulang, menggunakan koma untuk setiap opsi baru",
"Income_will_reset_every_this_day": "Pendapatan akan direset setiap hari ini",
"edit_at_config_php": "edit di config.php",
"Hide_Dashboard_Content": "Sembunyikan Konten Dasbor",
"Redirect_URL_after_Activation": "Pengalihan URL setelah Aktivasi",
"Enable_Radius": "Aktifkan Radius",
"Radius_Instructions": "Petunjuk Radius",
"Customer_can_request_to_extend_expirations": "Pelanggan dapat meminta perpanjangan masa berlaku",
"i_agree_to_extends_and_will_paid_full_after_this": "Saya setuju untuk memperpanjang dan akan membayar penuh setelah ini",
"Customer_Balance_System": "Sistem Saldo Pelanggan",
"Telegram_Bot_Token": "Token Bot Telegram",
"Telegram_User_Channel_Group_ID": "ID Pengguna\/Saluran\/Grup Telegram",
"You_will_get_Payment_and_Error_notification": "Anda akan mendapatkan pemberitahuan Pembayaran dan Kesalahan",
"Test_SMS": "Tes SMS",
"SMS_Server_URL": "URL Server SMS",
"Must_include": "Harus menyertakan",
"it_will_be_replaced_": "itu akan diganti.",
"Or_use_Mikrotik_SMS": "Atau gunakan Mikrotik SMS",
"Select_Router": "Pilih Router",
"You_can_use": "Anda dapat menggunakan",
"in_here_too_": "di sini juga.",
"Free_Server": "Server Gratis",
"WhatsApp_Server_URL": "URL Server WhatsApp",
"Empty_this_to_use_internal_mail___PHP": "Kosongkan ini untuk menggunakan internal mail() PHP",
"SMTP_Username": "Nama Pengguna SMTP",
"SMTP_Password": "Kata Sandi SMTP",
"SMTP_Security": "Keamanan SMTP",
"Mail_Reply_To": "Balas Email Ke",
"Customer_will_reply_email_to_this_address__empty_if_you_want_to_use_From_Address": "Pelanggan akan membalas email ke alamat ini, kosong jika Anda ingin menggunakan Alamat Dari",
"None": "Tidak ada",
"By_WhatsApp": "Melalui WhatsApp",
"By_SMS": "Melalui SMS",
"By_Email": "Melalui Email",
"From_Direct_Chat_Link_": "Dari Tautan Obrolan Langsung.",
"Access_Token": "Token Akses",
"Empty_this_to_randomly_created_API_key": "Kosongkan ini ke kunci API yang dibuat secara acak",
"Enable_Session_Timeout": "Aktifkan Batas Waktu Sesi",
"Logout_Admin_if_not_Available_Online_a_period_of_time": "Logout Admin jika tidak tersedia\/Online dalam jangka waktu tertentu",
"Timeout_Duration": "Durasi Waktu Habis",
"Enter_the_session_timeout_duration__minutes_": "Masukkan durasi batas waktu sesi (menit)",
"Idle_Timeout__Logout_Admin_if_Idle_for_xx_minutes": "Waktu Habis Idle, Keluar dari Admin jika Idle selama xx menit",
"New_Version_Notification": "Pemberitahuan Versi Baru",
"This_is_to_notify_you_when_new_updates_is_available": "Ini untuk memberi tahu Anda ketika pembaruan baru tersedia",
"Router_Check": "Pemeriksaan Router",
"If_enabled__the_system_will_notify_Admin_when_router_goes_Offline__If_admin_have_10_or_more_router_and_many_customers__it_will_get_overlapping__you_can_disabled": "Jika diaktifkan, sistem akan memberitahu Admin ketika router Offline, Jika admin memiliki 10 atau lebih router dan banyak pelanggan, maka akan terjadi tumpang tindih, Anda dapat menonaktifkannya",
"Phone_OTP_Required": "Diperlukan OTP Telepon",
"OTP_is_required_when_user_want_to_change_phone_number_and_registration": "OTP diperlukan ketika pengguna ingin mengubah nomor telepon dan registrasi",
"by_WhatsApp": "melalui WhatsApp",
"By_WhatsApp_and_SMS": "Melalui WhatsApp dan SMS",
"Email_OTP_Required": "Email OTP Diperlukan",
"OTP_is_required_when_user_want_to_change_Email_Address": "OTP diperlukan ketika pengguna ingin mengubah Alamat Email",
"Show_Bandwidth_Plan": "Tampilkan Paket Bandwidth",
"_for_Customer": "untuk Pelanggan",
"Custome": "Pelanggan",
"Custome_Tax_Rate": "Tarif Pajak Bea Cukai",
"Enter_Custome_Tax_Rate": "Masukkan Tarif Pajak Pelanggan",
"Authentication": "Autentikasi",
"Github_Username": "Nama Pengguna Github",
"Github_Token": "Token Github",
"Create_GitHub_personal_access_token": "Buat token akses pribadi GitHub",
"only_need_repo_scope": "hanya butuh cakupan repo",
"This_will_allow_you_to_download_plugin_from_private_paid_repository": "Ini akan memungkinkan Anda mengunduh plugin dari repositori pribadi\/berbayar",
"Expired_Cronjob_Every_5_Minutes": "Cronjob Kedaluwarsa Setiap 5 Menit",
"Expired_Cronjob_Every_1_Hour": "Cronjob Kedaluwarsa Setiap 1 Jam",
"Reminder_Cronjob_Every_7_AM": "Pengingat Cronjob Setiap Jam 7 Pagi",
"Check_if_Customer_Online": "Periksa apakah Pelanggan Online",
"Active_Customers": "Pelanggan Aktif",
"This_will_show_is_Customer_currently_is_online_or_not": "Ini akan menunjukkan apakah Pelanggan sedang online atau tidak",
"3_Months": "3 Bulan",
"Used_Date": "Tanggal Penggunaan",
"Plugin_Installer": "Pemasang Plugin",
"Upload_Zip_Plugin_Theme_Device": "Unggah Plugin\/Tema\/Perangkat Zip",
"Install": "Memasang",
"via_SMS": "melalui SMS",
"Via_WhatsApp": "Melalui WhatsApp",
"Via_WhatsApp_and_SMS": "Melalui WhatsApp dan SMS",
"Send_Bulk_Message": "Kirim Pesan Massal",
"Group": "Kelompok",
"All_Customers": "Semua Pelanggan",
"New_Customers": "Pelanggan Baru",
"Expired_Customers": "Pelanggan yang Kedaluwarsa",
"Message_per_time": "Pesan per waktu",
"5_Messages": "5 Pesan",
"10_Messages": "10 Pesan",
"15_Messages": "15 Pesan",
"20_Messages": "20 Pesan",
"30_Messages": "30 Pesan",
"40_Messages": "40 Pesan",
"50_Messages": "50 Pesan",
"60_Messages": "60 Pesan",
"Use_20_and_above_if_you_are_sending_to_all_customers_to_avoid_server_time_out": "Gunakan 20 dan di atasnya jika Anda mengirim ke semua pelanggan untuk menghindari waktu server habis",
"Delay": "Menunda",
"No_Delay": "Tidak Ada Penundaan",
"5_Seconds": "5 Detik",
"10_Seconds": "10 Detik",
"15_Seconds": "15 Detik",
"20_Seconds": "20 Detik",
"Use_at_least_5_secs_if_you_are_sending_to_all_customers_to_avoid_being_banned_by_your_message_provider": "Gunakan setidaknya 5 detik jika Anda mengirim ke semua pelanggan untuk menghindari pemblokiran oleh penyedia pesan Anda",
"Testing__if_checked_no_real_message_is_sent_": "Pengujian [jika dicentang, tidak ada pesan nyata yang dikirim]",
"Message_Results": "Hasil Pesan",
"VPN_Plans": "Paket VPN",
"VPN_Package": "Paket VPN",
"Balance_Package": "Paket Saldo",
"New_Service_Package": "Paket Layanan Baru",
"Package_Price": "Harga Paket",
"Radius_Package": "Paket Radius",
"Hotspot_Package": "Paket Hotspot",
"Maintenance_Mode_Settings": "Pengaturan Mode Pemeliharaan",
"Status_": "Status:",
"Force_Logout_": "Paksa Keluar:",
"End_Date_": "Tanggal Berakhir:",
"Save": "Menyimpan",
"Not_Active": "Tidak Aktif",
"Limit": "Membatasi",
"Create_expired_Internet_Package": "Buat Paket Internet yang Kedaluwarsa",
"When_customer_expired__you_can_move_it_to_Expired_Internet_Package": "Ketika pelanggan telah kedaluwarsa, Anda dapat memindahkannya ke Paket Internet Kedaluwarsa",
"Miscellaneous_Settings": "Pengaturan Lain-Lain",
"Minute": "Menit",
"Hour": "Jam",
"Buy_Balance_Plans": "Beli Paket Saldo",
"New_Voucher_for_10mbps_Created": "Voucher Baru untuk 10mbps Dibuat",
"Previous": "Sebelumnya",
"Share": "Membagikan",
"Agent": "Agen",
"Sub_District": "Kecamatan",
"Ward": "Bangsal",
"Profile": "Profil",
"Credentials": "Kredensial",
"Cron_has_not_run_for_over_1_hour__Please_check_your_setup_": "Cron tidak berjalan selama lebih dari 1 jam. Harap periksa pengaturan Anda.",
"Photo": "Foto",
"just_now": "baru saja",
"Face_Detection": "Deteksi Wajah",
"Password_should_be_minimum_6_characters": "Kata sandi minimal harus 6 karakter",
"Username_should_be_between_3_to_45_characters": "Nama pengguna harus terdiri dari 3 hingga 45 karakter",
"Single_session_Admin": "Sesi Tunggal Admin",
"Admin_can_only_have_single_session_login__it_will_logout_another_session": "Admin hanya dapat memiliki login satu sesi, maka akan keluar dari sesi berikutnya",
"For_Registration_and_Update_Phone_Number": "Untuk Registrasi dan Update Nomor Telepon",
"Login_as_Customer": "Masuk sebagai Pelanggan",
"Invalid_or_Expired_CSRF_Token": "Token CSRF Tidak Valid atau Kedaluwarsa",
"Edit_Service_Package": "Edit Paket Layanan",
"Package_Type": "Tipe Paket",
"Package_Validity": "Validitas Paket",
"Expired_Internet_Package": "Paket Internet Kedaluwarsa",
"Default___Remove_Customer": "Default - Hapus Pelanggan",
"When_Expired__customer_will_be_move_to_selected_internet_package": "Jika masa berlaku habis, pelanggan akan dipindahkan ke paket internet yang dipilih",
"Data_Change": "Perubahan Data",
"Home_Address": "Alamat Rumah",
"Email_Address": "Alamat Email",
"Custom_Balance": "Saldo Kustom",
"Input_Desired_Amount": "Masukkan Jumlah yang Diinginkan",
"Advanced_Hotspot_System": "Sistem Hotspot Canggih",
"Successful_Payments": "Pembayaran Berhasil",
"More_Info": "Info lebih lanjut",
"Failed_Payments": "Pembayaran Gagal",
"Pending_Payments": "Pembayaran Tertunda",
"Cancelled_Payments": "Pembayaran yang Dibatalkan",
"Daily_Sales": "Penjualan Harian",
"Monthly_Sales": "Penjualan Bulanan",
"Weekly_Sales": "Penjualan Mingguan",
"How_its_Works": "Cara kerjanya",
"_Click_this": "Klik ini",
"_to_visit_the_hotspot_login_page": "untuk mengunjungi halaman login hotspot",
"_Choose_your_desired_plan__enter_your_phone_number_and_click_Pay_Now__you_will_be_redirected_to_payment_portal_": "Pilih paket yang Anda inginkan, masukkan nomor telepon Anda dan klik Bayar Sekarang, Anda akan diarahkan ke portal pembayaran.",
"_Pay_with_Demo_Success_": "Bayar dengan Demo Sukses.",
"_After_Successful_Payment_you_will_be_awarded_the_package_and_you_will_received_your_Voucher_Code_for_login_": "Setelah Pembayaran Berhasil, Anda akan diberikan paket dan Anda akan menerima Kode Voucher untuk login.",
"_Come_back_here_to_see_your_hotspot_performance_at_a_glance_": "Kembali ke sini untuk melihat sekilas kinerja hotspot Anda.",
"Hotspot_Payment_History": "Riwayat Pembayaran Hotspot",
"Search_Phone_Number_____": "Cari Nomor Telepon.....",
"Transaction_ID": "ID Transaksi",
"Transaction_Ref": "Referensi Transaksi",
"Voucher_Code": "Kode Voucher",
"Amount": "Jumlah",
"Transaction_Status": "Status Transaksi",
"Payment_Method": "Metode Pembayaran",
"Payment_Date": "Tanggal Pembayaran",
"Plan_Expiry_Date": "Tanggal Kedaluwarsa Paket",
"Created_on": "Dibuat pada",
"Expires_on": "Kedaluwarsa pada",
"Package_Details": "Rincian Paket",
"Summary": "Ringkasan",
"Allow_Balance_custom_amount": "Izinkan Saldo jumlah khusus",
"Allow_Customer_buy_balance_with_any_amount": "Izinkan Pelanggan membeli saldo dengan jumlah berapa pun",
"Or": "Atau",
"Filter": "Menyaring",
"Show_chart": "Tampilkan grafik",
"Start_Date": "Tanggal Mulai",
"Start_time": "Waktu Mulai",
"End_Date": "Tanggal Akhir",
"End_Time": "Waktu berakhir",
"Internet_Plans": "Paket Internet",
"Methods": "Metode",
"Hap_Lite": "Hap Lite",
"balance": "saldo",
"radius": "radius",
"Max_30_days": "Maksimal 30 hari",
"Information": "Informasi",
"Export_and_Print_will_show_all_data_without_pagination": "Ekspor dan Cetak akan menampilkan semua data tanpa pagination",
"First_Name": "Nama Depan",
"Last_Name": "Nama Belakang",
"General": "Umum",
"Registration": "Pendaftaran",
"Allow_Registration": "Izinkan Registrasi",
"Voucher_Only": "Hanya Voucher",
"No_Registration": "Tidak Ada Registrasi",
"Registration_Username": "Nama Pengguna Registrasi",
"Customer_Registration_need_to_validate_using_OTP": "Registrasi Pelanggan perlu divalidasi menggunakan OTP",
"SMS_Notification": "Pemberitahuan SMS",
"Tax_Rates_by_percentage": "Tarif Pajak Berdasarkan Persentase",
"Settings_For_Mikrotik": "Pengaturan Untuk Mikrotik",
"Settings_For_Cron_Expired": "Pengaturan Untuk Cron Kedaluwarsa",
"Choose_one__above_or_below": "Pilih salah satu, di atas atau di bawah",
"Settings_For_Cron_Reminder": "Pengaturan Untuk Pengingat Cron",
"Security": "Keamanan",
"Enable_CSRF_Validation": "Aktifkan Validasi CSRF",
"Cross_site_request_forgery": "Pemalsuan permintaan lintas situs",
"Forgot_Password": "Lupa Kata Sandi",
"Validity_Periode": "Periode Validitas",
"Transaction_History_List": "Daftar Riwayat Transaksi",
"plan": "paket",
"package": "paket",
"Cards": "Kartu",
"CRM": "CRM",
"Coupons": "Kupon",
"Custom_Fields": "Bidang Kustom",
"Search_Coupons": "Cari Kupon",
"Add_Coupon": "Tambahkan Kupon",
"Value": "Nilai",
"Max_Usage": "Penggunaan Maksimal",
"Usage_Count": "Jumlah Pemakaian",
"Min_Order": "Pesanan Min",
"Max_Discount": "Diskon Maksimal",
"Updated_Date": "Tanggal Diperbarui",
"Action": "Tindakan",
"No_coupons_found_": "Tidak ada kupon yang ditemukan.",
"Delete_Selected": "Hapus yang Dipilih",
"Coupon_Code": "Kode Kupon",
"Random": "Acak",
"Unique_code_for_the_coupon": "Kode unik untuk kupon",
"Fixed_Discount": "Diskon Tetap",
"Percent_Discount": "Diskon Persen",
"Discount_Value": "Nilai Diskon",
"Value_of_the_discount__amount_or_percentage_": "Nilai diskon (jumlah atau persentase)",
"Brief_explanation_of_the_coupon": "Penjelasan singkat tentang kupon",
"Maximum_number_of_times_this_coupon_can_be_used_0_is_Unlimited": "Jumlah maksimum penggunaan kupon ini 0 adalah Tidak Terbatas",
"Minimum_Order_Amount": "Jumlah Pesanan Minimum",
"Minimum_cart_total_required_to_use_this_coupon": "Total keranjang minimum yang diperlukan untuk menggunakan kupon ini",
"Max_Discount_Amount": "Jumlah Diskon Maksimum",
"Maximum_discount_amount_applicable__for_percent_type_": "Jumlah diskon maksimum yang berlaku (untuk jenis persen)",
"Value_of_the_discount__percentage__max_100_": "Nilai diskon (persentase, maks 100)",
"Value_of_the_discount__amount_": "Nilai diskon (jumlah)",
"Voucher_Cards": "Kartu Voucher",
"Create_Date": "Tanggal Pembuatan",
"Postpaid_Recharge_for_the_first_time_use": "Isi Ulang Pascabayar untuk penggunaan pertama kali",
"Select_Balance_Package_or_Custom_Amount": "Pilih Paket Saldo atau Jumlah Kustom",
"Or_custom_balance_amount_below": "Atau jumlah saldo khusus di bawah ini",
"Balance_Amount": "Jumlah Saldo",
"Input_custom_balance__will_ignore_plan_above": "Masukkan saldo khusus, akan mengabaikan rencana di atas",
"Note": "Catatan",
"Customer_Login_Page_Settings": "Pengaturan Halaman Login Pelanggan",
"Choose_Template": "Pilih Template",
"Select_your_login_template_type": "Pilih jenis template login Anda",
"Select_Login_Page": "Pilih Halaman Login",
"Select_your_preferred_login_template": "Pilih template login pilihan Anda",
"Page_Heading___Company_Name": "Judul Halaman \/ Nama Perusahaan",
"This_Name_will_be_shown_on_the_login_wallpaper": "Nama ini akan ditampilkan pada wallpaper login",
"Page_Description": "Deskripsi Halaman",
"This_will_also_display_on_wallpaper__You_can_use_html_tag": "Ini juga akan ditampilkan di wallpaper, Anda dapat menggunakan tag html",
"Favicon": "Ikon favicon",
"Best_size_30_x_30___uploaded_image_will_be_autosize": "Ukuran terbaik 30 x 30 | gambar yang diunggah akan berukuran otomatis",
"Login_Page_Logo": "Logo Halaman Login",
"Best_size_300_x_60___uploaded_image_will_be_autosize": "Ukuran terbaik 300 x 60 | gambar yang diunggah akan berukuran otomatis",
"Login_Page_Wallpaper": "Wallpaper Halaman Login",
"Best_size_1920_x_1080___uploaded_image_will_be_autosize": "Ukuran terbaik 1920 x 1080 | gambar yang diunggah akan berukuran otomatis",
"Photo_Required": "Foto Diperlukan",
"Customer_Registration_need_to_upload_their_photo": "Registrasi Pelanggan perlu mengunggah foto mereka",
"Notify_Admin": "Beritahu Admin",
"Notify_Admin_upon_self_registration": "Beritahu Admin saat registrasi mandiri",
"Mandatory_Fields": "Bidang yang wajib diisi",
"Single_Admin_Session": "Sesi Admin Tunggal",
"Mikrotik_SMS_Command": "Perintah SMS Mikrotik",
"Expired_Cronjob_Every_5_Minutes__Recommended_": "Cronjob Kedaluwarsa Setiap 5 Menit [Direkomendasikan]"
}

View File

@ -5,10 +5,10 @@
"Registration_Info": "Informaci\u00f3n de registro",
"Voucher_not_found__please_buy_voucher_befor_register": "Cup\u00f3n no encontrado, compre el cup\u00f3n antes de registrarse",
"Register_Success__You_can_login_now": "\u00a1Registro exitoso! Puedes iniciar sesi\u00f3n ahora",
"Log_in_to_Member_Panel": "Log in to Member Panel",
"Log_in_to_Member_Panel": "Iniciar sesi\u00f3n en el panel de miembros",
"Register_as_Member": "Reg\u00edstrese como miembro",
"Enter_Admin_Area": "Panel de administraci\u00f3n",
"PHPNuxBill": "DIGITAL-RED",
"PHPNuxBill": "WENJEI",
"Username": "Usuario",
"Password": "Contrase\u00f1a",
"Passwords_does_not_match": "Las contrase\u00f1as no coinciden",
@ -27,7 +27,7 @@
"Failed_to_save_page__make_sure_i_can_write_to_folder_pages___i_chmod_664_pages___html_i_": "No se pudo guardar la p\u00e1gina, aseg\u00farese de que pueda escribir en las p\u00e1ginas de la carpeta, <i>chmod 664 pages\/*.html<i>",
"Saving_page_success": "Guardando el \u00e9xito de la p\u00e1gina",
"Sometimes_you_need_to_refresh_3_times_until_content_change": "A veces es necesario actualizar 3 veces hasta que cambie el contenido",
"Dashboard": "Dashboard",
"Dashboard": "Panel",
"Search_Customers___": "Buscar clientes...",
"My_Account": "Mi cuenta",
"My_Profile": "Mi perfil",
@ -140,7 +140,7 @@
"Administrator_Users": "Usuarios administradores",
"Manage_Administrator": "Administrar administrador",
"Add_New_Administrator": "Agregar nuevo administrador",
"Localisation": "Localizaci\u00f3n",
"Localisation": "Idioma y Fecha",
"Backup_Restore": "Copia de seguridad\/restauracion",
"General_Settings": "Configuraci\u00f3n general",
"Date": "Fecha",
@ -213,165 +213,357 @@
"Folder_Name": "Nombre de la carpeta",
"Translator": "Traducir",
"Language_Name_Already_Exist": "El nombre del idioma ya existe",
"Payment_Gateway": "Payment Gateway",
"Community": "Community",
"1_user_can_be_used_for_many_devices_": "1 user can be used for many devices?",
"Cannot_be_change_after_saved": "Cannot be change after saved",
"Explain_Coverage_of_router": "Jelaskan Cakupan wilayah hotspot",
"Name_of_Area_that_router_operated": "Nama Lokasi\/Wilayah Router beroperasi",
"Payment_Notification_URL__Recurring_Notification_URL__Pay_Account_Notification_URL": "Payment Notification URL, Recurring Notification URL, Pay Account Notification URL",
"Finish_Redirect_URL__Unfinish_Redirect_URL__Error_Redirect_URL": "Finish Redirect URL, Unfinish Redirect URL, Error Redirect URL",
"Status": "Status",
"Plan_Not_found": "Plan Not found",
"Failed_to_create_transaction_": "Failed to create transaction.",
"Seller_has_not_yet_setup_Xendit_payment_gateway": "Seller has not yet setup Xendit payment gateway",
"Admin_has_not_yet_setup_Xendit_payment_gateway__please_tell_admin": "Admin has not yet setup Xendit payment gateway, please tell admin",
"Buy_this__your_active_package_will_be_overwrite": "Buy this? your active package will be overwrite",
"You_already_have_unpaid_transaction__cancel_it_or_pay_it_": "You already have unpaid transaction, cancel it or pay it.",
"Transaction_Not_found": "Transaction Not found",
"Cancel_it_": "Cancel it?",
"expired": "expired",
"Check_for_Payment": "Check for Payment",
"Transaction_still_unpaid_": "Transaction still unpaid.",
"Paid_Date": "Paid Date",
"Transaction_has_been_paid_": "Transaction has been paid.",
"PAID": "PAID",
"CANCELED": "CANCELED",
"UNPAID": "UNPAID",
"PAY_NOW": "PAY NOW",
"Buy_Hotspot_Plan": "Buy Hotspot Plan",
"Buy_PPOE_Plan": "Buy PPOE Plan",
"Package": "Package",
"Order_Internet_Package": "Order Internet Package",
"Unknown_Command_": "Unknown Command.",
"Checking_payment": "Checking payment",
"Create_Transaction_Success": "Create Transaction Success",
"You_have_unpaid_transaction": "You have unpaid transaction",
"TripayPayment_Channel": "TripayPayment Channel",
"Payment_Channel": "Payment Channel",
"Payment_check_failed_": "Payment check failed.",
"Order_Package": "Order Package",
"Transactions": "Transactions",
"Payments": "Payments",
"History": "History",
"Order_History": "Order History",
"Gateway": "Gateway",
"Date_Done": "Date Done",
"Unpaid_Order": "Unpaid Order",
"Payment_Gateway_Not_Found": "Payment Gateway Not Found",
"Payment_Gateway_saved_successfully": "Payment Gateway saved successfully",
"ORDER": "ORDER",
"Package_History": "Package History",
"Buy_History": "Buy History",
"Activation_History": "Activation History",
"Buy_Package": "Buy Package",
"Email": "Email",
"Company_Footer": "Company Footer",
"Will_show_below_user_pages": "Will show below user pages",
"Request_OTP": "Request OTP",
"Verification_Code": "Verification Code",
"SMS_Verification_Code": "SMS Verification Code",
"Please_enter_your_email_address": "Please enter your email address",
"Failed_to_create_Paypal_transaction_": "Failed to create Paypal transaction.",
"Payment_Gateway": "Pasarela de Pago",
"Community": "Comunidad",
"1_user_can_be_used_for_many_devices_": "1 usuario puede utilizarse para varios dispositivos?",
"Cannot_be_change_after_saved": "No se puede cambiar despu\u00e9s de guardar",
"Explain_Coverage_of_router": "Explicar la cobertura del enrutador",
"Name_of_Area_that_router_operated": "Nombre del \u00e1rea que operaba el enrutador",
"Payment_Notification_URL__Recurring_Notification_URL__Pay_Account_Notification_URL": "URL de notificaci\u00f3n de pago, URL de notificaci\u00f3n recurrente, URL de notificaci\u00f3n de cuenta de pago",
"Finish_Redirect_URL__Unfinish_Redirect_URL__Error_Redirect_URL": "URL de redireccionamiento finalizada, URL de redireccionamiento incompleta, URL de redireccionamiento con error",
"Status": "Estado",
"Plan_Not_found": "Plan no encontrado",
"Failed_to_create_transaction_": "No se pudo crear la transacci\u00f3n.",
"Seller_has_not_yet_setup_Xendit_payment_gateway": "El vendedor a\u00fan no ha configurado la pasarela de pago Xendit",
"Admin_has_not_yet_setup_Xendit_payment_gateway__please_tell_admin": "El administrador a\u00fan no ha configurado la pasarela de pago Xendit, inf\u00f3rmeselo al administrador.",
"Buy_this__your_active_package_will_be_overwrite": "\u00bfCompraste esto? Tu paquete activo se sobrescribir\u00e1",
"You_already_have_unpaid_transaction__cancel_it_or_pay_it_": "Ya tienes transacci\u00f3n impaga, canc\u00e9lala o p\u00e1gala.",
"Transaction_Not_found": "Transacci\u00f3n no encontrada",
"Cancel_it_": "\u00bfCancelalo?",
"expired": "Caducada",
"Check_for_Payment": "Verificar pago",
"Transaction_still_unpaid_": "Transacci\u00f3n a\u00fan impaga.",
"Paid_Date": "Fecha de pago",
"Transaction_has_been_paid_": "La transacci\u00f3n ha sido pagada.",
"PAID": "PAGADA",
"CANCELED": "CANCELADA",
"UNPAID": "NO PAGADO",
"PAY_NOW": "PAGAR AHORA",
"Buy_Hotspot_Plan": "Comprar plan de hotspot",
"Buy_PPOE_Plan": "Comprar Plan PPPoE",
"Package": "Paquete",
"Order_Internet_Package": "Solicitar paquete de Internet",
"Unknown_Command_": "Comando desconocido.",
"Checking_payment": "Comprobando el pago",
"Create_Transaction_Success": "Crear transacciones exitosas",
"You_have_unpaid_transaction": "Tienes transacci\u00f3n impaga",
"TripayPayment_Channel": "Canal de pago Tripay",
"Payment_Channel": "Canal de pago",
"Payment_check_failed_": "El cheque de pago fall\u00f3.",
"Order_Package": "Paquete de pedido",
"Transactions": "Transacciones",
"Payments": "Pagos",
"History": "Historial",
"Order_History": "Historial de pedidos",
"Gateway": "Puerta",
"Date_Done": "Fecha de finalizaci\u00f3n",
"Unpaid_Order": "Orden no pagada",
"Payment_Gateway_Not_Found": "Pasarela de pago no encontrada",
"Payment_Gateway_saved_successfully": "Pasarela de pago guardada exitosamente",
"ORDER": "ORDEN",
"Package_History": "Historial de paquetes",
"Buy_History": "Historial de compra",
"Activation_History": "Historial de activaci\u00f3n",
"Buy_Package": "Comprar paquete",
"Email": "Correo",
"Company_Footer": "Pie de p\u00e1gina",
"Will_show_below_user_pages": "Se mostrar\u00e1n debajo de las p\u00e1ginas de usuario.",
"Request_OTP": "Solicitar OTP",
"Verification_Code": "Verificacion por codigo",
"SMS_Verification_Code": "C\u00f3digo de verificaci\u00f3n por SMS",
"Please_enter_your_email_address": "Por favor, introduzca su direcci\u00f3n de correo electr\u00f3nico",
"Failed_to_create_Paypal_transaction_": "No se pudo crear la transacci\u00f3n de Paypal.",
"Plugin": "Plugin",
"Plugin_Manager": "Plugin Manager",
"User_Notification": "User Notification",
"Expired_Notification": "Expired Notification",
"User_will_get_notification_when_package_expired": "User will get notification when package expired",
"Expired_Notification_Message": "Expired Notification Message",
"Payment_Notification": "Payment Notification",
"User_will_get_invoice_notification_when_buy_package_or_package_refilled": "User will get invoice notification when buy package or package refilled",
"Current_IP": "Current IP",
"Current_MAC": "Current MAC",
"Login_Status": "Login Status",
"Login_Request_successfully": "Login Request successfully",
"Logout_Request_successfully": "Logout Request successfully",
"Disconnect_Internet_": "Disconnect Internet?",
"Not_Online__Login_now_": "Not Online, Login now?",
"You_are_Online__Logout_": "You are Online, Logout?",
"Connect_to_Internet_": "Connect to Internet?",
"Your_account_not_connected_to_internet": "Your account not connected to internet",
"Balance": "Balance",
"Balance_System": "Balance System",
"Enable_System": "Enable System",
"Allow_Transfer": "Allow Transfer",
"Telegram_Notification": "Telegram Notification",
"SMS_OTP_Registration": "SMS OTP Registration",
"Whatsapp_Notification": "Whatsapp Notification",
"Plugin_Manager": "Administraci\u00f3n de Plugins",
"User_Notification": "Notificaci\u00f3n de usuario",
"Expired_Notification": "Notificaci\u00f3n caducada",
"User_will_get_notification_when_package_expired": "El usuario recibir\u00e1 una notificaci\u00f3n cuando el paquete caduque",
"Expired_Notification_Message": "Mensaje de notificaci\u00f3n caducado",
"Payment_Notification": "Notificacion de pago",
"User_will_get_invoice_notification_when_buy_package_or_package_refilled": "El usuario recibir\u00e1 una notificaci\u00f3n de factura cuando compre el paquete o recargue el paquete.",
"Current_IP": "IP actual",
"Current_MAC": "MAC actual",
"Login_Status": "Estado de inicio de sesi\u00f3n",
"Login_Request_successfully": "Solicitud de inicio de sesi\u00f3n exitosa",
"Logout_Request_successfully": "Solicitud de cierre de sesi\u00f3n exitosa",
"Disconnect_Internet_": "\u00bfDesconectar Internet?",
"Not_Online__Login_now_": "No en l\u00ednea, \u00bfiniciar sesi\u00f3n ahora?",
"You_are_Online__Logout_": "Est\u00e1s en l\u00ednea, \u00bfcerrar sesi\u00f3n?",
"Connect_to_Internet_": "\u00bfConectado a Internet?",
"Your_account_not_connected_to_internet": "Su cuenta no est\u00e1 conectada a Internet",
"Balance": "Saldo",
"Balance_System": "Sistema de Saldo",
"Enable_System": "Habilitar sistema",
"Allow_Transfer": "Permitir transferencia",
"Telegram_Notification": "Notificaci\u00f3n de Telegram",
"SMS_OTP_Registration": "Registro OTP por SMS",
"Whatsapp_Notification": "Notificaci\u00f3n de whatsapp",
"Tawk_to_Chat_Widget": "Tawk.to Chat Widget",
"Invoice": "Invoice",
"Country_Code_Phone": "Country Code Phone",
"Voucher_activation_menu_will_be_hidden": "Voucher activation menu will be hidden",
"Customer_can_deposit_money_to_buy_voucher": "Customer can deposit money to buy voucher",
"Allow_balance_transfer_between_customers": "Allow balance transfer between customers",
"Refill_Balance": "Refill Balance",
"Balance_Plans": "Balance Plans",
"Failed_to_create_transaction__": "Failed to create transaction. ",
"Failed_to_check_status_transaction__": "Failed to check status transaction. ",
"Disable_Voucher": "Disable Voucher",
"Reminder_Notification": "Reminder Notification",
"Reminder_Notification_Message": "Reminder Notification Message",
"Reminder_7_days": "Reminder 7 days",
"Reminder_3_days": "Reminder 3 days",
"Reminder_1_day": "Reminder 1 day",
"PPPOE_Password": "PPPOE Password",
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_user_password": "User Cannot change this, only admin. if it Empty it will use user password",
"Invoice_Balance_Message": "Invoice Balance Message",
"Invoice_Notification_Payment": "Invoice Notification Payment",
"Balance_Notification_Payment": "Balance Notification Payment",
"Buy_Balance": "Buy Balance",
"Price": "Price",
"Validity": "Validity",
"Disable_auto_renewal_": "Disable auto renewal?",
"Auto_Renewal_On": "Auto Renewal On",
"Enable_auto_renewal_": "Enable auto renewal?",
"Auto_Renewal_Off": "Auto Renewal Off",
"Invoice_Footer": "Invoice Footer",
"Pay_With_Balance": "Pay With Balance",
"Pay_this_with_Balance__your_active_package_will_be_overwrite": "Pay this with Balance? your active package will be overwrite",
"Success_to_buy_package": "Success to buy package",
"Auto_Renewal": "Auto Renewal",
"View": "View",
"Back": "Back",
"Active": "Active",
"Transfer_Balance": "Transfer Balance",
"Send_your_balance_": "Send your balance?",
"Send": "Send",
"Cannot_send_to_yourself": "Cannot send to yourself",
"Sending_balance_success": "Sending balance success",
"From": "From",
"To": "To",
"insufficient_balance": "insufficient balance",
"Send_Balance": "Send Balance",
"Received_Balance": "Received Balance",
"Minimum_Balance_Transfer": "Minimum Balance Transfer",
"Minimum_Transfer": "Minimum Transfer",
"Company_Logo": "Company Logo",
"Expired_IP_Pool": "Expired IP Pool",
"Invoice": "Factura",
"Country_Code_Phone": "C\u00f3digo de pa\u00eds Tel\u00e9fono",
"Voucher_activation_menu_will_be_hidden": "El men\u00fa de activaci\u00f3n del vale estar\u00e1 oculto",
"Customer_can_deposit_money_to_buy_voucher": "El cliente puede depositar dinero para comprar un vale.",
"Allow_balance_transfer_between_customers": "Permitir transferencia de saldo entre clientes",
"Refill_Balance": "Saldo de recarga",
"Balance_Plans": "Planes de Saldo",
"Failed_to_create_transaction__": "No se pudo crear la transacci\u00f3n.",
"Failed_to_check_status_transaction__": "No se pudo verificar el estado de la transacci\u00f3n.",
"Disable_Voucher": "Desactivar Fichas",
"Reminder_Notification": "Notificaci\u00f3n de recordatorio",
"Reminder_Notification_Message": "Mensaje de notificaci\u00f3n de recordatorio",
"Reminder_7_days": "Recordatorio 7 d\u00edas",
"Reminder_3_days": "Recordatorio 3 d\u00edas",
"Reminder_1_day": "Recordatorio 1 d\u00edas",
"PPPOE_Password": "Contrase\u00f1a PPPOE",
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_user_password": "El usuario no puede cambiar esto, solo el administrador. si est\u00e1 vac\u00edo usar\u00e1 la contrase\u00f1a de usuario",
"Invoice_Balance_Message": "Mensaje de saldo de factura",
"Invoice_Notification_Payment": "Pago de notificaci\u00f3n de factura",
"Balance_Notification_Payment": "Pago de notificaci\u00f3n de saldo",
"Buy_Balance": "Comprar Saldo",
"Price": "Precio",
"Validity": "Validez",
"Disable_auto_renewal_": "\u00bfDesactivar la renovaci\u00f3n autom\u00e1tica?",
"Auto_Renewal_On": "Renovaci\u00f3n autom\u00e1tica activada",
"Enable_auto_renewal_": "\u00bfHabilitar la renovaci\u00f3n autom\u00e1tica?",
"Auto_Renewal_Off": "Renovaci\u00f3n autom\u00e1tica desactivada",
"Invoice_Footer": "Pie de p\u00e1gina de factura",
"Pay_With_Balance": "Pagar con saldo",
"Pay_this_with_Balance__your_active_package_will_be_overwrite": "\u00bfPagar esto con Saldo? su paquete activo ser\u00e1 sobrescrito",
"Success_to_buy_package": "\u00c9xito al comprar el paquete.",
"Auto_Renewal": "Auto renovaci\u00f3n",
"View": "Vista",
"Back": "Atras",
"Active": "Activo",
"Transfer_Balance": "Transferir saldo",
"Send_your_balance_": "\u00bfEnviar tu saldo?",
"Send": "Enviar",
"Cannot_send_to_yourself": "No puedes enviarte a ti mismo",
"Sending_balance_success": "Env\u00edo de saldo exitoso",
"From": "De",
"To": "A",
"insufficient_balance": "Saldo Insuficiente",
"Send_Balance": "Saldo Enviado",
"Received_Balance": "Saldo Recibido",
"Minimum_Balance_Transfer": "Transferencia de saldo m\u00ednimo",
"Minimum_Transfer": "Transferencia m\u00ednima",
"Company_Logo": "Logo de la compa\u00f1\u00eda",
"Expired_IP_Pool": "Grupo de IP caducado",
"Proxy": "Proxy",
"Proxy_Server": "Proxy Server",
"Proxy_Server_Login": "Proxy Server Login",
"Proxy_Server": "Servidor Proxy",
"Proxy_Server_Login": "Iniciar sesi\u00f3n en el servidor proxy",
"Hotspot_Plan": "Hotspot Plan",
"PPPOE_Plan": "PPPOE Plan",
"UNKNOWN": "UNKNOWN",
"Are_You_Sure_": "Are You Sure?",
"Success_to_send_package": "Success to send package",
"Target_has_active_plan__different_with_current_plant_": "Target has active plan, different with current plant.",
"Recharge_a_friend": "Recharge a friend",
"Buy_for_friend": "Buy for friend",
"Buy_this_for_friend_account_": "Buy this for friend account?",
"Review_package_before_recharge": "Review package before recharge",
"Activate": "Activate",
"Deactivate": "Deactivate",
"Sync": "Sync",
"Failed_to_create_PaymeTrust_transaction_": "Failed to create PaymeTrust transaction.",
"Location": "Location",
"Voucher_Format": "Voucher Format",
"Service_Type": "Service Type",
"Others": "Others",
"PPPOE_Plan": "PPPoE Plan",
"UNKNOWN": "DESCONOCIDO",
"Are_You_Sure_": "Estas seguro\/a?",
"Success_to_send_package": "\u00c9xito al enviar el paquete",
"Target_has_active_plan__different_with_current_plant_": "Objetivo tiene plan activo, diferente con planta actual.",
"Recharge_a_friend": "Recargar a un amigo\/a",
"Buy_for_friend": "Comprar para amigo\/a",
"Buy_this_for_friend_account_": "\u00bfCompra esto por cuenta de amigo\/a?",
"Review_package_before_recharge": "Revisar paquete antes de recargar",
"Activate": "Activado",
"Deactivate": "Desactivado",
"Sync": "Sincronizar",
"Failed_to_create_PaymeTrust_transaction_": "No se pudo crear la transacci\u00f3n PaymeTrust.",
"Location": "Localizacion",
"Voucher_Format": "Formato de cup\u00f3n",
"Service_Type": "Tipo de Servicios",
"Others": "Otros",
"PPPoE": "PPPoE",
"Hotspot": "Hotspot",
"Monthly_Registered_Customers": "Monthly Registered Customers",
"Total_Monthly_Sales": "Total Monthly Sales",
"Active_Users": "Active Users"
"Monthly_Registered_Customers": "Clientes registrados mensualmente",
"Total_Monthly_Sales": "Ventas mensuales totales",
"Active_Users": "Usuarios activos",
"Languge_set_to_spanish": "Idioma establecido en espa\u00f1ol",
"Login": "Acceso",
"Forgot_Password": "Has olvidado tu contrase\u00f1a",
"success": "Logueado con Exito",
"Click_Here": "Haga clic aqu\u00ed",
"Search_Users": "Buscar usuarios",
"SuperAdmin": "Superadministrador",
"Lists": "Listas de Clientes",
"Vouchers": "Fichas",
"Refill_Customer": "Recarga de cliente",
"Recharge_Customer": "Recargar Cliente",
"Internet_Plan": "Plan de Internet",
"Send_Message": "Enviar mensaje",
"Single_Customer": "Cliente \u00fanico",
"Bulk_Customers": "Clientes al por mayor",
"Routers_Maps": "Mapas de enrutadores",
"Theme_Voucher": "Cup\u00f3n tem\u00e1tico",
"Customer_Announcement": "Anuncio para el cliente",
"Payment_Info": "Informaci\u00f3n de pago",
"Privacy_Policy": "Pol\u00edtica de Privacidad",
"Terms_and_Conditions": "T\u00e9rminos y condiciones",
"Maintenance_Mode": "Modo de mantenimiento",
"Devices": "Dispositivos",
"Logs": "Registros",
"Documentation": "Documentaci\u00f3n",
"Expired": "Venci\u00f3",
"Customers": "Clientes",
"Package_Name": "Nombre del paquete",
"Created___Expired": "Creado \/ Expirado",
"Internet_Package": "Paquete de Internet",
"year": "A\u00f1o",
"month": "Mes",
"week": "Semana",
"day": "D\u00eda",
"hour": "Hora",
"minute": "Minuto",
"second": "Segundo",
"ago": "Atr\u00e1s",
"Prev": "Anterior",
"Cron_Job_last_ran_on": "La \u00faltima ejecuci\u00f3n del trabajo cron se realiz\u00f3 el",
"All_Users_Insights": "Informaci\u00f3n de todos los usuarios",
"Created_Date": "Fecha de creaci\u00f3n",
"Ascending": "Ascendente",
"Descending": "Descendiendo",
"Banned": "Prohibido",
"Disabled": "Desactivado",
"Inactive": "Inactivo",
"Suspended": "Suspendido",
"Add": "Agregar",
"Account_Type": "Tipo de cuenta",
"Contact": "Contacto",
"Active_Customers": "Clientes activos",
"Extend": "Extender",
"Application_Name___Company_Name": "Nombre de la aplicaci\u00f3n \/ Nombre de la empresa",
"For_PDF_Reports___Best_size_1078_x_200___uploaded_image_will_be_autosize": "Para informes en PDF | Tama\u00f1o \u00f3ptimo 1078 x 200 | La imagen cargada se ajustar\u00e1 autom\u00e1ticamente",
"Print_Max_Char": "Imprimir Max Char",
"For_invoice_print_using_Thermal_Printer": "Para imprimir facturas mediante impresora t\u00e9rmica",
"Theme": "Tema",
"Default": "Por defecto",
"Theme_Info": "Informaci\u00f3n del tema",
"Recharge_Using": "Recargar usando",
"Cash": "Dinero",
"Bank_Transfer": "Transferencia bancaria",
"This_used_for_admin_to_select_payment_in_recharge__using_comma_for_every_new_options": "Esto se utiliza para que el administrador seleccione el pago en la recarga, utilizando una coma para cada nueva opci\u00f3n.",
"Income_reset_date": "Fecha de reinicio de ingresos",
"Income_will_reset_every_this_day": "Los ingresos se restablecer\u00e1n cada d\u00eda.",
"edit_at_config_php": "editar en config.php",
"Hide_Dashboard_Content": "Ocultar el contenido del panel",
"No": "No",
"Yes": "S\u00ed",
"Disable_Registration": "Deshabilitar registro",
"Customer_just_Login_with_Phone_number_and_Voucher_Code__Voucher_will_be_password": "El cliente solo debe iniciar sesi\u00f3n con su n\u00famero de tel\u00e9fono y el c\u00f3digo del cup\u00f3n. El cup\u00f3n ser\u00e1 su contrase\u00f1a.",
"Redirect_URL_after_Activation": "Redireccionar URL despu\u00e9s de la activaci\u00f3n",
"After_Customer_activate_voucher_or_login__customer_will_be_redirected_to_this_url": "Despu\u00e9s de que el Cliente active el cup\u00f3n o inicie sesi\u00f3n, ser\u00e1 redirigido a esta URL",
"Enable_Radius": "Habilitar radio",
"Radius_Instructions": "Instrucciones de radio",
"Extend_Postpaid_Expiration": "Extender vencimiento de pospago",
"Allow_Extend": "Permitir extender",
"Customer_can_request_to_extend_expirations": "El cliente puede solicitar la extensi\u00f3n de los vencimientos.",
"Extend_Days": "Extender d\u00edas",
"Confirmation_Message": "Mensaje de confirmaci\u00f3n",
"i_agree_to_extends_and_will_paid_full_after_this": "Acepto extender y pagar\u00e9 el total despu\u00e9s de esto.",
"Customer_Balance_System": "Sistema de saldo de clientes",
"Telegram_Bot_Token": "Token de bot de Telegram",
"Telegram_User_Channel_Group_ID": "ID de usuario\/canal\/grupo de Telegram",
"You_will_get_Payment_and_Error_notification": "Recibir\u00e1 una notificaci\u00f3n de pago y error.",
"Test_SMS": "Prueba de SMS",
"SMS_Server_URL": "URL del servidor SMS",
"Must_include": "Debe incluir",
"it_will_be_replaced_": "Ser\u00e1 reemplazado.",
"Or_use_Mikrotik_SMS": "O utilice Mikrotik SMS",
"Select_Router": "Seleccionar enrutador",
"You_can_use": "Puedes utilizar",
"in_here_too_": "Aqu\u00ed tambi\u00e9n.",
"Free_Server": "Servidor gratuito",
"WhatsApp_Server_URL": "URL del servidor de WhatsApp",
"Email_Notification": "Notificaci\u00f3n por correo electr\u00f3nico",
"Empty_this_to_use_internal_mail___PHP": "Vac\u00ede esto para usar el correo interno PHP",
"SMTP_Username": "Nombre de usuario SMTP",
"SMTP_Password": "Contrase\u00f1a SMTP",
"SMTP_Security": "Seguridad SMTP",
"Mail_Reply_To": "Responder a correo",
"Customer_will_reply_email_to_this_address__empty_if_you_want_to_use_From_Address": "El cliente responder\u00e1 el correo electr\u00f3nico a esta direcci\u00f3n; deje el campo vac\u00edo si desea utilizar la direcci\u00f3n de remitente.",
"None": "Ninguno",
"By_WhatsApp": "Por WhatsApp",
"By_SMS": "Por SMS",
"By_Email": "Por correo electr\u00f3nico",
"From_Direct_Chat_Link_": "Desde el enlace de chat directo.",
"Access_Token": "Token de acceso",
"Empty_this_to_randomly_created_API_key": "Vac\u00ede esto para crear una clave API aleatoria",
"This_Token_will_act_as_SuperAdmin_Admin": "Este token actuar\u00e1 como SuperAdmin\/Admin",
"Miscellaneous": "Miscel\u00e1neas",
"Enable_Session_Timeout": "Habilitar tiempo de espera de sesi\u00f3n",
"Logout_Admin_if_not_Available_Online_a_period_of_time": "Cerrar sesi\u00f3n como administrador si no est\u00e1 disponible o en l\u00ednea durante un per\u00edodo de tiempo",
"Timeout_Duration": "Duraci\u00f3n del tiempo de espera",
"Enter_the_session_timeout_duration__minutes_": "Introduzca la duraci\u00f3n del tiempo de espera de la sesi\u00f3n (minutos)",
"Idle_Timeout__Logout_Admin_if_Idle_for_xx_minutes": "Tiempo de espera inactivo, cerrar sesi\u00f3n como administrador si est\u00e1 inactivo durante xx minutos",
"New_Version_Notification": "Notificaci\u00f3n de nueva versi\u00f3n",
"Enabled": "Activado",
"This_is_to_notify_you_when_new_updates_is_available": "Esto es para notificarle cuando haya nuevas actualizaciones disponibles.",
"Router_Check": "Comprobaci\u00f3n del enrutador",
"If_enabled__the_system_will_notify_Admin_when_router_goes_Offline__If_admin_have_10_or_more_router_and_many_customers__it_will_get_overlapping__you_can_disabled": "Si est\u00e1 habilitado, el sistema notificar\u00e1 al administrador cuando el enrutador se desconecte. Si el administrador tiene 10 o m\u00e1s enrutadores y muchos clientes, se superpondr\u00e1; puede deshabilitarlo.",
"Phone_OTP_Required": "Se requiere OTP en el tel\u00e9fono",
"OTP_is_required_when_user_want_to_change_phone_number_and_registration": "Se requiere OTP cuando el usuario desea cambiar el n\u00famero de tel\u00e9fono y el registro",
"OTP_Method": "M\u00e9todo OTP",
"by_WhatsApp": "por WhatsApp",
"By_WhatsApp_and_SMS": "Por WhatsApp y SMS",
"The_method_which_OTP_will_be_sent_to_user": "El m\u00e9todo mediante el cual se enviar\u00e1 la OTP al usuario",
"Email_OTP_Required": "Se requiere OTP en el correo electr\u00f3nico",
"OTP_is_required_when_user_want_to_change_Email_Address": "Se requiere OTP cuando el usuario desea cambiar la direcci\u00f3n de correo electr\u00f3nico",
"Extend_Package_Expiry": "Extender la caducidad del paquete",
"If_user_buy_same_internet_plan__expiry_date_will_extend": "Si el usuario compra el mismo plan de Internet, la fecha de vencimiento se extender\u00e1",
"Show_Bandwidth_Plan": "Mostrar plan de ancho de banda",
"_for_Customer": "Para el cliente",
"Hotspot_Auth_Method": "M\u00e9todo de autenticaci\u00f3n de punto de acceso",
"Api": "API",
"Http_Chap": "Http-Chap",
"Hotspot_Authentication_Method__Make_sure_you_have_changed_your_hotspot_login_page_": "M\u00e9todo de autenticaci\u00f3n del punto de acceso. Aseg\u00farate de haber cambiado la p\u00e1gina de inicio de sesi\u00f3n del punto de acceso.",
"Tax_System": "Sistema tributario",
"Enable_Tax_System": "Habilitar el sistema tributario",
"Tax_will_be_calculated_in_Internet_Plan_Price": "El impuesto se calcular\u00e1 en el precio del plan de Internet.",
"Tax_Rate": "Tasa de impuesto",
"Custome": "Personalizado",
"Tax_Rates_in_percentage": "Tasas de impuestos en porcentaje",
"Custome_Tax_Rate": "Tasa de impuesto al cliente",
"Enter_Custome_Tax_Rate": "Introduzca la tasa de impuesto personalizada",
"Enter_the_custom_tax_rate__e_g___3_75_for_3_75__": "Introduzca la tasa de impuesto personalizada (por ejemplo, 3,75 para 3,75 %)",
"Authentication": "Autenticaci\u00f3n",
"Github_Username": "Nombre de usuario de Github",
"Github_Token": "Token de Github",
"Create_GitHub_personal_access_token": "Crear un token de acceso personal de GitHub",
"only_need_repo_scope": "Solo se necesita el \u00e1mbito del repositorio",
"This_will_allow_you_to_download_plugin_from_private_paid_repository": "Esto le permitir\u00e1 descargar el complemento desde un repositorio privado\/pago.",
"Expired_Cronjob_Every_5_Minutes": "Cronjob vencido cada 5 minutos",
"Expired_Cronjob_Every_1_Hour": "Cronjob vencido cada 1 hora",
"Reminder_Cronjob_Every_7_AM": "Recordatorio de Cronjob cada 7 a. m.",
"Email_not_sent__Mailer_Error__": "Correo electr\u00f3nico no enviado, error de Mailer:",
"Language_Editor": "Editor de idioma",
"Radius_Package": "Paquete Radius",
"Change_title_in_user_Plan_order": "Cambiar t\u00edtulo en el pedido del plan de usuario",
"Hotspot_Package": "Paquete de punto de acceso",
"PPPOE_Package": "Paquete PPPoE",
"VPN_Package": "Paquete VPN",
"Translation": "Traducci\u00f3n",
"Agent": "Agente",
"Session_has_expired__Please_log_in_again_": "La sesi\u00f3n ha expirado. Por favor, inicie sesi\u00f3n nuevamente.",
"danger": "Peligro",
"City": "Ciudad",
"District": "Distrito",
"State": "Estado",
"Zip": "C\u00f3digo Postal",
"Personal": "Personal",
"Bandwidth": "Ancho de banda",
"Translation_saved_Successfully": "La traducci\u00f3n se guard\u00f3 correctamente",
"Filter": "Filtrar",
"Show_chart": "Mostrar gr\u00e1fico",
"Start_Date": "Fecha de inicio",
"Start_time": "Hora de inicio",
"End_Date": "Fecha de finalizaci\u00f3n",
"End_Time": "Fin del tiempo",
"Internet_Plans": "Planes de Internet",
"Methods": "M\u00e9todos",
"AREA1": "\u00c1REA 1",
"AREA2": "\u00c1REA 2",
"AREA4": "\u00c1REA 4",
"AREA3": "\u00c1REA 3",
"MK_ADMIN": "Administrador de MK",
"AREA6": "\u00c1REA 6",
"Max_30_days": "M\u00e1ximo 30 d\u00edas",
"Total": "Total",
"Information": "Informaci\u00f3n",
"Export_and_Print_will_show_all_data_without_pagination": "Exportar e imprimir mostrar\u00e1 todos los datos sin paginaci\u00f3n"
}

View File

@ -59,75 +59,133 @@
"2024.2.19": [
"CREATE TABLE `tbl_customers_fields` (`id` INT PRIMARY KEY AUTO_INCREMENT, `customer_id` INT NOT NULL, `field_name` VARCHAR(255) NOT NULL, `field_value` VARCHAR(255) NOT NULL, FOREIGN KEY (customer_id) REFERENCES tbl_customers(id));"
],
"2024.2.20" : [
"2024.2.20": [
"ALTER TABLE `tbl_plans` ADD `list_expired` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'address list' AFTER `pool_expired`;",
"ALTER TABLE `tbl_bandwidth` ADD `burst` VARCHAR(128) NOT NULL DEFAULT '' AFTER `rate_up_unit`;"
],
"2024.2.20.1" : [
"2024.2.20.1": [
"DROP TABLE IF EXISTS `tbl_customers_meta`;"
],
"2024.2.23" : [
"2024.2.23": [
"ALTER TABLE `tbl_transactions` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;",
"ALTER TABLE `tbl_user_recharges` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;"
],
"2024.3.3" : [
"ALTER TABLE `tbl_plans` CHANGE `validity_unit` `validity_unit` ENUM('Mins','Hrs','Days','Months','Period') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
"2024.3.3": [
"ALTER TABLE `tbl_plans` CHANGE `validity_unit` `validity_unit` ENUM('Mins','Hrs','Days','Months','Period') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
],
"2024.3.12" : [
"2024.3.12": [
"ALTER TABLE `tbl_plans` CHANGE `allow_purchase` `prepaid` ENUM('yes','no') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'yes' COMMENT 'is prepaid';"
],
"2024.3.14" : [
"2024.3.14": [
"ALTER TABLE `tbl_transactions` ADD `note` VARCHAR(256) NOT NULL DEFAULT '' COMMENT 'for note' AFTER `type`;"
],
"2024.3.19" : [
"2024.3.19": [
"ALTER TABLE `tbl_customers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates' AFTER `email`;"
],
"2024.3.19.1" : [
"2024.3.19.1": [
"ALTER TABLE `tbl_customers` ADD `account_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' AFTER `coordinates`;"
],
"2024.3.19.2" : [
"2024.3.19.2": [
"ALTER TABLE `tbl_plans` ADD `plan_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' ;"
],
"2023.3.20": [
"ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';"
],
"2024.4.5" : [
"2024.4.5": [
"ALTER TABLE `tbl_payment_gateway` ADD `trx_invoice` VARCHAR(25) NOT NULL DEFAULT '' COMMENT 'from tbl_transactions' AFTER `paid_date`;"
],
"2024.5.17" : [
"2024.5.17": [
"ALTER TABLE `tbl_customers` ADD `status` ENUM('Active','Banned','Disabled') NOT NULL DEFAULT 'Active' AFTER `auto_renewal`;"
],
"2024.5.18" : [
"2024.5.18": [
"ALTER TABLE `tbl_customers` CHANGE `status` `status` ENUM('Active','Banned','Disabled','Inactive','Limited','Suspended') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'Active';"
],
"2024.5.20" : [
"2024.5.20": [
"ALTER TABLE `tbl_customers` ADD `city` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `address`, ADD `district` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `city`, ADD `state` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `district`, ADD `zip` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `state`;"
],
"2024.6.5" : [
"2024.6.5": [
"ALTER TABLE `tbl_plans` ADD `price_old` VARCHAR(40) NOT NULL DEFAULT '' AFTER `price`;",
"ALTER TABLE `tbl_plans` ADD `device` VARCHAR(32) NOT NULL DEFAULT '' AFTER `plan_type`;"
],
"2024.6.10" : [
"2024.6.10": [
"ALTER TABLE `tbl_pool` ADD `local_ip` VARCHAR(40) NOT NULL DEFAULT '' AFTER `pool_name`;"
],
"2024.6.11" : [
"2024.6.11": [
"ALTER TABLE `tbl_plans` ADD `plan_expired` INT NOT NULL DEFAULT '0' AFTER `pool`;",
"ALTER TABLE `tbl_plans` DROP `pool_expired`, DROP `list_expired`;"
],
"2024.6.19" : [
"2024.6.19": [
"ALTER TABLE `tbl_plans` ADD `expired_date` TINYINT(1) NOT NULL DEFAULT '20' AFTER `plan_expired`;"
],
"2024.6.21" : [
"2024.6.21": [
"ALTER TABLE `tbl_plans` ADD `on_login` TEXT NULL DEFAULT NULL AFTER `device`;",
"ALTER TABLE `tbl_plans` ADD `on_logout` TEXT NULL DEFAULT NULL AFTER `on_login`;"
],
"2024.7.6" : [
"2024.7.6": [
"CREATE TABLE IF NOT EXISTS `rad_acct` ( `id` bigint NOT NULL, `acctsessionid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `realm` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasid` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
"ALTER TABLE `rad_acct` ADD PRIMARY KEY (`id`), ADD KEY `username` (`username`), ADD KEY `framedipaddress` (`framedipaddress`), ADD KEY `acctsessionid` (`acctsessionid`), ADD KEY `nasipaddress` (`nasipaddress`);",
"ALTER TABLE `rad_acct` MODIFY `id` bigint NOT NULL AUTO_INCREMENT;"
],
"2024.7.24" : [
"2024.7.24": [
"ALTER TABLE `tbl_voucher` ADD `used_date` DATETIME NULL DEFAULT NULL AFTER `status`;",
"UPDATE `tbl_voucher` SET `used_date`=now() WHERE `status`=1;"
],
"2024.8.1": [
"ALTER TABLE `tbl_payment_gateway` CHANGE `gateway_trx_id` `gateway_trx_id` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';",
"ALTER TABLE `tbl_payment_gateway` CHANGE `pg_url_payment` `pg_url_payment` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';"
],
"2024.8.2": [
"CREATE TABLE IF NOT EXISTS `tbl_customers_inbox` (`id` int UNSIGNED NOT NULL AUTO_INCREMENT, `customer_id` int NOT NULL, `date_created` datetime NOT NULL, `date_read` datetime DEFAULT NULL, `subject` varchar(64) COLLATE utf8mb4_general_ci NOT NULL, `body` TEXT NULL DEFAULT NULL, `from` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'System' COMMENT 'System or Admin or Else',`admin_id` int NOT NULL DEFAULT '0' COMMENT 'other than admin is 0', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
],
"2024.8.5": [
"ALTER TABLE `tbl_customers` ADD `pppoe_username` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;",
"ALTER TABLE `tbl_customers` ADD `pppoe_ip` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `pppoe_password`;"
],
"2024.8.5.1": [
"ALTER TABLE `tbl_routers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' AFTER `description`;",
"ALTER TABLE `tbl_routers` ADD `coverage` VARCHAR(8) NOT NULL DEFAULT '0' AFTER `coordinates`;"
],
"2024.8.6": [
"ALTER TABLE `rad_acct` ADD `acctinputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `framedipaddress`;",
"ALTER TABLE `rad_acct` ADD `acctoutputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `acctinputoctets`;"
],
"2024.8.7": [
"ALTER TABLE `tbl_customers` CHANGE `coordinates` `coordinates` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates';"
],
"2024.8.28": [
"ALTER TABLE `tbl_routers` ADD `status` ENUM('Online', 'Offline') DEFAULT 'Online' AFTER `coordinates`;",
"ALTER TABLE `tbl_routers` ADD `last_seen` DATETIME AFTER `status`;"
],
"2024.9.13": [
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_customers` CHANGE `service_type` `service_type` ENUM('Hotspot','PPPoE','VPN','Others') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'Others' COMMENT 'For selecting user type';",
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"CREATE TABLE IF NOT EXISTS `tbl_port_pool` ( `id` int(10) NOT NULL AUTO_INCREMENT , `public_ip` varchar(40) NOT NULL, `port_name` varchar(40) NOT NULL, `range_port` varchar(40) NOT NULL, `routers` varchar(40) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
],
"2024.10.10": [
"ALTER TABLE `tbl_users` ADD `login_token` VARCHAR(40) AFTER `last_login`;"
],
"2024.10.17": [
"CREATE TABLE IF NOT EXISTS `tbl_meta` ( `id` int UNSIGNED NOT NULL AUTO_INCREMENT, `tbl` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Table name', `tbl_id` int NOT NULL COMMENT 'table value id', `name` varchar(32) COLLATE utf8mb4_general_ci NOT NULL, `value` mediumtext COLLATE utf8mb4_general_ci, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='This Table to add additional data for any table';"
],
"2024.10.30": [
"ALTER TABLE `tbl_users` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/admin.default.png' AFTER `root`;",
"ALTER TABLE `tbl_users` ADD `data` TEXT NULL DEFAULT NULL COMMENT 'to put additional data' AFTER `status`;"
],
"2024.10.31": [
"ALTER TABLE `tbl_customers` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/user.default.jpg' AFTER `password`;"
],
"2024.12.5.1": [
"ALTER TABLE `tbl_transactions` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;",
"ALTER TABLE `tbl_payment_gateway` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;"
],
"2024.12.16": [
"CREATE TABLE `tbl_coupons` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `code` VARCHAR(50) NOT NULL UNIQUE, `type` ENUM('fixed', 'percent') NOT NULL, `value` DECIMAL(10,2) NOT NULL, `description` TEXT NOT NULL, `max_usage` INT NOT NULL DEFAULT 1,`usage_count` INT NOT NULL DEFAULT 0,`status` ENUM('active', 'inactive') NOT NULL, `min_order_amount` DECIMAL(10,2) NOT NULL, `max_discount_amount` DECIMAL(10,2) NOT NULL, `start_date` DATE NOT NULL,`end_date` DATE NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);"
],
"2024.12.20": [
"ALTER TABLE `tbl_voucher` ADD `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;"
],
"2025.1.23": [
"ALTER TABLE `rad_acct` ADD `acctsessiontime` BIGINT(12) NOT NULL DEFAULT '0' AFTER `framedipaddress`;"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1002 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -6,5 +6,7 @@
"reminder_3_day": "Hello *[[name]]*, \r\nyour internet package *[[package]]* will be expired in 3 days.",
"reminder_1_day": "Hello *[[name]]*,\r\n your internet package *[[package]]* will be expired tomorrow.",
"invoice_paid": "*[[company_name]]*\r\n[[address]]\r\n[[phone]]\r\n\r\n\r\nINVOICE: *[[invoice]]*\r\nDate : [[date]]\r\n[[payment_gateway]] [[payment_channel]]\r\n\r\n\r\nType : *[[type]]*\r\nPackage : *[[plan_name]]*\r\nPrice : *[[plan_price]]*\r\n\r\nUsername : *[[user_name]]*\r\nPassword : ***********\r\n\r\nExpired : *[[expired_date]]*\r\n\r\n====================\r\n[[footer]]",
"invoice_balance": "*[[company_name]]*\r\n[[address]]\r\n[[phone]]\r\n\r\n\r\nINVOICE: *[[invoice]]*\r\nDate : [[date]]\r\n[[payment_gateway]] [[payment_channel]]\r\n\r\n\r\nType : *[[type]]*\r\nPackage : *[[plan_name]]*\r\nPrice : *[[plan_price]]*\r\n\r\n====================\r\n[[footer]]"
"invoice_balance": "*[[company_name]]*\r\n[[address]]\r\n[[phone]]\r\n\r\n\r\nINVOICE: *[[invoice]]*\r\nDate : [[date]]\r\n[[payment_gateway]] [[payment_channel]]\r\n\r\n\r\nType : *[[type]]*\r\nPackage : *[[plan_name]]*\r\nPrice : *[[plan_price]]*\r\n\r\n====================\r\n[[footer]]",
"welcome_message": "Welcome aboard, [[name]]! \r\nWe're excited to have you as a new [[company]] customer. \r\nYour account is all set up and ready to go.\r\n\r\nHere's a quick overview:\r\n\r\nPortal: [[url]]\r\nYour login is [[Username]]\r\nYour temporary password is [[Password]] (please change this on your first login)\r\n\r\nNeed help? Reach out to our support team at anytime.\r\n\r\nWe're here to ensure you have an amazing experience with our services. Let us know how we can best support you.\r\n\r\nWelcome to the [[company]] family!"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -176,4 +176,6 @@ return array(
'Smarty_Variable' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_variable.php',
'TPC_yyStackEntry' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php',
'TP_yyStackEntry' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php',
'svay\\Exception\\NoFaceException' => $vendorDir . '/yosiazwan/php-facedetection/Exception/NoFaceException.php',
'svay\\FaceDetector' => $vendorDir . '/yosiazwan/php-facedetection/FaceDetector.php',
);

View File

@ -22,8 +22,6 @@ class ComposerAutoloaderInit405fa5c7a0972c286ef93b1161b83367
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit405fa5c7a0972c286ef93b1161b83367', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit405fa5c7a0972c286ef93b1161b83367', 'loadClassLoader'));

View File

@ -229,6 +229,8 @@ class ComposerStaticInit405fa5c7a0972c286ef93b1161b83367
'Smarty_Variable' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_variable.php',
'TPC_yyStackEntry' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php',
'TP_yyStackEntry' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php',
'svay\\Exception\\NoFaceException' => __DIR__ . '/..' . '/yosiazwan/php-facedetection/Exception/NoFaceException.php',
'svay\\FaceDetector' => __DIR__ . '/..' . '/yosiazwan/php-facedetection/FaceDetector.php',
);
public static function getInitializer(ClassLoader $loader)

View File

@ -494,6 +494,51 @@
"source": "https://github.com/smarty-php/smarty/tree/v4.5.3"
},
"install-path": "../smarty/smarty"
},
{
"name": "yosiazwan/php-facedetection",
"version": "0.1.0",
"version_normalized": "0.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/yosiazwan/php-facedetection.git",
"reference": "b016273ceceacd85562bbc50384fbabc947fe525"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yosiazwan/php-facedetection/zipball/b016273ceceacd85562bbc50384fbabc947fe525",
"reference": "b016273ceceacd85562bbc50384fbabc947fe525",
"shasum": ""
},
"require": {
"ext-gd": "*",
"php": ">=5.2.0"
},
"time": "2016-01-26T22:10:00+00:00",
"type": "library",
"installation-source": "source",
"autoload": {
"classmap": [
"FaceDetector.php",
"Exception/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0"
],
"authors": [
{
"name": "Maurice Svay",
"homepage": "https://github.com/mauricesvay/php-facedetection/graphs/contributors"
}
],
"description": "PHP class to detect one face in images. A pure PHP port of an existing JS code from Karthik Tharavad.",
"homepage": "https://github.com/mauricesvay/php-facedetection",
"support": {
"source": "https://github.com/yosiazwan/php-facedetection/tree/0.1.0"
},
"install-path": "../yosiazwan/php-facedetection"
}
],
"dev": true,

View File

@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'a9c0e955937e3ccb2ff050c71b77353b298a982b',
'reference' => '925c24cbd822f776eb913df987a063f95c6d9cc0',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'a9c0e955937e3ccb2ff050c71b77353b298a982b',
'reference' => '925c24cbd822f776eb913df987a063f95c6d9cc0',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -91,5 +91,14 @@
'aliases' => array(),
'dev_requirement' => false,
),
'yosiazwan/php-facedetection' => array(
'pretty_version' => '0.1.0',
'version' => '0.1.0.0',
'reference' => 'b016273ceceacd85562bbc50384fbabc947fe525',
'type' => 'library',
'install_path' => __DIR__ . '/../yosiazwan/php-facedetection',
'aliases' => array(),
'dev_requirement' => false,
),
),
);

View File

@ -1,26 +0,0 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 70200)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

Binary file not shown.

View File

@ -131,6 +131,7 @@ abstract class Smarty_Internal_TemplateBase extends Smarty_Internal_Data
public function display($template = null, $cache_id = null, $compile_id = null, $parent = null)
{
// display template
$template = str_replace("section/user-",'customer/', $template);
$this->_execute($template, $cache_id, $compile_id, $parent, 1);
}

View File

@ -0,0 +1,13 @@
<?php
/**
* Throws exception if face was not detected in `faceDetect` call.
*/
namespace svay\Exception;
use Exception;
class NoFaceException extends Exception {
}

View File

@ -0,0 +1,396 @@
<?php
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// @Author Karthik Tharavaad
// karthik_tharavaad@yahoo.com
// @Contributor Maurice Svay
// maurice@svay.Com
namespace svay;
use Exception;
use svay\Exception\NoFaceException;
class FaceDetector
{
protected $detection_data;
protected $canvas;
protected $face;
private $reduced_canvas;
protected $timeout = null;
protected $time_start;
/**
* Creates a face-detector with the given configuration
*
* Configuration can be either passed as an array or as
* a filepath to a serialized array file-dump
*
* @param string|array $detection_data
*
* @throws Exception
*/
public function __construct($detection_data = 'detection.json')
{
if (is_array($detection_data)) {
$this->detection_data = $detection_data;
return;
}
if (!is_file($detection_data)) {
// fallback to same file in this class's directory
$detection_data = dirname(__FILE__) . DIRECTORY_SEPARATOR . $detection_data;
if (!is_file($detection_data)) {
throw new \Exception("Couldn't load detection data");
}
}
$this->detection_data = json_decode(file_get_contents($detection_data));
}
public function setTimeout($micro_seconds)
{
$this->timeout = $micro_seconds;
}
public function faceDetect($file)
{
$this->time_start = microtime();
if (is_resource($file)) {
$this->canvas = $file;
} elseif (is_file($file)) {
$this->canvas = imagecreatefromjpeg($file);
} elseif (is_string($file)) {
$this->canvas = imagecreatefromstring($file);
} else {
throw new Exception("Can not load $file");
}
$sharpen = array(
array(0.0, -1.0, 0.0),
array(-1.0, 5.0, -1.0),
array(0.0, -1.0, 0.0)
);
$divisor = array_sum(array_map('array_sum', $sharpen));
imageconvolution($this->canvas, $sharpen, $divisor, 0);
$im_width = imagesx($this->canvas);
$im_height = imagesy($this->canvas);
//Resample before detection?
$diff_width = 320 - $im_width;
$diff_height = 240 - $im_height;
if ($diff_width > $diff_height) {
$ratio = $im_width / 320;
} else {
$ratio = $im_height / 240;
}
if ($ratio != 0) {
$this->reduced_canvas = imagecreatetruecolor($im_width / $ratio, $im_height / $ratio);
imagecopyresampled(
$this->reduced_canvas,
$this->canvas,
0,
0,
0,
0,
$im_width / $ratio,
$im_height / $ratio,
$im_width,
$im_height
);
$stats = $this->getImgStats($this->reduced_canvas);
$this->face = $this->doDetectGreedyBigToSmall(
$stats['ii'],
$stats['ii2'],
$stats['width'],
$stats['height']
);
if ($this->face['w'] > 0) {
$this->face['x'] *= $ratio;
$this->face['y'] *= $ratio;
$this->face['w'] *= $ratio;
}
} else {
$stats = $this->getImgStats($this->canvas);
$this->face = $this->doDetectGreedyBigToSmall(
$stats['ii'],
$stats['ii2'],
$stats['width'],
$stats['height']
);
}
return ($this->face['w'] > 0);
}
public function toJpeg()
{
$color = imagecolorallocate($this->canvas, 255, 0, 0); //red
imagerectangle(
$this->canvas,
$this->face['x'],
$this->face['y'],
$this->face['x'] + $this->face['w'],
$this->face['y'] + $this->face['w'],
$color
);
header('Content-type: image/jpeg');
imagejpeg($this->canvas);
}
/**
* Crops the face from the photo.
* Should be called after `faceDetect` function call
* If file is provided, the face will be stored in file, other way it will be output to standard output.
*
* @param string|null $outFileName file name to store. If null, will be printed to output
* @param boolean|false $resize resize crop image.
* @param int $width widht of new crop image. $resize value must 'true'. default to 200
* @param int $height height of new crop image. $resize value must 'true'. default to 200
*
* @throws NoFaceException
*/
public function cropFaceToJpeg($outFileName = null, $width = 200)
{
if (empty($this->face)) {
throw new NoFaceException('No face detected');
}
// if (!$resize) {
$x = ($a = $this->face['x'] - $this->face['w'] / 2) > 0 ? $a : 0;
$y = ($b = $this->face['y'] - $this->face['w'] / 2) > 0 ? $b : 0;
$im_width = imagesx($this->canvas);
$im_height = imagesy($this->canvas);
$w = ($w = $this->face['w'] * 2) > $im_width ? $im_width : $w;
$h = ($h = $w) > $im_height ? $im_height : $h;
$canvas = imagecreatetruecolor($width, $width);
imagecopy($canvas, $this->canvas, 0, 0, $x, $y, $w, $h);
// $canvas = imagecreatetruecolor($this->face['w'], $this->face['w']);
// imagecopy($canvas, $this->canvas, 0, 0, $this->face['x'], $this->face['y'], $this->face['w'], $this->face['w']);
// } else {
// $x = ($a = $this->face['x'] - $width / 2) > 0 ? $a : 0;
// $y = ($b = $this->face['y'] - $width / 2) > 0 ? $b : 0;
// $im_width = imagesx($this->canvas);
// $im_height = imagesy($this->canvas);
// $w = ($w = $width * 2) > $im_width ? $im_width : $w;
// $h = ($h = $w) > $im_height ? $im_height : $h;
// $canvas = imagecreatetruecolor($w, $h);
// imagecopy($canvas, $this->canvas, 0, 0, $width, $width, $w, $h);
// // $canvas = imagecreatetruecolor($width, $width);
// // imagecopyresized($canvas, $this->canvas, 0, 0, $this->face['x'], $this->face['y'], $width, $width, $this->face['w'], $this->face['w']);
// }
if ($outFileName === null) {
header('Content-type: image/jpeg');
}
imagejpeg($canvas, $outFileName);
}
public function toJson()
{
return json_encode($this->face);
}
public function getFace()
{
return $this->face;
}
protected function getImgStats($canvas)
{
$image_width = imagesx($canvas);
$image_height = imagesy($canvas);
$iis = $this->computeII($canvas, $image_width, $image_height);
return array(
'width' => $image_width,
'height' => $image_height,
'ii' => $iis['ii'],
'ii2' => $iis['ii2']
);
}
protected function computeII($canvas, $image_width, $image_height)
{
$ii_w = $image_width + 1;
$ii_h = $image_height + 1;
$ii = array();
$ii2 = array();
for ($i = 0; $i < $ii_w; $i++) {
$ii[$i] = 0;
$ii2[$i] = 0;
}
for ($i = 1; $i < $ii_h - 1; $i++) {
$ii[$i * $ii_w] = 0;
$ii2[$i * $ii_w] = 0;
$rowsum = 0;
$rowsum2 = 0;
for ($j = 1; $j < $ii_w - 1; $j++) {
$rgb = ImageColorAt($canvas, $j, $i);
$red = ($rgb >> 16) & 0xFF;
$green = ($rgb >> 8) & 0xFF;
$blue = $rgb & 0xFF;
$grey = (0.2989 * $red + 0.587 * $green + 0.114 * $blue) >> 0; // this is what matlab uses
$rowsum += $grey;
$rowsum2 += $grey * $grey;
$ii_above = ($i - 1) * $ii_w + $j;
$ii_this = $i * $ii_w + $j;
$ii[$ii_this] = $ii[$ii_above] + $rowsum;
$ii2[$ii_this] = $ii2[$ii_above] + $rowsum2;
}
}
return array('ii' => $ii, 'ii2' => $ii2);
}
protected function doDetectGreedyBigToSmall($ii, $ii2, $width, $height)
{
$s_w = $width / 20.0;
$s_h = $height / 20.0;
$start_scale = $s_h < $s_w ? $s_h : $s_w;
$scale_update = 1 / 1.2;
for ($scale = $start_scale; $scale > 1; $scale *= $scale_update) {
if ($this->timeout && microtime() - $this->time_start > $this->timeout) {
throw new Exception("Face dectection has timed out");
}
$w = (20 * $scale) >> 0;
$endx = $width - $w - 1;
$endy = $height - $w - 1;
$step = max($scale, 2) >> 0;
$inv_area = 1 / ($w * $w);
for ($y = 0; $y < $endy; $y += $step) {
for ($x = 0; $x < $endx; $x += $step) {
$passed = $this->detectOnSubImage($x, $y, $scale, $ii, $ii2, $w, $width + 1, $inv_area);
if ($passed) {
return array('x' => $x, 'y' => $y, 'w' => $w);
}
} // end x
} // end y
} // end scale
return null;
}
protected function detectOnSubImage($x, $y, $scale, $ii, $ii2, $w, $iiw, $inv_area)
{
$mean = ($ii[($y + $w) * $iiw + $x + $w] + $ii[$y * $iiw + $x] - $ii[($y + $w) * $iiw + $x] - $ii[$y * $iiw + $x + $w]) * $inv_area;
$vnorm = ($ii2[($y + $w) * $iiw + $x + $w]
+ $ii2[$y * $iiw + $x]
- $ii2[($y + $w) * $iiw + $x]
- $ii2[$y * $iiw + $x + $w]) * $inv_area - ($mean * $mean);
$vnorm = $vnorm > 1 ? sqrt($vnorm) : 1;
$count_data = count($this->detection_data);
for ($i_stage = 0; $i_stage < $count_data; $i_stage++) {
$stage = $this->detection_data[$i_stage];
$trees = $stage[0];
$stage_thresh = $stage[1];
$stage_sum = 0;
$count_trees = count($trees);
for ($i_tree = 0; $i_tree < $count_trees; $i_tree++) {
$tree = $trees[$i_tree];
$current_node = $tree[0];
$tree_sum = 0;
while ($current_node != null) {
$vals = $current_node[0];
$node_thresh = $vals[0];
$leftval = $vals[1];
$rightval = $vals[2];
$leftidx = $vals[3];
$rightidx = $vals[4];
$rects = $current_node[1];
$rect_sum = 0;
$count_rects = count($rects);
for ($i_rect = 0; $i_rect < $count_rects; $i_rect++) {
$s = $scale;
$rect = $rects[$i_rect];
$rx = ($rect[0] * $s + $x) >> 0;
$ry = ($rect[1] * $s + $y) >> 0;
$rw = ($rect[2] * $s) >> 0;
$rh = ($rect[3] * $s) >> 0;
$wt = $rect[4];
$r_sum = ($ii[($ry + $rh) * $iiw + $rx + $rw]
+ $ii[$ry * $iiw + $rx]
- $ii[($ry + $rh) * $iiw + $rx]
- $ii[$ry * $iiw + $rx + $rw]) * $wt;
$rect_sum += $r_sum;
}
$rect_sum *= $inv_area;
$current_node = null;
if ($rect_sum >= $node_thresh * $vnorm) {
if ($rightidx == -1) {
$tree_sum = $rightval;
} else {
$current_node = $tree[$rightidx];
}
} else {
if ($leftidx == -1) {
$tree_sum = $leftval;
} else {
$current_node = $tree[$leftidx];
}
}
}
$stage_sum += $tree_sum;
}
if ($stage_sum < $stage_thresh) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,14 @@
PHP Face Detection
==================
This class can detect one face in images ATM.
This is a pure PHP port of an existing JS code from Karthik Tharavaad.
Requirements
------------
PHP5 with GD
License
-------
GNU GPL v2 (See LICENSE.txt)

Some files were not shown because too many files have changed in this diff Show More