Compare commits

...

221 commits

Author SHA1 Message Date
iBNu Maksum
4a441c5763
fix variable inside ', it must be inside " 2025-03-20 10:43:52 +07:00
iBNu Maksum
d506dd66ff
2025.3.19 2025-03-19 14:20:00 +07:00
iBNu Maksum
e9b0cfd8f0
Merge branch 'master' into Development 2025-03-19 14:19:27 +07:00
iBNu Maksum
aa4dbc0cea
Merge pull request #410 from dicobaja/patch/MR-cache-file
update path of monthly registered cache file
2025-03-19 14:18:44 +07:00
dicobaja
4ef054466d
fix: update $cacheMRfile path to cache folder 2025-03-19 11:38:12 +07:00
iBNu Maksum
41a3cbe700
Merge pull request #408 from dicobaja/patch/sql-tbl-port-pool
missing `tbl_port_pool` in phpnuxbill.sql
2025-03-19 09:18:42 +07:00
dicobaja
4938840c5d
fix: missing tbl_port_pool in phpnuxbill.sql 2025-03-19 09:00:03 +07:00
Focuslinkstech
127d43e45d Update .gitignore to include invoices directory and ensure paid.png is tracked 2025-03-18 17:35:43 +01:00
Focuslinkstech
cdfbab7119 Refactor invoice handling to use 'invoice' key and improve payment link generation
Still in development
2025-03-18 16:51:37 +01:00
Focuslinkstech
1a2b85ae4f Merge branch 'Development' of https://github.com/hotspotbilling/phpnuxbill into Development 2025-03-18 15:28:51 +01:00
Focuslinkstech
2e2d967a5b Refactor Email invoice template handling and enhance payment link generation 2025-03-18 15:24:24 +01:00
Focuslinkstech
c45e19189a Fix case sensitivity in invoice template file paths 2025-03-18 13:12:30 +01:00
Focuslinkstech
d372bf4711 remove debug 2025-03-18 13:09:33 +01:00
Focuslinkstech
8b8a0357f0 Fix custom login page image uploading 2025-03-18 13:08:19 +01:00
Focuslinkstech
1cb0e30e6b Implement custom login page settings with validation and image upload support 2025-03-18 13:07:19 +01:00
iBNu Maksum
20916b44f0
throw error if failed disconnect customer 2025-03-17 14:50:56 +07:00
iBNu Maksum
c63545d33a
invoice link 2025-03-17 14:48:01 +07:00
Focuslinkstech
84500cdfc9 Add DejaVuSansCondensed-Oblique font to the project 2025-03-16 13:08:41 +01:00
Focuslinkstech
5b21ffcde5 Add option to allow custom balance amounts and update toggle icon 2025-03-16 13:07:58 +01:00
iBNu Maksum
009040cd3c
Fix expired list widget 2025-03-14 10:27:33 +07:00
iBNu Maksum
781481e118
Fix expired list widget 2025-03-14 10:26:47 +07:00
Focuslinkstech
66d67cb61d Add email attachment support and improve message formatting 2025-03-13 11:05:32 +01:00
iBNu Maksum
3372da2ac4
2025.3.13 new Invoice print 2025-03-13 17:04:07 +07:00
iBNu Maksum
803d04a91d
add fullname to log extend 2025-03-13 15:59:55 +07:00
iBNu Maksum
17de653752
paid icon 2025-03-13 15:22:25 +07:00
iBNu Maksum
9301f1058c
save pdf invoice customer from admin page 2025-03-13 15:07:15 +07:00
iBNu Maksum
0868d61271
Download PDF invoice Customers 2025-03-13 15:07:15 +07:00
iBNu Maksum
5f353392e3
Merge pull request #407 from ahmadhusein17/Development
Fix Text
2025-03-13 15:06:52 +07:00
Ahmad Husein
fdd8dad509
Update indonesia.json 2025-03-12 00:29:50 +07:00
Ahmad Husein
3563fa531b
Update community.tpl
Minor fixes to make it look better
2025-03-12 00:24:11 +07:00
Ahmad Husein
525f2311fc
Update community.tpl
Update a bit to make it look better
2025-03-12 00:18:46 +07:00
Ahmad Husein
3b6a6d2f55
Update recharge-confirm.tpl 2025-03-11 23:10:52 +07:00
Focuslinkstech
3cebfa2171 Refactor login and registration templates to support dynamic logo, wallpaper, and favicon loading; change footer position to fixed 2025-03-11 15:50:06 +01:00
Focuslinkstech
c65b569f94 Enhance mobile responsiveness and improve touch device support in login template 2025-03-11 09:20:00 +01:00
iBNu Maksum
ca5a7d60cf
API Rest Documentation? 2025-03-11 14:51:18 +07:00
iBNu Maksum
5987ffafce
Insomnia API Rest Collection 2025-03-11 14:39:56 +07:00
iBNu Maksum
d7bbb4d18f
Fix API request 2025-03-11 14:39:05 +07:00
iBNu Maksum
e3c173bea4
Different expired message 2025-03-11 12:29:46 +07:00
iBNu Maksum
78e1e2f989
add new Docs wiki 2025-03-11 12:28:22 +07:00
iBNu Maksum
3c7e6c7a64
Different Reminder for PPPOE and Hotspot using <divider> 2025-03-11 11:55:48 +07:00
iBNu Maksum
827bb8706d
2025.3.10 2025-03-10 13:53:43 +07:00
iBNu Maksum
4731a506bc
only admin can recharge Rp. 0 2025-03-10 13:53:02 +07:00
iBNu Maksum
597c051948
Telegram message to topik 2025-03-10 13:35:54 +07:00
iBNu Maksum
40d3127ca1
Merge pull request #406 from gerandonk/Development
add [[bills]] for invoice message
2025-03-10 12:34:25 +07:00
gerandonk
c75133c9d2 add [[bills]] for invoice message 2025-03-10 03:30:27 +07:00
iBNu Maksum
ed3369244c
Merge pull request #405 from gerandonk/Development
bandwith 0=unlimited
2025-03-08 21:05:51 +07:00
gerandonk
cf9abbfb45 bandwith 0=unlimited
add bandwith 0=unlimited or null for device MikrotikHotspot and MikrotikPppoe
2025-03-08 18:01:21 +07:00
Focuslinkstech
c74d2bf7ec
Merge pull request #404 from ahmadhusein17/Development
Development
2025-03-06 18:51:24 +01:00
Focuslinkstech
ec9a06f468 feat: add service type selection and display in message bulk management 2025-03-06 18:44:20 +01:00
Focuslinkstech
4e3d89a23c feat: add message logging functionality with CSV export and management 2025-03-06 11:45:45 +01:00
Ahmad Husein
30ca1d1b2d
Update recharge_a_friend.tpl 2025-03-06 00:04:44 +07:00
Ahmad Husein
1cbff8f9a4
Update account_info.tpl 2025-03-06 00:03:06 +07:00
Focuslinkstech
43b1025d3c refactor: enhance code formatting and add demo mode check for actions 2025-03-05 15:02:58 +01:00
Focuslinkstech
da14a7bfef refactor: improve code readability by formatting conditional statements and adding demo mode checks 2025-03-05 13:59:43 +01:00
iBNu Maksum
8429ed763e
remove widget html_only, add widget html_php_card 2025-03-04 16:14:10 +07:00
iBNu Maksum
3b7d478635
fix method query 2025-03-04 15:16:48 +07:00
iBNu Maksum
ee3dcc05a0
fix reports by methods 2025-03-04 13:37:52 +07:00
iBNu Maksum
1e7d9cf6ef
Merge branch 'master' into Development 2025-03-03 14:28:27 +07:00
iBNu Maksum
986016083b
routeros 7.18 changelog "has put !empty sentence when API query returns nothing"; so i try to add that type, maybe works 2025-03-03 14:28:18 +07:00
iBNu Maksum
c95de08ed8
ignore .idea 2025-03-03 10:15:53 +07:00
iBNu Maksum
d657632876
remove .idea 2025-03-03 10:15:43 +07:00
Focuslinkstech
d8dbe68f51 Update notification reminder conditions to allow for more flexible configuration 2025-03-02 15:36:14 +01:00
Focuslinkstech
366ef73d57 Add notification reminder settings and improve PHP configuration 2025-03-02 15:29:57 +01:00
iBNu Maksum
dfdf35286f
Merge branch 'master' into Development 2025-02-26 10:12:26 +07:00
iBNu Maksum
eea99d218d
6,6 2025-02-26 10:11:44 +07:00
iBNu Maksum
08d1de563e
6.6 2025-02-26 10:11:44 +07:00
iBNu Maksum
51a0b859ab
2025.2.25 2025-02-26 10:11:44 +07:00
iBNu Maksum
47a57912ba
Default widget for agent and sales 2025-02-26 10:11:44 +07:00
Ahmad Husein
7bbaee1b88
Update indonesia.json 2025-02-26 10:11:44 +07:00
Ahmad Husein
791e6d55d0
Update indonesia.json 2025-02-26 10:11:44 +07:00
Ahmad Husein
4564ef90d5
Update system.tpl 2025-02-26 10:11:44 +07:00
Ahmad Husein
23524e4c1f
Update widgets_add_edit.tpl 2025-02-26 10:11:44 +07:00
Ahmad Husein
c486acaaf6
Update notifications.tpl 2025-02-26 10:11:43 +07:00
Ahmad Husein
e3476a971c
Update widgets_add_edit.tpl 2025-02-26 10:11:43 +07:00
Ahmad Husein
b9ccc7561c
Update widgets.tpl 2025-02-26 10:11:43 +07:00
Ahmad Husein
5a1bb441af
Update active.tpl 2025-02-26 10:11:43 +07:00
iBNu Maksum
19a48c0c88
Widget For Customer 2025-02-26 10:11:43 +07:00
iBNu Maksum
8d2c334da0
Different Widget for Admin, Agent, Sales, Customer. Agent, Sales not yet have widget 2025-02-26 10:11:43 +07:00
iBNu Maksum
9163f02717
sql default widget for customer 2025-02-26 10:11:43 +07:00
iBNu Maksum
da86f2c422
Different Widget for admin, Agent, Sales and Customers Editor 2025-02-26 10:11:43 +07:00
iBNu Maksum
f929560384
fix pppoe_username accounting radius rest 2025-02-26 10:11:43 +07:00
iBNu Maksum
1387f32865
add user in the Table Widget 2025-02-26 10:11:43 +07:00
Ahmad Husein
a39ff8a9a4
Update widgets_add_edit.tpl 2025-02-26 10:11:43 +07:00
Ahmad Husein
eb96c7a1e4
Update widgets.tpl 2025-02-26 10:11:42 +07:00
iBNu Maksum
83a2505eed
6,6 2025-02-25 14:43:31 +07:00
iBNu Maksum
e91fbe89ba
6.6 2025-02-25 14:42:44 +07:00
iBNu Maksum
925097c0b2
2025.2.25 2025-02-25 14:38:51 +07:00
iBNu Maksum
6cd46cdf7c
Default widget for agent and sales 2025-02-25 14:38:35 +07:00
iBNu Maksum
9b331813d1
Merge pull request #402 from ahmadhusein17/Development
Development
2025-02-25 14:38:04 +07:00
Ahmad Husein
1e500d8941
Update indonesia.json 2025-02-21 19:47:15 +07:00
Ahmad Husein
c433363d31
Update indonesia.json 2025-02-21 19:36:42 +07:00
Ahmad Husein
3901213a73
Update system.tpl 2025-02-21 18:58:54 +07:00
Ahmad Husein
8860ad1d13
Update widgets_add_edit.tpl 2025-02-21 18:53:01 +07:00
Ahmad Husein
5df32c7119
Update notifications.tpl 2025-02-21 18:46:55 +07:00
Ahmad Husein
08be62053e
Update widgets_add_edit.tpl 2025-02-21 18:42:14 +07:00
Ahmad Husein
71bf301ccf
Update widgets.tpl 2025-02-21 18:37:29 +07:00
Ahmad Husein
0e32f128af
Update active.tpl 2025-02-21 18:32:32 +07:00
iBNu Maksum
686f3de2d3
Widget For Customer 2025-02-21 16:04:53 +07:00
iBNu Maksum
b379266973
Different Widget for Admin, Agent, Sales, Customer. Agent, Sales not yet have widget 2025-02-21 16:04:53 +07:00
iBNu Maksum
9332063c87
sql default widget for customer 2025-02-21 16:04:18 +07:00
iBNu Maksum
db4c643f93
Different Widget for admin, Agent, Sales and Customers Editor 2025-02-21 16:04:18 +07:00
iBNu Maksum
1a2f2e24cd
fix pppoe_username accounting radius rest 2025-02-21 16:04:18 +07:00
iBNu Maksum
92ce363b16
add user in the Table Widget 2025-02-21 16:04:18 +07:00
ORConsulTech
5ddbf8258c
Translation into Spanish 2025-02-21 16:04:18 +07:00
ORConsulTech
4105ad0b8b
Translation into Spanish 2025-02-21 16:04:18 +07:00
ORConsulTech
af22a26c4c
Update add.tpl 2025-02-21 16:04:18 +07:00
ORConsulTech
a2ade795c7
Delete .idea/misc.xml 2025-02-21 16:04:18 +07:00
ORConsulTech
7b36a46c50
Delete .idea/misc.xml
Delete .idea/vcs.xml

Delete .idea/phpnuxbill.iml

Delete .idea/modules.xml

Revert "Delete .idea/misc.xml"

This reverts commit e9a947a24a.
2025-02-21 16:04:18 +07:00
ORConsulTech
fa9fe241e2
Translation into Spanish 2025-02-21 16:04:18 +07:00
iBNu Maksum
1aa4110552
Merge pull request #401 from ormendoza/Development
Translation into Spanish
2025-02-21 16:03:12 +07:00
iBNu Maksum
8917b2ab7b
Merge pull request #400 from ahmadhusein17/Development
Update translation
2025-02-21 10:44:35 +07:00
ORConsulTech
8238061510 Translation into Spanish 2025-02-20 18:25:47 -04:00
ORConsulTech
096b4fb3af Translation into Spanish 2025-02-19 11:05:59 -04:00
ORConsulTech
5d3eea1f45 Update add.tpl 2025-02-19 09:54:30 -04:00
ORConsulTech
ee6098774a Merge remote-tracking branch 'origin/Development' into Development 2025-02-19 09:53:49 -04:00
ORConsulTech
d272e572c0
Delete .idea/modules.xml 2025-02-19 10:41:42 -04:00
ORConsulTech
2be111e9df
Delete .idea/phpnuxbill.iml 2025-02-19 10:41:26 -04:00
ORConsulTech
6cf7d6b5de
Delete .idea/vcs.xml 2025-02-19 10:41:10 -04:00
ORConsulTech
e9a947a24a
Delete .idea/misc.xml 2025-02-19 10:40:41 -04:00
ORConsulTech
20c0543143 Delete .idea/misc.xml
Delete .idea/vcs.xml

Delete .idea/phpnuxbill.iml

Delete .idea/modules.xml

Revert "Delete .idea/misc.xml"

This reverts commit e9a947a24a.
2025-02-19 09:43:31 -04:00
ORConsulTech
cd1034c3ab Translation into Spanish 2025-02-19 09:36:28 -04:00
Ahmad Husein
f9a938830d
Update widgets_add_edit.tpl 2025-02-18 17:41:13 +07:00
Ahmad Husein
5b79432f9a
Update widgets.tpl 2025-02-18 17:37:12 +07:00
iBNu Maksum
023b4884d1
full widget support 2025-02-18 15:52:53 +07:00
iBNu Maksum
fca86ac4dc
remove hidden dashboard content on settings 2025-02-18 15:52:41 +07:00
iBNu Maksum
ef51d833d8
fix link 2025-02-18 15:50:06 +07:00
iBNu Maksum
c05a943ff5
add widget settings 2025-02-18 15:49:46 +07:00
iBNu Maksum
693d7f4acf
widget multiple section 2025-02-18 14:28:55 +07:00
iBNu Maksum
5b9cdd6681
reformat code 2025-02-18 14:28:32 +07:00
iBNu Maksum
a013373554
default widget value 2025-02-17 14:40:47 +07:00
iBNu Maksum
aeae936f17
Spanish Translation by @ORConsulTech 2025-02-17 14:40:33 +07:00
iBNu Maksum
ea203ebf64
widget add edit 2025-02-17 14:39:23 +07:00
iBNu Maksum
da0779e368
panel-footer color 2025-02-17 14:39:02 +07:00
iBNu Maksum
4bb5361d4b
default widgets 2025-02-17 13:54:30 +07:00
iBNu Maksum
69a7d842e0
Merge pull request #399 from ahmadhusein17/Development
Development
2025-02-17 13:46:03 +07:00
Ahmad Husein
fefcd56801
Update change-password.tpl 2025-02-16 20:07:46 +07:00
Ahmad Husein
5f0df84fe7
Update activation.tpl 2025-02-16 19:59:57 +07:00
Ahmad Husein
80d213bea3
Update activation-list.tpl 2025-02-16 19:57:42 +07:00
Ahmad Husein
0b52d3eb35
Update inbox.tpl 2025-02-16 19:51:03 +07:00
Ahmad Husein
569d5e3670
Update dbstatus.tpl 2025-02-16 19:19:10 +07:00
Ahmad Husein
7396bcdddf
Update customfield.tpl 2025-02-16 19:05:22 +07:00
Ahmad Husein
eee1ad72f8
Update customfield.tpl 2025-02-16 18:59:38 +07:00
Ahmad Husein
2b80f5e6f1
Update widgets.tpl 2025-02-16 18:41:35 +07:00
Ahmad Husein
f98aecc61f
Update widgets_add_edit.tpl 2025-02-16 18:29:50 +07:00
iBNu Maksum
4cc85b9261
add tbl_widgets 2025-02-14 17:55:56 +07:00
iBNu Maksum
eff0c7dab7
getting ready for customizeable dashboard with widget 2025-02-14 17:08:05 +07:00
iBNu Maksum
30bdb89d91
fix save settings 2025-02-14 10:07:12 +07:00
Focuslinks Digital Solutions
8fc7afb173 Merge branch 'Development' of https://github.com/hotspotbilling/phpnuxbill into Development 2025-02-12 08:12:11 +01:00
iBNu Maksum
fa5be4c196
delete debug 2025-02-12 11:56:18 +07:00
iBNu Maksum
304de7a999
fix 404 2025-02-12 11:50:04 +07:00
iBNu Maksum
1ad1320779
Fix setting save 2025-02-12 11:35:20 +07:00
Focuslinks Digital Solutions
a3f66fdd84 update toggle icon for dark mode switch 2025-02-11 18:30:23 +01:00
iBNu Maksum
86b18e27cd
Merge branch 'master' into Development 2025-02-11 13:30:14 +07:00
iBNu Maksum
535a44bb54
2025.2.11 2025-02-11 13:29:23 +07:00
iBNu Maksum
929f75dc4d
add connect and wait timeout 2025-02-11 13:29:22 +07:00
iBNu Maksum
8e7a1dc0f2
2025.2.11 2025-02-11 13:27:46 +07:00
iBNu Maksum
6865d321c3
add connect and wait timeout 2025-02-11 13:07:53 +07:00
Focuslinks Digital Solutions
75d6f17eb5 fix csrf token 2025-02-09 18:37:19 +01:00
Focuslinks Digital Solutions
4bc47a8d85 fix language 2025-02-09 16:37:13 +01:00
Focuslinks Digital Solutions
0a3205915f Enhancement and Improvements
Refactor CSRF class: improve token handling and update session variable names

Replace bulk message with ajax based message sending.
Added support for multiple recipients in bulk message, and also router based filtering.

Add support for multiple recipients in bulk message from customer list as requested by one of our Member. you can now send messages to multiple recipients at once from customer list.

Added Exception for CRON but not tested yet. i dont have multiple routers.
Added notify to know if cron has been executed or not.
2025-02-09 16:06:59 +01:00
iBNu Maksum
60d945d87f
Merge pull request #397 from gerandonk/Development
fix postpaid invoice display from attributes
2025-02-09 10:03:24 +07:00
gerandonk
685b325ef3 fix postpaid invoice display from attributes 2025-02-09 01:23:06 +07:00
iBNu Maksum
e0884c0a5a
2025.2.7 2025-02-07 15:19:04 +07:00
iBNu Maksum
b6fadae2e5
fix cookies 2025-02-07 15:18:50 +07:00
iBNu Maksum
91271b9f00
Fix sub folder 2025-02-07 15:18:41 +07:00
iBNu Maksum
a2237390e0
fix redirect to install 2025-02-06 11:31:52 +07:00
iBNu Maksum
b3169a2060
Merge branch 'Development' 2025-02-05 16:38:37 +07:00
iBNu Maksum
b3f4edb2d7
remove debug 2025-02-05 16:38:09 +07:00
iBNu Maksum
f301382a72
finishing 2025-02-05 16:38:09 +07:00
iBNu Maksum
92f2caeece
2025.2.5 2025-02-05 16:38:09 +07:00
iBNu Maksum
9fd8feb16f
fix javascript 2025-02-05 16:38:09 +07:00
iBNu Maksum
ee022781b1
fix orderplan 2025-02-05 16:38:09 +07:00
Ahmad Husein
85c5441934
Update Barcode Scan UI 2025-02-05 16:38:09 +07:00
iBNu Maksum
9cc7c0c811
pretty url stage 8 2025-02-05 16:38:09 +07:00
iBNu Maksum
7f785a7c4a
pretty url stage 7, thanks regex 2025-02-05 16:38:09 +07:00
iBNu Maksum
05aa1499ab
pretty url stage 6 2025-02-05 16:38:09 +07:00
iBNu Maksum
2469aa6b99
pretty url stage 5 2025-02-05 16:38:09 +07:00
iBNu Maksum
4d71c1d48e
pretty url stage 4 2025-02-05 16:38:09 +07:00
iBNu Maksum
bdef2b62c3
fix test wa email sms 2025-02-05 16:38:09 +07:00
Ahmad Husein
1cae09763f
Update 404.tpl 2025-02-05 16:38:08 +07:00
Ahmad Husein
4ba7c14693
Update 404.tpl 2025-02-05 16:38:08 +07:00
Ahmad Husein
aa9a2e4277
Update login.tpl 2025-02-05 16:38:07 +07:00
iBNu Maksum
bd87f0143b
remove debug 2025-02-05 16:25:37 +07:00
iBNu Maksum
8c94e9ea01
finishing 2025-02-05 16:25:03 +07:00
iBNu Maksum
d09e657ed5
2025.2.5 2025-02-05 16:10:40 +07:00
iBNu Maksum
fbe541ef9c
fix javascript 2025-02-05 16:02:40 +07:00
iBNu Maksum
5feeb2625e
fix orderplan 2025-02-05 15:49:02 +07:00
iBNu Maksum
0d116fcb5b
Merge pull request #396 from ahmadhusein17/Development
Update Barcode Scan UI
2025-02-05 15:44:32 +07:00
iBNu Maksum
8d704f496e
pretty url stage 8 2025-02-05 15:44:06 +07:00
Ahmad Husein
94569bca0e
Update Barcode Scan UI 2025-02-05 15:29:26 +07:00
iBNu Maksum
129302d558
pretty url stage 7, thanks regex 2025-02-05 14:45:04 +07:00
iBNu Maksum
56762ef277
pretty url stage 6 2025-02-05 14:13:21 +07:00
iBNu Maksum
75880e8366
pretty url stage 5 2025-02-05 09:48:05 +07:00
iBNu Maksum
cbacb1c52f
pretty url stage 4 2025-02-05 09:40:02 +07:00
iBNu Maksum
01d966082c
fix test wa email sms 2025-02-05 09:39:06 +07:00
iBNu Maksum
e4124bd210
Merge pull request #395 from ahmadhusein17/patch-2
Update 404.tpl
2025-02-05 09:38:41 +07:00
iBNu Maksum
0d4fde6481
Merge pull request #394 from ahmadhusein17/patch-1
Update 404.tpl
2025-02-05 09:38:34 +07:00
iBNu Maksum
6790fdd6d4
Merge pull request #393 from ahmadhusein17/patch-3
Update login.tpl
2025-02-05 09:38:25 +07:00
iBNu Maksum
40d8a4edfc
Merge pull request #392 from ahmadhusein17/Development
Back button tidied up
2025-02-05 09:38:20 +07:00
Ahmad Husein
09eedf4999
Update login.tpl 2025-02-04 22:09:35 +07:00
Ahmad Husein
9adf1412bc
Update 404.tpl 2025-02-04 21:57:26 +07:00
Ahmad Husein
d3e813e7df
Update 404.tpl 2025-02-04 21:48:26 +07:00
iBNu Maksum
6ccabf8b45
Merge pull request #391 from gerandonk/Development
add [[payment_link]] for single message
2025-02-04 20:03:38 +07:00
iBNu Maksum
af064ac5bb
Merge pull request #390 from ahmadhusein17/Development
Development
2025-02-04 20:02:49 +07:00
gerandonk
0ec7176d1e add [[payment_link]] for single message
add [[payment_link]] for single message to able send payment link manualy to customer.
2025-02-04 19:44:06 +07:00
Ahmad Husein
c594cf28bb
Merge branch 'hotspotbilling:Development' into Development 2025-02-04 18:03:44 +07:00
Ahmad Husein
2a038d63bb
Update login.tpl 2025-02-04 18:03:24 +07:00
iBNu Maksum
7170b63890
Merge pull request #389 from ahmadhusein17/Development
Update indonesia.json
2025-02-04 17:42:06 +07:00
Ahmad Husein
d44c58f610
Update indonesia.json 2025-02-04 17:31:08 +07:00
iBNu Maksum
10adbe48ab
fix url recharge and voucher 2025-02-04 17:22:11 +07:00
iBNu Maksum
b3cb6de028
2025.2.4 2025-02-04 17:13:30 +07:00
iBNu Maksum
96dca1a38b
fix order/gateway url 2025-02-04 16:53:30 +07:00
iBNu Maksum
face7360e8
fix logout button 2025-02-04 16:06:05 +07:00
iBNu Maksum
f520352bc9
remove maps menu under Network 2025-02-04 15:50:15 +07:00
iBNu Maksum
a81bb5c4f5
Fix bulk Message delay and no phone number 2025-02-04 15:29:29 +07:00
iBNu Maksum
8d9919afa7
fix bulk broadcasting message 2025-02-04 15:13:42 +07:00
iBNu Maksum
3386b17b1b
fix maps marker 2025-02-04 13:51:43 +07:00
iBNu Maksum
60c7f61579
strip unused js css 2025-02-04 13:50:43 +07:00
iBNu Maksum
fa05ffa58b
stage 3 restructure ui folder 2025-02-04 10:56:02 +07:00
iBNu Maksum
fa3e256174
stage 2 restructured ui folder 2025-02-04 10:22:14 +07:00
iBNu Maksum
0346a843ea
change 404 header footer and admin tpl 2025-02-04 09:23:55 +07:00
273 changed files with 11331 additions and 24671 deletions

6
.gitignore vendored
View file

@ -53,4 +53,8 @@ docker-compose.yml
docs/**
!docs/*.html
!docs/*.md
.htaccess
.htaccess
.idea
!docs/insomnia.rest.json
!system/uploads/paid.png
system/uploads/invoices/**

File diff suppressed because one or more lines are too long

1
docs/insomnia.rest.json Normal file

File diff suppressed because one or more lines are too long

View file

@ -42,7 +42,7 @@ spl_autoload_register('_autoloader');
if (!file_exists($root_path . 'config.php')) {
$root_path .= '..' . DIRECTORY_SEPARATOR;
if (!file_exists($root_path . 'config.php')) {
r2(getUrl('install'));
r2('./install');
}
}
@ -56,6 +56,7 @@ $UPLOAD_PATH = $root_path . File::pathFixer('system/uploads');
$CACHE_PATH = $root_path . File::pathFixer('system/cache');
$PAGES_PATH = $root_path . File::pathFixer('pages');
$PLUGIN_PATH = $root_path . File::pathFixer('system/plugin');
$WIDGET_PATH = $root_path . File::pathFixer('system/widgets');
$PAYMENTGATEWAY_PATH = $root_path . File::pathFixer('system/paymentgateway');
$UI_PATH = 'ui';
@ -111,6 +112,24 @@ $result = ORM::for_table('tbl_appconfig')->find_many();
foreach ($result as $value) {
$config[$value['setting']] = $value['value'];
}
if(empty($config['dashboard_Admin'])){
$config['dashboard_Admin'] = "12.7,5.12";
}
if(empty($config['dashboard_Agent'])){
$config['dashboard_Agent'] = "12.7,5.12";
}
if(empty($config['dashboard_Sales'])){
$config['dashboard_Sales'] = "12.7,5.12";
}
if(empty($config['dashboard_Customer'])){
$config['dashboard_Customer'] = "6,6";
}
$_c = $config;
if (empty($http_proxy) && !empty($config['http_proxy'])) {
$http_proxy = $config['http_proxy'];
@ -349,7 +368,7 @@ function _alert($text, $type = 'success', $url = "home", $time = 3)
$ui->assign('type', $type);
$ui->assign('time', $time);
$ui->assign('url', $url);
$ui->display('alert.tpl');
$ui->display('admin/alert.tpl');
die();
}
@ -367,7 +386,7 @@ function displayMaintenanceMessage(): void
}
http_response_code(503);
$ui->assign('companyName', $config['CompanyName']);
$ui->display('maintenance.tpl');
$ui->display('admin/maintenance.tpl');
die();
}

View file

@ -1374,7 +1374,7 @@ pre {
color: #333;
word-break: break-all;
word-wrap: break-word;
background-color: #f5f5f5;
background-color: #f6f8fa;
border: 1px solid #ccc;
border-radius: 4px;
}
@ -2126,7 +2126,7 @@ th {
}
.table-hover > tbody > tr:hover > td,
.table-hover > tbody > tr:hover > th {
background-color: #f5f5f5;
background-color: #f6f8fa;
}
table col[class*="col-"] {
position: static;
@ -2151,7 +2151,7 @@ table th[class*="col-"] {
.table > thead > tr.active > th,
.table > tbody > tr.active > th,
.table > tfoot > tr.active > th {
background-color: #f5f5f5;
background-color: #f6f8fa;
}
.table-hover > tbody > tr > td.active:hover,
.table-hover > tbody > tr > th.active:hover,
@ -3170,7 +3170,7 @@ tbody.collapse.in {
.dropdown-menu > li > a:focus {
color: #262626;
text-decoration: none;
background-color: #f5f5f5;
background-color: #f6f8fa;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
@ -4293,7 +4293,7 @@ fieldset[disabled] .navbar-inverse .btn-link:focus {
padding: 8px 15px;
margin-bottom: 20px;
list-style: none;
background-color: #f5f5f5;
background-color: #f6f8fa;
border-radius: 4px;
}
.breadcrumb > li {
@ -4711,7 +4711,7 @@ a.thumbnail.active {
height: 20px;
margin-bottom: 20px;
overflow: hidden;
background-color: #f5f5f5;
background-color: #f6f8fa;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
@ -4854,7 +4854,7 @@ a.list-group-item:hover,
a.list-group-item:focus {
color: #555;
text-decoration: none;
background-color: #f5f5f5;
background-color: #f6f8fa;
}
.list-group-item.disabled,
.list-group-item.disabled:hover,
@ -5023,7 +5023,7 @@ a.list-group-item-danger.active:focus {
}
.panel-footer {
padding: 10px 15px;
background-color: #f5f5f5;
background-color: #f6f8fa;
border-top: 1px solid #ddd;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 3px;
@ -5197,7 +5197,7 @@ a.list-group-item-danger.active:focus {
}
.panel-default > .panel-heading {
color: #333;
background-color: #f5f5f5;
background-color: #f6f8fa;
border-color: #ddd;
}
.panel-default > .panel-heading + .panel-collapse > .panel-body {
@ -5329,7 +5329,7 @@ a.list-group-item-danger.active:focus {
min-height: 20px;
padding: 19px;
margin-bottom: 20px;
background-color: #f5f5f5;
background-color: #f6f8fa;
border: 1px solid #e3e3e3;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);

View file

@ -251,6 +251,16 @@ CREATE TABLE `tbl_customers_inbox` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
DROP TABLE IF EXISTS `tbl_port_pool`;
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;
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',
@ -277,6 +287,28 @@ CREATE TABLE IF NOT EXISTS `tbl_coupons` (
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `tbl_widgets` (
`id` int NOT NULL AUTO_INCREMENT,
`orders` int NOT NULL DEFAULT '99',
`position` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1. top 2. left 3. right 4. bottom',
`user` ENUM('Admin','Agent','Sales','Customer') NOT NULL DEFAULT 'Admin',
`enabled` tinyint(1) NOT NULL DEFAULT '1',
`title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`widget` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`content` text COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE tbl_message_logs (
`id` SERIAL PRIMARY KEY,
`message_type` VARCHAR(50),
`recipient` VARCHAR(255),
`message_content` TEXT,
`status` VARCHAR(50),
`error_message` TEXT,
`sent_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
ALTER TABLE `rad_acct`
ADD PRIMARY KEY (`id`),
ADD KEY `username` (`username`),
@ -397,3 +429,48 @@ VALUES (
'2022-09-06 16:09:50',
'2014-06-23 01:43:07'
);
INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES
(1, 1, 1, 'Admin', 1, 'Top Widget', 'top_widget', ''),
(2, 2, 1, 'Admin', 1, 'Default Info', 'default_info_row', ''),
(3, 1, 2, 'Admin', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''),
(4, 2, 2, 'Admin', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''),
(5, 3, 2, 'Admin', 1, 'Voucher Stocks', 'voucher_stocks', ''),
(6, 4, 2, 'Admin', 1, 'Customer Expired', 'customer_expired', ''),
(7, 1, 3, 'Admin', 1, 'Cron Monitor', 'cron_monitor', ''),
(8, 2, 3, 'Admin', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''),
(9, 3, 3, 'Admin', 1, 'Info Payment Gateway', 'info_payment_gateway', ''),
(10, 4, 3, 'Admin', 1, 'Graph Customers Insight', 'graph_customers_insight', ''),
(11, 5, 3, 'Admin', 1, 'Activity Log', 'activity_log', ''),
(30, 1, 1, 'Agent', 1, 'Top Widget', 'top_widget', ''),
(31, 2, 1, 'Agent', 1, 'Default Info', 'default_info_row', ''),
(32, 1, 2, 'Agent', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''),
(33, 2, 2, 'Agent', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''),
(34, 3, 2, 'Agent', 1, 'Voucher Stocks', 'voucher_stocks', ''),
(35, 4, 2, 'Agent', 1, 'Customer Expired', 'customer_expired', ''),
(36, 1, 3, 'Agent', 1, 'Cron Monitor', 'cron_monitor', ''),
(37, 2, 3, 'Agent', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''),
(38, 3, 3, 'Agent', 1, 'Info Payment Gateway', 'info_payment_gateway', ''),
(39, 4, 3, 'Agent', 1, 'Graph Customers Insight', 'graph_customers_insight', ''),
(40, 5, 3, 'Agent', 1, 'Activity Log', 'activity_log', ''),
(41, 1, 1, 'Sales', 1, 'Top Widget', 'top_widget', ''),
(42, 2, 1, 'Sales', 1, 'Default Info', 'default_info_row', ''),
(43, 1, 2, 'Sales', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''),
(44, 2, 2, 'Sales', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''),
(45, 3, 2, 'Sales', 1, 'Voucher Stocks', 'voucher_stocks', ''),
(46, 4, 2, 'Sales', 1, 'Customer Expired', 'customer_expired', ''),
(47, 1, 3, 'Sales', 1, 'Cron Monitor', 'cron_monitor', ''),
(48, 2, 3, 'Sales', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''),
(49, 3, 3, 'Sales', 1, 'Info Payment Gateway', 'info_payment_gateway', ''),
(50, 4, 3, 'Sales', 1, 'Graph Customers Insight', 'graph_customers_insight', ''),
(51, 5, 3, 'Sales', 1, 'Activity Log', 'activity_log', ''),
(60, 1, 2, 'Customer', 1, 'Account Info', 'account_info', ''),
(61, 3, 1, 'Customer', 1, 'Active Internet Plan', 'active_internet_plan', ''),
(62, 4, 1, 'Customer', 1, 'Balance Transfer', 'balance_transfer', ''),
(63, 1, 1, 'Customer', 1, 'Unpaid Order', 'unpaid_order', ''),
(64, 2, 1, 'Customer', 1, 'Announcement', 'announcement', ''),
(65, 5, 1, 'Customer', 1, 'Recharge A Friend', 'recharge_a_friend', ''),
(66, 2, 2, 'Customer', 1, 'Voucher Activation', 'voucher_activation', '');

View file

@ -207,7 +207,7 @@ try {
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){
if ($c) {
$username = $c['username'];
$tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->find_one();
}
@ -274,7 +274,7 @@ try {
}
header("HTTP/1.1 200 ok");
$d = ORM::for_table('rad_acct')
->whereRaw("BINARY username = '$username' AND macaddr = '"._post('macAddr')."' AND nasid = '"._post('nasid')."'")
->whereRaw("BINARY username = '$username' AND macaddr = '" . _post('macAddr') . "' AND nasid = '" . _post('nasid') . "'")
->findOne();
if (!$d) {
$d = ORM::for_table('rad_acct')->create();
@ -292,19 +292,27 @@ try {
$d->username = $username;
$d->realm = _post('realm');
$d->nasipaddress = _post('nasIpAddress');
$d->acctsessiontime = intval(_post('acctSessionTime'));
$d->acctsessiontime = intval(_post('acctSessionTime'));
$d->nasid = _post('nasid');
$d->nasportid = _post('nasPortId');
$d->nasporttype = _post('nasPortType');
$d->framedipaddress = _post('framedIPAddress');
if(in_array(_post('acctStatusType'), ['Start', 'Stop'])){
if (in_array(_post('acctStatusType'), ['Start', 'Stop'])) {
$d->acctstatustype = _post('acctStatusType');
}
$d->macaddr = _post('macAddr');
$d->dateAdded = date('Y-m-d H:i:s');
// 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){
if (!$tur) {
// check if pppoe_username
$c = ORM::for_table('tbl_customers')->select('username')->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();
}
}
if ($tur) {
$d->save();
if (_post('acctStatusType') == 'Start') {
$plan = ORM::for_table('tbl_plans')->where('id', $tur['plan_id'])->find_one();
@ -349,15 +357,15 @@ function process_radiust_rest($tur, $code)
$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'")
$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 (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 {

View file

@ -4,25 +4,66 @@
<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>
<title>QR Code Scanner</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f2f2f2;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
#container {
background-color: #ffffff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
text-align: center;
}
#qr-reader {
width: 100%;
height: auto;
margin-bottom: 20px;
}
#qr-reader-results {
padding: 10px;
background-color: #f8f8f8;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
margin-top: 30px;
margin-bottom: 30px;
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #45a049;
}
</style>
</head>
<body>
<div id="qr-reader" style="width:100%; height:auto"></div>
<div id="qr-reader-results"></div>
<div id="container">
<h2>QR Code Scanner</h2>
<div id="qr-reader"></div>
<div id="qr-reader-results"></div>
<button id="camera-button">Open Camera</button>
</div>
<script src="qrcode.min.js"></script>
<script>
function docReady(fn) {
// see if DOM is already available
// lihat apakah DOM sudah tersedia
if (document.readyState === "complete" ||
document.readyState === "interactive") {
// call on next available tick
// panggil di detik berikutnya yang tersedia
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
@ -51,6 +92,9 @@
docReady(function() {
var resultContainer = document.getElementById('qr-reader-results');
var lastResult, countResults = 0;
var html5QrcodeScanner;
var isCameraOpen = false;
function onScanSuccess(decodedText, decodedResult) {
if (decodedText !== lastResult) {
++countResults;
@ -67,14 +111,26 @@
}
}
var html5QrcodeScanner = new Html5QrcodeScanner(
"qr-reader", {
fps: 10,
qrbox: 250
});
html5QrcodeScanner.render(onScanSuccess);
function toggleCamera() {
if (isCameraOpen) {
html5QrcodeScanner.clear();
document.getElementById('camera-button').textContent = "Open Camera";
isCameraOpen = false;
} else {
html5QrcodeScanner = new Html5QrcodeScanner(
"qr-reader", {
fps: 10,
qrbox: 250
});
html5QrcodeScanner.render(onScanSuccess);
document.getElementById('camera-button').textContent = "Close Camera";
isCameraOpen = true;
}
}
document.getElementById('camera-button').addEventListener('click', toggleCamera);
});
</script>
</body>
</html>
</html>

View file

@ -48,7 +48,28 @@ $ui = new class($key)
}
function getAll()
{
return $this->assign;
$result = [];
foreach ($this->assign as $key => $value) {
if($value instanceof ORM){
$result[$key] = $value->as_array();
}else if($value instanceof IdiormResultSet){
$count = count($value);
for($n=0;$n<$count;$n++){
foreach ($value[$n] as $k=>$v) {
$result[$key][$n][$k] = $v;
}
}
}else{
$result[$key] = $value;
}
}
return $result;
}
function fetch()
{
return "";
}
};

View file

@ -22,8 +22,8 @@ class Csrf
public static function check($token)
{
global $config;
if($config['csrf_enabled'] == 'yes') {
global $config, $isApi;
if($config['csrf_enabled'] == 'yes' && !$isApi) {
if (isset($_SESSION['csrf_token'], $_SESSION['csrf_token_time'], $token)) {
$storedToken = $_SESSION['csrf_token'];
$tokenTime = $_SESSION['csrf_token_time'];

View file

@ -87,7 +87,7 @@ class File
$src_img = $image_create($source_file);
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $nwidth, $nheight, $width, $height);
$image($dst_img, $dst_dir, $quality);
imagepng($dst_img, $dst_dir);
if ($dst_img) imagedestroy($dst_img);
if ($src_img) imagedestroy($src_img);

View file

@ -14,14 +14,14 @@
class Http
{
public static function getData($url, $headers = [])
public static function getData($url, $headers = [], $connect_timeout = 3000, $wait_timeout = 3000)
{
global $http_proxy, $http_proxyauth, $admin;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 0);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 100);
curl_setopt($ch, CURLOPT_TIMEOUT, 100);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
curl_setopt($ch, CURLOPT_TIMEOUT, $wait_timeout);
if (is_array($headers) && count($headers) > 0) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
@ -49,15 +49,15 @@ class Http
return (!empty($server_output)) ? $server_output : $error_msg;
}
public static function postJsonData($url, $array_post, $headers = [], $basic = null)
public static function postJsonData($url, $array_post, $headers = [], $basic = null, $connect_timeout = 3000, $wait_timeout = 3000)
{
global $http_proxy, $http_proxyauth, $admin;
$headers[] = 'Content-Type: application/json';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 100);
curl_setopt($ch, CURLOPT_TIMEOUT, 100);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
curl_setopt($ch, CURLOPT_TIMEOUT, $wait_timeout);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLINFO_HEADER_OUT, false);
if (!empty($http_proxy)) {
@ -92,15 +92,15 @@ class Http
}
public static function postData($url, $array_post, $headers = [], $basic = null)
public static function postData($url, $array_post, $headers = [], $basic = null, $connect_timeout = 3000, $wait_timeout = 3000)
{
global $http_proxy, $http_proxyauth, $admin;
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 100);
curl_setopt($ch, CURLOPT_TIMEOUT, 100);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout);
curl_setopt($ch, CURLOPT_TIMEOUT, $wait_timeout);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLINFO_HEADER_OUT, false);
if (!empty($http_proxy)) {

252
system/autoload/Invoice.php Normal file
View file

@ -0,0 +1,252 @@
<?php
use Mpdf\Mpdf;
class Invoice
{
public static function generateInvoice($invoiceData)
{
try {
if (empty($invoiceData['invoice'])) {
throw new Exception("Invoice ID is required");
}
$template = Lang::getNotifText('email_invoice');
if (!$template) {
throw new Exception("Invoice template not found");
}
if (strpos($template, '<body') === false) {
$template = "<html><body>$template</body></html>";
}
$processedHtml = self::renderTemplate($template, $invoiceData);
// Debugging: Save processed HTML to file for review
// file_put_contents('debug_invoice.html', $processedHtml);
// Generate PDF
$mpdf = new Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'margin_left' => 10,
'margin_right' => 10,
'margin_top' => 10,
'margin_bottom' => 10,
'default_font' => 'helvetica',
'orientation' => 'P',
]);
$mpdf->SetDisplayMode('fullpage');
$mpdf->SetProtection(['print']);
$mpdf->shrink_tables_to_fit = 1;
$mpdf->SetWatermarkText(strtoupper($invoiceData['status'] ?? 'UNPAID'), 0.15);
$mpdf->showWatermarkText = true;
$mpdf->WriteHTML($processedHtml);
// Save PDF
$filename = "invoice_{$invoiceData['invoice']}.pdf";
$outputPath = "system/uploads/invoices/{$filename}";
$mpdf->Output($outputPath, 'F');
if (!file_exists($outputPath)) {
throw new Exception("Failed to save PDF file");
}
return $filename;
} catch (\Exception $e) {
_log("Invoice generation failed: " . $e->getMessage());
sendTelegram("Invoice generation failed: " . $e->getMessage());
return false;
}
}
private static function renderTemplate($template, $invoiceData)
{
return preg_replace_callback('/\[\[(\w+)\]\]/', function ($matches) use ($invoiceData) {
$key = $matches[1];
if (!isset($invoiceData[$key])) {
_log("Missing invoice key: $key");
return '';
}
if (in_array($key, ['created_at', 'due_date'])) {
return date('F j, Y', strtotime($invoiceData[$key]));
}
if (in_array($key, ['amount', 'total', 'subtotal', 'tax'])) {
return $invoiceData['currency_code'] . number_format((float) $invoiceData[$key], 2);
}
if ($key === 'bill_rows') {
return html_entity_decode($invoiceData[$key]);
}
return htmlspecialchars($invoiceData[$key] ?? '');
}, $template);
}
public static function sendInvoice($userId, $status = "Unpaid")
{
global $config, $root_path, $UPLOAD_PATH;
if (empty($config['currency_code'])) {
$config['currency_code'] = '$';
}
$account = ORM::for_table('tbl_customers')->find_one($userId);
if (!$account) {
_log("Failed to send invoice: User not found");
sendTelegram("Failed to send invoice: User not found");
return false;
}
$invoice = ORM::for_table("tbl_transactions")->where("username", $account->username)->find_one();
if (!$invoice) {
_log("Failed to send invoice: Transaction not found");
sendTelegram("Failed to send invoice: Transaction not found");
return false;
}
[$additionalBills, $add_cost] = User::getBills($account->id);
$invoiceItems = [
[
'description' => $invoice->plan_name,
'details' => 'Monthly Subscription',
'amount' => (float) $invoice->price
]
];
$subtotal = (float) $invoice->price;
if ($add_cost > 0 && $invoice->routers != 'balance') {
foreach ($additionalBills as $description => $amount) {
if (is_numeric($amount)) {
$invoiceItems[] = [
'description' => $description,
'details' => 'Additional Bill',
'amount' => (float) $amount
];
$subtotal += (float) $amount;
} else {
_log("Invalid bill amount for {$description}: {$amount}");
}
}
}
$tax_rate = (float) ($config['tax_rate'] ?? 0);
$tax = $config['enable_tax'] ? Package::tax($subtotal) : 0;
$total = ($tax > 0) ? $subtotal + $tax : $subtotal + $tax;
$token = User::generateToken($account->id, 1);
if (!empty($token['token'])) {
$tur = ORM::for_table('tbl_user_recharges')
->where('customer_id', $account->id)
->where('namebp', $invoice->plan_name);
switch ($status) {
case 'Paid':
$tur->where('status', 'on');
break;
default:
$tur->where('status', 'off');
break;
}
$turResult = $tur->find_one();
$payLink = $turResult ? '?_route=home&recharge=' . $turResult['id'] . '&uid=' . urlencode($token['token']) : '?_route=home';
} else {
$payLink = '?_route=home';
}
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$logo = (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) ? $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png?' . time() : $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.default.png';
$invoiceData = [
'invoice' => "INV-" . Package::_raid(),
'fullname' => $account->fullname,
'email' => $account->email,
'address' => $account->address,
'phone' => $account->phonenumber,
'bill_rows' => self::generateBillRows($invoiceItems, $config['currency_code'], $subtotal, $tax_rate, $tax, $total),
'status' => $status,
'created_at' => date('Y-m-d H:i:s'),
'due_date' => date('Y-m-d H:i:s', strtotime('+7 days')),
'currency' => $config['currency_code'],
'company_address' => $config['address'],
'company_name' => $config['CompanyName'],
'company_phone' => $config['phone'],
'logo' => $logo,
'payment_link' => $payLink
];
if (!isset($invoiceData['bill_rows']) || empty($invoiceData['bill_rows'])) {
_log("Invoice Error: Bill rows data is empty.");
}
$filename = self::generateInvoice($invoiceData);
if ($filename) {
$pdfPath = "system/uploads/invoices/{$filename}";
try {
Message::sendEmail(
$account->email,
"Invoice for Account {$account->fullname}",
"Please find your invoice attached",
$pdfPath
);
return true;
} catch (\Exception $e) {
_log("Failed to send invoice email: " . $e->getMessage());
sendTelegram("Failed to send invoice email: " . $e->getMessage());
return false;
}
}
return false;
}
private static function generateBillRows($items, $currency, $subtotal, $tax_rate, $tax, $total)
{
$html = "<table style='width: 100%; border-collapse: collapse; margin: 20px 0;'>
<thead>
<tr>
<th style='background: #3498db; color: white; padding: 12px; text-align: left;'>Description</th>
<th style='background: #3498db; color: white; padding: 12px; text-align: left;'>Details</th>
<th style='background: #3498db; color: white; padding: 12px; text-align: left;'>Amount</th>
</tr>
</thead>
<tbody>";
foreach ($items as $item) {
$html .= "<tr>
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$item['description']}</td>
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$item['details']}</td>
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$currency}" . number_format((float) $item['amount'], 2) . "</td>
</tr>";
}
$html .= "<tr>
<td colspan='2' style='text-align: right; padding: 10px; border-top: 2px solid #3498db;'>Subtotal:</td>
<td style='padding: 10px; border-top: 2px solid #3498db;'>{$currency}" . number_format($subtotal, 2) . "</td>
</tr>
<tr>
<td colspan='2' style='text-align: right; padding: 10px;'>TAX ({$tax_rate}%):</td>
<td style='padding: 10px;'>{$currency}" . number_format($tax, 2) . "</td>
</tr>
<tr>
<td colspan='2' style='text-align: right; padding: 10px; font-weight: bold;'>Total:</td>
<td style='padding: 10px; font-weight: bold;'>{$currency}" . number_format($total, 2) . "</td>
</tr>";
$html .= "</tbody></table>";
return $html;
}
}

View file

@ -17,15 +17,18 @@ require $root_path . 'system/autoload/mail/SMTP.php';
class Message
{
public static function sendTelegram($txt, $chat_id = null)
public static function sendTelegram($txt, $chat_id = null, $topik = '')
{
global $config;
run_hook('send_telegram', [$txt, $chat_id = null]); #HOOK
run_hook('send_telegram', [$txt, $chat_id, $topik]); #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));
if (!empty($topik)) {
$topik = "message_thread_id=$topik&";
}
return Http::getData('https://api.telegram.org/bot' . $config['telegram_bot'] . '/sendMessage?' . $topik . 'chat_id=' . $chat_id . '&text=' . urlencode($txt));
}
}
@ -44,23 +47,31 @@ class Message
try {
foreach ($txts as $txt) {
self::sendSMS($config['sms_url'], $phone, $txt);
self::logMessage('SMS', $phone, $txt, 'Success');
}
} catch (Exception $e) {
} catch (Throwable $e) {
// ignore, add to logs
_log("Failed to send SMS using Mikrotik.\n" . $e->getMessage(), 'SMS', 0);
self::logMessage('SMS', $phone, $txt, 'Error', $e->getMessage());
}
} else {
try {
self::MikrotikSendSMS($config['sms_url'], $phone, $txt);
} catch (Exception $e) {
self::logMessage('MikroTikSMS', $phone, $txt, 'Success');
} catch (Throwable $e) {
// ignore, add to logs
_log("Failed to send SMS using Mikrotik.\n" . $e->getMessage(), 'SMS', 0);
self::logMessage('MikroTikSMS', $phone, $txt, 'Error', $e->getMessage());
}
}
} else {
$smsurl = str_replace('[number]', urlencode($phone), $config['sms_url']);
$smsurl = str_replace('[text]', urlencode($txt), $smsurl);
return Http::getData($smsurl);
try {
$response = Http::getData($smsurl);
self::logMessage('SMS HTTP Response', $phone, $txt, 'Success', $response);
return $response;
} catch (Throwable $e) {
self::logMessage('SMS HTTP Request', $phone, $txt, 'Error', $e->getMessage());
}
}
}
}
@ -92,15 +103,24 @@ class Message
if (empty($txt)) {
return "kosong";
}
run_hook('send_whatsapp', [$phone, $txt]); #HOOK
run_hook('send_whatsapp', [$phone, $txt]); // HOOK
if (!empty($config['wa_url'])) {
$waurl = str_replace('[number]', urlencode(Lang::phoneFormat($phone)), $config['wa_url']);
$waurl = str_replace('[text]', urlencode($txt), $waurl);
return Http::getData($waurl);
try {
$response = Http::getData($waurl);
self::logMessage('WhatsApp HTTP Response', $phone, $txt, 'Success', $response);
return $response;
} catch (Throwable $e) {
self::logMessage('WhatsApp HTTP Request', $phone, $txt, 'Error', $e->getMessage());
}
}
}
public static function sendEmail($to, $subject, $body)
public static function sendEmail($to, $subject, $body, $attachmentPath = null)
{
global $config, $PAGES_PATH, $debug_mail;
if (empty($body)) {
@ -119,18 +139,19 @@ class Message
$attr .= "Reply-To: " . $config['mail_reply_to'] . "\r\n";
}
mail($to, $subject, $body, $attr);
self::logMessage('Email', $to, $body, 'Success');
} else {
$mail = new PHPMailer();
$mail->isSMTP();
if (isset($debug_mail) && $debug_mail == 'Dev') {
$mail->SMTPDebug = SMTP::DEBUG_SERVER;
}
$mail->Host = $config['smtp_host'];
$mail->SMTPAuth = true;
$mail->Username = $config['smtp_user'];
$mail->Password = $config['smtp_pass'];
$mail->Host = $config['smtp_host'];
$mail->SMTPAuth = true;
$mail->Username = $config['smtp_user'];
$mail->Password = $config['smtp_pass'];
$mail->SMTPSecure = $config['smtp_ssltls'];
$mail->Port = $config['smtp_port'];
$mail->Port = $config['smtp_port'];
if (!empty($config['mail_from'])) {
$mail->setFrom($config['mail_from']);
}
@ -140,6 +161,10 @@ class Message
$mail->addAddress($to);
$mail->Subject = $subject;
// Attachments
if (!empty($attachmentPath)) {
$mail->addAttachment($attachmentPath);
}
if (!file_exists($PAGES_PATH . DIRECTORY_SEPARATOR . 'Email.html')) {
if (!copy($PAGES_PATH . '_template' . DIRECTORY_SEPARATOR . 'Email.html', $PAGES_PATH . DIRECTORY_SEPARATOR . 'Email.html')) {
@ -154,13 +179,17 @@ class Message
$html = str_replace('[[Company_Name]]', nl2br($config['CompanyName']), $html);
$html = str_replace('[[Body]]', nl2br($body), $html);
$mail->isHTML(true);
$mail->Body = $html;
$mail->Body = $html;
$mail->Body = $html;
} else {
$mail->isHTML(false);
$mail->Body = $body;
$mail->Body = $body;
}
if (!$mail->send()) {
_log(Lang::T("Email not sent, Mailer Error: ") . $mail->ErrorInfo);
$errorMessage = Lang::T("Email not sent, Mailer Error: ") . $mail->ErrorInfo;
self::logMessage('Email', $to, $body, 'Error', $errorMessage);
} else {
self::logMessage('Email', $to, $body, 'Success');
}
//<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">
@ -198,7 +227,7 @@ class Message
$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;
$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);
@ -279,7 +308,7 @@ class Message
public static function sendInvoice($cust, $trx)
{
global $config;
global $config, $db_pass;
$textInvoice = Lang::getNotifText('invoice_paid');
$textInvoice = str_replace('[[company_name]]', $config['CompanyName'], $textInvoice);
$textInvoice = str_replace('[[address]]', $config['address'], $textInvoice);
@ -295,7 +324,7 @@ class Message
$textInvoice = str_replace('[[payment_channel]]', trim($gc[1]), $textInvoice);
$textInvoice = str_replace('[[type]]', $trx['type'], $textInvoice);
$textInvoice = str_replace('[[plan_name]]', $trx['plan_name'], $textInvoice);
$textInvoice = str_replace('[[plan_price]]', Lang::moneyFormat($trx['price']), $textInvoice);
$textInvoice = str_replace('[[plan_price]]', Lang::moneyFormat($trx['price']), $textInvoice);
$textInvoice = str_replace('[[name]]', $cust['fullname'], $textInvoice);
$textInvoice = str_replace('[[note]]', $cust['note'], $textInvoice);
$textInvoice = str_replace('[[user_name]]', $trx['username'], $textInvoice);
@ -305,6 +334,46 @@ class Message
$textInvoice = str_replace('[[expired_date]]', Lang::dateAndTimeFormat($trx['expiration'], $trx['time']), $textInvoice);
$textInvoice = str_replace('[[footer]]', $config['note'], $textInvoice);
$inv_url = "?_route=voucher/invoice/$trx[id]/" . md5($trx['id'] . $db_pass);
$textInvoice = str_replace('[[invoice_link]]', $inv_url, $textInvoice);
// Calculate bills and additional costs
list($bills, $add_cost) = User::getBills($cust['id']);
// Initialize note and total variables
$note = "";
$total = $trx['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";
}
$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($trx['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
$textInvoice = str_replace('[[bills]]', $note, $textInvoice);
if ($config['user_notification_payment'] == 'sms') {
Message::sendSMS($cust['phonenumber'], $textInvoice);
} else if ($config['user_notification_payment'] == 'email') {
@ -317,12 +386,43 @@ class Message
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();
$user = User::find($to_customer_id);
try {
$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();
self::logMessage("Inbox", $user->username, $body, "Success");
} catch (Throwable $e) {
$errorMessage = Lang::T("Error adding message to inbox: " . $e->getMessage());
self::logMessage('Inbox', $user->username, $body, 'Error', $errorMessage);
}
}
public static function getMessageType($type, $message)
{
if (strpos($message, "<divider>") === false) {
return $message;
}
$msgs = explode("<divider>", $message);
if ($type == "PPPOE") {
return $msgs[1];
} else {
return $msgs[0];
}
}
public static function logMessage($messageType, $recipient, $messageContent, $status, $errorMessage = null)
{
$log = ORM::for_table('tbl_message_logs')->create();
$log->message_type = $messageType;
$log->recipient = $recipient;
$log->message_content = $messageContent;
$log->status = $status;
$log->error_message = $errorMessage;
$log->save();
}
}

View file

@ -46,7 +46,11 @@ class Response extends Message
/**
* The last response for a request.
*/
const TYPE_FINAL = '!done';
const TYPE_FINAL = '!done';/**
* The empty response for a request.
*/
const TYPE_EMPTY = '!empty';
/**
* A response with data.
@ -246,6 +250,7 @@ class Response extends Message
{
switch ($type) {
case self::TYPE_FINAL:
case self::TYPE_EMPTY:
case self::TYPE_DATA:
case self::TYPE_ERROR:
case self::TYPE_FATAL:

View file

@ -10,7 +10,7 @@ class Paginator
{
public static function findMany($query, $search = [], $per_page = '10', $append_url = "", $toArray = false)
{
global $routes, $ui;
global $routes, $ui, $isApi;
$adjacents = "2";
$page = _get('p', 1);
$page = (empty($page) ? 1 : $page);
@ -19,6 +19,7 @@ class Paginator
$url .= '&' . http_build_query($search);
}
$url .= $append_url.'&p=';
$url = Text::fixUrl($url);
$totalReq = $query->count();
$lastpage = ceil($totalReq / $per_page);
$lpm1 = $lastpage - 1;
@ -71,7 +72,7 @@ class Paginator
if ($ui) {
$ui->assign('paginator', $result);
}
if($toArray){
if($toArray || $isApi){
return $query->offset($startpoint)->limit($per_page)->find_array();
}else{
return $query->offset($startpoint)->limit($per_page)->find_many();

View file

@ -110,6 +110,12 @@ class Text
return $result;
}
/**
* ...$data means it can take any number of arguments.
* it can url($var1, $var2, $var3) or url($var1)
* and variable will be merge with implode
* @return string the URL with all the arguments combined.
*/
public static function url(...$data){
global $config;
$url = implode("", $data);
@ -125,4 +131,22 @@ class Text
return U . $url;
}
}
public static function fixUrl($url){
//if url dont have ? then add it with replace first & to ?
if(strpos($url, '?') === false && strpos($url, '&')!== false){
return substr($url, 0, strpos($url, '&')). '?'. substr($url, strpos($url, '&')+1);
}
return $url;
}
// this will return & or ?
public static function isQA(){
global $config;
if ($config['url_canonical'] == 'yes') {
return '?';
} else {
return '&';
}
}
}

View file

@ -29,7 +29,7 @@ class User
public static function getTawkToHash($email)
{
global $config;
if (!empty($config['tawkto_api_key']) && !Empty($email)) {
if (!empty($config['tawkto_api_key']) && !empty($email)) {
return hash_hmac('sha256', $email, $config['tawkto_api_key']);
}
return '';
@ -169,11 +169,11 @@ class User
public static function generateToken($uid, $validDays = 30)
{
global $db_pass;
if($validDays>=30){
if ($validDays >= 30) {
$time = time();
}else{
} else {
// for customer, deafult expired is 30 days
$time = strtotime('+ '.(30 - $validDays).' days');
$time = strtotime('+ ' . (30 - $validDays) . ' days');
}
return [
@ -187,7 +187,7 @@ class User
global $db_pass;
if (isset($uid)) {
$token = self::generateToken($uid);
setcookie('uid', $token['token'], time() + 86400 * 30);
setcookie('uid', $token['token'], time() + 86400 * 30, "/");
return $token;
} else {
return false;
@ -197,7 +197,7 @@ class User
public static function removeCookie()
{
if (isset($_COOKIE['uid'])) {
setcookie('uid', '', time() - 86400);
setcookie('uid', '', time() - 86400, "/");
}
}
@ -277,45 +277,53 @@ class User
return $d;
}
public static function setFormCustomField($uid = 0){
public static function setFormCustomField($uid = 0)
{
global $UPLOAD_PATH;
$fieldPath = $UPLOAD_PATH . DIRECTORY_SEPARATOR . "customer_field.json";
if(!file_exists($fieldPath)){
if (!file_exists($fieldPath)) {
return '';
}
$fields = json_decode(file_get_contents($fieldPath), true);
foreach($fields as $field){
if(!empty(_post($field['name']))){
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){
public static function getFormCustomField($ui, $register = false, $uid = 0)
{
global $UPLOAD_PATH;
$fieldPath = $UPLOAD_PATH . DIRECTORY_SEPARATOR . "customer_field.json";
if(!file_exists($fieldPath)){
if (!file_exists($fieldPath)) {
return '';
}
$fields = json_decode(file_get_contents($fieldPath), true);
$attrs = [];
if(!$register){
if (!$register) {
$attrs = self::getAttributes('', $uid);
$ui->assign('attrs', $attrs);
}
$html = '';
$ui->assign('register', $register);
foreach($fields as $field){
if($register){
if($field['register']){
foreach ($fields as $field) {
if ($register) {
if ($field['register']) {
$ui->assign('field', $field);
$html .= $ui->fetch('customer/custom_field.tpl');
}
}else{
} else {
$ui->assign('field', $field);
$html .= $ui->fetch('customer/custom_field.tpl');
}
}
return $html;
}
public static function find($id)
{
return ORM::for_table('tbl_customers')->find_one($id);
}
}

View file

@ -0,0 +1,50 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
/**
* Validator class
*/
class Widget
{
public static function rows($rows, $result){
$result .= '<div class="row">';
foreach($rows as $row){
}
$result .= '</div>';
}
public static function columns($cols, $result){
$c = count($cols);
switch($c){
case 1:
$result .= '<div class="col-md-12">';
break;
case 2:
$result .= '<div class="col-md-6">';
break;
case 3:
$result .= '<div class="col-md-4">';
break;
case 4:
$result .= '<div class="col-md-4">';
break;
case 5:
$result .= '<div class="col-md-4">';
break;
default:
$result .= '<div class="col-md-1">';
break;
}
foreach($cols as $col){
}
$result .= '</div>';
}
}

View file

@ -67,21 +67,25 @@ if (isset($_SESSION['notify'])) {
unset($_SESSION['ntype']);
}
if(!isset($_GET['_route'])) {
$req = ltrim(parse_url($_SERVER['REQUEST_URI'])['path'], '/');
}else{
if (!isset($_GET['_route'])) {
$req = ltrim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
$len = strlen(ltrim(parse_url(APP_URL, PHP_URL_PATH), '/'));
if ($len > 0) {
$req = ltrim(substr($req, $len), '/');
}
} else {
// Routing Engine
$req = _get('_route');
}
$routes = explode('/', $req);
$ui->assign('_routes', $routes);
$handler = $routes[0];
if ($handler == '') {
$handler = 'default';
}
try {
if(!empty($_GET['uid'])){
if (!empty($_GET['uid'])) {
$_COOKIE['uid'] = $_GET['uid'];
}
$admin = Admin::_info();
@ -124,11 +128,15 @@ try {
unset($menus, $menu_registered);
include($sys_render);
} else {
// header 404
header("HTTP/1.0 404 Not Found");
header("Content-Type: text/html; charset=utf-8");
echo "404 Not Found";
die();
if( empty($_SERVER["HTTP_SEC_FETCH_DEST"]) || $_SERVER["HTTP_SEC_FETCH_DEST"] != 'document' ){
// header 404
header("HTTP/1.0 404 Not Found");
header("Content-Type: text/html; charset=utf-8");
echo "404 Not Found";
die();
}else{
r2(getUrl('login'));
}
}
} catch (Throwable $e) {
Message::sendTelegram(
@ -137,11 +145,12 @@ try {
$e->getTraceAsString()
);
if (empty($_SESSION['aid'])) {
$ui->display('customer/error.tpl'); die();
$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('error.tpl');
$ui->display('admin/error.tpl');
die();
} catch (Exception $e) {
Message::sendTelegram(
@ -150,10 +159,11 @@ try {
$e->getTraceAsString()
);
if (empty($_SESSION['aid'])) {
$ui->display('customer/error.tpl'); die();
$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('error.tpl');
$ui->display('admin/error.tpl');
die();
}

View file

@ -420,5 +420,5 @@ switch ($action) {
break;
default:
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View file

@ -63,6 +63,6 @@ switch ($do) {
run_hook('view_login'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('admin-login.tpl');
$ui->display('admin/admin/login.tpl');
break;
}

View file

@ -26,7 +26,7 @@ switch ($action) {
}
$ui->assign('routers', $routers);
$ui->assign('d', $d);
$ui->display('autoload-pool.tpl');
$ui->display('admin/autoload/pool.tpl');
break;
case 'bw_name':
$bw = ORM::for_table('tbl_bandwidth')->select("name_bw")->find_one($routes['2']);
@ -44,7 +44,7 @@ switch ($action) {
$d = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
$ui->assign('d', $d);
$ui->display('autoload-server.tpl');
$ui->display('admin/autoload/server.tpl');
break;
case 'pppoe_ip_used':
if (!empty(_get('ip'))) {
@ -100,7 +100,7 @@ switch ($action) {
}
$ui->assign('d', $d);
$ui->display('autoload.tpl');
$ui->display('admin/autoload/plan.tpl');
break;
case 'customer_is_active':
if ($config['check_customer_online'] == 'yes') {
@ -181,5 +181,5 @@ switch ($action) {
echo json_encode(['results' => $json]);
die();
default:
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View file

@ -79,5 +79,5 @@ switch ($action) {
}
die();
default:
$ui->display('404.tpl');
die();
}

View file

@ -18,7 +18,6 @@ if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
switch ($action) {
case 'list':
$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 != '') {
@ -30,7 +29,7 @@ switch ($action) {
}
$ui->assign('d', $d);
$ui->display('bandwidth.tpl');
$ui->display('admin/bandwidth/list.tpl');
break;
case 'add':
@ -38,7 +37,7 @@ switch ($action) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
run_hook('view_add_bandwidth'); #HOOK
$ui->display('bandwidth-add.tpl');
$ui->display('admin/bandwidth/add.tpl');
break;
case 'edit':
@ -51,7 +50,7 @@ switch ($action) {
if ($d) {
$ui->assign('burst', explode(" ", $d['burst']));
$ui->assign('d', $d);
$ui->display('bandwidth-edit.tpl');
$ui->display('admin/bandwidth/edit.tpl');
} else {
r2(getUrl('bandwidth/list'), 'e', Lang::T('Account Not Found'));
}
@ -186,5 +185,5 @@ switch ($action) {
break;
default:
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View file

@ -19,8 +19,8 @@ switch ($action) {
$ui->assign('masters', $masters);
$ui->assign('devs', $devs);
$ui->display('community-rollback.tpl');
$ui->display('admin/rollback.tpl');
break;
default:
$ui->display('community.tpl');
$ui->display('admin/community.tpl');
}

View file

@ -22,7 +22,7 @@ switch ($action) {
}
$ui->assign('_title', Lang::T('Add Coupon'));
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('coupons-add.tpl');
$ui->display('admin/coupons/add.tpl');
break;
case 'add-post':
@ -131,7 +131,7 @@ switch ($action) {
$ui->assign('coupon', $coupon);
$ui->assign('_title', Lang::T('Edit Coupon: ' . $coupon['code']));
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('coupons-edit.tpl');
$ui->display('admin/coupons/edit.tpl');
break;
case 'edit-post':
@ -310,6 +310,6 @@ switch ($action) {
$coupons = Paginator::findMany($couponsData, ['search' => $search], 5, '');
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->assign('coupons', $coupons);
$ui->display('coupons.tpl');
$ui->display('admin/coupons/list.tpl');
break;
}

View file

@ -158,7 +158,7 @@ switch ($action) {
$ui->assign('xheader', $leafletpickerHeader);
run_hook('view_add_customer'); #HOOK
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('customers-add.tpl');
$ui->display('admin/customers/add.tpl');
break;
case 'recharge':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
@ -176,6 +176,10 @@ switch ($action) {
$channel = $admin['fullname'];
$cust = User::_info($id_customer);
$plan = ORM::for_table('tbl_plans')->find_one($b['plan_id']);
$add_inv = User::getAttribute("Invoice", $id_customer);
if (!empty($add_inv)) {
$plan['price'] = $add_inv;
}
$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;
@ -224,8 +228,9 @@ switch ($action) {
$ui->assign('channel', $channel);
$ui->assign('server', $b['routers']);
$ui->assign('plan', $plan);
$ui->assign('add_inv', $add_inv);
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('recharge-confirm.tpl');
$ui->display('admin/plan/recharge-confirm.tpl');
} else {
r2(getUrl('customers/view/') . $id_customer, 'e', 'Cannot find active plan');
}
@ -364,7 +369,7 @@ switch ($action) {
$ui->assign('customFields', $customFields);
$ui->assign('xheader', $leafletpickerHeader);
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('customers-view.tpl');
$ui->display('admin/customers/view.tpl');
} else {
r2(getUrl('customers/list'), 'e', Lang::T('Account Not Found'));
}
@ -403,7 +408,7 @@ switch ($action) {
$ui->assign('customFields', $customFields);
$ui->assign('xheader', $leafletpickerHeader);
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('customers-edit.tpl');
$ui->display('admin/customers/edit.tpl');
} else {
r2(getUrl('customers/list'), 'e', Lang::T('Account Not Found'));
}
@ -908,6 +913,6 @@ switch ($action) {
$ui->assign('order_pos', $order_pos[$order]);
$ui->assign('orderby', $orderby);
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('customers.tpl');
$ui->display('admin/customers/list.tpl');
break;
}

View file

@ -48,6 +48,6 @@ switch ($action) {
$fields = json_decode(file_get_contents($fieldPath), true);
}
$ui->assign('fields', $fields);
$ui->display('customfield.tpl');
$ui->display('admin/settings/customfield.tpl');
break;
}

View file

@ -1,233 +1,66 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
_admin();
$ui->assign('_title', Lang::T('Dashboard'));
$ui->assign('_admin', $admin);
if (isset($_GET['refresh'])) {
$files = scandir($CACHE_PATH);
foreach ($files as $file) {
$ext = pathinfo($file, PATHINFO_EXTENSION);
if (is_file($CACHE_PATH . DIRECTORY_SEPARATOR . $file) && $ext == 'temp') {
unlink($CACHE_PATH . DIRECTORY_SEPARATOR . $file);
}
}
r2(getUrl('dashboard'), 's', 'Data Refreshed');
}
$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"));
}
$current_date = date('Y-m-d');
$month_n = date('n');
$iday = ORM::for_table('tbl_transactions')
->where('recharged_on', $current_date)
->where_not_equal('method', 'Customer - Balance')
->where_not_equal('method', 'Recharge Balance - Administrator')
->sum('price');
if ($iday == '') {
$iday = '0.00';
}
$ui->assign('iday', $iday);
$imonth = ORM::for_table('tbl_transactions')
->where_not_equal('method', 'Customer - Balance')
->where_not_equal('method', 'Recharge Balance - Administrator')
->where_gte('recharged_on', $start_date)
->where_lte('recharged_on', $current_date)->sum('price');
if ($imonth == '') {
$imonth = '0.00';
}
$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';
}
$ui->assign('u_act', $u_act);
$u_all = ORM::for_table('tbl_user_recharges')->count();
if (empty($u_all)) {
$u_all = '0';
}
$ui->assign('u_all', $u_all);
$c_all = ORM::for_table('tbl_customers')->count();
if (empty($c_all)) {
$c_all = '0';
}
$ui->assign('c_all', $c_all);
if ($config['hide_uet'] != 'yes') {
//user expire
$query = ORM::for_table('tbl_user_recharges')
->where_lte('expiration', $current_date)
->order_by_desc('expiration');
$expire = Paginator::findMany($query);
// Get the total count of expired records for pagination
$totalCount = ORM::for_table('tbl_user_recharges')
->where_lte('expiration', $current_date)
->count();
// Pass the total count and current page to the paginator
$paginator['total_count'] = $totalCount;
// Assign the pagination HTML to the template variable
$ui->assign('expire', $expire);
}
//activity log
$dlog = ORM::for_table('tbl_logs')->limit(5)->order_by_desc('id')->find_many();
$ui->assign('dlog', $dlog);
$log = ORM::for_table('tbl_logs')->count();
$ui->assign('log', $log);
if ($config['hide_vs'] != 'yes') {
$cacheStocksfile = $CACHE_PATH . File::pathFixer('/VoucherStocks.temp');
$cachePlanfile = $CACHE_PATH . File::pathFixer('/VoucherPlans.temp');
//Cache for 5 minutes
if (file_exists($cacheStocksfile) && time() - filemtime($cacheStocksfile) < 600) {
$stocks = json_decode(file_get_contents($cacheStocksfile), true);
$plans = json_decode(file_get_contents($cachePlanfile), true);
} else {
// Count stock
$tmp = $v = ORM::for_table('tbl_plans')->select('id')->select('name_plan')->find_many();
$plans = array();
$stocks = array("used" => 0, "unused" => 0);
$n = 0;
foreach ($tmp as $plan) {
$unused = ORM::for_table('tbl_voucher')
->where('id_plan', $plan['id'])
->where('status', 0)->count();
$used = ORM::for_table('tbl_voucher')
->where('id_plan', $plan['id'])
->where('status', 1)->count();
if ($unused > 0 || $used > 0) {
$plans[$n]['name_plan'] = $plan['name_plan'];
$plans[$n]['unused'] = $unused;
$plans[$n]['used'] = $used;
$stocks["unused"] += $unused;
$stocks["used"] += $used;
$n++;
}
}
file_put_contents($cacheStocksfile, json_encode($stocks));
file_put_contents($cachePlanfile, json_encode($plans));
}
}
$cacheMRfile = File::pathFixer('/monthlyRegistered.temp');
//Cache for 1 hour
if (file_exists($cacheMRfile) && time() - filemtime($cacheMRfile) < 3600) {
$monthlyRegistered = json_decode(file_get_contents($cacheMRfile), true);
} else {
//Monthly Registered Customers
$result = ORM::for_table('tbl_customers')
->select_expr('MONTH(created_at)', 'month')
->select_expr('COUNT(*)', 'count')
->where_raw('YEAR(created_at) = YEAR(NOW())')
->group_by_expr('MONTH(created_at)')
->find_many();
$monthlyRegistered = [];
foreach ($result as $row) {
$monthlyRegistered[] = [
'date' => $row->month,
'count' => $row->count
];
}
file_put_contents($cacheMRfile, json_encode($monthlyRegistered));
}
$cacheMSfile = $CACHE_PATH . File::pathFixer('/monthlySales.temp');
//Cache for 12 hours
if (file_exists($cacheMSfile) && time() - filemtime($cacheMSfile) < 43200) {
$monthlySales = json_decode(file_get_contents($cacheMSfile), true);
} else {
// Query to retrieve monthly data
$results = ORM::for_table('tbl_transactions')
->select_expr('MONTH(recharged_on)', 'month')
->select_expr('SUM(price)', 'total')
->where_raw("YEAR(recharged_on) = YEAR(CURRENT_DATE())") // Filter by the current year
->where_not_equal('method', 'Customer - Balance')
->where_not_equal('method', 'Recharge Balance - Administrator')
->group_by_expr('MONTH(recharged_on)')
->find_many();
// Create an array to hold the monthly sales data
$monthlySales = array();
// Iterate over the results and populate the array
foreach ($results as $result) {
$month = $result->month;
$totalSales = $result->total;
$monthlySales[$month] = array(
'month' => $month,
'totalSales' => $totalSales
);
}
// Fill in missing months with zero sales
for ($month = 1; $month <= 12; $month++) {
if (!isset($monthlySales[$month])) {
$monthlySales[$month] = array(
'month' => $month,
'totalSales' => 0
);
}
}
// Sort the array by month
ksort($monthlySales);
// Reindex the array
$monthlySales = array_values($monthlySales);
file_put_contents($cacheMSfile, json_encode($monthlySales));
}
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);
$ui->assign('monthlySales', $monthlySales);
$ui->assign('xfooter', '');
$ui->assign('monthlyRegistered', $monthlyRegistered);
$ui->assign('stocks', $stocks);
$ui->assign('plans', $plans);
run_hook('view_dashboard'); #HOOK
$ui->display('dashboard.tpl');
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
_admin();
$ui->assign('_title', Lang::T('Dashboard'));
$ui->assign('_admin', $admin);
if (isset($_GET['refresh'])) {
$files = scandir($CACHE_PATH);
foreach ($files as $file) {
$ext = pathinfo($file, PATHINFO_EXTENSION);
if (is_file($CACHE_PATH . DIRECTORY_SEPARATOR . $file) && $ext == 'temp') {
unlink($CACHE_PATH . DIRECTORY_SEPARATOR . $file);
}
}
r2(getUrl('dashboard'), 's', 'Data Refreshed');
}
$tipeUser = _req("user");
if (empty($tipeUser)) {
$tipeUser = 'Admin';
}
$ui->assign('tipeUser', $tipeUser);
$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"));
}
$current_date = date('Y-m-d');
$ui->assign('start_date', $start_date);
$ui->assign('current_date', $current_date);
$tipeUser = $admin['user_type'];
if (in_array($tipeUser, ['SuperAdmin', 'Admin'])) {
$tipeUser = 'Admin';
}
$widgets = ORM::for_table('tbl_widgets')->where("enabled", 1)->where('user', $tipeUser)->order_by_asc("orders")->findArray();
$count = count($widgets);
for ($i = 0; $i < $count; $i++) {
try{
if(file_exists($WIDGET_PATH . DIRECTORY_SEPARATOR . $widgets[$i]['widget'].".php")){
require_once $WIDGET_PATH . DIRECTORY_SEPARATOR . $widgets[$i]['widget'].".php";
$widgets[$i]['content'] = (new $widgets[$i]['widget'])->getWidget($widgets[$i]);
}else{
$widgets[$i]['content'] = "Widget not found";
}
} catch (Throwable $e) {
$widgets[$i]['content'] = $e->getMessage();
}
}
$ui->assign('widgets', $widgets);
run_hook('view_dashboard'); #HOOK
$ui->display('admin/dashboard.tpl');

View file

@ -5,9 +5,13 @@
**/
if(Admin::getID()){
r2(getUrl('dashboard'));
}if(User::getID()){
r2(getUrl('home'));
//r2(getUrl('dashboard'));
$handler = 'dashboard';
}else if(User::getID()){
//r2(getUrl('home'));
$handler = 'home';
}else{
r2(getUrl('login'));
//r2(getUrl('login'));
$handler = 'login';
}
include($root_path . File::pathFixer('system/controllers/' . $handler . '.php'));

View file

@ -83,7 +83,7 @@ switch ($action) {
$ui->assign('mdate', $mdate);
$ui->assign('recharged_on', $mdate);
run_hook('print_by_date'); #HOOK
$ui->display('print-by-date.tpl');
$ui->display('admin/print/by-date.tpl');
break;
case 'pdf-by-date':
@ -281,7 +281,7 @@ EOF;
$ui->assign('tdate', $tdate);
$ui->assign('stype', $stype);
run_hook('print_by_period'); #HOOK
$ui->display('print-by-period.tpl');
$ui->display('admin/print/by-period.tpl');
break;
@ -427,5 +427,5 @@ EOF;
break;
default:
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View file

@ -95,8 +95,7 @@ if (_post('send') == 'balance') {
}
r2(getUrl('home'), 'w', Lang::T('Your friend do not have active package'));
}
$_bill = User::_billing();
$ui->assign('_bills', $_bill);
// Sync plan to router
if (isset($_GET['sync']) && !empty($_GET['sync'])) {
@ -147,7 +146,7 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
$routers = ORM::for_table('tbl_routers')->where('name', $bill['routers'])->find_one();
$router = $routers['id'];
}
r2(getUrl('order/gateway/$router/$bill[plan_id]'));
r2(getUrl("order/gateway/$router/$bill[plan_id]"));
}
} else if (!empty(_get('extend'))) {
if ($user['status'] != 'Active') {
@ -197,8 +196,8 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
$tur->save();
App::setToken(_get('stoken'), $id);
file_put_contents($path, $m);
_log("Customer $tur[customer_id] $tur[username] extend for $days days", "Customer", $user['id']);
Message::sendTelegram("#u$user[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
_log("Customer $tur[customer_id] $user[fullname] ($tur[username]) extend for $days days", "Customer", $user['id']);
Message::sendTelegram("#u$user[username] ($user[fullname]) #id$tur[customer_id] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
"\nLocation: " . $p['routers'] .
"\nCustomer: " . $user['fullname'] .
"\nNew Expired: " . Lang::dateAndTimeFormat($expiration, $tur['time']));
@ -310,46 +309,25 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSI
}
}
$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();
// check expired payments
if ($unpaid) {
try {
if (strtotime($unpaid['expired_date']) < time()) {
$unpaid->status = 4;
$unpaid->save();
$unpaid = [];
$widgets = ORM::for_table('tbl_widgets')->where("enabled", 1)->where('user', 'Customer')->order_by_asc("orders")->findArray();
$count = count($widgets);
for ($i = 0; $i < $count; $i++) {
try{
if(file_exists($WIDGET_PATH . DIRECTORY_SEPARATOR . 'customer' . DIRECTORY_SEPARATOR . $widgets[$i]['widget'].".php")){
require_once $WIDGET_PATH . DIRECTORY_SEPARATOR . 'customer' . DIRECTORY_SEPARATOR . $widgets[$i]['widget'].".php";
$widgets[$i]['content'] = (new $widgets[$i]['widget'])->getWidget($widgets[$i]);
}else{
$widgets[$i]['content'] = "Widget not found";
}
} 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) {
$widgets[$i]['content'] = $e->getMessage();
}
}
$ui->assign('unpaid', $unpaid);
$ui->assign('code', alphanumeric(_get('code'), "-"));
$ui->assign('widgets', $widgets);
$abills = User::getAttributes("Bill");
$ui->assign('abills', $abills);
$ui->assign('code', alphanumeric(_get('code'), "-"));
run_hook('view_customer_dashboard'); #HOOK
$ui->display('customer/dashboard.tpl');

View file

@ -80,6 +80,39 @@ switch ($action) {
}
break;
case 'message-csv':
$logs = ORM::for_table('tbl_message_logs')
->select('id')
->select('message_type')
->select('recipient')
->select('message_content')
->select('status')
->select('error_message')
->select('sent_at')
->order_by_asc('id')->find_array();
$h = false;
set_time_limit(-1);
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-type: text/csv");
header('Content-Disposition: attachment;filename="message-logs_' . date('Y-m-d_H_i') . '.csv"');
header('Content-Transfer-Encoding: binary');
foreach ($logs as $log) {
$ks = [];
$vs = [];
foreach ($log as $k => $v) {
$ks[] = $k;
$vs[] = $v;
}
if (!$h) {
echo '"' . implode('";"', $ks) . "\"\n";
$h = true;
}
echo '"' . implode('";"', $vs) . "\"\n";
}
break;
case 'list':
$q = (_post('q') ? _post('q') : _get('q'));
$keep = _post('keep');
@ -97,7 +130,7 @@ switch ($action) {
$ui->assign('d', $d);
$ui->assign('q', $q);
$ui->display('logs.tpl');
$ui->display('admin/logs/system.tpl');
break;
case 'radius':
$q = (_post('q') ? _post('q') : _get('q'));
@ -116,9 +149,36 @@ switch ($action) {
$ui->assign('d', $d);
$ui->assign('q', $q);
$ui->display('logs-radius.tpl');
$ui->display('admin/logs/radius.tpl');
break;
case 'message':
$q = _post('q') ?: _get('q');
$keep = (int) _post('keep');
if (!empty($keep)) {
ORM::raw_execute("DELETE FROM tbl_message_logs WHERE UNIX_TIMESTAMP(sent_at) < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
r2(getUrl('logs/message/'), 's', "Deleted logs older than $keep days");
}
if ($q !== null && $q !== '') {
$query = ORM::for_table('tbl_message_logs')
->whereRaw("message_type LIKE '%$q%' OR recipient LIKE '%$q%' OR message_content LIKE '%$q%' OR status LIKE '%$q%' OR error_message LIKE '%$q%'")
->order_by_desc('sent_at');
$d = Paginator::findMany($query, ['q' => $q]);
} else {
$query = ORM::for_table('tbl_message_logs')->order_by_desc('sent_at');
$d = Paginator::findMany($query);
}
if ($d) {
$ui->assign('d', $d);
} else {
$ui->assign('d', []);
}
$ui->assign('q', $q);
$ui->display('admin/logs/message.tpl');
break;
default:
r2(getUrl('logs/list/'), 's', '');

View file

@ -15,6 +15,9 @@ if (empty($action)) {
$action = 'customer';
}
$ui->assign('xheader', '<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css">');
$ui->assign('xfooter', '<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>');
switch ($action) {
case 'customer':
if(!empty(_req('search'))){
@ -42,12 +45,22 @@ switch ($action) {
}
$ui->assign('search', $search);
$ui->assign('customers', $customerData);
$ui->assign('xheader', '<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css">');
$ui->assign('_title', Lang::T('Customer Geo Location Information'));
$ui->assign('xfooter', '<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>');
$ui->display('customers-map.tpl');
$ui->display('admin/maps/customers.tpl');
break;
case 'routers':
$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->where_like('name', '%' . $name . '%');
}
$d = Paginator::findMany($query, ['name' => $name], '20', '', true);
$ui->assign('name', $name);
$ui->assign('d', $d);
$ui->assign('_title', Lang::T('Routers Geo Location Information'));
$ui->display('admin/maps/routers.tpl');
break;
default:
r2(getUrl('map/customer'), 'e', 'action not defined');
break;

View file

@ -22,6 +22,8 @@ switch ($action) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$appUrl = APP_URL;
$select2_customer = <<<EOT
<script>
document.addEventListener("DOMContentLoaded", function(event) {
@ -30,9 +32,9 @@ document.addEventListener("DOMContentLoaded", function(event) {
ajax: {
url: function(params) {
if(params.term != undefined){
return './?_route=autoload/customer_select2&s='+params.term;
return '{$appUrl}/?_route=autoload/customer_select2&s='+params.term;
}else{
return './?_route=autoload/customer_select2';
return '{$appUrl}/?_route=autoload/customer_select2';
}
}
}
@ -46,7 +48,7 @@ EOT;
$id = $routes['2'];
$ui->assign('id', $id);
$ui->assign('xfooter', $select2_customer);
$ui->display('message.tpl');
$ui->display('admin/message/single.tpl');
break;
case 'send-post':
@ -72,6 +74,22 @@ EOT;
$message = str_replace('[[user_name]]', $c['username'], $message);
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
if (strpos($message, '[[payment_link]]') !== false) {
// token only valid for 1 day, for security reason
$token = User::generateToken($c['id'], 1);
if (!empty($token['token'])) {
$tur = ORM::for_table('tbl_user_recharges')
->where('customer_id', $c['id'])
//->where('namebp', $package)
->find_one();
if ($tur) {
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
$message = str_replace('[[payment_link]]', $url, $message);
}
} else {
$message = str_replace('[[payment_link]]', '', $message);
}
}
//Send the message
@ -96,143 +114,318 @@ EOT;
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
// Get form data
$group = $_POST['group'];
$message = $_POST['message'];
$via = $_POST['via'];
$test = isset($_POST['test']) && $_POST['test'] === 'on' ? 'yes' : 'no';
$batch = $_POST['batch'];
$delay = $_POST['delay'];
$ui->assign('routers', ORM::forTable('tbl_routers')->where('enabled', '1')->find_many());
$ui->display('admin/message/bulk.tpl');
break;
// Initialize counters
case 'send_bulk_ajax':
// Check user permissions
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
die(json_encode(['status' => 'error', 'message' => 'Permission denied']));
}
set_time_limit(0);
// Get request parameters
$group = $_REQUEST['group'] ?? '';
$message = $_REQUEST['message'] ?? '';
$via = $_REQUEST['via'] ?? '';
$batch = $_REQUEST['batch'] ?? 100;
$page = $_REQUEST['page'] ?? 0;
$router = $_REQUEST['router'] ?? null;
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on' ? true : false;
$service = $_REQUEST['service'] ?? '';
if (empty($group) || empty($message) || empty($via) || empty($service)) {
die(json_encode(['status' => 'error', 'message' => 'All fields are required']));
}
// Get batch of customers based on group
$startpoint = $page * $batch;
$customers = [];
$totalCustomers = 0;
if (isset($router) && !empty($router)) {
switch ($router) {
case 'radius':
$routerName = 'Radius';
break;
default:
$router = ORM::for_table('tbl_routers')->find_one($router);
if (!$router) {
die(json_encode(['status' => 'error', 'message' => 'Invalid router']));
}
$routerName = $router->name;
break;
}
}
if (isset($router) && !empty($router)) {
$query = ORM::for_table('tbl_user_recharges')
->left_outer_join('tbl_customers', 'tbl_user_recharges.customer_id = tbl_customers.id')
->where('tbl_user_recharges.routers', $routerName);
switch ($service) {
case 'all':
break;
default:
$validServices = ['PPPoE', 'Hotspot', 'VPN'];
if (in_array($service, $validServices)) {
$query->where('type', $service);
}
break;
}
$totalCustomers = $query->count();
$query->offset($startpoint)
->limit($batch);
switch ($group) {
case 'all':
break;
case 'new':
$query->where_raw("DATE(recharged_on) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)");
break;
case 'expired':
$query->where('tbl_user_recharges.status', 'off');
break;
case 'active':
$query->where('tbl_user_recharges.status', 'on');
break;
}
// Fetch the customers
$query->selects([
['tbl_customers.phonenumber', 'phonenumber'],
['tbl_user_recharges.customer_id', 'customer_id'],
['tbl_customers.fullname', 'fullname'],
]);
$customers = $query->find_array();
} else {
switch ($group) {
case 'all':
$totalCustomersQuery = ORM::for_table('tbl_customers');
switch ($service) {
case 'all':
break;
default:
$validServices = ['PPPoE', 'Hotspot', 'VPN'];
if (in_array($service, $validServices)) {
$totalCustomersQuery->where('service_type', $service);
}
break;
}
$totalCustomers = $totalCustomersQuery->count();
$customers = $totalCustomersQuery->offset($startpoint)->limit($batch)->find_array();
break;
case 'new':
$totalCustomersQuery = ORM::for_table('tbl_customers')
->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)");
switch ($service) {
case 'all':
break;
default:
$validServices = ['PPPoE', 'Hotspot', 'VPN'];
if (in_array($service, $validServices)) {
$totalCustomersQuery->where('service_type', $service);
}
break;
}
$totalCustomers = $totalCustomersQuery->count();
$customers = $totalCustomersQuery->offset($startpoint)->limit($batch)->find_array();
break;
case 'expired':
$totalCustomersQuery = ORM::for_table('tbl_user_recharges')
->where('status', 'off');
switch ($service) {
case 'all':
break;
default:
$validServices = ['PPPoE', 'Hotspot', 'VPN'];
if (in_array($service, $validServices)) {
$totalCustomersQuery->where('type', $service);
}
break;
}
$totalCustomers = $totalCustomersQuery->count();
$customers = $totalCustomersQuery->select('customer_id')->offset($startpoint)->limit($batch)->find_array();
break;
case 'active':
$totalCustomersQuery = ORM::for_table('tbl_user_recharges')
->where('status', 'on');
switch ($service) {
case 'all':
break;
default:
$validServices = ['PPPoE', 'Hotspot', 'VPN'];
if (in_array($service, $validServices)) {
$totalCustomersQuery->where('type', $service);
}
break;
}
$totalCustomers = $totalCustomersQuery->count();
$customers = $totalCustomersQuery->select('customer_id')->offset($startpoint)->limit($batch)->find_array(); // Get customer data
break;
}
}
// Ensure $customers is always an array
if (!$customers) {
$customers = [];
}
// Send messages
$totalSMSSent = 0;
$totalSMSFailed = 0;
$totalWhatsappSent = 0;
$totalWhatsappFailed = 0;
$batchStatus = [];
if (_req('send') == 'now') {
// Check if fields are empty
if ($group == '' || $message == '' || $via == '') {
r2(getUrl('message/send_bulk'), 'e', Lang::T('All fields are required'));
foreach ($customers as $customer) {
$currentMessage = str_replace(
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
$message
);
$phoneNumber = preg_replace('/\D/', '', $customer['phonenumber']);
if (empty($phoneNumber)) {
$batchStatus[] = [
'name' => $customer['fullname'],
'phone' => '',
'status' => 'No Phone Number'
];
continue;
}
if ($test) {
$batchStatus[] = [
'name' => $customer['fullname'],
'phone' => $customer['phonenumber'],
'status' => 'Test Mode',
'message' => $currentMessage,
'service' => $service,
'router' => $routerName,
];
} else {
// Get customer details from the database based on the selected group
if ($group == 'all') {
$customers = ORM::for_table('tbl_customers')->find_many()->as_array();
} elseif ($group == 'new') {
// Get customers created just a month ago
$customers = ORM::for_table('tbl_customers')->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)")->find_many()->as_array();
} elseif ($group == 'expired') {
// Get expired user recharges where status is 'off'
$expired = ORM::for_table('tbl_user_recharges')->where('status', 'off')->find_many();
$customer_ids = [];
foreach ($expired as $recharge) {
$customer_ids[] = $recharge->customer_id;
if ($via == 'sms' || $via == 'both') {
if (Message::sendSMS($customer['phonenumber'], $currentMessage)) {
$totalSMSSent++;
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Sent', 'message' => $currentMessage];
} else {
$totalSMSFailed++;
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Failed', 'message' => $currentMessage];
}
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_many()->as_array();
} elseif ($group == 'active') {
// Get active user recharges where status is 'on'
$active = ORM::for_table('tbl_user_recharges')->where('status', 'on')->find_many();
$customer_ids = [];
foreach ($active as $recharge) {
$customer_ids[] = $recharge->customer_id;
}
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_many()->as_array();
}
// Set the batch size
$batchSize = $batch;
// Calculate the number of batches
$totalCustomers = count($customers);
$totalBatches = ceil($totalCustomers / $batchSize);
// Loop through batches
for ($batchIndex = 0; $batchIndex < $totalBatches; $batchIndex++) {
// Get the starting and ending index for the current batch
$start = $batchIndex * $batchSize;
$end = min(($batchIndex + 1) * $batchSize, $totalCustomers);
$batchCustomers = array_slice($customers, $start, $end - $start);
// Loop through customers in the current batch and send messages
foreach ($batchCustomers as $customer) {
// Create a copy of the original message for each customer and save it as currentMessage
$currentMessage = $message;
$currentMessage = str_replace('[[name]]', $customer['fullname'], $currentMessage);
$currentMessage = str_replace('[[user_name]]', $customer['username'], $currentMessage);
$currentMessage = str_replace('[[phone]]', $customer['phonenumber'], $currentMessage);
$currentMessage = str_replace('[[company_name]]', $config['CompanyName'], $currentMessage);
// Send the message based on the selected method
if ($test === 'yes') {
// Only for testing, do not send messages to customers
$batchStatus[] = [
'name' => $customer['fullname'],
'phone' => $customer['phonenumber'],
'message' => $currentMessage,
'status' => 'Test Mode - Message not sent'
];
} else {
// Send the actual messages
if ($via == 'sms' || $via == 'both') {
$smsSent = Message::sendSMS($customer['phonenumber'], $currentMessage);
if ($smsSent) {
$totalSMSSent++;
$batchStatus[] = [
'name' => $customer['fullname'],
'phone' => $customer['phonenumber'],
'message' => $currentMessage,
'status' => 'SMS Message Sent'
];
} else {
$totalSMSFailed++;
$batchStatus[] = [
'name' => $customer['fullname'],
'phone' => $customer['phonenumber'],
'message' => $currentMessage,
'status' => 'SMS Message Failed'
];
}
}
if ($via == 'wa' || $via == 'both') {
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentMessage);
if ($waSent) {
$totalWhatsappSent++;
$batchStatus[] = [
'name' => $customer['fullname'],
'phone' => $customer['phonenumber'],
'message' => $currentMessage,
'status' => 'WhatsApp Message Sent'
];
} else {
$totalWhatsappFailed++;
$batchStatus[] = [
'name' => $customer['fullname'],
'phone' => $customer['phonenumber'],
'message' => $currentMessage,
'status' => 'WhatsApp Message Failed'
];
}
}
}
}
// Introduce a delay between each batch
if ($batchIndex < $totalBatches - 1) {
sleep($delay);
if ($via == 'wa' || $via == 'both') {
if (Message::sendWhatsapp($customer['phonenumber'], $currentMessage)) {
$totalWhatsappSent++;
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
} else {
$totalWhatsappFailed++;
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
}
}
}
}
$ui->assign('batchStatus', $batchStatus);
$ui->assign('totalSMSSent', $totalSMSSent);
$ui->assign('totalSMSFailed', $totalSMSFailed);
$ui->assign('totalWhatsappSent', $totalWhatsappSent);
$ui->assign('totalWhatsappFailed', $totalWhatsappFailed);
$ui->display('message-bulk.tpl');
// Calculate if there are more customers to process
$hasMore = ($startpoint + $batch) < $totalCustomers;
// Return JSON response
echo json_encode([
'status' => 'success',
'page' => $page + 1,
'batchStatus' => $batchStatus,
'message' => $currentMessage,
'totalSent' => $totalSMSSent + $totalWhatsappSent,
'totalFailed' => $totalSMSFailed + $totalWhatsappFailed,
'hasMore' => $hasMore,
'service' => $service,
'router' => $routerName,
]);
break;
case 'send_bulk_selected':
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Set headers
header('Content-Type: application/json');
header('Cache-Control: no-cache, no-store, must-revalidate');
// Get the posted data
$customerIds = $_POST['customer_ids'] ?? [];
$via = $_POST['message_type'] ?? '';
$message = isset($_POST['message']) ? trim($_POST['message']) : '';
if (empty($customerIds) || empty($message) || empty($via)) {
echo json_encode(['status' => 'error', 'message' => Lang::T('Invalid customer IDs, Message, or Message Type.')]);
exit;
}
// Prepare to send messages
$sentCount = 0;
$failedCount = 0;
$subject = Lang::T('Notification Message');
$form = 'Admin';
foreach ($customerIds as $customerId) {
$customer = ORM::for_table('tbl_customers')->where('id', $customerId)->find_one();
if ($customer) {
$messageSent = false;
// Check the message type and send accordingly
try {
if ($via === 'sms' || $via === 'all') {
$messageSent = Message::sendSMS($customer['phonenumber'], $message);
}
if (!$messageSent && ($via === 'wa' || $via === 'all')) {
$messageSent = Message::sendWhatsapp($customer['phonenumber'], $message);
}
if (!$messageSent && ($via === 'inbox' || $via === 'all')) {
Message::addToInbox($customer['id'], $subject, $message, $form);
$messageSent = true;
}
if (!$messageSent && ($via === 'email' || $via === 'all')) {
$messageSent = Message::sendEmail($customer['email'], $subject, $message);
}
} catch (Throwable $e) {
$messageSent = false;
$failedCount++;
sendTelegram('Failed to send message to ' . $e->getMessage());
_log('Failed to send message to ' . $customer['fullname'] . ': ' . $e->getMessage());
continue;
}
if ($messageSent) {
$sentCount++;
} else {
$failedCount++;
}
} else {
$failedCount++;
}
}
// Prepare the response
echo json_encode([
'status' => 'success',
'totalSent' => $sentCount,
'totalFailed' => $failedCount
]);
} else {
header('Content-Type: application/json');
echo json_encode(['status' => 'error', 'message' => Lang::T('Invalid request method.')]);
}
break;
default:
r2(getUrl('message/send_sms'), 'e', 'action not defined');
}

View file

@ -371,7 +371,7 @@ switch ($action) {
$d->trx_invoice = $result;
$d->status = 2;
$d->save();
r2(getUrl('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 .
@ -410,6 +410,10 @@ switch ($action) {
if ($router['name'] != 'balance') {
list($bills, $add_cost) = User::getBills($id_customer);
}
$add_inv = User::getAttribute("Invoice", $id_customer);
if (!empty($add_inv)) {
$plan['price'] = $add_inv;
}
if($config['enable_coupons']){
if (!isset($_SESSION['coupon_attempts'])) {

View file

@ -60,9 +60,9 @@ if (strpos($action, "-reset") !== false) {
$ui->assign("writeable", is_writable($path));
$ui->assign("pageHeader", str_replace('_', ' ', $action));
$ui->assign("PageFile", $action);
$ui->display('page-edit.tpl');
$ui->display('admin/settings/page.tpl');
} else
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
} else {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
@ -83,5 +83,5 @@ if (strpos($action, "-reset") !== false) {
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
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View file

@ -34,7 +34,7 @@ switch ($action) {
$ui->assign('pgs', $pgs);
$ui->assign('pg', $pg);
$ui->assign('q', $q);
$ui->display('paymentgateway-audit.tpl');
$ui->display('admin/paymentgateway/audit.tpl');
break;
case 'auditview':
$pg = alphanumeric($routes[2]);
@ -43,7 +43,7 @@ switch ($action) {
$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');
$ui->display('admin/paymentgateway/audit-view.tpl');
break;
default:
if (_post('save') == 'actives') {
@ -70,13 +70,13 @@ switch ($action) {
if (function_exists($action . '_save_config')) {
call_user_func($action . '_save_config');
} else {
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}
} else {
if (function_exists($action . '_show_config')) {
call_user_func($action . '_show_config');
} else {
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}
}
} else {
@ -92,7 +92,7 @@ switch ($action) {
$ui->assign('_title', 'Payment Gateway Settings');
$ui->assign('pgs', $pgs);
$ui->assign('actives', explode(',', $config['payment_gateway']));
$ui->display('paymentgateway.tpl');
$ui->display('admin/paymentgateway/list.tpl');
}
}
}

View file

@ -12,6 +12,8 @@ $ui->assign('_system_menu', 'plan');
$action = $routes['1'];
$ui->assign('_admin', $admin);
$appUrl = APP_URL;
$select2_customer = <<<EOT
<script>
document.addEventListener("DOMContentLoaded", function(event) {
@ -20,9 +22,9 @@ document.addEventListener("DOMContentLoaded", function(event) {
ajax: {
url: function(params) {
if(params.term != undefined){
return './?_route=autoload/customer_select2&s='+params.term;
return '{$appUrl}/?_route=autoload/customer_select2&s='+params.term;
}else{
return './?_route=autoload/customer_select2';
return '{$appUrl}/?_route=autoload/customer_select2';
}
}
}
@ -30,7 +32,7 @@ document.addEventListener("DOMContentLoaded", function(event) {
});
</script>
EOT;
getUrl('docs');
switch ($action) {
case 'sync':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
@ -82,7 +84,7 @@ switch ($action) {
}
$ui->assign('usings', $usings);
run_hook('view_recharge'); #HOOK
$ui->display('recharge.tpl');
$ui->display('admin/plan/recharge.tpl');
break;
case 'recharge-confirm':
@ -105,11 +107,15 @@ switch ($action) {
$cust = User::_info($id_customer);
$plan = ORM::for_table('tbl_plans')->find_one($planId);
list($bills, $add_cost) = User::getBills($id_customer);
$add_inv = User::getAttribute("Invoice", $id_customer);
if (!empty($add_inv)) {
$plan['price'] = $add_inv;
}
// 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;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float) $config['custom_tax_rate'] : null;
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
@ -158,7 +164,8 @@ switch ($action) {
$ui->assign('server', $server);
$ui->assign('using', $using);
$ui->assign('plan', $plan);
$ui->display('recharge-confirm.tpl');
$ui->assign('add_inv', $add_inv);
$ui->display('admin/plan/recharge-confirm.tpl');
} else {
r2(getUrl('plan/recharge'), 'e', $msg);
}
@ -180,7 +187,7 @@ switch ($action) {
$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');
$ui->display('admin/plan/invoice.tpl');
die();
}
@ -198,7 +205,7 @@ switch ($action) {
// 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;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float) $config['custom_tax_rate'] : null;
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
@ -239,7 +246,7 @@ switch ($action) {
$in = ORM::for_table('tbl_transactions')->where('username', $cust['username'])->order_by_desc('id')->find_one();
Package::createInvoice($in);
App::setVoucher($svoucher, $cust['username']);
$ui->display('invoice.tpl');
$ui->display('admin/plan/invoice.tpl');
_log('[' . $admin['username'] . ']: ' . 'Recharge ' . $cust['username'] . ' [' . $in['plan_name'] . '][' . Lang::moneyFormat($in['price']) . ']', $admin['user_type'], $admin['id']);
} else {
r2(getUrl('plan/recharge'), 'e', "Failed to recharge account");
@ -262,8 +269,21 @@ switch ($action) {
r2(getUrl('plan/view/') . $id, 'd', "Customer not found");
}
Package::createInvoice($in);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$logo = '';
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) {
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png';
$imgsize = getimagesize($logo);
$width = $imgsize[0];
$height = $imgsize[1];
$ui->assign('wlogo', $width);
$ui->assign('hlogo', $height);
}
$ui->assign('public_url', getUrl("voucher/invoice/$id/".md5($id. $db_pass)));
$ui->assign('logo', $logo);
$ui->assign('_title', 'View Invoice');
$ui->display('invoice.tpl');
$ui->display('admin/plan/invoice.tpl');
break;
@ -285,14 +305,14 @@ switch ($action) {
$ui->assign('date', Lang::dateAndTimeFormat($d['recharged_on'], $d['recharged_time']));
}
run_hook('print_invoice'); #HOOK
$ui->display('invoice-print.tpl');
$ui->display('admin/plan/invoice-print.tpl');
break;
case 'edit':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$id = $routes['2'];
$id = $routes['2'];
$d = ORM::for_table('tbl_user_recharges')->find_one($id);
if ($d) {
$ui->assign('d', $d);
@ -312,7 +332,7 @@ switch ($action) {
$ui->assign('p', $ps);
run_hook('view_edit_customer_plan'); #HOOK
$ui->assign('_title', 'Edit Plan');
$ui->display('plan-edit.tpl');
$ui->display('admin/plan/edit.tpl');
} else {
r2(getUrl('plan/list'), 'e', Lang::T('Account Not Found'));
}
@ -322,7 +342,7 @@ switch ($action) {
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'];
$id = $routes['2'];
$d = ORM::for_table('tbl_user_recharges')->find_one($id);
if ($d) {
run_hook('delete_customer_active_plan'); #HOOK
@ -446,7 +466,7 @@ switch ($action) {
$append_url = "&search=" . urlencode($search) . "&router=" . urlencode($router) . "&customer=" . urlencode($customer) . "&plan=" . urlencode($plan) . "&status=" . urlencode($status);
// option customers
$ui->assign('customers', ORM::for_table('tbl_voucher')->distinct()->select("user")->whereNotEqual("user", '0')->findArray());
$ui->assign('customers', ORM::for_table('tbl_voucher')->distinct()->select("user")->whereNotEqual("user", '0')->findArray());
// option plans
$plns = ORM::for_table('tbl_voucher')->distinct()->select("id_plan")->findArray();
if (count($plns) > 0) {
@ -510,7 +530,7 @@ switch ($action) {
$ui->assign('search', $search);
$ui->assign('page', $page);
run_hook('view_list_voucher'); #HOOK
$ui->display('voucher.tpl');
$ui->display('admin/voucher/list.tpl');
break;
case 'add-voucher':
@ -525,7 +545,7 @@ switch ($action) {
$r = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
$ui->assign('r', $r);
run_hook('view_add_voucher'); #HOOK
$ui->display('voucher-add.tpl');
$ui->display('admin/voucher/add.tpl');
break;
case 'remove-voucher':
@ -556,9 +576,11 @@ switch ($action) {
if (empty($vpl)) {
$vpl = 3;
}
if ($pagebreak < 1) $pagebreak = 12;
if ($pagebreak < 1)
$pagebreak = 12;
if ($limit < 1) $limit = $pagebreak * 2;
if ($limit < 1)
$limit = $pagebreak * 2;
if (empty($from_id)) {
$from_id = 0;
}
@ -619,7 +641,7 @@ switch ($action) {
$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)
->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'))
@ -684,12 +706,15 @@ switch ($action) {
//for counting pagebreak
$ui->assign('jml', 0);
run_hook('view_print_voucher'); #HOOK
$ui->display('print-voucher.tpl');
$ui->display('admin/print/voucher.tpl');
break;
case 'voucher-post':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
if ($_app_stage == 'Demo') {
r2(getUrl('plan/add-voucher/'), 'e', 'You cannot perform this action in Demo mode');
}
$type = _post('type');
$plan = _post('plan');
@ -791,7 +816,7 @@ switch ($action) {
$ui->assign('from_id', 0);
$ui->assign('vpl', '3');
$ui->assign('pagebreak', $voucherPerPage);
$ui->display('print-voucher.tpl');
$ui->display('admin/print/voucher.tpl');
}
if ($numbervoucher == 1) {
@ -891,7 +916,7 @@ switch ($action) {
$content .= Lang::pad($config['note'], ' ', 2) . "\n";
$ui->assign('_title', Lang::T('View'));
$ui->assign('whatsapp', urlencode("```$content```"));
$ui->display('voucher-view.tpl');
$ui->display('admin/voucher/view.tpl');
} else {
r2(getUrl('plan/voucher/'), 'e', Lang::T('Voucher Not Found'));
}
@ -900,7 +925,7 @@ switch ($action) {
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'];
$id = $routes['2'];
run_hook('delete_voucher'); #HOOK
$d = ORM::for_table('tbl_voucher')->find_one($id);
if ($d) {
@ -916,7 +941,7 @@ switch ($action) {
$ui->assign('xfooter', $select2_customer);
$ui->assign('_title', Lang::T('Refill Account'));
run_hook('view_refill'); #HOOK
$ui->display('refill.tpl');
$ui->display('admin/plan/refill.tpl');
break;
@ -936,7 +961,7 @@ switch ($action) {
$v1->save();
$in = ORM::for_table('tbl_transactions')->where('username', $user['username'])->order_by_desc('id')->find_one();
Package::createInvoice($in);
$ui->display('invoice.tpl');
$ui->display('admin/plan/invoice.tpl');
} else {
r2(getUrl('plan/refill'), 'e', "Failed to refill account");
}
@ -956,7 +981,7 @@ switch ($action) {
$ui->assign('p', ORM::for_table('tbl_plans')->where('enabled', '1')->where('type', 'Balance')->find_many());
}
run_hook('view_deposit'); #HOOK
$ui->display('deposit.tpl');
$ui->display('admin/plan/deposit.tpl');
break;
case 'deposit-post':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
@ -971,7 +996,7 @@ switch ($action) {
if (App::getVoucherValue($svoucher)) {
$in = ORM::for_table('tbl_transactions')->find_one(App::getVoucherValue($svoucher));
Package::createInvoice($in);
$ui->display('invoice.tpl');
$ui->display('admin/plan/invoice.tpl');
die();
}
@ -987,7 +1012,7 @@ switch ($action) {
if (!empty($svoucher)) {
App::setVoucher($svoucher, $trxId);
}
$ui->display('invoice.tpl');
$ui->display('admin/plan/invoice.tpl');
} else {
r2(getUrl('plan/refill'), 'e', "Failed to refill account");
}
@ -1000,7 +1025,7 @@ switch ($action) {
if (!empty($svoucher)) {
App::setVoucher($svoucher, $trxId);
}
$ui->display('invoice.tpl');
$ui->display('admin/plan/invoice.tpl');
} else {
r2(getUrl('plan/refill'), 'e', "Failed to refill account");
}
@ -1050,18 +1075,17 @@ switch ($action) {
} else {
r2(getUrl('plan'), 's', "Customer not found");
}
Message::sendTelegram("#u$tur[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
Message::sendTelegram("#u$tur[username] #id$tur[customer_id] #extend by $admin[fullname] #" . $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']);
_log("$admin[fullname] extend Customer $tur[customer_id] $tur[username] #$tur[customer_id] for $days days", $admin['user_type'], $admin['id']);
r2(getUrl('plan'), 's', "Extend until $expiration");
} else {
r2(getUrl('plan'), 's', "Customer is not expired yet");
}
break;
default:
$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');
@ -1102,6 +1126,6 @@ switch ($action) {
$d = Paginator::findMany($query, ['search' => $search], 25, $append_url);
run_hook('view_list_billing'); #HOOK
$ui->assign('d', $d);
$ui->display('plan.tpl');
$ui->display('admin/plan/active.tpl');
break;
}

View file

@ -34,11 +34,12 @@ if (file_exists($cache) && time() - filemtime($cache) < (24 * 60 * 60)) {
}
switch ($action) {
case 'refresh':
if (file_exists($cache)) unlink($cache);
if (file_exists($cache))
unlink($cache);
r2(getUrl('pluginmanager'), 's', 'Refresh success');
break;
case 'dlinstall':
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
r2(getUrl('pluginmanager'), 'e', 'Demo Mode cannot install as it Security risk');
}
if (!is_writeable($CACHE_PATH)) {
@ -121,7 +122,7 @@ switch ($action) {
$zip->extractTo($cache);
$zip->close();
$folder = $cache . DIRECTORY_SEPARATOR . $plugin . '-main' . DIRECTORY_SEPARATOR;
if(!file_exists($folder)) {
if (!file_exists($folder)) {
$folder = $cache . DIRECTORY_SEPARATOR . $plugin . '-master' . DIRECTORY_SEPARATOR;
}
$success = 0;
@ -161,6 +162,9 @@ switch ($action) {
}
break;
case 'delete':
if ($_app_stage == 'Demo') {
r2(getUrl('pluginmanager'), 'e', 'You cannot perform this action in Demo mode');
}
if (!is_writeable($CACHE_PATH)) {
r2(getUrl('pluginmanager'), 'e', 'Folder cache/ is not writable');
}
@ -171,7 +175,8 @@ switch ($action) {
$tipe = $routes['2'];
$plugin = $routes['3'];
$file = $CACHE_PATH . DIRECTORY_SEPARATOR . $plugin . '.zip';
if (file_exists($file)) unlink($file);
if (file_exists($file))
unlink($file);
if ($tipe == 'plugin') {
foreach ($json['plugins'] as $plg) {
if ($plg['id'] == $plugin) {
@ -212,6 +217,9 @@ switch ($action) {
}
break;
case 'install':
if ($_app_stage == 'Demo') {
r2(getUrl('pluginmanager'), 'e', 'You cannot perform this action in Demo mode');
}
if (!is_writeable($CACHE_PATH)) {
r2(getUrl('pluginmanager'), 'e', 'Folder cache/ is not writable');
}
@ -222,7 +230,8 @@ switch ($action) {
$tipe = $routes['2'];
$plugin = $routes['3'];
$file = $CACHE_PATH . DIRECTORY_SEPARATOR . $plugin . '.zip';
if (file_exists($file)) unlink($file);
if (file_exists($file))
unlink($file);
if ($tipe == 'plugin') {
foreach ($json['plugins'] as $plg) {
if ($plg['id'] == $plugin) {
@ -345,7 +354,7 @@ switch ($action) {
$ui->assign('plugins', $json['plugins']);
$ui->assign('pgs', $json['payment_gateway']);
$ui->assign('dvcs', $json['devices']);
$ui->display('plugin-manager.tpl');
$ui->display('admin/settings/plugin-manager.tpl');
}

View file

@ -20,7 +20,6 @@ require_once $DEVICE_PATH . DIRECTORY_SEPARATOR . 'MikrotikPppoe' . '.php';
switch ($action) {
case 'list':
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/pool.js"></script>');
$name = _post('name');
if ($name != '') {
@ -33,14 +32,14 @@ switch ($action) {
$ui->assign('d', $d);
run_hook('view_pool'); #HOOK
$ui->display('pool.tpl');
$ui->display('admin/pool/list.tpl');
break;
case 'add':
$r = ORM::for_table('tbl_routers')->find_many();
$ui->assign('r', $r);
run_hook('view_add_pool'); #HOOK
$ui->display('pool-add.tpl');
$ui->display('admin/pool/add.tpl');
break;
case 'edit':
@ -49,7 +48,7 @@ switch ($action) {
if ($d) {
$ui->assign('d', $d);
run_hook('view_edit_pool'); #HOOK
$ui->display('pool-edit.tpl');
$ui->display('admin/pool/edit.tpl');
} else {
r2(getUrl('pool/list'), 'e', Lang::T('Account Not Found'));
}
@ -149,7 +148,6 @@ switch ($action) {
}
case 'port':
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/pool.js"></script>');
$name = _post('name');
if ($name != '') {
@ -162,14 +160,14 @@ switch ($action) {
$ui->assign('d', $d);
run_hook('view_port'); #HOOK
$ui->display('port.tpl');
$ui->display('admin/port/list.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');
$ui->display('admin/port/add.tpl');
break;
case 'edit-port':
@ -178,7 +176,7 @@ switch ($action) {
if ($d) {
$ui->assign('d', $d);
run_hook('view_edit_port'); #HOOK
$ui->display('port-edit.tpl');
$ui->display('admin/port/edit.tpl');
} else {
r2(getUrl('pool/port'), 'e', Lang::T('Account Not Found'));
}

View file

@ -22,7 +22,7 @@ switch ($action) {
$ui->assign('_system_menu', 'radius');
$ui->assign('_title', "Network Access Server");
$ui->assign('routers', ORM::for_table('tbl_routers')->find_many());
$ui->display('radius-nas-add.tpl');
$ui->display('admin/radius/nas-add.tpl');
break;
case 'nas-add-post':
$shortname = _post('shortname');
@ -78,7 +78,7 @@ switch ($action) {
if ($d) {
$ui->assign('routers', ORM::for_table('tbl_routers')->find_many());
$ui->assign('d', $d);
$ui->display('radius-nas-edit.tpl');
$ui->display('admin/radius/nas-edit.tpl');
} else {
r2(getUrl('radius/list'), 'e', Lang::T('Account Not Found'));
}
@ -147,5 +147,5 @@ switch ($action) {
}
$ui->assign('name', $name);
$ui->assign('nas', $nas);
$ui->display('radius-nas.tpl');
$ui->display('admin/radius/nas.tpl');
}

View file

@ -113,7 +113,8 @@ switch ($do) {
$d->save();
}
}
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
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>';
@ -147,8 +148,45 @@ switch ($do) {
// 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');
$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'));
$ui->assign('customFields', User::getFormCustomField($ui, true));
switch ($config['login_page_type']) {
case 'custom':
$ui->display('customer/reg-login-custom-' . $config['login_Page_template'] . '.tpl');
break;
default:
$ui->display('customer/register.tpl');
break;
}
}
}
break;
@ -196,6 +234,36 @@ switch ($do) {
$ui->display('customer/register-rotp.tpl');
}
} else {
$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'));
$ui->assign('customFields', User::getFormCustomField($ui, true));
$ui->assign('username', "");
$ui->assign('fullname', "");
@ -204,7 +272,15 @@ switch ($do) {
$ui->assign('otp', false);
$ui->assign('_title', Lang::T('Register'));
run_hook('view_register'); #HOOK
$ui->display('customer/register.tpl');
switch ($config['login_page_type']) {
case 'custom':
$ui->display('customer/reg-login-custom-' . $config['login_Page_template'] . '.tpl');
break;
default:
$ui->display('customer/register.tpl');
break;
}
}
break;
}

View file

@ -55,9 +55,13 @@ switch ($action) {
->where('type', $tp);
if (count($mts) > 0) {
if (count($mts) != count($methods)) {
$w = [];
$v = [];
foreach ($mts as $mt) {
$query->where_like('method', "$mt - %");
$w[] ='method';
$v[] = "$mt - %";
}
$query->where_likes($w, $v);
}
}
if (count($rts) > 0) {
@ -84,9 +88,13 @@ switch ($action) {
}
if (count($mts) > 0) {
if (count($mts) != count($methods)) {
$w = [];
$v = [];
foreach ($mts as $mt) {
$query->where_like('method', "$mt - %");
$w[] ='method';
$v[] = "$mt - %";
}
$query->where_likes($w, $v);
}
}
if (count($rts) > 0) {
@ -114,13 +122,6 @@ switch ($action) {
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;
@ -157,9 +158,13 @@ switch ($action) {
}
if (count($mts) > 0) {
if (count($mts) != count($methods)) {
$w = [];
$v = [];
foreach ($mts as $mt) {
$query->where_like('method', "$mt - %");
$w[] ='method';
$v[] = "$mt - %";
}
$query->where_likes($w, $v);
}
}
if (count($rts) > 0) {
@ -269,7 +274,7 @@ switch ($action) {
$ui->assign('activation', $d);
$ui->assign('q', $q);
$ui->display('reports-activation.tpl');
$ui->display('admin/reports/activation.tpl');
break;
case 'by-period':
@ -277,7 +282,7 @@ switch ($action) {
$ui->assign('mtime', $mtime);
$ui->assign('tdate', $tdate);
run_hook('view_reports_by_period'); #HOOK
$ui->display('reports-period.tpl');
$ui->display('admin/reports/period.tpl');
break;
case 'period-view':
@ -310,7 +315,7 @@ switch ($action) {
$ui->assign('tdate', $tdate);
$ui->assign('stype', $stype);
run_hook('view_reports_period'); #HOOK
$ui->display('reports-period-view.tpl');
$ui->display('admin/reports/period-view.tpl');
break;
case 'daily-report':
@ -348,11 +353,13 @@ switch ($action) {
$query->where_in('type', $tps);
}
if (count($mts) > 0) {
if (count($mts) != count($methods)) {
foreach ($mts as $mt) {
$query->where_like('method', "$mt - %");
}
$w = [];
$v = [];
foreach ($mts as $mt) {
$w[] ='method';
$v[] = "$mt - %";
}
$query->where_likes($w, $v);
}
if (count($rts) > 0) {
$query->where_in('routers', $rts);
@ -384,6 +391,6 @@ switch ($action) {
$ui->assign('dr', $dr);
$ui->assign('mdate', $mdate);
run_hook('view_daily_reports'); #HOOK
$ui->display('reports.tpl');
$ui->display('admin/reports/list.tpl');
break;
}

View file

@ -23,24 +23,9 @@ $leafletpickerHeader = <<<EOT
EOT;
switch ($action) {
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->where_like('name', '%' . $name . '%');
}
$d = Paginator::findMany($query, ['name' => $name], '20', '', true);
$ui->assign('name', $name);
$ui->assign('d', $d);
$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');
$ui->display('admin/routers/add.tpl');
break;
case 'edit':
@ -53,7 +38,7 @@ switch ($action) {
if ($d) {
$ui->assign('d', $d);
run_hook('view_router_edit'); #HOOK
$ui->display('routers-edit.tpl');
$ui->display('admin/routers/edit.tpl');
} else {
r2(getUrl('routers/list'), 'e', Lang::T('Account Not Found'));
}
@ -204,7 +189,6 @@ switch ($action) {
break;
default:
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/routers.js"></script>');
$name = _post('name');
$name = _post('name');
@ -215,6 +199,6 @@ switch ($action) {
$d = Paginator::findMany($query, ['name' => $name]);
$ui->assign('d', $d);
run_hook('view_list_routers'); #HOOK
$ui->display('routers.tpl');
$ui->display('admin/routers/list.tpl');
break;
}

View file

@ -53,7 +53,6 @@ switch ($action) {
}
r2(getUrl('services/hotspot'), 'w', 'Unknown command');
case 'hotspot':
$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');
@ -139,7 +138,7 @@ switch ($action) {
$d = Paginator::findMany($query, ['name' => $name], 20, $append_url);
$ui->assign('d', $d);
run_hook('view_list_plans'); #HOOK
$ui->display('hotspot.tpl');
$ui->display('admin/hotspot/list.tpl');
break;
case 'add':
$d = ORM::for_table('tbl_bandwidth')->find_many();
@ -156,7 +155,7 @@ switch ($action) {
}
$ui->assign('devices', $devices);
run_hook('view_add_plan'); #HOOK
$ui->display('hotspot-add.tpl');
$ui->display('admin/hotspot/add.tpl');
break;
case 'edit':
@ -191,7 +190,7 @@ switch ($action) {
}
$ui->assign('exps', $exps);
run_hook('view_edit_plan'); #HOOK
$ui->display('hotspot-edit.tpl');
$ui->display('admin/hotspot/edit.tpl');
} else {
r2(getUrl('services/hotspot'), 'e', Lang::T('Account Not Found'));
}
@ -428,7 +427,6 @@ switch ($action) {
case 'pppoe':
$ui->assign('_title', Lang::T('PPPOE Plans'));
$ui->assign('xfooter', '<script type="text/javascript" src="'.APP_URL.'/ui/lib/c/pppoe.js"></script>');
$name = _post('name');
$name = _req('name');
@ -516,7 +514,7 @@ switch ($action) {
$ui->assign('d', $d);
run_hook('view_list_ppoe'); #HOOK
$ui->display('pppoe.tpl');
$ui->display('admin/pppoe/list.tpl');
break;
case 'pppoe-add':
@ -535,7 +533,7 @@ switch ($action) {
}
$ui->assign('devices', $devices);
run_hook('view_add_ppoe'); #HOOK
$ui->display('pppoe-add.tpl');
$ui->display('admin/pppoe/add.tpl');
break;
case 'pppoe-edit':
@ -578,7 +576,7 @@ switch ($action) {
}
$ui->assign('exps', $exps);
run_hook('view_edit_ppoe'); #HOOK
$ui->display('pppoe-edit.tpl');
$ui->display('admin/pppoe/edit.tpl');
} else {
r2(getUrl('services/pppoe'), 'e', Lang::T('Account Not Found'));
}
@ -819,12 +817,12 @@ switch ($action) {
$ui->assign('d', $d);
run_hook('view_list_balance'); #HOOK
$ui->display('balance.tpl');
$ui->display('admin/balance/list.tpl');
break;
case 'balance-add':
$ui->assign('_title', Lang::T('Balance Plans'));
run_hook('view_add_balance'); #HOOK
$ui->display('balance-add.tpl');
$ui->display('admin/balance/add.tpl');
break;
case 'balance-edit':
$ui->assign('_title', Lang::T('Balance Plans'));
@ -832,7 +830,7 @@ switch ($action) {
$d = ORM::for_table('tbl_plans')->find_one($id);
$ui->assign('d', $d);
run_hook('view_edit_balance'); #HOOK
$ui->display('balance-edit.tpl');
$ui->display('admin/balance/edit.tpl');
break;
case 'balance-delete':
$id = $routes['2'];
@ -921,7 +919,6 @@ switch ($action) {
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');
@ -1009,7 +1006,7 @@ switch ($action) {
$ui->assign('d', $d);
run_hook('view_list_vpn'); #HOOK
$ui->display('vpn.tpl');
$ui->display('admin/vpn/list.tpl');
break;
case 'vpn-add':
@ -1028,7 +1025,7 @@ switch ($action) {
}
$ui->assign('devices', $devices);
run_hook('view_add_vpn'); #HOOK
$ui->display('vpn-add.tpl');
$ui->display('admin/vpn/add.tpl');
break;
case 'vpn-edit':
@ -1071,7 +1068,7 @@ switch ($action) {
}
$ui->assign('exps', $exps);
run_hook('view_edit_vpn'); #HOOK
$ui->display('vpn-edit.tpl');
$ui->display('admin/vpn/edit.tpl');
} else {
r2(getUrl('services/vpn'), 'e', Lang::T('Account Not Found'));
}
@ -1300,5 +1297,5 @@ switch ($action) {
}
break;
default:
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View file

@ -23,7 +23,7 @@ switch ($action) {
$d->value = 'yes';
$d->save();
}
r2('./docs');
r2(APP_URL . '/docs');
break;
case 'devices':
$files = scandir($DEVICE_PATH);
@ -50,7 +50,7 @@ switch ($action) {
}
}
$ui->assign('devices', $devices);
$ui->display('app-devices.tpl');
$ui->display('admin/settings/devices.tpl');
break;
case 'app':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
@ -58,23 +58,35 @@ switch ($action) {
}
if (!empty(_get('testWa'))) {
if ($_app_stage == 'Demo') {
r2(getUrl('settings/app'), 'e', 'You cannot perform this action in Demo mode');
}
$result = Message::sendWhatsapp(_get('testWa'), 'PHPNuxBill Test Whatsapp');
r2(getUrl('settings/app'), 's', 'Test Whatsapp has been send<br>Result: ' . $result);
}
if (!empty(_get('testSms'))) {
if ($_app_stage == 'Demo') {
r2(getUrl('settings/app'), 'e', 'You cannot perform this action in Demo mode');
}
$result = Message::sendSMS(_get('testSms'), 'PHPNuxBill Test SMS');
r2(getUrl('settings/app'), 's', 'Test SMS has been send<br>Result: ' . $result);
}
if (!empty(_get('testEmail'))) {
if ($_app_stage == 'Demo') {
r2(getUrl('settings/app'), 'e', 'You cannot perform this action in Demo mode');
}
Message::sendEmail(_get('testEmail'), 'PHPNuxBill Test Email', 'PHPNuxBill Test Email Body');
r2(getUrl('settings/app'), 's', 'Test Email has been send');
}
if (!empty(_get('testTg'))) {
if ($_app_stage == 'Demo') {
r2(getUrl('settings/app'), 'e', 'You cannot perform this action in Demo mode');
}
$result = Message::sendTelegram('PHPNuxBill Test Telegram');
r2(getUrl('settings/app'), 's', 'Test Telegram has been send<br>Result: ' . $result);
}
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) {
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png?' . time();
} else {
@ -165,10 +177,15 @@ switch ($action) {
run_hook('view_app_settings'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('app-settings.tpl');
$ui->display('admin/settings/app.tpl');
break;
case 'app-post':
if ($_app_stage == 'Demo') {
r2(getUrl('settings/app'), 'e', 'You cannot perform this action in Demo mode');
}
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
@ -186,9 +203,11 @@ switch ($action) {
run_hook('save_settings'); #HOOK
if (!empty($_FILES['logo']['name'])) {
if (function_exists('imagecreatetruecolor')) {
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) unlink($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png');
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png'))
unlink($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png');
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']);
if (file_exists($_FILES['logo']['tmp_name']))
unlink($_FILES['logo']['tmp_name']);
} else {
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
@ -205,7 +224,7 @@ 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('error.tpl');
$ui->display('admin/error.tpl');
die();
}
}
@ -216,6 +235,88 @@ switch ($action) {
$_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;
$_POST['notification_reminder_1day'] = isset($_POST['notification_reminder_1day']) ? 'yes' : 'no';
$_POST['notification_reminder_3days'] = isset($_POST['notification_reminder_3days']) ? 'yes' : 'no';
$_POST['notification_reminder_7days'] = isset($_POST['notification_reminder_7days']) ? 'yes' : 'no';
// hide dashboard
$_POST['hide_mrc'] = _post('hide_mrc', 'no');
$_POST['hide_tms'] = _post('hide_tms', 'no');
$_POST['hide_al'] = _post('hide_al', 'no');
$_POST['hide_uet'] = _post('hide_uet', 'no');
$_POST['hide_vs'] = _post('hide_vs', 'no');
$_POST['hide_pg'] = _post('hide_pg', 'no');
$_POST['hide_aui'] = _post('hide_aui', 'no');
// 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;
}
$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);
$_POST['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);
$_POST['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);
$_POST['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 ($_POST as $key => $value) {
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
if ($d) {
@ -234,98 +335,6 @@ switch ($action) {
}
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");
@ -352,16 +361,19 @@ switch ($action) {
run_hook('view_localisation'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('app-localisation.tpl');
$ui->display('admin/settings/localisation.tpl');
break;
case 'localisation-post':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
if ($_app_stage == 'Demo') {
r2(getUrl('settings/localisation'), 'e', 'You cannot perform this action in Demo mode');
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/app'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('settings/localisation'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$tzone = _post('tzone');
$date_format = _post('date_format');
@ -369,7 +381,7 @@ switch ($action) {
$lan = _post('lan');
run_hook('save_localisation'); #HOOK
if ($tzone == '' or $date_format == '' or $lan == '') {
r2(getUrl('settings/app'), 'e', Lang::T('All field is required'));
r2(getUrl('settings/localisation'), 'e', Lang::T('All field is required'));
} else {
$d = ORM::for_table('tbl_appconfig')->where('setting', 'timezone')->find_one();
$d->value = $tzone;
@ -472,11 +484,11 @@ switch ($action) {
} else if ($admin['user_type'] == 'Admin') {
$query = ORM::for_table('tbl_users')
->where_like('username', '%' . $search . '%')->where_any_is([
['user_type' => 'Report'],
['user_type' => 'Agent'],
['user_type' => 'Sales'],
['id' => $admin['id']]
])->order_by_asc('id');
['user_type' => 'Report'],
['user_type' => 'Agent'],
['user_type' => 'Sales'],
['id' => $admin['id']]
])->order_by_asc('id');
$d = Paginator::findMany($query, ['search' => $search]);
} else {
$query = ORM::for_table('tbl_users')
@ -527,7 +539,7 @@ switch ($action) {
run_hook('view_list_admin'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('admin.tpl');
$ui->display('admin/admin/list.tpl');
break;
case 'users-add':
@ -538,17 +550,17 @@ switch ($action) {
$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('admin-add.tpl');
$ui->display('admin/admin/add.tpl');
break;
case 'users-view':
$ui->assign('_title', Lang::T('Edit User'));
$id = $routes['2'];
$id = $routes['2'];
if (empty($id)) {
$id = $admin['id'];
}
//allow see himself
if ($admin['id'] == $id) {
$d = ORM::for_table('tbl_users')->where('id', $id)->find_array($id)[0];
$d = ORM::for_table('tbl_users')->where('id', $id)->find_array()[0];
} else {
if (in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
// Super Admin can see anyone
@ -567,7 +579,7 @@ switch ($action) {
$ui->assign('_title', $d['username']);
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('admin-view.tpl');
$ui->display('admin/admin/view.tpl');
} else {
r2(getUrl('settings/users'), 'e', Lang::T('Account Not Found'));
}
@ -577,7 +589,7 @@ switch ($action) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$ui->assign('_title', Lang::T('Edit User'));
$id = $routes['2'];
$id = $routes['2'];
if (empty($id)) {
$id = $admin['id'];
}
@ -623,7 +635,7 @@ switch ($action) {
run_hook('view_edit_admin'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('admin-edit.tpl');
$ui->display('admin/admin/edit.tpl');
} else {
r2(getUrl('settings/users'), 'e', Lang::T('Account Not Found'));
}
@ -633,8 +645,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");
}
$id = $routes['2'];
if ($_app_stage == 'Demo') {
r2(getUrl('settings/users'), 'e', 'You cannot perform this action in Demo mode');
}
$id = $routes['2'];
if (($admin['id']) == $id) {
r2(getUrl('settings/users'), 'e', 'Sorry You can\'t delete yourself');
}
@ -652,6 +666,9 @@ 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");
}
if ($_app_stage == 'Demo') {
r2(getUrl('settings/users-add'), 'e', 'You cannot perform this action in Demo mode');
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/users-add'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
@ -720,6 +737,9 @@ switch ($action) {
break;
case 'users-edit-post':
if ($_app_stage == 'Demo') {
r2(getUrl('settings/users-edit/'), 'e', 'You cannot perform this action in Demo mode');
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/users-edit/'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
@ -813,7 +833,7 @@ switch ($action) {
}
}
if (file_exists($imgPath)) {
if ($d['photo'] != '' && strpos($d['photo'], 'default') === false) {
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')) {
@ -823,7 +843,8 @@ switch ($action) {
}
$d->photo = '/photos/' . $subfolder . '/' . $hash . '.jpg';
}
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
if (file_exists($_FILES['photo']['tmp_name']))
unlink($_FILES['photo']['tmp_name']);
} else {
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
@ -869,10 +890,13 @@ switch ($action) {
run_hook('view_change_password'); #HOOK
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('change-password.tpl');
$ui->display('admin/change-password.tpl');
break;
case 'change-password-post':
if ($_app_stage == 'Demo') {
r2(getUrl('settings/change-password'), 'e', 'You cannot perform this action in Demo mode');
}
$password = _post('password');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
@ -926,9 +950,12 @@ switch ($action) {
$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');
$ui->display('admin/settings/notifications.tpl');
break;
case 'notifications-post':
if ($_app_stage == 'Demo') {
r2(getUrl('settings/notifications'), 'e', 'You cannot perform this action in Demo mode');
}
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
@ -953,11 +980,14 @@ switch ($action) {
}
$ui->assign('tables', $tables);
run_hook('view_database'); #HOOK
$ui->display('dbstatus.tpl');
$ui->display('admin/settings/dbstatus.tpl');
}
break;
case 'dbbackup':
if ($_app_stage == 'Demo') {
r2(getUrl('settings/dbstatus'), 'e', 'You cannot perform this action in Demo mode');
}
if (!in_array($admin['user_type'], ['SuperAdmin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
@ -978,6 +1008,9 @@ switch ($action) {
echo json_encode($array);
break;
case 'dbrestore':
if ($_app_stage == 'Demo') {
r2(getUrl('settings/dbstatus'), 'e', 'You cannot perform this action in Demo mode');
}
if (!in_array($admin['user_type'], ['SuperAdmin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
@ -1025,7 +1058,8 @@ switch ($action) {
} catch (Throwable $e) {
} catch (Exception $e) {
}
if (file_exists($_FILES['json']['tmp_name'])) unlink($_FILES['json']['tmp_name']);
if (file_exists($_FILES['json']['tmp_name']))
unlink($_FILES['json']['tmp_name']);
r2(getUrl('settings/dbstatus'), 's', "Restored $suc success $fal failed");
} else {
r2(getUrl('settings/dbstatus'), 'e', 'Upload failed');
@ -1043,10 +1077,13 @@ switch ($action) {
}
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->display('language-add.tpl');
$ui->display('admin/settings/language-add.tpl');
break;
case 'lang-post':
if ($_app_stage == 'Demo') {
r2(getUrl('settings/dbstatus'), 'e', 'You cannot perform this action in Demo mode');
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/language'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
@ -1062,6 +1099,9 @@ switch ($action) {
}
if (_post('save') == 'save') {
if ($_app_stage == 'Demo') {
r2(getUrl('settings/maintenance'), 'e', 'You cannot perform this action in Demo mode');
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/maintenance'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
@ -1095,7 +1135,7 @@ switch ($action) {
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_c', $config);
$ui->assign('_title', Lang::T('Maintenance Mode Settings'));
$ui->display('maintenance-mode.tpl');
$ui->display('admin/settings/maintenance-mode.tpl');
break;
case 'miscellaneous':
@ -1104,6 +1144,9 @@ switch ($action) {
exit;
}
if (_post('save') == 'save') {
if ($_app_stage == 'Demo') {
r2(getUrl('settings/miscellaneous'), 'e', 'You cannot perform this action in Demo mode');
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(getUrl('settings/miscellaneous'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
@ -1127,9 +1170,9 @@ switch ($action) {
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_c', $config);
$ui->assign('_title', Lang::T('Miscellaneous Settings'));
$ui->display('app-miscellaneous.tpl');
$ui->display('admin/settings/miscellaneous.tpl');
break;
default:
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View file

@ -4,11 +4,47 @@
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
_auth();
$ui->assign('_title', Lang::T('Voucher'));
$ui->assign('_system_menu', 'voucher');
$action = $routes['1'];
if(!_auth(false)){
if($action== 'invoice'){
$id = $routes[2];
$sign = $routes[3];
if($sign != md5($id. $db_pass)) {
die("beda");
}
if (empty($id)) {
$in = ORM::for_table('tbl_transactions')->order_by_desc('id')->find_one();
} else {
$in = ORM::for_table('tbl_transactions')->where('id', $id)->find_one();
}
if ($in) {
Package::createInvoice($in);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$logo = '';
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) {
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png';
$imgsize = getimagesize($logo);
$width = $imgsize[0];
$height = $imgsize[1];
$ui->assign('wlogo', $width);
$ui->assign('hlogo', $height);
}
$ui->assign('public_url', getUrl("voucher/invoice/$id/".md5($id. $db_pass)));
$ui->assign('logo', $logo);
$ui->display('customer/invoice-customer.tpl');
die();
} else {
r2(getUrl('voucher/list-activated'), 'e', Lang::T('Not Found'));
}
}else{
r2(getUrl('login'));
}
}
$user = User::_info();
$ui->assign('_user', $user);
@ -64,11 +100,23 @@ switch ($action) {
}
if ($in) {
Package::createInvoice($in);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$logo = '';
if (file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png')) {
$logo = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png';
$imgsize = getimagesize($logo);
$width = $imgsize[0];
$height = $imgsize[1];
$ui->assign('wlogo', $width);
$ui->assign('hlogo', $height);
}
$ui->assign('public_url', getUrl("voucher/invoice/$id/".md5($id. $db_pass)));
$ui->assign('logo', $logo);
$ui->display('customer/invoice-customer.tpl');
} else {
r2(getUrl('voucher/list-activated'), 'e', Lang::T('Not Found'));
}
break;
default:
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View file

@ -0,0 +1,147 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
_admin();
$ui->assign('_title', Lang::T('Dashboard Widgets'));
$ui->assign('_system_menu', 'settings');
$action = alphanumeric($routes['1']);
$ui->assign('_admin', $admin);
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
r2(getUrl('dashboard'), 'e', Lang::T('You do not have permission to access this page'));
}
$tipeUser = _req("user");
if (empty($tipeUser)) {
$tipeUser = 'Admin';
}
if($tipeUser == 'Customer') {
$WIDGET_PATH .= DIRECTORY_SEPARATOR. 'customer';
}
$ui->assign('tipeUser', $tipeUser);
$max = ORM::for_table('tbl_widgets')->where("user", $tipeUser)->max('position');
$max2 = substr_count($config['dashboard_' . $tipeUser], '.') + substr_count($config['dashboard_' . $tipeUser], ',') + 1;
if ($max2 > $max) {
$max = $max2;
}
$ui->assign('max', $max);
if ($action == 'add') {
$pos = alphanumeric($routes['2']);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$orders = alphanumeric($_POST['orders']);
$position = alphanumeric($_POST['position']);
$tipeUser = alphanumeric($_POST['tipeUser']);
$enabled = alphanumeric($_POST['enabled']);
$title = _post('title');
$widget = _post('widget');
$d = ORM::for_table('tbl_widgets')->create();
$d->orders = $orders;
$d->position = $position;
$d->user = $tipeUser;
$d->enabled = $enabled;
$d->title = $title;
$d->widget = $widget;
$d->content = _post('content');
$d->save();
if ($d->id() > 0) {
r2(getUrl('widgets&user=' . $tipeUser), 's', 'Widget Added Successfully');
}
}
$files = scandir($WIDGET_PATH);
$widgets = [];
foreach ($files as $file) {
if (strpos($file, '.php') !== false) {
$name = ucwords(str_replace('.php', '', str_replace('_', ' ', $file)));
$widgets[str_replace('.php', '', $file)] = $name;
}
}
$widget['position'] = $pos;
$widget['user'] = $tipeUser;
$ui->assign('users', ORM::for_table('tbl_widgets')->getEnum("user"));
$ui->assign('do', 'add');
$ui->assign('widgets', $widgets);
$ui->assign('widget', $widget);
$ui->display('admin/settings/widgets_add_edit.tpl');
} else if ($action == 'edit') {
// if request method post then save data
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$id = alphanumeric($_POST['id']);
$orders = alphanumeric($_POST['orders']);
$position = alphanumeric($_POST['position']);
$tipeUser = alphanumeric($_POST['tipeUser']);
$enabled = alphanumeric($_POST['enabled']);
$title = _post('title');
$widget = _post('widget');
$d = ORM::for_table('tbl_widgets')->find_one($id);
$d->orders = $orders;
$d->position = $position;
$d->user = $tipeUser;
$d->enabled = $enabled;
$d->title = $title;
$d->widget = $widget;
$d->content = _post('content');
$d->save();
r2(getUrl('widgets&user=' . $tipeUser), 's', 'Widget Saved Successfully');
}
$id = alphanumeric($routes['2']);
$widget = ORM::for_table('tbl_widgets')->find_one($id);
$files = scandir($WIDGET_PATH);
$widgets = [];
foreach ($files as $file) {
if (strpos($file, '.php') !== false) {
$name = ucwords(str_replace('.php', '', str_replace('_', ' ', $file)));
$widgets[str_replace('.php', '', $file)] = $name;
}
}
$ui->assign('users', ORM::for_table('tbl_widgets')->getEnum("user"));
$ui->assign('do', 'edit');
$ui->assign('widgets', $widgets);
$ui->assign('widget', $widget);
$ui->display('admin/settings/widgets_add_edit.tpl');
} else if ($action == 'delete') {
$id = alphanumeric($routes['2']);
$d = ORM::for_table('tbl_widgets')->find_one($id);
if ($d) {
$d->delete();
r2(getUrl('widgets&user=' . $tipeUser), 's', 'Widget Deleted Successfully');
}
r2(getUrl('widgets&user=' . $tipeUser), 'e', 'Widget Not Found');
} else if (!empty($action) && file_exists("system/widget/$action.php") && !empty($routes['2'])) {
require_once "system/widget/$action.php";
try {
(new $action)->run_command($routes['2']);
} catch (Throwable $e) {
//nothing to do
}
} else if ($action == 'pos') {
$jml = count($_POST['orders']);
for ($i = 0; $i < $jml; $i++) {
$d = ORM::for_table('tbl_widgets')->find_one($_POST['id'][$i]);
$d->orders = $_POST['orders'][$i];
$d->save();
}
r2(getUrl('widgets&user=' . $tipeUser), 's', 'Widget order Saved Successfully');
} else {
if (_post("save") == 'struct') {
$d = ORM::for_table('tbl_appconfig')->where('setting', 'dashboard_' . $tipeUser)->find_one();
if ($d) {
$d->value = _post('dashboard');
$d->save();
} else {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = 'dashboard_' . $tipeUser;
$d->value = _post('dashboard');
$d->save();
}
_alert("Dashboard Structure Saved Successfully", "success", getUrl('widgets&user=' . $tipeUser));
}
$widgets = ORM::for_table('tbl_widgets')->where("user", $tipeUser)->order_by_asc("orders")->find_many();
$ui->assign('widgets', $widgets);
$ui->display('admin/settings/widgets.tpl');
}

View file

@ -30,7 +30,7 @@ if (php_sapi_name() !== 'cli') {
echo "PHP Time\t" . date('Y-m-d H:i:s') . "\n";
$res = ORM::raw_execute('SELECT NOW() AS WAKTU;');
$statement = ORM::get_last_statement();
$rows = array();
$rows = [];
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
echo "MYSQL Time\t" . $row['WAKTU'] . "\n";
}
@ -45,80 +45,111 @@ echo "Found " . count($d) . " user(s)\n";
run_hook('cronjob'); #HOOK
foreach ($d as $ds) {
$date_now = strtotime(date("Y-m-d H:i:s"));
$expiration = strtotime($ds['expiration'] . ' ' . $ds['time']);
echo $ds['expiration'] . " : " . (($isCli) ? $ds['username'] : Lang::maskText($ds['username']));
if ($date_now >= $expiration) {
echo " : EXPIRED \r\n";
$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 (file_exists($dvc)) {
require_once $dvc;
(new $p['device'])->remove_customer($c, $p);
} else {
echo "Cron error Devices $p[device] not found, cannot disconnect $c[username]";
Message::sendTelegram("Cron error Devices $p[device] not found, cannot disconnect $c[username]");
}
}
echo Message::sendPackageNotification($c, $u['namebp'], $p['price'], $textExpired, $config['user_notification_expired']) . "\n";
//update database user dengan status off
$u->status = 'off';
$u->save();
try {
$date_now = strtotime(date("Y-m-d H:i:s"));
$expiration = strtotime($ds['expiration'] . ' ' . $ds['time']);
echo $ds['expiration'] . " : " . ($isCli ? $ds['username'] : Lang::maskText($ds['username']));
// 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 (!empty($add_cost)) {
if ($date_now >= $expiration) {
echo " : EXPIRED \r\n";
// Fetch user recharge details
$u = ORM::for_table('tbl_user_recharges')->where('id', $ds['id'])->find_one();
if (!$u) {
throw new Exception("User recharge record not found for ID: " . $ds['id']);
}
// Fetch customer details
$c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
if (!$c) {
$c = $u;
}
// Fetch plan details
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
if (!$p) {
throw new Exception("Plan not found for ID: " . $u['plan_id']);
}
$dvc = Package::getDevice($p);
if ($_app_stage != 'demo') {
if (file_exists($dvc)) {
require_once $dvc;
(new $p['device'])->remove_customer($c, $p);
} else {
throw new Exception("Cron error: Devices " . $p['device'] . "not found, cannot disconnect ".$c['username']."\n");
}
}
// Send notification and update user status
try {
echo Message::sendPackageNotification(
$c,
$u['namebp'],
$p['price'],
Message::getMessageType($p['type'], $textExpired),
$config['user_notification_expired']
) . "\n";
$u->status = 'off';
$u->save();
} catch (Throwable $e) {
_log($e->getMessage());
sendTelegram($e->getMessage());
echo "Error: " . $e->getMessage() . "\n";
}
// Auto-renewal from deposit
if ($config['enable_balance'] == 'yes' && $c['auto_renewal']) {
[$bills, $add_cost] = User::getBills($ds['customer_id']);
if ($add_cost != 0) {
$p['price'] += $add_cost;
}
}
if ($p && $c['balance'] >= $p['price']) {
if (Package::rechargeUser($ds['customer_id'], $ds['routers'], $p['id'], 'Customer', 'Balance')) {
// if success, then get the balance
Balance::min($ds['customer_id'], $p['price']);
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
echo "auto renewall Success\n";
if ($p && $c['balance'] >= $p['price']) {
if (Package::rechargeUser($ds['customer_id'], $ds['routers'], $p['id'], 'Customer', 'Balance')) {
Balance::min($ds['customer_id'], $p['price']);
echo "plan enabled: " . (string) $p['enabled'] . " | User balance: " . (string) $c['balance'] . " | price " . (string) $p['price'] . "\n";
echo "auto renewal Success\n";
} else {
echo "plan enabled: " . $p['enabled'] . " | User balance: " . $c['balance'] . " | price " . $p['price'] . "\n";
echo "auto renewal Failed\n";
Message::sendTelegram("FAILED RENEWAL #cron\n\n#u." . $c['username'] . " #buy #Hotspot \n" . $p['name_plan'] .
"\nRouter: " . $p['routers'] .
"\nPrice: " . $p['price']);
}
} else {
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
echo "auto renewall Failed\n";
Message::sendTelegram("FAILED RENEWAL #cron\n\n#u$c[username] #buy #Hotspot \n" . $p['name_plan'] .
"\nRouter: " . $p['routers'] .
"\nPrice: " . $p['price']);
echo "no renewal | plan enabled: " . (string) $p['enabled'] . " | User balance: " . (string) $c['balance'] . " | price " . (string) $p['price'] . "\n";
}
} else {
echo "no renewall | plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
echo "no renewal | balance" . $config['enable_balance'] . " auto_renewal " . $c['auto_renewal'] . "\n";
}
} else {
echo "no renewall | balance $config[enable_balance] auto_renewal $c[auto_renewal]\n";
echo " : ACTIVE \r\n";
}
} else {
echo " : ACTIVE \r\n";
} catch (Throwable $e) {
// Catch any unexpected errors
_log($e->getMessage());
sendTelegram($e->getMessage());
echo "Unexpected Error: " . $e->getMessage() . "\n";
}
}
//Cek interim-update radiusrest
if ($config['frrest_interim_update'] != 0) {
//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();
->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();
}
}
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']) {
@ -137,7 +168,7 @@ if ($config['router_check']) {
foreach ($routers as $router) {
// check if custom port
if (strpos($router->ip_address, ':') === false){
if (strpos($router->ip_address, ':') === false) {
$ip = $router->ip_address;
$port = 8728;
} else {
@ -207,14 +238,7 @@ if ($config['router_check']) {
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>";
echo "Router monitoring finished checking.\n";
}
flock($lock, LOCK_UN);
@ -224,5 +248,5 @@ unlink($lockFile);
$timestampFile = "$UPLOAD_PATH/cron_last_run.txt";
file_put_contents($timestampFile, time());
run_hook('cronjob_end'); #HOOK
echo "Cron job finished and completed successfully.\n";

View file

@ -23,7 +23,7 @@ run_hook('cronjob_reminder'); #HOOK
echo "PHP Time\t" . date('Y-m-d H:i:s') . "\n";
$res = ORM::raw_execute('SELECT NOW() AS WAKTU;');
$statement = ORM::get_last_statement();
$rows = array();
$rows = [];
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
echo "MYSQL Time\t" . $row['WAKTU'] . "\n";
}
@ -39,22 +39,52 @@ foreach ($d as $ds) {
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
$c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
if ($p['validity_unit'] == 'Period') {
// Postpaid price from field
$add_inv = User::getAttribute("Invoice", $ds['customer_id']);
if (empty ($add_inv) or $add_inv == 0) {
$price = $p['price'];
} else {
$price = $add_inv;
}
} else {
// Postpaid price from field
$add_inv = User::getAttribute("Invoice", $ds['customer_id']);
if (empty($add_inv) or $add_inv == 0) {
$price = $p['price'];
} else {
$price = $add_inv;
}
} else {
$price = $p['price'];
}
if ($ds['expiration'] == $day7) {
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_7_day'), $config['user_notification_reminder']) . "\n";
} else if ($ds['expiration'] == $day3) {
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_3_day'), $config['user_notification_reminder']) . "\n";
} else if ($ds['expiration'] == $day1) {
echo Message::sendPackageNotification($c, $p['name_plan'], $price, Lang::getNotifText('reminder_1_day'), $config['user_notification_reminder']) . "\n";
if ($ds['expiration'] == $day7 && $config['notification_reminder_7day'] !== 'no') {
try {
echo Message::sendPackageNotification(
$c,
$p['name_plan'],
$price,
Message::getMessageType($p['type'], Lang::getNotifText('reminder_7_day')),
$config['user_notification_reminder']
) . "\n";
} catch (Exception $e) {
sendTelegram("Cron Reminder failed to send 7-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
}
} else if ($ds['expiration'] == $day3 && $config['notification_reminder_3day'] !== 'no') {
try {
echo Message::sendPackageNotification(
$c,
$p['name_plan'],
$price,
Message::getMessageType($p['type'], Lang::getNotifText('reminder_3_day')),
$config['user_notification_reminder']
) . "\n";
} catch (Exception $e) {
sendTelegram("Cron Reminder failed to send 3-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
}
} else if ($ds['expiration'] == $day1 && $config['notification_reminder_1day'] !== 'no') {
try {
echo Message::sendPackageNotification(
$c,
$p['name_plan'],
$price,
Message::getMessageType($p['type'], Lang::getNotifText('reminder_1_day')),
$config['user_notification_reminder']
) . "\n";
} catch (Exception $e) {
sendTelegram("Cron Reminder failed to send 1-day reminder to " . $ds['username'] . " Error: " . $e->getMessage());
}
}
}
}
}

View file

@ -122,6 +122,9 @@ class MikrotikHotspot
if (!empty(trim($bw['burst']))) {
$rate .= ' ' . $bw['burst'];
}
if ($bw['rate_up'] == '0' || $bw['rate_down'] == '0') {
$rate = '';
}
$addRequest = new RouterOS\Request('/ip/hotspot/user/profile/add');
$client->sendSync(
$addRequest
@ -202,6 +205,9 @@ class MikrotikHotspot
if (!empty(trim($bw['burst']))) {
$rate .= ' ' . $bw['burst'];
}
if ($bw['rate_up'] == '0' || $bw['rate_down'] == '0') {
$rate = '';
}
$setRequest = new RouterOS\Request('/ip/hotspot/user/profile/set');
$client->sendSync(
$setRequest

View file

@ -149,6 +149,9 @@ class MikrotikPppoe
if(!empty(trim($bw['burst']))){
$rate .= ' '.$bw['burst'];
}
if ($bw['rate_up'] == '0' || $bw['rate_down'] == '0') {
$rate = '';
}
$pool = ORM::for_table("tbl_pool")->where("pool_name", $plan['pool'])->find_one();
$addRequest = new RouterOS\Request('/ppp/profile/add');
$client->sendSync(
@ -205,6 +208,9 @@ class MikrotikPppoe
if(!empty(trim($bw['burst']))){
$rate .= ' '.$bw['burst'];
}
if ($bw['rate_up'] == '0' || $bw['rate_down'] == '0') {
$rate = '';
}
$pool = ORM::for_table("tbl_pool")->where("pool_name", $new_plan['pool'])->find_one();
$setRequest = new RouterOS\Request('/ppp/profile/set');
$client->sendSync(

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@
"Announcement": "Pemberitahuan",
"Registration_Info": "Info Pendaftaran",
"Voucher_not_found__please_buy_voucher_befor_register": "Voucher tidak ditemukan, silakan beli voucher sebelum mendaftar",
"Register_Success__You_can_login_now": "Daftar Sukses! Anda dapat masuk sekarang",
"Register_Success__You_can_login_now": "Daftar berhadil! Anda dapat masuk sekarang",
"Log_in_to_Member_Panel": "Masuk ke Panel Anggota",
"Register_as_Member": "Daftar sebagai Anggota",
"Enter_Admin_Area": "Masuk ke Admin Panel",
@ -13,11 +13,12 @@
"Password": "Kata Sandi",
"Passwords_does_not_match": "Kata sandi tidak cocok",
"Account_already_axist": "Akun telah ada",
"Manage": "Mengelola",
"Manage": "Kelola",
"Submit": "Kirim",
"Save_Changes": "Simpan Perubahan",
"Cancel": "Batal",
"Edit": "Sunting",
"Order": "Urutan",
"Delete": "Hapus",
"Welcome": "Selamat Datang",
"Data_Created_Successfully": "Data Berhasil Dibuat",
@ -123,7 +124,7 @@
"Period_Reports": "Laporan Periode",
"All_Transactions": "Semua Transaksi",
"Total_Income": "Jumlah Pemasukan",
"All_Transactions_at_Date": "Semua transaksi pada ganggal",
"All_Transactions_at_Date": "Semua transaksi pada tanggal",
"Export_for_Print": "Ekspor untuk cetak",
"Print": "Cetak",
"Export_to_PDF": "Ekspor ke PDF",
@ -199,7 +200,7 @@
"Search_by_Username": "Cari berdasarkan nama pengguna",
"Search_by_Name": "Cari berdasarkan nama",
"Search_by_Code_Voucher": "Cari berdasarkan kode voucher",
"Search": "Mencari",
"Search": "Cari",
"Select_a_customer": "Pilih pelanggan",
"Select_Routers": "Pilih Router",
"Select_Plans": "Pilih Paket",
@ -331,7 +332,7 @@
"Pay_this_with_Balance__your_active_package_will_be_overwrite": "Bayar ini dengan Saldo? Paket aktif Anda akan ditimpa",
"Success_to_buy_package": "Berhasil membeli paket",
"Auto_Renewal": "Perpanjangan otomatis",
"View": "Melihat",
"View": "Lihat",
"Back": "Kembali",
"Active": "Aktif",
"Transfer_Balance": "Kirim saldo",
@ -383,6 +384,7 @@
"Vouchers": "Voucher",
"Refill_Customer": "Isi Ulang Voucher",
"Recharge_Customer": "Isi Ulang Paket",
"Plan": "Paket",
"Plans": "Paket",
"PPPOE": "PPPOE",
"Bandwidth": "Bandwidth",
@ -411,7 +413,7 @@
"Paid": "Dibayar",
"Personal": "Pribadi",
"Coordinates": "Koordinat",
"Confirm": "Mengonfirmasi",
"Confirm": "Konfirmasi",
"Name": "Nama",
"Plan": "Paket",
"Using": "Menggunakan",
@ -419,7 +421,7 @@
"Additional_Cost": "Biaya tambahan",
"Resend": "Kirim ulang",
"Login": "Masuk",
"success": "Sukses",
"success": "Berhasil",
"Click_Here": "Klik disini",
"Your_friend_do_not_have_active_package": "Teman Anda tidak memiliki paket aktif",
"If_your_friend_have_Additional_Cost__you_will_pay_for_that_too": "Jika teman Anda memiliki biaya tambahan, Anda juga akan membayarnya",
@ -503,11 +505,11 @@
"Time": "Waktu",
"Data": "Data",
"1_Period___1_Month__Expires_the_20th_of_each_month": "1 Periode = 1 Bulan, Berakhir pada tanggal 20 setiap bulannya",
"Expired_Date": "Tanggal kadaluarsa",
"Expired_Date": "Tanggal kadaluwarsa",
"Expired_Action": "Tindakan Kedaluwarsa",
"Optional": "Opsional",
"Expired_Internet_Plan": "Paket Internet Kedaluwarsa",
"When_Expired__customer_will_be_move_to_selected_internet_plan": "Ketika Expired, pelanggan akan dipindahkan ke paket internet yang dipilih",
"When_Expired__customer_will_be_move_to_selected_internet_plan": "Ketika kedaluwarsa, pelanggan akan dipindahkan ke paket internet yang dipilih",
"Period": "Periode",
"Rate": "Kecepatan",
"Burst": "Burst",
@ -522,7 +524,8 @@
"Ascending": "Naik",
"Descending": "Menurun",
"Query": "Query",
"Add": "Menambahkan",
"Add": "Tambah",
"Search": "Cari",
"Logout_Successful": "Berhasil Keluar",
"warning": "peringatan",
"Created___Expired": "Dibuat \/ Kedaluwarsa",
@ -532,8 +535,8 @@
"Customer_can_login_but_cannot_buy_internet_plan__Admin_cannot_recharge_customer": "Pelanggan dapat login tetapi tidak dapat membeli paket internet, Admin tidak dapat mengisi ulang pelanggan",
"Don_t_forget_to_deactivate_all_active_plan_too": "Jangan lupa untuk menonaktifkan semua paket aktif juga",
"Attributes": "Atribut",
"Additional_Information": "informasi tambahan",
"City_of_Resident": "Kota Residen",
"Additional_Information": "Informasi tambahan",
"City_of_Resident": "Kota Tempat Tinggal",
"State_of_Resident": "Negara Bagian Tempat Tinggal",
"Zip_Code": "Kode Pos",
"Phone": "Telepon",
@ -554,7 +557,7 @@
"Input_your_phone_number": "Masukkan nomor telepon Anda",
"OTP": "OTP",
"Enter_OTP_that_was_sent_to_your_phone": "Masukkan OTP yang dikirimkan ke ponsel Anda",
"Update": "Memperbarui",
"Update": "Perbarui",
"Verification_code_has_been_sent_to_your_phone": "Kode verifikasi telah dikirimkan ke ponsel Anda",
"Please_wait_1039_seconds_before_sending_another_SMS": "Harap tunggu 1039 detik sebelum mengirim SMS lainnya",
"Please_wait_1015_seconds_before_sending_another_SMS": "Harap tunggu 1015 detik sebelum mengirim SMS lainnya",
@ -567,7 +570,7 @@
"Api": "Api",
"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",
"Languge_set_to_indonesia": "Bahasa diatur ke Indonesia",
"Enable": "Aktifkan",
"Diable": "Nonaktifkan",
"Verification_code": "Kod3 V3r1fik@s1",
@ -583,37 +586,37 @@
"_": "-",
"Search_Users": "Pencarian Pengguna",
"Routers_Maps": "Peta Router",
"Theme_Voucher": "Voucher Tema",
"Theme_Voucher": "Tema Voucher",
"Payment_Info": "Info Pembayaran",
"Documentation": "Dokumentasi",
"Customers": "Pelanggan",
"Package_Name": "Nama Paket",
"Routers_Offline": "Router Offline",
"Routers_Offline": "Router Off",
"Cron_appear_not_been_setup__please_check_your_cron_setup_": "Cron tampaknya belum disiapkan, silakan periksa pengaturan cron Anda.",
"Buy": "Membeli",
"Buy": "Beli",
"You_are_already_logged_in": "Anda sudah masuk",
"PPPOE_Package": "Paket PPPoE",
"Prepaid": "Prabayar",
"Postpaid": "Pascabayar",
"Enabled": "Diaktifkan",
"Enabled": "Aktifkan",
"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",
"For_Discount_Rate__this_is_price_before_get_discount__must_be_more_expensive_with_real_price": "Untuk tarif diskon. Ini adalah harga sebelum mendapat diskon, pasti lebih mahal dari harga sebenarnya",
"on_login___on_up": "saat masuk \/ saat naik",
"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",
"Buy_this__your_active_package_will_be_overwritten": "Beli paket 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",
"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_Username_and_Password_are_correct": "Pastikan nama pengguna dan kata sandi 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",
@ -669,11 +672,11 @@
"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",
"Idle_Timeout__Logout_Admin_if_Idle_for_xx_minutes": "Batas waktu 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",
"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 Off, 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",
@ -701,7 +704,7 @@
"Used_Date": "Tanggal Penggunaan",
"Plugin_Installer": "Pemasang Plugin",
"Upload_Zip_Plugin_Theme_Device": "Unggah Plugin\/Tema\/Perangkat Zip",
"Install": "Memasang",
"Install": "Pasang",
"via_SMS": "melalui SMS",
"Via_WhatsApp": "Melalui WhatsApp",
"Via_WhatsApp_and_SMS": "Melalui WhatsApp dan SMS",
@ -740,9 +743,9 @@
"Status_": "Status:",
"Force_Logout_": "Paksa Keluar:",
"End_Date_": "Tanggal Berakhir:",
"Save": "Menyimpan",
"Save": "Simpan",
"Not_Active": "Tidak Aktif",
"Limit": "Membatasi",
"Limit": "Batasi",
"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",
@ -751,10 +754,10 @@
"Buy_Balance_Plans": "Beli Paket Saldo",
"New_Voucher_for_10mbps_Created": "Voucher Baru untuk 10mbps Dibuat",
"Previous": "Sebelumnya",
"Share": "Membagikan",
"Share": "Bagikan",
"Agent": "Agen",
"Sub_District": "Kecamatan",
"Ward": "Bangsal",
"Ward": "Kelurahan",
"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.",
@ -765,7 +768,7 @@
"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",
"For_Registration_and_Update_Phone_Number": "Untuk Registrasi dan Perbarui 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",
@ -778,8 +781,8 @@
"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",
"Input_Desired_Amount": "Masukkan jumlah yang diinginkan",
"Advanced_Hotspot_System": "Sistem Hotspot Lanjutan",
"Successful_Payments": "Pembayaran Berhasil",
"More_Info": "Info lebih lanjut",
"Failed_Payments": "Pembayaran Gagal",
@ -792,7 +795,7 @@
"_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.",
"_Pay_with_Demo_Success_": "Bayar dengan Demo berhasil.",
"_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",
@ -825,7 +828,7 @@
"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",
"Export_and_Print_will_show_all_data_without_pagination": "Ekspor dan Cetak akan menampilkan semua data tanpa paginasi",
"First_Name": "Nama Depan",
"Last_Name": "Nama Belakang",
"General": "Umum",
@ -885,7 +888,7 @@
"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",
"Input_custom_balance__will_ignore_plan_above": "Masukkan saldo khusus, akan mengabaikan paket di atas",
"Note": "Catatan",
"Customer_Login_Page_Settings": "Pengaturan Halaman Login Pelanggan",
"Choose_Template": "Pilih Template",
@ -909,5 +912,7 @@
"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]"
}
"Expired_Cronjob_Every_5_Minutes__Recommended_": "Cronjob Kedaluwarsa Setiap 5 Menit [Direkomendasikan]",
"Visit": "Kunjungi",
"sync": "Sinkron"
}

View file

@ -8,7 +8,7 @@
"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": "WENJEI",
"PHPNuxBill": "Compañia",
"Username": "Usuario",
"Password": "Contrase\u00f1a",
"Passwords_does_not_match": "Las contrase\u00f1as no coinciden",
@ -541,7 +541,7 @@
"Agent": "Agente",
"Session_has_expired__Please_log_in_again_": "La sesi\u00f3n ha expirado. Por favor, inicie sesi\u00f3n nuevamente.",
"danger": "Peligro",
"City": "Ciudad",
"City": "Pais",
"District": "Distrito",
"State": "Estado",
"Zip": "C\u00f3digo Postal",
@ -565,5 +565,278 @@
"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"
"Export_and_Print_will_show_all_data_without_pagination": "Exportar e imprimir mostrar\u00e1 todos los datos sin paginaci\u00f3n",
"Maps": "Mapas",
"Custom_Fields": "Campos personalizados",
"Cron_appear_not_been_setup__please_check_your_cron_setup_": "Aparece que no se ha configurado Cron, consulte su configuraci\u00f3n de Cron.",
"Check_if_Router_Online_": "\u00bfVerifique si Router Online?",
"To_check_whether_the_Router_is_online_or_not__please_visit_the_following_page": "Para verificar si el enrutador est\u00e1 en l\u00ednea o no, visite la p\u00e1gina siguiente",
"Cek_Now": "Cek ahora",
"Enable": "Permitir",
"Disable": "Desactivar",
"Router_Name___Location": "Nombre \/ ubicaci\u00f3n del enrutador",
"Coordinates": "Coordenadas",
"Coverage": "Cobertura",
"Continue_the_process_of_changing_Routers_": "\u00bfContinuar el proceso de cambiar los enrutadores?",
"First_Name": "Nombre",
"Last_Name": "Apellido",
"Plugin_Installer": "Instalador de plugin",
"Upload_Zip_Plugin_Theme_Device": "Subir el complemento\/tema\/dispositivo con zip",
"Install": "Instalar",
"CPU_Load": "Carga de la CPU",
"Temperature": "Temperatura",
"Voltage": "Voltaje",
"Wireless_Status": "Estado inal\u00e1mbrico",
"Interface_Status": "Estado de la interfaz",
"Hotspot_Online_Users": "Usuarios en l\u00ednea de hotspot",
"PPPoE_Online_Users": "Usuarios en l\u00ednea de PPPOE",
"Traffic_Monitor": "Monitor de tr\u00e1fico",
"Interface_Name": "Nombre de la interfaz",
"Tx__bytes_Out_": "Tx (bytes fuera)",
"Rx__bytes_In_": "Rx (bytes en)",
"Total_Usage": "Uso total",
"Uptime": "Tiempo de actividad",
"Server": "Servidor",
"Mac_Address": "Direcci\u00f3n MAC",
"Session_Time_Left": "Tiempo de sesi\u00f3n restante",
"Upload__RX_": "Cargue (RX)",
"Download__TX_": "Descargar (TX)",
"Service": "Servicio",
"Caller_ID": "Identificador de llamadas",
"Download": "Descargar",
"Upload": "Subir",
"Interface": "Interfaz",
"Last_Ip": "\u00daltima IP",
"Last_Activity": "\u00daltima actividad",
"Signal_Strength": "Resistencia a la se\u00f1al",
"Tx___Rx_CCQ": "TX \/ RX CCQ",
"Rx_Rate": "Tasa de rx",
"Tx_Rate": "Tasa de tx",
"Interace": "Interacci\u00f3n",
"TX": "Tx",
"RX": "Rx",
"Date_Time": "Fecha\/hora",
"Topic": "Tema",
"Send_Personal_Message": "Enviar mensaje personal",
"Send_Via": "Enviar",
"via_SMS": "a trav\u00e9s de SMS",
"Via_WhatsApp": "A trav\u00e9s de whatsapp",
"Via_WhatsApp_and_SMS": "A trav\u00e9s de whatsapp y sms",
"Compose_your_message___": "Componga tu mensaje ...",
"Use_placeholders_": "Use marcadores de posici\u00f3n:",
"Customer_Name": "Nombre del cliente",
"Customer_Username": "Nombre de usuario del cliente",
"Customer_Phone": "Tel\u00e9fono del cliente",
"Your_Company_Name": "Nombre de su empresa",
"Sign_in_into_your_account": "Inicie sesi\u00f3n en su cuenta",
"Usernames": "Nombre de usuario",
"Don_t_have_an_account_": "\u00bfNo tienes una cuenta?",
"General": "General",
"Pretty_URL": "Bonita url",
"rename__htaccess_firewall_to__htaccess": "renombrar .htaccess_firewall a .htaccess",
"Customer_Login_Page_Settings": "Configuraci\u00f3n de la p\u00e1gina de inicio de sesi\u00f3n del cliente",
"Choose_Template": "Elija plantilla",
"Custom": "Costumbre",
"Select_your_login_template_type": "Seleccione su tipo de plantilla de inicio de sesi\u00f3n",
"Select_Login_Page": "Seleccione la p\u00e1gina de inicio de sesi\u00f3n",
"Select_your_preferred_login_template": "Seleccione su plantilla de inicio de sesi\u00f3n preferida",
"Page_Heading___Company_Name": "Encabezado de p\u00e1gina \/ nombre de la empresa",
"This_Name_will_be_shown_on_the_login_wallpaper": "Este nombre se mostrar\u00e1 en el fondo de pantalla de inicio de sesi\u00f3n.",
"Page_Description": "Descripci\u00f3n de la p\u00e1gina",
"This_will_also_display_on_wallpaper__You_can_use_html_tag": "Esto tambi\u00e9n se mostrar\u00e1 en papel tapiz, puede usar la etiqueta HTML",
"Favicon": "Favic\u00f3n",
"Best_size_30_x_30___uploaded_image_will_be_autosize": "El mejor tama\u00f1o 30 x 30 | La imagen cargada se automatizar\u00e1",
"Login_Page_Logo": "Logotipo de la p\u00e1gina de inicio de sesi\u00f3n",
"Best_size_300_x_60___uploaded_image_will_be_autosize": "El mejor tama\u00f1o 300 x 60 | La imagen cargada se automatizar\u00e1",
"Login_Page_Wallpaper": "Fondo de pantalla de la p\u00e1gina de inicio de sesi\u00f3n",
"Best_size_1920_x_1080___uploaded_image_will_be_autosize": "El mejor tama\u00f1o 1920 x 1080 | La imagen cargada se automatizar\u00e1",
"Registration": "Registro",
"Allow_Registration": "Permitir el registro",
"No_Registration": "Sin registro",
"Registration_Username": "Nombre de usuario de registro",
"Photo_Required": "Foto requerida",
"Customer_Registration_need_to_upload_their_photo": "El registro del cliente necesita subir su foto",
"Customer_Registration_need_to_validate_using_OTP": "El registro del cliente debe validar con OTP",
"For_Registration_and_Update_Phone_Number": "Para el n\u00famero de tel\u00e9fono de registro y actualizaci\u00f3n",
"Notify_Admin": "Notificar a administrador",
"Notify_Admin_upon_self_registration": "Notificar al administrador sobre el registro autom\u00e1tico",
"Mandatory_Fields": "Campos obligatorios",
"Security": "Seguridad",
"Single_Admin_Session": "Sesi\u00f3n de administrador \u00fanica",
"Admin_can_only_have_single_session_login__it_will_logout_another_session": "El administrador solo puede tener inicio de sesi\u00f3n de una sola sesi\u00f3n, iniciar\u00e1 sesi\u00f3n en otra sesi\u00f3n",
"Enable_CSRF_Validation": "Habilitar la validaci\u00f3n de CSRF",
"Cross_site_request_forgery": "Falsificaci\u00f3n de solicitud de sitio cruzado",
"SMS_Notification": "Notificaci\u00f3n de SMS",
"Mikrotik_SMS_Command": "Comando de mikrotik sms",
"Tax_Rates_by_percentage": "Tasas impositivas por porcentaje",
"Settings_For_Mikrotik": "Configuraci\u00f3n para Mikrotik",
"Settings_For_Cron_Expired": "Configuraci\u00f3n para Cron expirado",
"Expired_Cronjob_Every_5_Minutes__Recommended_": "Cronjob vencido cada 5 minutos [recomendado]",
"Settings_For_Cron_Reminder": "Configuraci\u00f3n para el recordatorio de Cron",
"Login_Page_Settings_Saved_Successfully": "Configuraci\u00f3n de la p\u00e1gina de inicio de sesi\u00f3n guardada correctamente",
"Logout_Successful": "INCOMPTIR SEXITO",
"warning": "Advertencia",
"Go_Back": "Volver",
"Validate": "Validar",
"Forgot_Usernames": "Olvid\u00e9 nombres de usuario",
"You_are_already_logged_in": "Ya est\u00e1s iniciado",
"Hello": "Hola",
"your_internet_package": "Tu paquete de Internet",
"has_been_expired": "ha sido expirado",
"will_be_replaced_with_Customer_Name": "ser\u00e1 reemplazado por el nombre del cliente",
"will_be_replaced_with_Customer_username": "ser\u00e1 reemplazado por el nombre de usuario del cliente",
"will_be_replaced_with_Package_name": "ser\u00e1 reemplazado por el nombre del paquete",
"will_be_replaced_with_Package_price": "ser\u00e1 reemplazado por el precio del paquete",
"additional_bills_for_customers": "facturas adicionales para los clientes",
"will_be_replaced_with_Expiration_date": "ser\u00e1 reemplazado por la fecha de vencimiento",
"Your_Company_Name_at_Settings": "Nombre de su empresa en la configuraci\u00f3n",
"Your_Company_Address_at_Settings": "Direcci\u00f3n de su empresa en la configuraci\u00f3n",
"Your_Company_Phone_at_Settings": "Tel\u00e9fono de su empresa en la configuraci\u00f3n",
"Invoice_number": "N\u00famero de factura",
"Date_invoice_created": "Factura de fecha creada",
"Payment_gateway_user_paid_from": "El usuario de la pasarela de pago pagado por",
"Payment_channel_user_paid_from": "Usuario de canal de pago pagado desde",
"is_Hotspot_or_PPPOE": "es hotspot o pppoe",
"Internet_Package_Prices": "Precios de paquetes de Internet",
"Receiver_name": "Nombre del receptor",
"Username_internet": "Nombre de usuario Internet",
"User_password": "Contrase\u00f1a de usuario",
"Expired_datetime": "Expirado de fecha y hora",
"For_Notes_by_admin": "Para notas de admin",
"Transaction_datetime": "Transacci\u00f3n data de data",
"Balance_Before": "Equilibrar antes",
"Balance_After": "Equilibrar",
"Welcome_Message": "Mensaje de bienvenida",
"will_be_replaced_with_Customer_password": "ser\u00e1 reemplazado por la contrase\u00f1a del cliente",
"will_be_replaced_with_Customer_Portal_URL": "ser\u00e1 reemplazado por URL del portal del cliente",
"will_be_replaced_with_Company_Name": "ser\u00e1 reemplazado por el nombre de la empresa",
"Save": "Guardar",
"Save_as_template": "Guardar como plantilla",
"Template_Name": "Nombre de plantilla",
"Package_Price": "Precio del paquete",
"Voucher_Code": "C\u00f3digo de cup\u00f3n",
"Voucher_Package": "Paquete de cupones",
"Counter": "Contador",
"Sub_District": "Distrito",
"Ward": "Código Postal",
"Profile": "Perfil",
"Photo": "Foto",
"Phone": "Tel\u00e9fono",
"Credentials": "Cartas credenciales",
"RB5009_39": "RB5009-39",
"Admin": "Administraci\u00f3n",
"VPN_Plans": "Planes VPN",
"Using": "Usando",
"Postpaid_Recharge_for_the_first_time_use": "Recarga pospago por primera vez uso",
"Or": "O",
"Confirm": "Confirmar",
"Name": "Nombre",
"Plan": "Plan",
"Resend": "Revender",
"Home_Address": "Direcci\u00f3n de la casa",
"Other": "Otro",
"Business": "Negocio",
"Not_Working_for_freeradius": "No funciona para freeradius",
"Also_Working_for_freeradius": "Tambi\u00e9n trabajando para freeradius",
"User_Cannot_change_this__only_admin__if_it_Empty_it_will_use_Customer_Credentials": "El usuario no puede cambiar esto, solo administrador. Si se vac\u00eda, usar\u00e1 las credenciales del cliente",
"Send_welcome_message": "Enviar mensaje de bienvenida",
"Notification_via": "Notificaci\u00f3n a trav\u00e9s de",
"SMS": "SMS",
"WA": "Whatsapp",
"Attributes": "Atributos",
"Additional_Information": "informaci\u00f3n adicional",
"City_of_Resident": "Ciudad del residente",
"State_of_Resident": "Estado de residente",
"Zip_Code": "C\u00f3digo postal",
"Continue_the_process_of_adding_Customer_Data_": "\u00bfContinuar el proceso de agregar datos de clientes?",
"Customer_cannot_buy_disabled_Package__but_admin_can_recharge_it__use_it_if_you_want_only_admin_recharge_it": "El cliente no puede comprar el paquete discapacitado, pero el administrador puede recargarlo, usarlo si solo desea recargarlo de administrador",
"Postpaid_will_have_fix_expired_date": "Postpaid tendr\u00e1 una fecha vencida",
"Prepaid": "Pagado",
"Postpaid": "Pospago",
"Personal_Package_will_only_show_to_personal_Customer__Business_Package_will_only_show_to_Business_Customer": "El paquete personal solo se mostrar\u00e1 al cliente personal, el paquete comercial solo se mostrar\u00e1 al cliente empresarial",
"Device": "Dispositivo",
"This_Device_are_the_logic_how_PHPNuxBill_Communicate_with_Mikrotik_or_other_Devices": "Este dispositivo es la l\u00f3gica de c\u00f3mo PhpNuxbill se comunica con Mikrotik u otros dispositivos",
"Price_Before_Discount": "Precio antes de descuento",
"For_Discount_Rate__this_is_price_before_get_discount__must_be_more_expensive_with_real_price": "Para una tasa de descuento, este es el precio antes de obtener descuento, debe ser m\u00e1s costoso con un precio real",
"1_Period___1_Month__Expires_the_20th_of_each_month": "1 per\u00edodo = 1 mes, expira el 20 de cada mes",
"Expired_Date": "Fecha vencida",
"Expired_will_be_this_date_every_month": "Caducado ser\u00e1 esta fecha cada mes",
"Expired_Action": "Acci\u00f3n vencida",
"Optional": "Opcional",
"Expired_Internet_Plan": "Plan de Internet vencido",
"When_Expired__customer_will_be_move_to_selected_internet_plan": "Cuando expire, el cliente se trasladar\u00e1 al plan de Internet seleccionado",
"on_login___on_up": "en el login \/ encendido",
"on_logout___on_down": "On-Logout \/ On-Down",
"Continue_the_PPPoE_Package_change_process_": "\u00bfContinuar el proceso de cambio de paquete PPPOE?",
"Period": "Per\u00edodo",
"Login_as_Customer": "Iniciar sesi\u00f3n como cliente",
"Rate": "Tasa",
"Create_Bandwidth_Package_for_expired_Internet_Package": "Crear paquete de ancho de banda para paquete de Internet vencido",
"When_customer_expired__you_can_move_it_to_Expired_Internet_Package": "Cuando el cliente caduque, podr\u00e1 moverlo a Paquete de Internet Caducado",
"Category": "Categor\u00eda",
"Create_expired_Internet_Plan": "Crear plan de Internet vencido",
"When_customer_expired__you_can_move_it_to_Expired_Internet_Plan": "Cuando el cliente caduque, podr\u00e1 moverlo a Plan de Internet Caducado",
"Personal_Package_will_only_show_to_personal_Customer__Business_package_will_only_show_to_Business_Customer": "El paquete personal solo se mostrar\u00e1 a los clientes personales, el paquete comercial solo se mostrar\u00e1 a los clientes comerciales",
"Continue_the_process_of_adding_the_PPPoE_Package_": "\u00bfContinuar con el proceso de agregar el paquete PPPoE?",
"Face_Detection": "Detecci\u00f3n de rostros",
"Customer_cannot_login_again": "El cliente no puede iniciar sesi\u00f3n nuevamente",
"Customer_can_login_but_cannot_buy_internet_package__Admin_cannot_recharge_customer": "El cliente puede iniciar sesi\u00f3n pero no puede comprar un paquete de Internet. El administrador no puede recargar al cliente.",
"Don_t_forget_to_deactivate_all_active_package_too": "No olvides desactivar tambi\u00e9n todos los paquetes activos.",
"Not_Working_with_Freeradius_Mysql": "No funciona con Freeradius Mysql",
"Continue_the_Customer_Data_change_process_": "\u00bfContinuar con el proceso de cambio de datos del cliente?",
"Port_Pool": "Grupo de puerto",
"New_port": "Nuevo puerto",
"Port_Name": "Nombre del puerto",
"Public_IP": "IP p\u00fablica",
"Range_Port": "Rango de Puertos",
"Add_Port_Pool": "Agregar grupo de puertos",
"Continue_the_process_of_adding_Ports_": "\u00bfContinuar con el proceso de agregar puertos?",
"Continue_the_VPN_creation_process_": "\u00bfContinuar con el proceso de creaci\u00f3n de VPN?",
"": "",
"Local_IP": "IP local",
"Test_Connection": "Conexi\u00f3n de prueba",
"Continue_the_process_of_adding_Routers_": "\u00bfContinuar con el proceso de agregar enrutadores?",
"Continue_the_Pool_addition_process_": "\u00bfContinuar con el proceso de adici\u00f3n de la piscina?",
"Private_IP": "IP privada",
"Sync_account_if_you_failed_login_to_internet": "Sincronizar cuenta si no se pudo iniciar sesi\u00f3n en Internet",
"Data_Change": "Cambio de datos",
"Face_Detect": "Detecci\u00f3n de rostro",
"Email_Address": "Direcci\u00f3n de correo electr\u00f3nico",
"Transaction_History_List": "Lista de historial de transacciones",
"Contabo": "Contabo",
"Not_Active": "No activo",
"New_Service_Package": "Nuevo paquete de servicios",
"Limit": "L\u00edmite",
"Time": "Tiempo",
"Data": "Datos",
"ID": "IDENTIFICACI\u00d3N",
"Create_expired_Internet_Package": "Crear paquete de Internet vencido",
"Oops__The_page_you_are_looking_for_was_not_found": "\u00a1Ups! La p\u00e1gina que est\u00e1 buscando no fue encontrada",
"Back_to_Dashboard": "Volver al panel de control",
"Add_User": "Agregar usuario",
"Report_Viewer": "Visor de informes",
"Super_Administrator": "Super administrador",
"Send_Notification": "Enviar notificaci\u00f3n",
"Don_t_Send": "No env\u00ede",
"Registration_successful": "Registro exitoso",
"New_User_Registration": "Registro de nuevo usuario",
"New_Field": "Nuevo campo",
"Installed_Devices": "Dispositivos instalados",
"Miscellaneous_Settings": "Configuraciones varias",
"Display_bandwidth_plan_for_customer": "Mostrar plan de ancho de banda para el cliente",
"Radius_Rest_Interim_Update": "Actualizaci\u00f3n provisional de Radius Rest",
"in_minutes__leave_0_to_disable_this_feature_": "en minutos, deje 0 para deshabilitar esta funci\u00f3n.",
"Check_if_Customer_Online": "Verificar si el cliente est\u00e1 en l\u00ednea",
"This_will_show_is_Customer_currently_is_online_or_not": "Esto mostrar\u00e1 si el cliente est\u00e1 actualmente en l\u00ednea o no.",
"Allow_Balance_Custom_Amount": "Permitir saldo Importe personalizado",
"Allow_Customer_buy_balance_with_any_amount": "Permitir al Cliente comprar saldo con cualquier monto",
"Make_sure_you_use_API_Port__Default_8728": "Aseg\u00farese de utilizar el puerto API, predeterminado 8728",
"Make_sure_Username_and_Password_are_correct": "Aseg\u00farese de que el nombre de usuario y la contrase\u00f1a sean correctos",
"Make_sure_your_hosting_not_blocking_port_to_external": "Aseg\u00farese de que su hosting no bloquee el puerto al externo",
"Make_sure_your_Mikrotik_accessible_from_PHPNuxBill": "Aseg\u00farese de que su Mikrotik sea accesible desde PHPNuxBill",
"If_you_just_update_PHPNuxBill_from_upload_files__try_click_Update": "Si simplemente actualiza PHPNuxBill desde la carga de archivos, intente hacer clic en Actualizar",
"Update": "Actualizar",
"Update_PHPNuxBill": "Actualizar PHPNuxBill",
"Ask_Github_Community": "Pregunta a la comunidad de Github",
"Ask_Telegram_Community": "Pregunta a la comunidad de Telegram"
}

View file

@ -1483,6 +1483,14 @@ class ORM implements ArrayAccess
return $this->_add_simple_where($column_name, 'LIKE', $value);
}
/**
* Add any WHERE ... LIKE clause to your query.
*/
public function where_likes($column = null, $values = null)
{
return $this->_addWhere('(' . implode(' LIKE ? OR ', $column) . ' LIKE ? )', $values);
}
/**
* Add where WHERE ... NOT LIKE clause to your query.
*/

View file

@ -182,10 +182,29 @@
"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": [
"2024.12.20": [
"ALTER TABLE `tbl_voucher` ADD `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;"
],
"2025.1.23": [
"2025.1.23": [
"ALTER TABLE `rad_acct` ADD `acctsessiontime` BIGINT(12) NOT NULL DEFAULT '0' AFTER `framedipaddress`;"
],
"2025.2.14": [
"CREATE TABLE IF NOT EXISTS `tbl_widgets` ( `id` int NOT NULL AUTO_INCREMENT, `orders` int NOT NULL DEFAULT '99', `position` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1. top 2. left 3. right 4. bottom',`enabled` tinyint(1) NOT NULL DEFAULT '1', `title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `widget` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `content` text COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
],
"2025.2.17" : [
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `enabled`, `title`, `widget`, `content`) VALUES (1, 1, 1, 1, 'Top Widget', 'top_widget', ''),(2, 2, 1, 1, 'Default Info', 'default_info_row', ''),(3, 1, 2, 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''),(4, 2, 2, 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''),(5, 3, 2, 1, 'Voucher Stocks', 'voucher_stocks', ''),(6, 4, 2, 1, 'Customer Expired', 'customer_expired', ''),(7, 1, 3, 1, 'Cron Monitor', 'cron_monitor', ''),(8, 2, 3, 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''),(9, 3, 3, 1, 'Info Payment Gateway', 'info_payment_gateway', ''),(10, 4, 3, 1, 'Graph Customers Insight', 'graph_customers_insight', ''),(11, 5, 3, 1, 'Activity Log', 'activity_log', '');"
],
"2025.2.19" : [
"ALTER TABLE `tbl_widgets` ADD `user` ENUM('Admin','Agent','Sales','Customer') NOT NULL DEFAULT 'Admin' AFTER `position`;"
],
"2025.2.21" : [
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (60, 1, 2, 'Customer', 1, 'Account Info', 'account_info', ''),(61, 3, 1, 'Customer', 1, 'Active Internet Plan', 'active_internet_plan', ''),(62, 4, 1, 'Customer', 1, 'Balance Transfer', 'balance_transfer', ''),(63, 1, 1, 'Customer', 1, 'Unpaid Order', 'unpaid_order', ''),(64, 2, 1, 'Customer', 1, 'Announcement', 'announcement', ''),(65, 5, 1, 'Customer', 1, 'Recharge A Friend', 'recharge_a_friend', ''),(66, 2, 2, 'Customer', 1, 'Voucher Activation', 'voucher_activation', '');"
],
"2025.2.25" : [
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (30, 1, 1, 'Agent', 1, 'Top Widget', 'top_widget', ''), (31, 2, 1, 'Agent', 1, 'Default Info', 'default_info_row', ''), (32, 1, 2, 'Agent', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''), (33, 2, 2, 'Agent', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''), (34, 3, 2, 'Agent', 1, 'Voucher Stocks', 'voucher_stocks', ''), (35, 4, 2, 'Agent', 1, 'Customer Expired', 'customer_expired', ''), (36, 1, 3, 'Agent', 1, 'Cron Monitor', 'cron_monitor', ''), (37, 2, 3, 'Agent', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''), (38, 3, 3, 'Agent', 1, 'Info Payment Gateway', 'info_payment_gateway', ''), (39, 4, 3, 'Agent', 1, 'Graph Customers Insight', 'graph_customers_insight', ''),(40, 5, 3, 'Agent', 1, 'Activity Log', 'activity_log', '');",
"INSERT INTO `tbl_widgets` (`id`, `orders`, `position`, `user`, `enabled`, `title`, `widget`, `content`) VALUES (41, 1, 1, 'Sales', 1, 'Top Widget', 'top_widget', ''), (42, 2, 1, 'Sales', 1, 'Default Info', 'default_info_row', ''), (43, 1, 2, 'Sales', 1, 'Graph Monthly Registered Customers', 'graph_monthly_registered_customers', ''), (44, 2, 2, 'Sales', 1, 'Graph Monthly Sales', 'graph_monthly_sales', ''), (45, 3, 2, 'Sales', 1, 'Voucher Stocks', 'voucher_stocks', ''), (46, 4, 2, 'Sales', 1, 'Customer Expired', 'customer_expired', ''), (47, 1, 3, 'Sales', 1, 'Cron Monitor', 'cron_monitor', ''), (48, 2, 3, 'Sales', 1, 'Mikrotik Cron Monitor', 'mikrotik_cron_monitor', ''), (49, 3, 3, 'Sales', 1, 'Info Payment Gateway', 'info_payment_gateway', ''), (50, 4, 3, 'Sales', 1, 'Graph Customers Insight', 'graph_customers_insight', ''), (51, 5, 3, 'Sales', 1, 'Activity Log', 'activity_log', '');"
],
"2025.3.5" : [
"CREATE TABLE IF NOT EXISTS `tbl_message_logs` ( `id` SERIAL PRIMARY KEY, `message_type` VARCHAR(50), `recipient` VARCHAR(255), `message_content` TEXT, `status` VARCHAR(50), `error_message` TEXT, `sent_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
]
}
}

File diff suppressed because one or more lines are too long

BIN
system/uploads/paid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

View file

@ -0,0 +1,15 @@
<?php
class activity_log
{
public function getWidget()
{
global $config, $ui, $current_date, $start_date;
$dlog = ORM::for_table('tbl_logs')->limit(5)->order_by_desc('id')->findArray();
$ui->assign('dlog', $dlog);
// $log = ORM::for_table('tbl_logs')->count();
// $ui->assign('log', $log);
return $ui->fetch('widget/activity_log.tpl');
}
}

View file

@ -0,0 +1,17 @@
<?php
class cron_monitor
{
public function getWidget()
{
global $UPLOAD_PATH,$ui;
$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));
}
return $ui->fetch('widget/cron_monitor.tpl');
}
}

View file

@ -0,0 +1,14 @@
<?php
class account_info
{
public function getWidget()
{
global $ui;
$abills = User::getAttributes("Bill");
$ui->assign('abills', $abills);
return $ui->fetch('widget/customers/account_info.tpl');
}
}

View file

@ -0,0 +1,20 @@
<?php
class active_internet_plan
{
public function getWidget()
{
global $ui, $user;
$_bill = User::_billing();
$ui->assign('_bills', $_bill);
$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);
return $ui->fetch('widget/customers/active_internet_plan.tpl');
}
}

View file

@ -0,0 +1,11 @@
<?php
class announcement
{
public function getWidget()
{
global $ui;
return $ui->fetch('widget/customers/announcement.tpl');
}
}

View file

@ -0,0 +1,11 @@
<?php
class balance_transfer
{
public function getWidget()
{
global $ui;
return $ui->fetch('widget/customers/balance_transfer.tpl');
}
}

View file

@ -0,0 +1,11 @@
<?php
class button_order_internet_plan
{
public function getWidget()
{
global $ui;
return $ui->fetch('widget/customers/button_order_internet_plan.tpl');
}
}

View file

@ -0,0 +1,11 @@
<?php
class html_only
{
public function getWidget($data = null)
{
global $ui;
return $data['content'];
}
}

View file

@ -0,0 +1,22 @@
<?php
class html_php
{
public function getWidget($data = null)
{
global $ui;
$ui->assign('card_header', $data['title']);
ob_start();
try{
eval('?>'. $data['content']);
}catch(Exception $e){
echo $e->getMessage();
echo "<br>";
echo $e->getTraceAsString();
}
$content = ob_get_clean();
$ui->assign('card_body', $content);
return $ui->fetch('widget/card_html.tpl');
}
}

View file

@ -0,0 +1,11 @@
<?php
class recharge_a_friend
{
public function getWidget()
{
global $ui;
return $ui->fetch('widget/customers/recharge_a_friend.tpl');
}
}

View file

@ -0,0 +1,39 @@
<?php
class unpaid_order
{
public function getWidget()
{
global $ui, $user;
$unpaid = ORM::for_table('tbl_payment_gateway')
->where('username', $user['username'])
->where('status', 1)
->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);
return $ui->fetch('widget/customers/unpaid_order.tpl');
}
}

View file

@ -0,0 +1,11 @@
<?php
class voucher_activation
{
public function getWidget()
{
global $ui;
return $ui->fetch('widget/customers/voucher_activation.tpl');
}
}

View file

@ -0,0 +1,63 @@
<?php
class customer_expired
{
public function getWidget()
{
global $ui, $current_date, $config;
//user expire
$query = ORM::for_table('tbl_user_recharges')
->table_alias('tur')
->selects([
'c.id',
'tur.username',
'c.fullname',
'c.phonenumber',
'c.email',
'tur.expiration',
'tur.time',
'tur.recharged_on',
'tur.recharged_time',
'tur.namebp',
'tur.routers'
])
->innerJoin('tbl_customers', ['tur.customer_id', '=', 'c.id'], 'c')
->where_lte('expiration', $current_date)
->order_by_desc('expiration');
$expire = Paginator::findMany($query);
// Get the total count of expired records for pagination
$totalCount = ORM::for_table('tbl_user_recharges')
->where_lte('expiration', $current_date)
->count();
// Pass the total count and current page to the paginator
$paginator['total_count'] = $totalCount;
if(!empty($_COOKIE['expdef']) && $_COOKIE['expdef'] != $config['customer_expired_expdef']) {
$d = ORM::for_table('tbl_appconfig')->where('setting', 'customer_expired_expdef')->find_one();
if ($d) {
$d->value = $_COOKIE['expdef'];
$d->save();
} else {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = 'customer_expired_expdef';
$d->value = $_COOKIE['expdef'];
$d->save();
}
}
if(!empty($config['customer_expired_expdef']) && empty($_COOKIE['expdef'])){
$_COOKIE['expdef'] = $config['customer_expired_expdef'];
setcookie('expdef', $config['customer_expired_expdef'], time() + (86400 * 30), "/");
}
// Assign the pagination HTML to the template variable
$ui->assign('expire', $expire);
$ui->assign('cookie', $_COOKIE);
return $ui->fetch('widget/customer_expired.tpl');
}
}

View file

@ -0,0 +1,17 @@
<?php
class default_info_row
{
public function getWidget()
{
global $config,$ui;
if ($config['enable_balance'] == 'yes'){
$cb = ORM::for_table('tbl_customers')->whereGte('balance', 0)->sum('balance');
$ui->assign('cb', $cb);
}
return $ui->fetch('widget/default_info_row.tpl');
}
}

View file

@ -0,0 +1,28 @@
<?php
class graph_customers_insight
{
public function getWidget()
{
global $CACHE_PATH,$ui;
$u_act = ORM::for_table('tbl_user_recharges')->where('status', 'on')->count();
if (empty($u_act)) {
$u_act = '0';
}
$ui->assign('u_act', $u_act);
$u_all = ORM::for_table('tbl_user_recharges')->count();
if (empty($u_all)) {
$u_all = '0';
}
$ui->assign('u_all', $u_all);
$c_all = ORM::for_table('tbl_customers')->count();
if (empty($c_all)) {
$c_all = '0';
}
$ui->assign('c_all', $c_all);
return $ui->fetch('widget/graph_customers_insight.tpl');
}
}

View file

@ -0,0 +1,38 @@
<?php
class graph_monthly_registered_customers
{
public function getWidget()
{
global $CACHE_PATH,$ui;
$cacheMRfile = $CACHE_PATH . File::pathFixer('/monthlyRegistered.temp');
//Compatibility for old path
if (file_exists($oldCacheMRfile = str_replace($CACHE_PATH, '', $cacheMRfile))) {
rename($oldCacheMRfile, $cacheMRfile);
}
//Cache for 1 hour
if (file_exists($cacheMRfile) && time() - filemtime($cacheMRfile) < 3600) {
$monthlyRegistered = json_decode(file_get_contents($cacheMRfile), true);
} else {
//Monthly Registered Customers
$result = ORM::for_table('tbl_customers')
->select_expr('MONTH(created_at)', 'month')
->select_expr('COUNT(*)', 'count')
->where_raw('YEAR(created_at) = YEAR(NOW())')
->group_by_expr('MONTH(created_at)')
->find_many();
$monthlyRegistered = [];
foreach ($result as $row) {
$monthlyRegistered[] = [
'date' => $row->month,
'count' => $row->count
];
}
file_put_contents($cacheMRfile, json_encode($monthlyRegistered));
}
$ui->assign('monthlyRegistered', $monthlyRegistered);
return $ui->fetch('widget/graph_monthly_registered_customers.tpl');
}
}

View file

@ -0,0 +1,60 @@
<?php
class graph_monthly_sales
{
public function getWidget()
{
global $CACHE_PATH, $ui;
$cacheMSfile = $CACHE_PATH . File::pathFixer('/monthlySales.temp');
//Cache for 12 hours
if (file_exists($cacheMSfile) && time() - filemtime($cacheMSfile) < 43200) {
$monthlySales = json_decode(file_get_contents($cacheMSfile), true);
} else {
// Query to retrieve monthly data
$results = ORM::for_table('tbl_transactions')
->select_expr('MONTH(recharged_on)', 'month')
->select_expr('SUM(price)', 'total')
->where_raw("YEAR(recharged_on) = YEAR(CURRENT_DATE())") // Filter by the current year
->where_not_equal('method', 'Customer - Balance')
->where_not_equal('method', 'Recharge Balance - Administrator')
->group_by_expr('MONTH(recharged_on)')
->find_many();
// Create an array to hold the monthly sales data
$monthlySales = array();
// Iterate over the results and populate the array
foreach ($results as $result) {
$month = $result->month;
$totalSales = $result->total;
$monthlySales[$month] = array(
'month' => $month,
'totalSales' => $totalSales
);
}
// Fill in missing months with zero sales
for ($month = 1; $month <= 12; $month++) {
if (!isset($monthlySales[$month])) {
$monthlySales[$month] = array(
'month' => $month,
'totalSales' => 0
);
}
}
// Sort the array by month
ksort($monthlySales);
// Reindex the array
$monthlySales = array_values($monthlySales);
file_put_contents($cacheMSfile, json_encode($monthlySales));
}
$ui->assign('monthlySales', $monthlySales);
return $ui->fetch('widget/graph_monthly_sales.tpl');
}
}

View file

@ -0,0 +1,21 @@
<?php
class html_php
{
public function getWidget($data = null)
{
global $ui;
$ui->assign('card_header', $data['title']);
ob_start();
try{
eval('?>'. $data['content']);
}catch(Exception $e){
echo $e->getMessage();
echo "<br>";
echo $e->getTraceAsString();
}
$content = ob_get_clean();
return $content;
}
}

View file

@ -0,0 +1,22 @@
<?php
class html_php
{
public function getWidget($data = null)
{
global $ui;
$ui->assign('card_header', $data['title']);
ob_start();
try{
eval('?>'. $data['content']);
}catch(Exception $e){
echo $e->getMessage();
echo "<br>";
echo $e->getTraceAsString();
}
$content = ob_get_clean();
$ui->assign('card_body', $content);
return $ui->fetch('widget/card_html.tpl');
}
}

View file

@ -0,0 +1,11 @@
<?php
class info_payment_gateway
{
public function getWidget($data = null)
{
global $ui;
return $ui->fetch('widget/info_payment_gateway.tpl');
}
}

View file

@ -0,0 +1,16 @@
<?php
class mikrotik_cron_monitor
{
public function getWidget()
{
global $config,$ui;
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);
}
return $ui->fetch('widget/mikrotik_cron_monitor.tpl');
}
}

View file

@ -0,0 +1,18 @@
<?php
```php
class widget_name
{
public static getWidget($data)
{
global $config, $ui;
return $ui->fetch('widget/template');
}
}
```

View file

@ -0,0 +1,51 @@
<?php
class top_widget
{
public function getWidget()
{
global $ui, $current_date, $start_date;
$iday = ORM::for_table('tbl_transactions')
->where('recharged_on', $current_date)
->where_not_equal('method', 'Customer - Balance')
->where_not_equal('method', 'Recharge Balance - Administrator')
->sum('price');
if ($iday == '') {
$iday = '0.00';
}
$ui->assign('iday', $iday);
$imonth = ORM::for_table('tbl_transactions')
->where_not_equal('method', 'Customer - Balance')
->where_not_equal('method', 'Recharge Balance - Administrator')
->where_gte('recharged_on', $start_date)
->where_lte('recharged_on', $current_date)->sum('price');
if ($imonth == '') {
$imonth = '0.00';
}
$ui->assign('imonth', $imonth);
$u_act = ORM::for_table('tbl_user_recharges')->where('status', 'on')->count();
if (empty($u_act)) {
$u_act = '0';
}
$ui->assign('u_act', $u_act);
$u_all = ORM::for_table('tbl_user_recharges')->count();
if (empty($u_all)) {
$u_all = '0';
}
$ui->assign('u_all', $u_all);
$c_all = ORM::for_table('tbl_customers')->count();
if (empty($c_all)) {
$c_all = '0';
}
$ui->assign('c_all', $c_all);
return $ui->fetch('widget/top_widget.tpl');
}
}

View file

@ -0,0 +1,43 @@
<?php
class voucher_stocks
{
public function getWidget()
{
global $CACHE_PATH,$ui;
$cacheStocksfile = $CACHE_PATH . File::pathFixer('/VoucherStocks.temp');
$cachePlanfile = $CACHE_PATH . File::pathFixer('/VoucherPlans.temp');
//Cache for 5 minutes
if (file_exists($cacheStocksfile) && time() - filemtime($cacheStocksfile) < 600) {
$stocks = json_decode(file_get_contents($cacheStocksfile), true);
$plans = json_decode(file_get_contents($cachePlanfile), true);
} else {
// Count stock
$tmp = $v = ORM::for_table('tbl_plans')->select('id')->select('name_plan')->find_many();
$plans = array();
$stocks = array("used" => 0, "unused" => 0);
$n = 0;
foreach ($tmp as $plan) {
$unused = ORM::for_table('tbl_voucher')
->where('id_plan', $plan['id'])
->where('status', 0)->count();
$used = ORM::for_table('tbl_voucher')
->where('id_plan', $plan['id'])
->where('status', 1)->count();
if ($unused > 0 || $used > 0) {
$plans[$n]['name_plan'] = $plan['name_plan'];
$plans[$n]['unused'] = $unused;
$plans[$n]['used'] = $used;
$stocks["unused"] += $unused;
$stocks["used"] += $used;
$n++;
}
}
file_put_contents($cacheStocksfile, json_encode($stocks));
file_put_contents($cachePlanfile, json_encode($plans));
}
$ui->assign('stocks', $stocks);
$ui->assign('plans', $plans);
return $ui->fetch('widget/voucher_stocks.tpl');
}
}

View file

View file

@ -1,9 +0,0 @@
$(document).on("click", ".cdelete", function(e) {
e.preventDefault();
var id = this.id;
bootbox.confirm("Are you sure?", function(result) {
if(result){
window.location.href = "index.php?_route=bandwidth/delete/" + id;
}
});
});

View file

@ -1,9 +0,0 @@
$(document).on("click", ".cdelete", function(e) {
e.preventDefault();
var id = this.id;
bootbox.confirm("Are you sure?", function(result) {
if(result){
window.location.href = "index.php?_route=customers/delete/" + id;
}
});
});

View file

@ -1,9 +0,0 @@
$(document).on("click", ".cdelete", function(e) {
e.preventDefault();
var id = this.id;
bootbox.confirm("Are you sure?", function(result) {
if(result){
window.location.href = "index.php?_route=services/delete/" + id;
}
});
});

View file

@ -1,8 +0,0 @@
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>

View file

@ -1,9 +0,0 @@
$(document).on("click", ".cdelete", function(e) {
e.preventDefault();
var id = this.id;
bootbox.confirm("Are you sure?", function(result) {
if(result){
window.location.href = "index.php?_route=plan/delete/" + id;
}
});
});

View file

@ -1,9 +0,0 @@
$(document).on("click", ".cdelete", function(e) {
e.preventDefault();
var id = this.id;
bootbox.confirm("Are you sure?", function(result) {
if(result){
window.location.href = "index.php?_route=pool/delete/" + id;
}
});
});

View file

@ -1,9 +0,0 @@
$(document).on("click", ".cdelete", function(e) {
e.preventDefault();
var id = this.id;
bootbox.confirm("Are you sure?", function(result) {
if(result){
window.location.href = "index.php?_route=services/pppoe-delete/" + id;
}
});
});

View file

@ -1,9 +0,0 @@
$(document).on("click", ".cdelete", function(e) {
e.preventDefault();
var id = this.id;
bootbox.confirm("Are you sure?", function(result) {
if(result){
window.location.href = "index.php?_route=routers/delete/" + id;
}
});
});

View file

@ -1,9 +0,0 @@
$(document).on("click", ".cdelete", function(e) {
e.preventDefault();
var id = this.id;
bootbox.confirm("Are you sure?", function(result) {
if(result){
window.location.href = "index.php?_route=settings/users-delete/" + id;
}
});
});

View file

@ -1,9 +0,0 @@
$(document).on("click", ".cdelete", function(e) {
e.preventDefault();
var id = this.id;
bootbox.confirm("Are you sure?", function(result) {
if(result){
window.location.href = "index.php?_route=plan/voucher-delete/" + id;
}
});
});

File diff suppressed because one or more lines are too long

View file

@ -1,8 +0,0 @@
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>

View file

@ -1,186 +0,0 @@
/*! DataTables Bootstrap 3 integration
* ©2011-2014 SpryMedia Ltd - datatables.net/license
*/
/**
* DataTables integration for Bootstrap 3. This requires Bootstrap 3 and
* DataTables 1.10 or newer.
*
* This file sets the defaults and adds options to DataTables to style its
* controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap
* for further information.
*/
(function(window, document, undefined){
var factory = function( $, DataTable ) {
"use strict";
/* Set the defaults for DataTables initialisation */
$.extend( true, DataTable.defaults, {
dom:
"<'row'<'col-sm-6'l><'col-sm-6'f>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-6'i><'col-sm-6'p>>",
renderer: 'bootstrap'
} );
/* Default class modification */
$.extend( DataTable.ext.classes, {
sWrapper: "dataTables_wrapper form-inline dt-bootstrap",
sFilterInput: "form-control input-sm",
sLengthSelect: "form-control input-sm"
} );
/* Bootstrap paging button renderer */
DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {
var api = new DataTable.Api( settings );
var classes = settings.oClasses;
var lang = settings.oLanguage.oPaginate;
var btnDisplay, btnClass;
var attach = function( container, buttons ) {
var i, ien, node, button;
var clickHandler = function ( e ) {
e.preventDefault();
if ( !$(e.currentTarget).hasClass('disabled') ) {
api.page( e.data.action ).draw( false );
}
};
for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
button = buttons[i];
if ( $.isArray( button ) ) {
attach( container, button );
}
else {
btnDisplay = '';
btnClass = '';
switch ( button ) {
case 'ellipsis':
btnDisplay = '&hellip;';
btnClass = 'disabled';
break;
case 'first':
btnDisplay = lang.sFirst;
btnClass = button + (page > 0 ?
'' : ' disabled');
break;
case 'previous':
btnDisplay = lang.sPrevious;
btnClass = button + (page > 0 ?
'' : ' disabled');
break;
case 'next':
btnDisplay = lang.sNext;
btnClass = button + (page < pages-1 ?
'' : ' disabled');
break;
case 'last':
btnDisplay = lang.sLast;
btnClass = button + (page < pages-1 ?
'' : ' disabled');
break;
default:
btnDisplay = button + 1;
btnClass = page === button ?
'active' : '';
break;
}
if ( btnDisplay ) {
node = $('<li>', {
'class': classes.sPageButton+' '+btnClass,
'aria-controls': settings.sTableId,
'tabindex': settings.iTabIndex,
'id': idx === 0 && typeof button === 'string' ?
settings.sTableId +'_'+ button :
null
} )
.append( $('<a>', {
'href': '#'
} )
.html( btnDisplay )
)
.appendTo( container );
settings.oApi._fnBindAction(
node, {action: button}, clickHandler
);
}
}
}
};
attach(
$(host).empty().html('<ul class="pagination"/>').children('ul'),
buttons
);
};
/*
* TableTools Bootstrap compatibility
* Required TableTools 2.1+
*/
if ( DataTable.TableTools ) {
// Set the classes that TableTools uses to something suitable for Bootstrap
$.extend( true, DataTable.TableTools.classes, {
"container": "DTTT btn-group",
"buttons": {
"normal": "btn btn-default",
"disabled": "disabled"
},
"collection": {
"container": "DTTT_dropdown dropdown-menu",
"buttons": {
"normal": "",
"disabled": "disabled"
}
},
"print": {
"info": "DTTT_print_info"
},
"select": {
"row": "active"
}
} );
// Have the collection use a bootstrap compatible drop down
$.extend( true, DataTable.TableTools.DEFAULTS.oTags, {
"collection": {
"container": "ul",
"button": "li",
"liner": "a"
}
} );
}
}; // /factory
// Define as an AMD module if possible
if ( typeof define === 'function' && define.amd ) {
define( ['jquery', 'datatables'], factory );
}
else if ( typeof exports === 'object' ) {
// Node/CommonJS
factory( require('jquery'), require('datatables') );
}
else if ( jQuery ) {
// Otherwise simply initialise as normal, stopping multiple evaluation
factory( jQuery, jQuery.fn.dataTable );
}
})(window, document);

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