Compare commits

...

286 Commits

Author SHA1 Message Date
8f099bdefb Merge pull request 'Upload files to "/"' (#4) from nestict/mitrobill:master into master
Reviewed-on: #4
2025-05-14 14:50:13 +02:00
9fae0f84fd Upload files to "/" 2025-05-14 14:37:41 +02:00
d50a66845f Update README.md
Signed-off-by: kevinowino869 <kevinowino869@www.codelab.nestict.africa>
2025-04-14 11:01:36 +02:00
915af6fc03 Merge pull request 'Development' (#2) from Development into master
Reviewed-on: #2
2025-04-14 11:00:04 +02:00
Focuslinkstech
0a29ec9a86 refactor: replace the javascript popup with sweetalert which prevent some devices to purchase data plans and packages via captive portal, clean up footer template and improve script formatting 2025-04-11 09:19:17 +01:00
Focuslinkstech
27fd677a0a feat: add coupon settings panel with enable/disable option and save functionality 2025-04-10 08:13:01 +01:00
Focuslinkstech
e2f24c0cc6 fix: correct subject field handling in message sending logic and update related IDs in bulk template 2025-04-09 20:16:11 +01:00
Focuslinkstech
cf60c470b1 feat: add subject validation for message types and display error if missing 2025-04-09 12:50:30 +01:00
Focuslinkstech
1cc7057dca feat: add subject field for messages and implement validation based on selected channel 2025-04-09 12:36:13 +01:00
Focuslinkstech
1740c568f9 feat: add subject field for bulk messaging and update validation logic 2025-04-09 11:39:00 +01:00
Focuslinkstech
3347b39f3b refactor: improve SMS and email handling in Message class; add success/failure return values and update language file with new translations 2025-04-09 10:29:22 +01:00
Focuslinkstech
f0b9b56bb0 feat: add invoice listing page with DataTables integration and update routing 2025-04-08 18:59:15 +01:00
Focuslinkstech
24b713804a feat: implement invoice listing functionality with DataTables integration 2025-04-08 18:30:36 +01:00
Focuslinkstech
182add517c feat: implement custom invoice generation functionality 2025-04-08 17:45:18 +01:00
Focuslinkstech
45cc2afab5 fix: update invoice parameter documentation for clarity 2025-04-08 14:30:48 +01:00
Focuslinkstech
ba19b1c569 Added new functionality to Invoice class: generate invoice PDF, save to database, and send email. Also, updated language file with new translations. 2025-04-08 14:30:24 +01:00
Focuslinkstech
5caa9f905b feat: add Font Awesome 6.4.0 stylesheet to admin header template 2025-04-04 08:08:34 +01:00
Focuslinkstech
28541f366c feat: add fullname column to transaction reports and update related templates 2025-04-02 11:29:25 +01:00
Focuslinkstech
ad7998ebbf feat: add fullname field to transaction reports and update report template 2025-04-02 11:23:17 +01:00
Focuslinkstech
4c64cfabd2 fix: update demo stage check to use consistent casing 2025-04-02 11:11:05 +01:00
Focuslinkstech
9bae41dbe7 fix: correct notification reminder configuration keys for 3-day and 7-day reminders 2025-03-30 11:26:40 +01:00
Focuslinkstech
bad0545be5 feat: enhance messaging system to support multiple channels including email and inbox 2025-03-24 10:24:08 +01:00
Focuslinkstech
43a92c5d3b feat: add fullname field to transaction reports, pdf export and update language file 2025-03-22 18:34:51 +01:00
Focuslinkstech
11e5ebe103 feat: update .gitignore to include invoices directory and add index.html 2025-03-22 17:52:12 +01:00
Focuslinkstech
c573c49fb9 "Added exception for system/uploads/invoices/ directory to .gitignore" 2025-03-22 17:41:04 +01:00
Focuslinkstech
7bfbdb1efb feat: add fullname field to activation report and update language file 2025-03-22 16:15:16 +01:00
Focuslinkstech
dc28298d53 feat: add username field to customer query 2025-03-22 15:51:36 +01:00
iBNu Maksum
655e0494d3
Merge pull request #411 from dicobaja/patch/which-php
use `where` command for windows host
2025-03-20 15:37:30 +07:00
iBNu Maksum
4a441c5763
fix variable inside ', it must be inside " 2025-03-20 10:43:52 +07:00
dicobaja
5072ff8ba2
fix: use where instead of which for windows host 2025-03-19 21:44:30 +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 e9a947a24a7d7da519690cd8d7058700bc1291f0.
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 e9a947a24a7d7da519690cd8d7058700bc1291f0.
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
iBNu Maksum
5a2e75017e
2025.2.3 2025-02-03 14:11:48 +07:00
iBNu Maksum
609a24df80
pretty url error page 2025-02-03 12:10:33 +07:00
iBNu Maksum
31dda69d3d
get ready for Pretty URL 2025-01-31 16:54:22 +07:00
iBNu Maksum
d60b1827d9
add $app_url 2025-01-31 16:23:16 +07:00
iBNu Maksum
1e43ac210a
url using getUrl 2025-01-31 16:22:58 +07:00
Focuslinkstech
9bf80467a1
Merge pull request #383 from agstrxyz/patch-24
accsessiontime
2025-01-26 05:14:15 +01:00
AGSTR
093ac7e336
Update phpnuxbill.sql (#384)
acctsessiontime
2025-01-26 05:13:38 +01:00
AGSTR
a67681424e
Update updates.json (#385)
accsessiontime
2025-01-26 05:12:10 +01:00
AGSTR
7799bf1aee
Update radius.php
nasipaddress
acctsessiontime
2025-01-23 18:02:52 +07:00
iBNu Maksum
3502be2c78
Merge pull request #382 from ahmadhusein17/Development
Language fixes
2025-01-19 15:31:36 +07:00
Ahmad Husein
f6084f6ca4
Update balance-edit.tpl 2025-01-19 15:26:48 +07:00
Ahmad Husein
123b74001a
Update bandwidth-add.tpl 2025-01-19 15:25:45 +07:00
Ahmad Husein
a39bbc3b34
Update bandwidth-edit.tpl 2025-01-19 15:24:47 +07:00
Ahmad Husein
a15f759dcb
Update coupons-add.tpl 2025-01-19 15:22:38 +07:00
Ahmad Husein
883eb1bbee
Update coupons-edit.tpl 2025-01-19 15:18:57 +07:00
Ahmad Husein
8af46e5e51
Update customers-add.tpl 2025-01-19 15:16:11 +07:00
Ahmad Husein
1f1e529e3a
Update customers-edit.tpl 2025-01-19 15:09:05 +07:00
Ahmad Husein
bed81136a1
Update hotspot-edit.tpl 2025-01-19 14:24:48 +07:00
Ahmad Husein
f322282c6d
Update language-add.tpl 2025-01-19 14:20:06 +07:00
Ahmad Husein
396ece50f4
Update pool-add.tpl 2025-01-19 14:19:07 +07:00
Ahmad Husein
681d0214c8
Update pool-edit.tpl 2025-01-19 14:18:30 +07:00
Ahmad Husein
c7d9f8e8c6
Update port-add.tpl 2025-01-19 14:17:58 +07:00
Ahmad Husein
4dd190a61d
Update port-edit.tpl 2025-01-19 14:17:14 +07:00
Ahmad Husein
88bb3a29f1
Update pppoe-add.tpl 2025-01-19 14:16:34 +07:00
Ahmad Husein
5e8414aa61
Update pppoe-edit.tpl 2025-01-19 14:13:03 +07:00
Ahmad Husein
01e832a955
Update radius-nas-add.tpl 2025-01-19 14:07:57 +07:00
Ahmad Husein
ada655a074
Update radius-nas-edit.tpl 2025-01-19 14:07:10 +07:00
Ahmad Husein
3f3435157c
Update routers-add.tpl 2025-01-19 14:04:57 +07:00
Ahmad Husein
ec6d6cf203
Update routers-edit.tpl 2025-01-19 14:03:42 +07:00
Ahmad Husein
7026438acc
Update voucher-add.tpl 2025-01-19 14:02:30 +07:00
Ahmad Husein
8a6ee9490b
Update vpn-add.tpl 2025-01-19 14:00:36 +07:00
Ahmad Husein
89808f3291
Update vpn-edit.tpl 2025-01-19 13:57:23 +07:00
Ahmad Husein
8f6bc9f66e
Update balance-add.tpl 2025-01-19 13:51:09 +07:00
Ahmad Husein
87ebabe1c0
Add files via upload 2025-01-19 13:48:10 +07:00
Ahmad Husein
bf95713512
Update hotspot-add.tpl 2025-01-19 13:26:53 +07:00
iBNu Maksum
0f19c80cfc
change to google maps 2025-01-17 10:37:24 +07:00
289 changed files with 13752 additions and 25784 deletions

8
.gitignore vendored
View File

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

View File

@ -36,7 +36,7 @@ Most current web servers with PHP & MySQL installed will be capable of running P
Minimum Requirements
- Linux or Windows OS
- Minimum PHP Version 7.4
- Minimum PHP Version 8.2
- Both PDO & MySQLi Support
- PHP-GD2 Image Library
- PHP-CURL
@ -83,28 +83,4 @@ Contact me at [Telegram](https://t.me/ibnux)
GNU General Public License version 2 or later
see [LICENSE](LICENSE) file
## Donate to ibnux
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/ibnux)
BCA: 5410454825
Mandiri: 163-000-1855-793
a.n Ibnu Maksum
## SPONSORS
- [mixradius.com](https://mixradius.com/) Paid Services Billing Radius
- [mlink.id](https://mlink.id)
- [https://github.com/sonyinside](https://github.com/sonyinside)
## Thanks
We appreciate all people who are participating in this project.
<a href="https://github.com/hotspotbilling/phpnuxbill/graphs/contributors">
<img src="https://contrib.rocks/image?repo=hotspotbilling/phpnuxbill" />
</a>
see [LICENSE](LICENSE) file

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('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'];
@ -196,7 +215,7 @@ function _auth($login = true)
return true;
} else {
if ($login) {
r2(U . 'login');
r2(getUrl('login'));
} else {
return false;
}
@ -209,7 +228,7 @@ function _admin($login = true)
return true;
} else {
if ($login) {
r2(U . 'login');
r2(getUrl('login'));
} else {
return false;
}
@ -261,6 +280,13 @@ function showResult($success, $message = '', $result = [], $meta = [])
die();
}
/**
* make url canonical or standar
*/
function getUrl($url)
{
return Text::url($url);
}
function generateUniqueNumericVouchers($totalVouchers, $length = 8)
{
@ -333,16 +359,16 @@ function _alert($text, $type = 'success', $url = "home", $time = 3)
if (!isset($ui)) return;
if (strlen($url) > 4) {
if (substr($url, 0, 4) != "http") {
$url = U . $url;
$url = getUrl($url);
}
} else {
$url = U . $url;
$url = getUrl($url);
}
$ui->assign('text', $text);
$ui->assign('type', $type);
$ui->assign('time', $time);
$ui->assign('url', $url);
$ui->display('alert.tpl');
$ui->display('admin/alert.tpl');
die();
}
@ -360,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

@ -230,6 +230,7 @@ CREATE TABLE `rad_acct` (
`nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`acctsessiontime` BIGINT NOT NULL DEFAULT '0',
`acctinputoctets` BIGINT NOT NULL DEFAULT '0',
`acctoutputoctets` BIGINT NOT NULL DEFAULT '0',
`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
@ -250,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',
@ -276,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`),
@ -396,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', '');

643
login.html Normal file
View File

@ -0,0 +1,643 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>yatmack</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/3.8.3/core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href='https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700&display=swap' rel='stylesheet'>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
navy: {
DEFAULT: '#023047',
50: '#e6f0f5',
100: '#cce1eb',
200: '#99c3d7',
300: '#66a5c3',
400: '#3387af',
500: '#023047',
600: '#022640',
700: '#011d39',
800: '#011332',
900: '#010a2b'
},
orange: {
DEFAULT: '#fb8500',
50: '#fff4e6',
100: '#ffe9cc',
200: '#ffd399',
300: '#ffbd66',
400: '#ffa733',
500: '#fb8500',
600: '#cc6d00',
700: '#995200',
800: '#663600',
900: '#331b00'
}
},
fontFamily: {
'lexend': ['Lexend', 'sans-serif'],
},
animation: {
'spin-fast': 'spin 0.5s linear infinite',
'fade-in': 'fadeIn 0.3s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out'
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' }
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' }
}
}
}
}
}
</script>
</head>
<body class="font-lexend min-h-screen bg-gradient-to-b from-navy to-navy/95 text-gray-100">
<div class="container mx-auto px-2 py-4 max-w-3xl">
<div class="bg-white/10 backdrop-blur-lg rounded-2xl shadow-xl mb-6 overflow-hidden border border-white/10">
<div class="p-3 relative">
<h1 class="text-2xl font-bold text-center text-orange mb-3">YATMACK HOTSPOT</h1>
<p class="text-gray-100/90 text-center text-sm mb-4">
Select package ? Enter M-Pesa number ? Complete payment
</p>
<div class="flex items-center justify-center gap-2 py-2 px-4 bg-white/5 rounded-xl">
<i class="fas fa-headset text-orange"></i>
<p class="text-sm font-medium">Support: 254705042522</p>
</div>
</div>
</div>
<div class="py-2 sm:py-1 lg:py-1">
<div class="mx-auto max-w-screen-2xl px-1 md:px-1">
<div class="mx-auto max-w-lg grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 gap-4 p-1" id="cards-container">
</div>
</div>
</div>
<button onclick="redeemVoucher()" class="w-full bg-orange hover:bg-orange/90 text-white font-medium py-4 px-6 rounded-xl shadow-lg transition duration-200 flex items-center justify-center gap-3 mb-6">
<i class="fas fa-ticket-alt"></i>
<span>Redeem Voucher</span>
</button>
<div class="bg-white/10 backdrop-blur-lg rounded-2xl shadow-xl overflow-hidden border border-white/10 mb-6">
<div class="p-6 space-y-8">
<!-- Reconnect with M-Pesa -->
<div class="space-y-4">
<h3 class="text-lg font-semibold text-orange">Reconnect with M-Pesa</h3>
<div class="flex flex-col sm:flex-row gap-3">
<input id="mpesaCodeInput" type="text" placeholder="Enter M-Pesa code (e.g., SCK15SKB4Z)" class="flex-grow px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:ring-2 focus:ring-orange/50 text-white placeholder:text-gray-400">
<button id="reconnectBtn" class="bg-orange hover:bg-orange/90 text-white font-medium py-3 px-6 rounded-xl transition duration-200">Reconnect</button>
</div>
</div>
<div class="space-y-4">
<h3 class="text-lg font-semibold text-orange">Active Package Login</h3>
<form id="loginForm" action="$(link-login-only)" method="post" $(if chap-id)onSubmit="return doLogin()" $(endif)>
<input type="hidden" name="dst" value="$(link-orig)">
<input type="hidden" name="popup" value="true">
<input type="hidden" name="mac" id="mac" value="$(mac)">
<div class="flex flex-col sm:flex-row gap-3">
<input id="usernameInput" name="username" type="text" placeholder="Enter Username (e.g., ACC123456)" class="flex-grow px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:ring-2 focus:ring-orange/50 text-white placeholder:text-gray-400">
<button id="submitBtn" type="button" onclick="submitLogin()" class="bg-orange hover:bg-orange/90 text-white font-medium py-3 px-6 rounded-xl transition duration-200">Connect</button>
</div>
<input type="hidden" name="password" value="1234">
</form>
</div>
</div>
</div>
<div class="text-center">
<p class="text-sm text-gray-400">&copy; 2025 yatmack. Created by Smartisp</p>
</div>
</div>
<script>
function fetchData() {
var domain = 'https://yatmack2.smartisp.co.ke/';
var siteUrl = domain + "/index.php?_route=plugin/hotspot_plan";
var routerName = encodeURIComponent("yatmack");
var dataparams = `routername=${routerName}`;
fetch(siteUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: dataparams
})
.then(response => {
if (!response.ok) {
throw new Error(`Error ${response.status}: ${response.statusText}`);
}
return response.json();
})
.then(data => {
populateCards(data);
})
.catch(error => {
console.error('Fetch error:', error);
});
}
function populateCards(data) {
var cardsContainer = document.getElementById('cards-container');
cardsContainer.innerHTML = ''; // Clear existing content
// Sort the plans by price in ascending order
data.data.forEach(router => {
// Sort hotspot plans by price
router.plans_hotspot.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
router.plans_hotspot.forEach(item => {
var cardDiv = document.createElement('div');
cardDiv.className = 'bg-white border border-black rounded-lg shadow-md overflow-hidden transition duration-300 hover:shadow-lg flex flex-col items-center justify-between mx-auto mb-4 w-40';
cardDiv.innerHTML = `
<div class="bg-blue-500 text-white w-full py-1">
<h2 class="text-sm font-medium uppercase text-center" style="font-size: clamp(0.75rem, 1.5vw, 1rem); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
${item.planname}
</h2>
</div>
<div class="px-4 py-2 flex-grow">
<p class="text-2xl font-bold text-blue-600 mb-1">
<span class="text-lg font-medium text-black">${item.currency}</span>
${item.price}
</p>
<p class="text-sm text-black mb-2">
Valid for ${item.validity} ${item.timelimit}
</p>
<hr class="border-black mb-2">
</div>
<div class="px-4 py-2 flex-shrink-0">
<a href="#" class="inline-block bg-gray-900 text-white hover:bg-blue-600 font-semibold py-1 px-4 rounded-lg transition duration-300 text-md"
onclick="handlePhoneNumberSubmission('${item.planId}', '${item.routerId}'); return false;"
data-plan-id="${item.planId}"
data-router-id="${item.routerId}">
Buy
</a>
</div>
`;
cardsContainer.appendChild(cardDiv);
});
});
}
fetchData();
function getMacAddress() {
return "$(mac)"; // MikroTik replaces this with the user's MAC address
}
function getOrCreateAccountId() {
var radiaxid = localStorage.getItem('radiaxid');
if (!radiaxid) {
radiaxid = getMacAddress();
localStorage.setItem('radiaxid', radiaxid);
setCookie('radiaxid', radiaxid, 365);
}
return radiaxid;
}
function getAccountId() {
return localStorage.getItem('radiaxid') || getCookie('radiaxid') || getMacAddress();
}
function formatPhoneNumber(phoneNumber) {
if (phoneNumber.startsWith('+')) {
phoneNumber = phoneNumber.substring(1);
}
if (phoneNumber.startsWith('0')) {
phoneNumber = '254' + phoneNumber.substring(1);
}
if (phoneNumber.match(/^(7|1)/)) {
phoneNumber = '254' + phoneNumber;
}
return phoneNumber;
}
function setCookie(name, value, days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
var loginTimeout;
function handlePhoneNumberSubmission(planId, routerId, price) {
var accountId = getOrCreateAccountId();
var modalHtml = `
<div id="paymentModal" class='fixed inset-0 bg-black/30 backdrop-blur-sm z-50 animate-fade-in'>
<div class="fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-full max-w-md">
<div class="bg-white text-black rounded-lg shadow-xl">
<div class="flex items-center justify-between p-4 border-b border-gray-300">
<h5 class="text-xl font-semibold">
Enter Your Mpesa Number
</h5>
<button class="text-gray-500 hover:text-black" onclick="closeModal('paymentModal')">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="p-4">
<div class="bg-navy-50 border-l-4 border-navy-400 p-4 rounded-md mb-4"> <div class="flex"> <div class="flex-shrink-0"> <svg class="h-5 w-5 text-navy-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1 4v.01m6.938-2.162A9 9 0 1111 3v0a9 9 0 018.938 10.838z" /> </svg> </div> <div class="ml-3"> <p class="text-sm text-navy-700"> You are about to initiate M-pesa payment. Enter phone number below and click Pay Now to initialize payment. </p> </div> </div></div> <input type="text" class="w-full px-4 py-3 border border-orange-300 rounded-lg focus:ring-2 focus:ring-navy-400 focus:border-blue-500 text-black" id="phoneNumberInput" required placeholder="e.g. 0712345678">
<div class="text-red-500 mt-1 hidden" id="invalidPhone">Please enter a valid phone number!</div>
</div>
<div class="flex justify-end space-x-2 p-4 border-t border-gray-300">
<button onclick="closeModal('paymentModal')" class="px-4 py-2 bg-orange text-black rounded-lg hover:bg-gray-300">Close</button>
<button id="payNowBtn" class="px-4 py-2 bg-navy text-white font-semibold hover:from-blue-600 hover:to-blue-500 flex items-center rounded-lg">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" />
</svg>
Pay Now
</button>
</div>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', modalHtml);
var modal = document.getElementById('paymentModal');
modal.classList.remove('hidden');
var payNowBtn = document.getElementById('payNowBtn');
var phoneInput = document.getElementById('phoneNumberInput');
phoneInput.focus();
payNowBtn.addEventListener('click', function() { handlePayment(); });
function handlePayment() {
if (!phoneInput.value) {
document.getElementById('invalidPhone').classList.remove('hidden');
return;
}
payNowBtn.disabled = true;
payNowBtn.innerHTML = `
<svg class='animate-spin -ml-1 mr-3 h-5 w-5 text-white' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
<circle class='opacity-25' cx='12' cy='12' r='10' stroke='currentColor' stroke-width='4'></circle>
<path class='opacity-75' fill='currentColor' d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'></path>
</svg>
Processing...
`;
var formattedPhoneNumber = formatPhoneNumber(phoneInput.value);
document.getElementById('usernameInput').value = accountId;
fetch('https://yatmack2.smartisp.co.ke/index.php?_route=plugin/CreateHotspotuser&type=grant', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
phone_number: formattedPhoneNumber,
plan_id: planId,
router_id: routerId,
account_id: accountId
})
})
.then(response => response.json())
.then(data => {
if (data.status === 'error') throw new Error(data.message);
closeModal('paymentModal');
showProcessingModal();
checkPaymentStatus(formattedPhoneNumber);
})
.catch(error => {
closeModal('paymentModal');
showErrorModal(error.message);
});
}
}
function showProcessingModal() {
var processingModalHtml = `
<div id='processingModal' class='fixed inset-0 bg-black/30 backdrop-blur-sm z-50 animate-fade-in'>
<div class='fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-full max-w-md animate-slide-up'>
<div class='bg-gradient-to-br from-white to-gray-50 shadow-2xl rounded-2xl p-8 text-center border border-gray-100'>
<div class='flex justify-center'>
<svg class='animate-spin h-20 w-20 text-navy-500' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
<circle class='opacity-25' cx='12' cy='12' r='10' stroke='currentColor' stroke-width='4'></circle>
<path class='opacity-75' fill='currentColor' d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'></path>
</svg>
</div>
<div class="mt-6 bg-navy-50/50 backdrop-blur-xs border border-navy-200 rounded-xl p-6 shadow-lg"> <div class="flex items-center space-x-4"> <svg class="h-8 w-8 text-navy-400 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1 4v.01m6.938-2.162A9 9 0 1111 3v0a9 9 0 018.938 10.838z" /> </svg> <div class="flex-1"> <h2 class="text-xl font-semibold text-navy-600 font-lexend">Initializing Payment</h2> <p class="text-navy-500 mt-2">A payment request has been sent to your phone. Please wait while we process your payment.</p> </div> </div> <div class="mt-4 h-2 w-full bg-navy-100 rounded-full overflow-hidden"> <div class="h-2 bg-navy-400 rounded-full animate-pulse"></div> </div> </div> </div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', processingModalHtml);
}
function closeModal(modalId) {
var modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('hidden');
setTimeout(function() { modal.remove(); }, 300);
}
}
function showSuccessModal() {
var successModalHtml = `
<div id='successModal' class='fixed inset-0 bg-black/60 backdrop-blur-sm z-50 transition-all duration-300'>
<div class='fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-full max-w-lg transition-all duration-300 ease-out'>
<div class='bg-white/95 backdrop-blur-md shadow-2xl rounded-3xl p-8 text-center border border-gray-200/50 ring-1 ring-gray-900/5'>
<div class='flex justify-center mb-6'>
<div class='relative'>
<div class='absolute inset-0 rounded-full bg-green-500/20 animate-pulse'></div>
<svg class='h-16 w-16 text-green-500' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'/>
</svg>
</div>
</div>
<div class='space-y-4'>
<h2 class='text-2xl font-semibold text-gray-900'>Payment Successful</h2>
<div class='bg-green-50 rounded-2xl p-6 border border-green-100'>
<div class='flex items-center gap-4'>
<svg class='h-6 w-6 text-green-500 flex-shrink-0' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 12l2 2 4-4'/>
</svg>
<p class='text-sm text-green-700'>Your transaction has been completed successfully. You will be redirected shortly.</p>
</div>
</div>
</div>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', successModalHtml);
setTimeout(function() { closeModal('successModal'); }, 2000);
}
function showErrorModal(errorMsg) {
var errorModalHtml = `
<div id='errorModal' class='fixed inset-0 bg-black/60 backdrop-blur-sm z-50 transition-all duration-300'>
<div class='fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-full max-w-lg transition-all duration-300 ease-out'>
<div class='bg-white/95 backdrop-blur-md shadow-2xl rounded-3xl p-8 text-center border border-gray-200/50 ring-1 ring-gray-900/5'>
<div class='flex justify-center mb-6'>
<div class='relative'>
<div class='absolute inset-0 rounded-full bg-red-500/20 animate-pulse'></div>
<svg class='h-16 w-16 text-red-500' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6 18L18 6M6 6l12 12'/>
</svg>
</div>
</div>
<div class='space-y-4'>
<h2 class='text-2xl font-semibold text-gray-900'>Payment Failed</h2>
<div class='bg-red-50 rounded-2xl p-6 border border-red-100'>
<div class='flex items-center gap-4'>
<svg class='h-6 w-6 text-red-500 flex-shrink-0' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'>
<path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'/>
</svg>
<p class='text-sm text-red-700'>An error occurred while processing your payment. Please try again.</p>
</div>
</div>
</div>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', errorModalHtml);
setTimeout(function() { closeModal('errorModal'); }, 2000);
}
function checkPaymentStatus(phoneNumber) {
var accountId = getOrCreateAccountId();
var checkInterval = setInterval(function() {
fetch('https://yatmack2.smartisp.co.ke/index.php?_route=plugin/CreateHotspotuser&type=verify', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({account_id: accountId})
})
.then(function(response) { return response.json(); })
.then(function(data) {
console.log('Raw Response:', data);
if (data.Resultcode === '3') {
clearInterval(checkInterval);
closeModal('processingModal');
showSuccessModal();
if (loginTimeout) clearTimeout(loginTimeout);
loginTimeout = setTimeout(function() { document.getElementById('loginForm').submit(); }, 2000);
} else if (data.Resultcode === '2') {
clearInterval(checkInterval);
closeModal('processingModal');
showErrorModal(data.Message);
} else {
console.error('Unexpected result code:', data.Resultcode);
}
})
.catch(function(error) {
console.error('Error during fetch request:', error);
clearInterval(checkInterval);
closeModal('processingModal');
showErrorModal('An error occurred while checking payment status.');
});
}, 1000);
setTimeout(function() {
clearInterval(checkInterval);
closeModal('processingModal');
showErrorModal('Timeout while waiting for payment confirmation.');
}, 300000); // 5 minutes
}
document.addEventListener('DOMContentLoaded', function() {
var accountId = getOrCreateAccountId();
var usernameInput = document.getElementById('usernameInput');
if (usernameInput && !usernameInput.value) {
usernameInput.value = accountId;
}
var submitBtn = document.getElementById('submitBtn');
if (submitBtn) {
submitBtn.addEventListener('click', function() {
document.getElementById('loginForm').submit();
});
}
});
var loginTimeout; // Variable to store the timeout ID
function redeemVoucher() {
Swal.fire({
title: 'Redeem Voucher',
input: 'text',
inputPlaceholder: 'Enter voucher code',
inputValidator: function(value) {
if (!value) {
return 'You need to enter a voucher code!';
}
},
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Redeem',
showLoaderOnConfirm: true,
preConfirm: (voucherCode) => {
var accountId = voucherCode;
return fetch('https://yatmack2.smartisp.co.ke/index.php?_route=plugin/CreateHotspotuser&type=voucher', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({voucher_code: voucherCode, account_id: accountId}),
})
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(data => {
if (data.status === 'error') throw new Error(data.message);
return data;
});
},
allowOutsideClick: () => !Swal.isLoading()
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
icon: 'success',
title: 'Voucher Redeemed',
text: result.value.message,
showConfirmButton: false,
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
var username = result.value.username;
console.log('Received username from server:', username);
var usernameInput = document.querySelector('input[name="username"]');
if (usernameInput) {
console.log('Found username input element.');
usernameInput.value = username;
loginTimeout = setTimeout(function() {
var loginForm = document.getElementById('loginForm');
if (loginForm) {
loginForm.submit();
} else {
console.error('Login form not found.');
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Login form not found. Please try again.',
});
}
}, 2000);
} else {
console.error('Username input element not found.');
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Username input not found. Please try again.',
});
}
}
});
}
}).catch(error => {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: error.message,
});
});
}
document.addEventListener('DOMContentLoaded', function() {
var reconnectBtn = document.getElementById('reconnectBtn');
var mpesaCodeInput = document.getElementById('mpesaCodeInput');
var macInput = document.getElementById('mac');
var loginForm = document.getElementById('loginForm');
if (reconnectBtn) {
reconnectBtn.addEventListener('click', function() {
// Validate inputs before processing
if (!mpesaCodeInput || !macInput || !loginForm) {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Required form elements are missing'
});
return;
}
var mpesaCode = mpesaCodeInput.value.trim().split(' ')[0];
var mac = macInput.value.trim();
fetch('https://yatmack2.smartisp.co.ke/index.php?_route=plugin/ReconnectUser', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ mpesa_code: mpesaCode, mac: mac }),
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('Response data:', data);
var resultCode = data.Resultcode;
var message = data.Message;
var status = data.Status;
var username = data.username;
if (resultCode === '1') {
Swal.fire({
icon: 'error',
title: 'Invalid Code',
text: message
});
}
else if (resultCode === '3') {
Swal.fire({
icon: 'error',
title: 'Expired Package',
text: message
});
}
else if (resultCode === '2') {
Swal.fire({
icon: 'success',
title: status,
text: message,
showConfirmButton: false,
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
console.log('Received username from server:', username);
var usernameInput = document.querySelector('input[name="username"]');
if (usernameInput) {
console.log('Found username input element.');
usernameInput.value = username;
setTimeout(function() {
var loginForm = document.getElementById('loginForm');
if (loginForm) {
loginForm.submit();
} else {
console.error('Login form not found.');
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Login form not found. Please try again.',
});
}
}, 2000);
} else {
console.error('Username input element not found.');
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Username input not found. Please try again.',
});
}
}
});
}
})
.catch(error => {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: error.message
});
});
});
} else {
console.error('Reconnect button not found');
}
});
</script>
</html>

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();
@ -291,19 +291,28 @@ try {
$d->acctsessionid = _post('acctSessionId');
$d->username = $username;
$d->realm = _post('realm');
$d->nasipaddress = _post('nasip');
$d->nasipaddress = _post('nasIpAddress');
$d->acctsessiontime = intval(_post('acctSessionTime'));
$d->nasid = _post('nasid');
$d->nasportid = _post('nasPortId');
$d->nasporttype = _post('nasPortType');
$d->framedipaddress = _post('framedIPAddress');
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();
@ -348,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 {
@ -452,4 +461,4 @@ function show_radius_result($array, $code = 200)
die();
}
die(json_encode($array));
}
}

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)) {

347
system/autoload/Invoice.php Normal file
View File

@ -0,0 +1,347 @@
<?php
use Mpdf\Mpdf;
class Invoice
{
public static function generateInvoice($invoiceData)
{
try {
if (empty($invoiceData['invoice'])) {
throw new Exception(Lang::T("Invoice No is required"));
}
$template = Lang::getNotifText('email_invoice');
if (!$template) {
throw new Exception(Lang::T("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}";
$dir = dirname($outputPath);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
$mpdf->Output($outputPath, 'F');
if (!file_exists($outputPath)) {
throw new Exception(Lang::T("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(Lang::T("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 $invoiceData[$key];
}
return htmlspecialchars($invoiceData[$key] ?? '');
}, $template);
}
/**
* Send invoice to user
*
* @param int $userId
* @param array $invoice
* @param array $bills
* @param string $status
* @param string $invoiceNo
* @return bool
*/
public static function sendInvoice($userId, $invoice = null, $bills = [], $status = "Unpaid", $invoiceNo = null)
{
global $config, $root_path, $UPLOAD_PATH;
// Set default currency code
$config['currency_code'] ??= '$';
$account = ORM::for_table('tbl_customers')->find_one($userId);
self::validateAccount($account);
if (!$invoiceNo) {
$invoiceNo = "INV-" . Package::_raid();
}
// Fetch invoice if not provided
if ($status === "Unpaid" && !$invoice) {
$data = ORM::for_table('tbl_user_recharges')->where('customer_id', $userId)
->where('status', 'off')
->left_outer_join('tbl_plans', 'tbl_user_recharges.namebp = tbl_plans.name_plan')
->select('tbl_plans.price', 'price')
->select('tbl_plans.name_plan', 'namebp')
->find_one();
if (!$data) {
$data = ORM::for_table('tbl_user_recharges')->where('username', $account->username)
->left_outer_join('tbl_plans', 'tbl_user_recharges.namebp = tbl_plans.name_plan')
->select('tbl_plans.price', 'price')
->select('tbl_plans.name_plan', 'namebp')
->where('status', 'off')
->find_one();
}
if (!$data) {
throw new Exception(Lang::T("No unpaid invoice found for username:") . $account->username);
}
$invoice = [
'price' => $data->price,
'plan_name' => $data->namebp,
'routers' => $data->routers,
];
} else if ($status === "Paid" && !$invoice) {
$invoice = ORM::for_table("tbl_transactions")->where("username", $account->username)->find_one();
}
if (!$invoice) {
throw new Exception(Lang::T("Transaction not found for username: ") . $account->username);
}
// Get additional bills if not provided
if (empty($bills)) {
[$bills, $add_cost] = User::getBills($account->id);
}
$invoiceItems = self::generateInvoiceItems($invoice, $bills, $add_cost);
$subtotal = array_sum(array_column($invoiceItems, 'amount'));
$tax = $config['enable_tax'] ? Package::tax($subtotal) : 0;
$tax_rate = $config['tax_rate'] ?? 0;
$total = $subtotal + $tax;
$payLink = self::generatePaymentLink($account, $invoice, $status);
$logo = self::getCompanyLogo($UPLOAD_PATH, $root_path);
$invoiceData = [
'invoice' => $invoiceNo,
'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 (empty($invoiceData['bill_rows'])) {
throw new Exception(Lang::T("Bill rows data is empty."));
}
$filename = self::generateInvoice($invoiceData);
if (!$filename) {
throw new Exception(Lang::T("Failed to generate invoice PDF"));
}
$pdfPath = "system/uploads/invoices/{$filename}";
self::saveToDatabase($filename, $account->id, $invoiceData, $total);
try {
Message::sendEmail(
$account->email,
Lang::T("Invoice for Account {$account->fullname}"),
Lang::T("Please find your invoice attached"),
$pdfPath
);
return true;
} catch (\Exception $e) {
throw new Exception(Lang::T("Failed to send email invoice to ") . $account->email . ". " . Lang::T("Reason: ") . $e->getMessage());
}
}
private static function validateAccount($account)
{
if (!$account) {
throw new Exception(Lang::T("User not found"));
}
if (!$account->email || !filter_var($account->email, FILTER_VALIDATE_EMAIL)) {
throw new Exception(Lang::T("Invalid user email"));
}
}
private static function generateInvoiceItems($invoice, $bills, $add_cost)
{
$items = [
[
'description' => $invoice['plan_name'],
'details' => Lang::T('Subscription'),
'amount' => (float) $invoice['price']
]
];
if ($invoice->routers != 'balance') {
foreach ($bills as $description => $amount) {
if (is_numeric($amount)) {
$items[] = [
'description' => $description,
'details' => Lang::T('Additional Bill'),
'amount' => (float) $amount
];
} else {
_log(Lang::T("Invalid bill amount for {$description}: {$amount}"));
}
}
}
return $items;
}
private static function generatePaymentLink($account, $invoice, $status)
{
$token = User::generateToken($account->id, 1);
if (empty($token['token'])) {
return '?_route=home';
}
$tur = ORM::for_table('tbl_user_recharges')
->where('customer_id', $account->id)
->where('namebp', $invoice->plan_name);
$tur->where('status', $status === 'Paid' ? 'on' : 'off');
$turResult = $tur->find_one();
return $turResult ? '?_route=home&recharge=' . $turResult['id'] . '&uid=' . urlencode($token['token']) : '?_route=home';
}
private static function getCompanyLogo($UPLOAD_PATH, $root_path)
{
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
return file_exists($UPLOAD_PATH . DIRECTORY_SEPARATOR . 'logo.png') ?
$UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.png?' . time() :
$UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'logo.default.png';
}
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) {
$desc = htmlspecialchars($item['description'], ENT_QUOTES);
$details = htmlspecialchars($item['details'], ENT_QUOTES);
$html .= "<tr>
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$desc}</td>
<td style='padding: 10px; border-bottom: 1px solid #ddd;'>{$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;
}
private static function saveToDatabase($filename, $customer_id, $invoiceData, $total)
{
$invoice = ORM::for_table('tbl_invoices')->create();
$invoice->number = $invoiceData['invoice'];
$invoice->customer_id = $customer_id;
$invoice->fullname = $invoiceData['fullname'];
$invoice->email = $invoiceData['email'];
$invoice->address = $invoiceData['address'];
$invoice->status = $invoiceData['status'];
$invoice->due_date = $invoiceData['due_date'];
$invoice->filename = $filename;
$invoice->amount = $total;
$invoice->data = json_encode($invoiceData);
$invoice->created_at = date('Y-m-d H:i:s');
$invoice->save();
return $invoice->id;
}
public static function getAll()
{
return ORM::for_table('tbl_invoices')->order_by_desc('id')->find_many();
}
public static function getById($id)
{
return ORM::for_table('tbl_invoices')->find_one($id);
}
public static function getByNumber($number)
{
return ORM::for_table('tbl_invoices')->where('number', $number)->find_one();
}
public static function delete($id)
{
$invoice = ORM::for_table('tbl_invoices')->find_one($id);
if ($invoice) {
$invoice->delete();
return true;
}
return false;
}
}

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));
}
}
@ -43,24 +46,32 @@ class Message
$txts = str_split($txt, 160);
try {
foreach ($txts as $txt) {
self::sendSMS($config['sms_url'], $phone, $txt);
self::sendSMS($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,20 @@ class Message
$attr .= "Reply-To: " . $config['mail_reply_to'] . "\r\n";
}
mail($to, $subject, $body, $attr);
self::logMessage('Email', $to, $body, 'Success');
return true;
} 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 +162,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 +180,19 @@ 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);
return false;
} else {
self::logMessage('Email', $to, $body, 'Success');
return true;
}
//<p style="font-family: Helvetica, sans-serif; font-size: 16px; font-weight: normal; margin: 0; margin-bottom: 16px;">
@ -198,7 +230,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 +311,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 +327,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 +337,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 +389,45 @@ 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");
return true;
} catch (Throwable $e) {
$errorMessage = Lang::T("Error adding message to inbox: " . $e->getMessage());
self::logMessage('Inbox', $user->username, $body, 'Error', $errorMessage);
return false;
}
}
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

@ -74,10 +74,10 @@ class Package
if (!$p['enabled']) {
if (!isset($admin) || !isset($admin['id']) || empty($admin['id'])) {
r2(U . 'home', 'e', Lang::T('Plan Not found'));
r2(getUrl('home'), 'e', Lang::T('Plan Not found'));
}
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
r2(U . 'dashboard', 'e', Lang::T('You do not have permission to access this page'));
r2(getUrl('dashboard'), 'e', Lang::T('You do not have permission to access this page'));
}
}

View File

@ -10,15 +10,16 @@ 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);
$url = U . implode('/', $routes);
$url = getUrl(implode('/', $routes));
if (count($search) > 0) {
$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();
@ -83,7 +84,7 @@ class Paginator
{
global $routes;
global $_L;
$url = U . implode('/', $routes);
$url = getUrl(implode('/', $routes));
$query = urlencode($query);
$adjacents = "2";
$page = (int)(empty(_get('p')) ? 1 : _get('p'));
@ -169,7 +170,7 @@ class Paginator
{
global $routes;
global $_L;
$url = U . $routes['0'] . '/' . $routes['1'] . '/';
$url = getUrl($routes['0'] . '/' . $routes['1'] . '/');
$adjacents = "2";
$page = (int)(!isset($routes['2']) ? 1 : $routes['2']);
$pagination = "";
@ -277,7 +278,7 @@ class Paginator
{
global $routes;
global $_L;
$url = U . $routes['0'] . '/' . $routes['1'] . '/';
$url = getUrl($routes['0'] . '/' . $routes['1'] . '/');
$adjacents = "2";
$page = (int)(!isset($routes['2']) ? 1 : $routes['2']);
$pagination = "";

View File

@ -109,4 +109,44 @@ 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);
if ($config['url_canonical'] == 'yes') {
$u = str_replace('?_route=', '', U);
$pos = strpos($url, '&');
if ($pos === false) {
return $u . $url;
} else {
return $u . substr($url, 0, $pos) . '?' . substr($url, $pos + 1);
}
} else {
return U . $url;
}
}
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, "/");
}
}
@ -206,7 +206,7 @@ class User
global $config;
if ($config['maintenance_mode'] == true) {
if ($config['maintenance_mode_logout'] == true) {
r2(U . 'logout', 'd', '');
r2(getUrl('logout'), 'd', '');
} else {
displayMaintenanceMessage();
}
@ -226,7 +226,7 @@ class User
global $config;
if ($config['maintenance_mode'] == true) {
if ($config['maintenance_mode_logout'] == true) {
r2(U . 'logout', 'd', '');
r2(getUrl('logout'), 'd', '');
} else {
displayMaintenanceMessage();
}
@ -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();
@ -96,7 +100,7 @@ try {
foreach ($menu_registered as $menu) {
if ($menu['admin'] && _admin(false)) {
if (count($menu['auth']) == 0 || in_array($admin['user_type'], $menu['auth'])) {
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . U . 'plugin/' . $menu['function'] . '">';
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . getUrl('plugin/' . $menu['function']) . '">';
if (!empty($menu['icon'])) {
$menus[$menu['position']] .= '<i class="' . $menu['icon'] . '"></i>';
}
@ -107,7 +111,7 @@ try {
$menus[$menu['position']] .= '<span class="text">' . $menu['name'] . '</span></a></li>';
}
} else if (!$menu['admin'] && _auth(false)) {
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . U . 'plugin/' . $menu['function'] . '">';
$menus[$menu['position']] .= '<li' . (($routes[1] == $menu['function']) ? ' class="active"' : '') . '><a href="' . getUrl('plugin/' . $menu['function']) . '">';
if (!empty($menu['icon'])) {
$menus[$menu['position']] .= '<i class="' . $menu['icon'] . '"></i>';
}
@ -124,7 +128,15 @@ try {
unset($menus, $menu_registered);
include($sys_render);
} else {
r2(U . 'dashboard', 'e', 'not found');
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(
@ -133,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(
@ -146,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

@ -27,7 +27,7 @@ switch ($action) {
$password = _post('password');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'accounts/change-password', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('accounts/change-password'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
run_hook('customer_change_password'); #HOOK
if ($password != '') {
@ -36,10 +36,10 @@ switch ($action) {
$cnpass = _post('cnpass');
if ($password == $d_pass) {
if (!Validator::Length($password, 36, 2)) {
r2(U . 'accounts/change-password', 'e', 'New Password must be 2 to 35 character');
r2(getUrl('accounts/change-password'), 'e', 'New Password must be 2 to 35 character');
}
if ($npass != $cnpass) {
r2(U . 'accounts/change-password', 'e', 'Both Password should be same');
r2(getUrl('accounts/change-password'), 'e', 'Both Password should be same');
}
$user->password = $npass;
$turs = ORM::for_table('tbl_user_recharges')->where('customer_id', $user['id'])->find_many();
@ -64,10 +64,10 @@ switch ($action) {
_log('[' . $user['username'] . ']: Password changed successfully', 'User', $user['id']);
_alert(Lang::T('Password changed successfully, Please login again'), 'success', "login");
} else {
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('accounts/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
} else {
r2(U . 'accounts/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('accounts/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
break;
@ -81,7 +81,7 @@ switch ($action) {
case 'edit-profile-post':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'accounts/profile', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('accounts/profile'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$fullname = _post('fullname');
$address = _post('address');
@ -142,7 +142,7 @@ switch ($action) {
}
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
} else {
r2(U . 'settings/app', 'e', 'PHP GD is not installed');
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
}
@ -160,9 +160,9 @@ switch ($action) {
$user->save();
_log('[' . $user['username'] . ']: ' . Lang::T('User Updated Successfully'), 'User', $user['id']);
r2(U . 'accounts/profile', 's', Lang::T('User Updated Successfully'));
r2(getUrl('accounts/profile'), 's', Lang::T('User Updated Successfully'));
}else{
r2(U . 'accounts/profile', 'e', $msg);
r2(getUrl('accounts/profile'), 'e', $msg);
}
break;
@ -177,7 +177,7 @@ switch ($action) {
case 'phone-update-otp':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$phone = Lang::phoneFormat(_post('phone'));
$username = $user['username'];
@ -185,16 +185,16 @@ switch ($action) {
$_SESSION['new_phone'] = $phone;
// Validate the phone number format
if (!preg_match('/^[0-9]{10,}$/', $phone) || empty($phone)) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Invalid phone number format'));
}
if (empty($config['sms_url'])) {
r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not Available, Please try again later'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('SMS server not Available, Please try again later'));
}
$d = ORM::for_table('tbl_customers')->whereNotEqual('username', $username)->where('phonenumber', $phone)->find_one();
if ($d) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Phone number already registered by another customer'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Phone number already registered by another customer'));
}
if (!file_exists($otpPath)) {
mkdir($otpPath);
@ -205,7 +205,7 @@ switch ($action) {
// expired 10 minutes
if (file_exists($otpFile) && time() - filemtime($otpFile) < 600) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Please wait ') . (600 - (time() - filemtime($otpFile))) . Lang::T(' seconds before sending another SMS'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Please wait ') . (600 - (time() - filemtime($otpFile))) . Lang::T(' seconds before sending another SMS'));
} else {
$otp = rand(100000, 999999);
file_put_contents($otpFile, $otp);
@ -220,7 +220,7 @@ switch ($action) {
Message::sendWhatsapp($phone, $config['CompanyName'] . "\n\n" . Lang::T("Verification code") . "\n$otp");
}
//redirect after sending OTP
r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code has been sent to your phone'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Verification code has been sent to your phone'));
}
break;
@ -228,7 +228,7 @@ switch ($action) {
case 'phone-update-post':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$phone = Lang::phoneFormat(_post('phone'));
$otp_code = _post('otp');
@ -237,11 +237,11 @@ switch ($action) {
// Validate the phone number format
if (!preg_match('/^[0-9]{10,}$/', $phone)) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Invalid phone number format'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Invalid phone number format'));
}
if (empty($config['sms_url'])) {
r2(U . 'accounts/phone-update', 'e', Lang::T('SMS server not Available, Please try again later'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('SMS server not Available, Please try again later'));
}
$otpFile = $otpPath . sha1($username . $db_pass) . ".txt";
@ -249,7 +249,7 @@ switch ($action) {
// Check if OTP file exists
if (!file_exists($otpFile)) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Please request OTP first'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Please request OTP first'));
exit();
}
@ -257,21 +257,21 @@ switch ($action) {
if (time() - filemtime($otpFile) > 1200) {
unlink($otpFile);
unlink($phoneFile);
r2(U . 'accounts/phone-update', 'e', Lang::T('Verification code expired'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Verification code expired'));
exit();
} else {
$code = file_get_contents($otpFile);
// Check if OTP code matches
if ($code != $otp_code) {
r2(U . 'accounts/phone-update', 'e', Lang::T('Wrong Verification code'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('Wrong Verification code'));
exit();
}
// Check if the phone number matches the one that requested the OTP
$savedPhone = file_get_contents($phoneFile);
if ($savedPhone !== $phone) {
r2(U . 'accounts/phone-update', 'e', Lang::T('The phone number does not match the one that requested the OTP'));
r2(getUrl('accounts/phone-update'), 'e', Lang::T('The phone number does not match the one that requested the OTP'));
exit();
}
@ -284,7 +284,7 @@ switch ($action) {
$user->phonenumber = Lang::phoneFormat($phone);
$user->save();
r2(U . 'accounts/profile', 's', Lang::T('Phone number updated successfully'));
r2(getUrl('accounts/profile'), 's', Lang::T('Phone number updated successfully'));
break;
case 'email-update':
@ -296,7 +296,7 @@ switch ($action) {
case 'email-update-otp':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'accounts/email-update', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('accounts/email-update'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$email = trim(_post('email'));
$username = $user['username'];
@ -304,16 +304,16 @@ switch ($action) {
$_SESSION['new_email'] = $email;
// Validate the phone number format
if (!Validator::Email($email)) {
r2(U . 'accounts/email-update', 'e', Lang::T('Invalid Email address format'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Invalid Email address format'));
}
if (empty($config['smtp_host'])) {
r2(U . 'accounts/email-update', 'e', Lang::T('Email server not Available, Please ask admin to configure it'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Email server not Available, Please ask admin to configure it'));
}
$d = ORM::for_table('tbl_customers')->whereNotEqual('username', $username)->where('email', $email)->find_one();
if ($d) {
r2(U . 'accounts/email-update', 'e', Lang::T('Email already used by another Customer'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Email already used by another Customer'));
}
if (!file_exists($otpPath)) {
mkdir($otpPath);
@ -324,7 +324,7 @@ switch ($action) {
// expired 10 minutes
if (file_exists($otpFile) && time() - filemtime($otpFile) < 600) {
r2(U . 'accounts/email-update', 'e', Lang::T('Please wait ') . (600 - (time() - filemtime($otpFile))) . Lang::T(' seconds before sending another Email'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Please wait ') . (600 - (time() - filemtime($otpFile))) . Lang::T(' seconds before sending another Email'));
} else {
$otp = rand(100000, 999999);
file_put_contents($otpFile, $otp);
@ -333,7 +333,7 @@ switch ($action) {
$body = Lang::T("Hello") . ' ' . $user['fullname'] . ",\n\n" . Lang::T("Your Email Verification Code is:") . " $otp";
Message::sendEmail($email, Lang::T('Change Email Verification Code'), $body);
//redirect after sending OTP
r2(U . 'accounts/email-update', 'e', Lang::T('Verification code has been sent to your email. Check Spam folder if not found.'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Verification code has been sent to your email. Check Spam folder if not found.'));
}
break;
@ -341,7 +341,7 @@ switch ($action) {
case 'email-update-post':
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'accounts/email-update', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('accounts/email-update'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$email = trim(_post('email'));
$otp_code = _post('otp');
@ -349,12 +349,12 @@ switch ($action) {
$otpPath = $CACHE_PATH . '/email/';
// Validate the phone number format
if (!Validator::Email($email)) {
r2(U . 'accounts/email-update', 'e', Lang::T('Invalid Email address format'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Invalid Email address format'));
exit();
}
if (empty($config['smtp_host'])) {
r2(U . 'accounts/email-update', 'e', Lang::T('Email server not Available, Please ask admin to configure it'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Email server not Available, Please ask admin to configure it'));
}
$otpFile = $otpPath . sha1($username . $db_pass) . ".txt";
@ -362,7 +362,7 @@ switch ($action) {
// Check if OTP file exists
if (!file_exists($otpFile)) {
r2(U . 'accounts/email-update', 'e', Lang::T('Please request OTP first'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Please request OTP first'));
exit();
}
@ -370,21 +370,21 @@ switch ($action) {
if (time() - filemtime($otpFile) > 1200) {
unlink($otpFile);
unlink($emailFile);
r2(U . 'accounts/email-update', 'e', Lang::T('Verification code expired'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Verification code expired'));
exit();
} else {
$code = file_get_contents($otpFile);
// Check if OTP code matches
if ($code != $otp_code) {
r2(U . 'accounts/email-update', 'e', Lang::T('Wrong Verification code'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('Wrong Verification code'));
exit();
}
// Check if the phone number matches the one that requested the OTP
$savedEmail = file_get_contents($emailFile);
if ($savedEmail !== $email) {
r2(U . 'accounts/email-update', 'e', Lang::T('The Email Address does not match the one that requested the OTP'));
r2(getUrl('accounts/email-update'), 'e', Lang::T('The Email Address does not match the one that requested the OTP'));
exit();
}
@ -396,7 +396,7 @@ switch ($action) {
$user->email = $email;
$user->save();
r2(U . 'accounts/profile', 's', Lang::T('Email Address updated successfully'));
r2(getUrl('accounts/profile'), 's', Lang::T('Email Address updated successfully'));
break;
case 'language-update-post':
@ -420,5 +420,5 @@ switch ($action) {
break;
default:
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View File

@ -10,7 +10,7 @@ header("Expires: Tue, 01 Jan 2000 00:00:00 GMT");
header("Pragma: no-cache");
if (Admin::getID()) {
r2(U . 'dashboard', "s", Lang::T("You are already logged in"));
r2(getUrl('dashboard'), "s", Lang::T("You are already logged in"));
}
if (isset($routes['1'])) {
@ -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

@ -25,10 +25,10 @@ switch ($action) {
if (file_exists($dvc)) {
require_once $dvc;
if ((new $p['device'])->online_customer($user, $bill['routers'])) {
die('<a href="' . U . 'home&mikrotik=logout&id=' . $bill['id'] . '" onclick="return confirm(\'' . Lang::T('Disconnect Internet?') . '\')" class="btn btn-success btn-xs btn-block">' . Lang::T('You are Online, Logout?') . '</a>');
die('<a href="' . getUrl('home&mikrotik=logout&id=' . $bill['id']) . '" onclick="return confirm(\'' . Lang::T('Disconnect Internet?') . '\')" class="btn btn-success btn-xs btn-block">' . Lang::T('You are Online, Logout?') . '</a>');
} else {
if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'])) {
die('<a href="' . U . 'home&mikrotik=login&id=' . $bill['id'] . '" onclick="return confirm(\'' . Lang::T('Connect to Internet?') . '\')" class="btn btn-danger btn-xs btn-block">' . Lang::T('Not Online, Login now?') . '</a>');
die('<a href="' . getUrl('home&mikrotik=login&id=' . $bill['id']) . '" onclick="return confirm(\'' . Lang::T('Connect to Internet?') . '\')" class="btn btn-danger btn-xs btn-block">' . Lang::T('Not Online, Login now?') . '</a>');
} else {
die(Lang::T('-'));
}
@ -58,7 +58,7 @@ switch ($action) {
case 'inbox':
$inboxs = ORM::for_table('tbl_customers_inbox')->selects(['id', 'subject', 'date_created'])->where('customer_id', $user['id'])->whereRaw('date_read is null')->order_by_desc('date_created')->limit(10)->find_many();
foreach ($inboxs as $inbox) {
echo '<li><a href="' . U . 'mail/view/' . $inbox['id'] . '">' . $inbox['subject'] . '<br><sub class="text-muted">' . Lang::dateTimeFormat($inbox['date_created']) . '</sub></a></li>';
echo '<li><a href="' . getUrl('mail/view/' . $inbox['id']) . '">' . $inbox['subject'] . '<br><sub class="text-muted">' . Lang::dateTimeFormat($inbox['date_created']) . '</sub></a></li>';
}
die();
case 'language':
@ -69,7 +69,7 @@ switch ($action) {
if (is_file('system/lan/' . $file) && !in_array($file, ['index.html', 'country.json', '.DS_Store'])) {
$file = str_replace(".json", "", $file);
if(!empty($file)){
echo '<li><a href="' . U . 'accounts/language-update-post&lang=' . $file. '">';
echo '<li><a href="' . getUrl('accounts/language-update-post&lang=' . $file) . '">';
if($select == $file){
echo '<span class="glyphicon glyphicon-ok"></span> ';
}
@ -79,5 +79,5 @@ switch ($action) {
}
die();
default:
$ui->display('404.tpl');
die();
}

View File

@ -13,12 +13,11 @@ $action = $routes['1'];
$ui->assign('_admin', $admin);
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
r2(U . "dashboard", 'e', Lang::T('You do not have permission to access this page'));
r2(getUrl('dashboard'), 'e', Lang::T('You do not have permission to access this page'));
}
switch ($action) {
case 'list':
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/bandwidth.js"></script>');
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,9 +50,9 @@ 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(U . 'bandwidth/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('bandwidth/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -66,7 +65,7 @@ switch ($action) {
$d = ORM::for_table('tbl_bandwidth')->find_one($id);
if ($d) {
$d->delete();
r2(U . 'bandwidth/list', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('bandwidth/list'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -123,9 +122,9 @@ switch ($action) {
$d->burst = $burst;
$d->save();
r2(U . 'bandwidth/list', 's', Lang::T('Data Created Successfully'));
r2(getUrl('bandwidth/list'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'bandwidth/add', 'e', $msg);
r2(getUrl('bandwidth/add'), 'e', $msg);
}
break;
@ -179,12 +178,12 @@ switch ($action) {
$d->burst = $burst;
$d->save();
r2(U . 'bandwidth/list', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('bandwidth/list'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'bandwidth/edit/' . $id, 'e', $msg);
r2(getUrl('bandwidth/edit/') . $id, 'e', $msg);
}
break;
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':
@ -79,14 +79,14 @@ switch ($action) {
}
if (!empty($error)) {
r2(U . 'coupons/add', 'e', implode('<br>', $error));
r2(getUrl('coupons/add'), 'e', implode('<br>', $error));
exit;
}
//check if coupon code already exists
$coupon = ORM::for_table('tbl_coupons')->where('code', $code)->find_one();
if ($coupon) {
r2(U . 'coupons/add', 'e', Lang::T('Coupon Code already exists'));
r2(getUrl('coupons/add'), 'e', Lang::T('Coupon Code already exists'));
exit;
}
@ -104,10 +104,10 @@ switch ($action) {
$coupon->created_at = date('Y-m-d H:i:s');
try {
$coupon->save();
r2(U . 'coupons', 's', Lang::T('Coupon has been added successfully'));
r2(getUrl('coupons'), 's', Lang::T('Coupon has been added successfully'));
} catch (Exception $e) {
_log(Lang::T('Error adding coupon: ' . $e->getMessage()));
r2(U . 'coupons/add', 'e', Lang::T('Error adding coupon: ' . $e->getMessage()));
r2(getUrl('coupons/add'), 'e', Lang::T('Error adding coupon: ' . $e->getMessage()));
}
break;
@ -120,18 +120,18 @@ switch ($action) {
$coupon_id = intval($routes['2']);
if (empty($coupon_id)) {
r2(U . 'coupons', 'e', Lang::T('Invalid Coupon ID'));
r2(getUrl('coupons'), 'e', Lang::T('Invalid Coupon ID'));
exit;
}
$coupon = ORM::for_table('tbl_coupons')->find_one($coupon_id);
if (!$coupon) {
r2(U . 'coupons', 'e', Lang::T('Coupon Not Found'));
r2(getUrl('coupons'), 'e', Lang::T('Coupon Not Found'));
exit;
}
$ui->assign('coupon', $coupon);
$ui->assign('_title', Lang::T('Edit Coupon: ' . $coupon['code']));
$ui->assign('csrf_token', Csrf::generateAndStoreToken());
$ui->display('coupons-edit.tpl');
$ui->display('admin/coupons/edit.tpl');
break;
case 'edit-post':
@ -189,7 +189,7 @@ switch ($action) {
$error[] = Lang::T('Coupon end date is required');
}
if (!empty($error)) {
r2(U . 'coupons/edit/' . $coupon_id, 'e', implode('<br>', $error));
r2(getUrl('coupons/edit/') . $coupon_id, 'e', implode('<br>', $error));
exit;
}
$coupon = ORM::for_table('tbl_coupons')->find_one($coupon_id);
@ -206,10 +206,10 @@ switch ($action) {
$coupon->updated_at = date('Y-m-d H:i:s');
try {
$coupon->save();
r2(U . 'coupons', 's', Lang::T('Coupon has been updated successfully'));
r2(getUrl('coupons'), 's', Lang::T('Coupon has been updated successfully'));
} catch (Exception $e) {
_log(Lang::T('Error updating coupon: ') . $e->getMessage());
r2(U . 'coupons/edit/' . $coupon_id, 'e', Lang::T('Error updating coupon: ') . $e->getMessage());
r2(getUrl('coupons/edit/') . $coupon_id, 'e', Lang::T('Error updating coupon: ') . $e->getMessage());
}
break;
@ -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

@ -27,7 +27,7 @@ switch ($action) {
}
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(U . 'customers', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('customers'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$cs = ORM::for_table('tbl_customers')
@ -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'])) {
@ -168,7 +168,7 @@ switch ($action) {
$plan_id = $routes['3'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(U . 'customers/view/' . $id_customer, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('customers/view/') . $id_customer, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$b = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('plan_id', $plan_id)->find_one();
if ($b) {
@ -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;
@ -192,13 +196,13 @@ switch ($action) {
list($bills, $add_cost) = User::getBills($id_customer);
if ($using == 'balance' && $config['enable_balance'] == 'yes') {
if (!$cust) {
r2(U . 'plan/recharge', 'e', Lang::T('Customer not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Customer not found'));
}
if (!$plan) {
r2(U . 'plan/recharge', 'e', Lang::T('Plan not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Plan not found'));
}
if ($cust['balance'] < ($plan['price'] + $add_cost + $tax)) {
r2(U . 'plan/recharge', 'e', Lang::T('insufficient balance'));
r2(getUrl('plan/recharge'), 'e', Lang::T('insufficient balance'));
}
$gateway = 'Recharge Balance';
}
@ -224,10 +228,11 @@ 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(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
r2(getUrl('customers/view/') . $id_customer, 'e', 'Cannot find active plan');
}
break;
case 'deactivate':
@ -238,7 +243,7 @@ switch ($action) {
$plan_id = $routes['3'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(U . 'customers/view/' . $id_customer, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('customers/view/') . $id_customer, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$b = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('plan_id', $plan_id)->find_one();
if ($b) {
@ -261,16 +266,16 @@ switch ($action) {
$b->save();
_log('Admin ' . $admin['username'] . ' Deactivate ' . $b['namebp'] . ' for ' . $b['username'], 'User', $b['customer_id']);
Message::sendTelegram('Admin ' . $admin['username'] . ' Deactivate ' . $b['namebp'] . ' for u' . $b['username']);
r2(U . 'customers/view/' . $id_customer, 's', 'Success deactivate customer to Mikrotik');
r2(getUrl('customers/view/') . $id_customer, 's', 'Success deactivate customer to Mikrotik');
}
}
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
r2(getUrl('customers/view/') . $id_customer, 'e', 'Cannot find active plan');
break;
case 'sync':
$id_customer = $routes['2'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(U . 'customers/view/' . $id_customer, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('customers/view/') . $id_customer, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$bs = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->where('status', 'on')->findMany();
if ($bs) {
@ -295,9 +300,9 @@ switch ($action) {
}
}
}
r2(U . 'customers/view/' . $id_customer, 's', 'Sync success to ' . implode(", ", $routers));
r2(getUrl('customers/view/') . $id_customer, 's', 'Sync success to ' . implode(", ", $routers));
}
r2(U . 'customers/view/' . $id_customer, 'e', 'Cannot find active plan');
r2(getUrl('customers/view/') . $id_customer, 'e', 'Cannot find active plan');
break;
case 'login':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
@ -306,7 +311,7 @@ switch ($action) {
$id = $routes['2'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(U . 'customers/view/' . $id, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('customers/view/') . $id, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$customer = ORM::for_table('tbl_customers')->find_one($id);
if ($customer) {
@ -364,9 +369,9 @@ 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(U . 'customers/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('customers/list'), 'e', Lang::T('Account Not Found'));
}
break;
case 'edit':
@ -403,9 +408,9 @@ 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(U . 'customers/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('customers/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -416,7 +421,7 @@ switch ($action) {
$id = $routes['2'];
$csrf_token = _req('token');
if (!Csrf::check($csrf_token)) {
r2(U . 'customers/view/' . $id, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('customers/view/') . $id, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
run_hook('delete_customer'); #HOOK
$c = ORM::for_table('tbl_customers')->find_one($id);
@ -448,7 +453,7 @@ switch ($action) {
$c->delete();
} catch (Exception $e) {
}
r2(U . 'customers/list', 's', Lang::T('User deleted Successfully'));
r2(getUrl('customers/list'), 's', Lang::T('User deleted Successfully'));
}
break;
@ -456,7 +461,7 @@ switch ($action) {
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'customers/add', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('customers/add'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$username = alphanumeric(_post('username'), ":+_.@-");
$fullname = _post('fullname');
@ -575,9 +580,9 @@ switch ($action) {
}
}
}
r2(U . 'customers/list', 's', Lang::T('Account Created Successfully'));
r2(getUrl('customers/list'), 's', Lang::T('Account Created Successfully'));
} else {
r2(U . 'customers/add', 'e', $msg);
r2(getUrl('customers/add'), 'e', $msg);
}
break;
@ -585,7 +590,7 @@ switch ($action) {
$id = _post('id');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'customers/edit/' . $id, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('customers/edit/') . $id, 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$username = alphanumeric(_post('username'), ":+_.@-");
$fullname = _post('fullname');
@ -708,7 +713,7 @@ switch ($action) {
}
if (file_exists($_FILES['photo']['tmp_name'])) unlink($_FILES['photo']['tmp_name']);
} else {
r2(U . 'settings/app', 'e', 'PHP GD is not installed');
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
}
if ($userDiff) {
@ -815,9 +820,9 @@ switch ($action) {
$tur->save();
}
}
r2(U . 'customers/view/' . $id, 's', 'User Updated Successfully');
r2(getUrl('customers/view/') . $id, 's', 'User Updated Successfully');
} else {
r2(U . 'customers/edit/' . $id, 'e', $msg);
r2(getUrl('customers/edit/') . $id, 'e', $msg);
}
break;
@ -856,7 +861,7 @@ switch ($action) {
if (_post('export', '') == 'csv') {
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'customers', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('customers'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$d = $query->findMany();
$h = false;
@ -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

@ -38,9 +38,9 @@ switch ($action) {
});
}
if(file_put_contents($fieldPath, json_encode($datas))){
r2(U . 'customfield', 's', 'Successfully saved custom fields!');
r2(getUrl('customfield'), 's', 'Successfully saved custom fields!');
}else{
r2(U . 'customfield', 'e', 'Failed to save custom fields!');
r2(getUrl('customfield'), 'e', 'Failed to save custom fields!');
}
default:
$fields = [];
@ -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(U . '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(U.'dashboard');
}if(User::getID()){
r2(U.'home');
//r2(getUrl('dashboard'));
$handler = 'dashboard';
}else if(User::getID()){
//r2(getUrl('home'));
$handler = 'home';
}else{
r2(U.'login');
//r2(getUrl('login'));
$handler = 'login';
}
include($root_path . File::pathFixer('system/controllers/' . $handler . '.php'));

View File

@ -71,8 +71,8 @@ switch ($action) {
if (count($plns) > 0) {
$query->where_in('plan_name', $plns);
}
$x = $query->find_array();
$xy = $query->sum('price');
$x = $query->find_array();
$xy = $query->sum('price');
$ui->assign('sd', $sd);
$ui->assign('ed', $ed);
@ -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':
@ -114,7 +114,10 @@ switch ($action) {
$query = ORM::for_table('tbl_transactions')
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
->order_by_desc('id');
->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
->select('tbl_transactions.*')
->select('tbl_customers.fullname', 'fullname')
->order_by_desc('tbl_transactions.id');
if (count($tps) > 0) {
$query->where_in('type', $tps);
}
@ -131,13 +134,13 @@ switch ($action) {
if (count($plns) > 0) {
$query->where_in('plan_name', $plns);
}
$x = $query->find_array();
$xy = $query->sum('price');
$x = $query->find_array();
$xy = $query->sum('price');
$title = ' Reports [' . $mdate . ']';
$title = str_replace('-', ' ', $title);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
if (file_exists($UPLOAD_PATH . '/logo.png')) {
$logo = $UPLOAD_URL_PATH . '/logo.png';
} else {
@ -154,10 +157,11 @@ switch ($action) {
</div>
<div id="logo"><img id="image" src="' . $logo . '" alt="logo" /></div>
</div>
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . Lang::dateAndTimeFormat($sd, $ts) .' - '. Lang::dateAndTimeFormat($ed, $te) . '</div>
<div id="header">' . Lang::T('All Transactions at Date') . ': ' . Lang::dateAndTimeFormat($sd, $ts) . ' - ' . Lang::dateAndTimeFormat($ed, $te) . '</div>
<table id="customers">
<tr>
<th>' . Lang::T('Username') . '</th>
<th>' . Lang::T('Fullname') . '</th>
<th>' . Lang::T('Plan Name') . '</th>
<th>' . Lang::T('Type') . '</th>
<th>' . Lang::T('Plan Price') . '</th>
@ -170,6 +174,7 @@ switch ($action) {
foreach ($x as $value) {
$username = $value['username'];
$fullname = $value['fullname'];
$plan_name = $value['plan_name'];
$type = $value['type'];
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
@ -181,6 +186,7 @@ switch ($action) {
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
<td>$username</td>
<td>$fullname</td>
<td>$plan_name</td>
<td>$type</td>
<td align='right'>$price</td>
@ -245,7 +251,7 @@ $style
$html
EOF;
$mpdf->WriteHTML($nhtml);
$mpdf->Output('phpnuxbill_reports_'.date('Ymd_His') . '.pdf', 'D');
$mpdf->Output('phpnuxbill_reports_' . date('Ymd_His') . '.pdf', 'D');
} else {
echo 'No Data';
}
@ -258,13 +264,17 @@ EOF;
$stype = _post('stype');
$d = ORM::for_table('tbl_transactions');
$d->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
->select('tbl_transactions.*')
->select('tbl_customers.fullname', 'fullname')
->order_by_desc('tbl_transactions.id');
if ($stype != '') {
$d->where('type', $stype);
}
$d->where_gte('recharged_on', $fdate);
$d->where_lte('recharged_on', $tdate);
$d->order_by_desc('id');
$x = $d->find_many();
$x = $d->find_many();
$dr = ORM::for_table('tbl_transactions');
if ($stype != '') {
@ -281,7 +291,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;
@ -290,6 +300,10 @@ EOF;
$tdate = _post('tdate');
$stype = _post('stype');
$d = ORM::for_table('tbl_transactions');
$d->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
->select('tbl_transactions.*')
->select('tbl_customers.fullname', 'fullname')
->order_by_desc('tbl_transactions.id');
if ($stype != '') {
$d->where('type', $stype);
}
@ -297,7 +311,7 @@ EOF;
$d->where_gte('recharged_on', $fdate);
$d->where_lte('recharged_on', $tdate);
$d->order_by_desc('id');
$x = $d->find_many();
$x = $d->find_many();
$dr = ORM::for_table('tbl_transactions');
if ($stype != '') {
@ -311,7 +325,7 @@ EOF;
$title = ' Reports [' . $mdate . ']';
$title = str_replace('-', ' ', $title);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
if (file_exists($UPLOAD_PATH . '/logo.png')) {
$logo = $UPLOAD_URL_PATH . '/logo.png';
} else {
@ -332,6 +346,7 @@ EOF;
<table id="customers">
<tr>
<th>' . Lang::T('Username') . '</th>
<th>' . Lang::T('Fullname') . '</th>
<th>' . Lang::T('Plan Name') . '</th>
<th>' . Lang::T('Type') . '</th>
<th>' . Lang::T('Plan Price') . '</th>
@ -344,6 +359,7 @@ EOF;
foreach ($x as $value) {
$username = $value['username'];
$fullname = $value['fullname'];
$plan_name = $value['plan_name'];
$type = $value['type'];
$price = $config['currency_code'] . ' ' . number_format($value['price'], 0, $config['dec_point'], $config['thousands_sep']);
@ -355,7 +371,8 @@ EOF;
$html .= "<tr" . (($c = !$c) ? ' class="alt"' : ' class=""') . ">" . "
<td>$username</td>
<td>$plan_name</td>
<td>$fullname</td>
<td>$plan_name</td>
<td>$type</td>
<td align='right'>$price</td>
<td>$recharged_on </td>
@ -427,5 +444,5 @@ EOF;
break;
default:
$ui->display('a404.tpl');
$ui->display('admin/404.tpl');
}

View File

@ -82,16 +82,16 @@ if ($step == 1) {
}
setcookie('forgot_username', '', time() - 3600, '/');
} else {
r2(U . 'forgot&step=1', 'e', Lang::T('Invalid Username or Verification Code'));
r2(getUrl('forgot&step=1'), 'e', Lang::T('Invalid Username or Verification Code'));
}
} else {
if (file_exists($otpPath)) {
unlink($otpPath);
}
r2(U . 'forgot&step=1', 'e', Lang::T('Invalid Username or Verification Code'));
r2(getUrl('forgot&step=1'), 'e', Lang::T('Invalid Username or Verification Code'));
}
} else {
r2(U . 'forgot&step=1', 'e', Lang::T('Invalid Username or Verification Code'));
r2(getUrl('forgot&step=1'), 'e', Lang::T('Invalid Username or Verification Code'));
}
} else if ($step == 7) {
$find = _post('find');

View File

@ -23,18 +23,18 @@ if (_post('send') == 'balance') {
}
$target = ORM::for_table('tbl_customers')->where('username', _post('username'))->find_one();
if (!$target) {
r2(U . 'home', 'd', Lang::T('Username not found'));
r2(getUrl('home'), 'd', Lang::T('Username not found'));
}
$username = _post('username');
$balance = _post('balance');
if ($user['balance'] < $balance) {
r2(U . 'home', 'd', Lang::T('insufficient balance'));
r2(getUrl('home'), 'd', Lang::T('insufficient balance'));
}
if (!empty($config['minimum_transfer']) && intval($balance) < intval($config['minimum_transfer'])) {
r2(U . 'home', 'd', Lang::T('Minimum Transfer') . ' ' . Lang::moneyFormat($config['minimum_transfer']));
r2(getUrl('home'), 'd', Lang::T('Minimum Transfer') . ' ' . Lang::moneyFormat($config['minimum_transfer']));
}
if ($user['username'] == $target['username']) {
r2(U . 'home', 'd', Lang::T('Cannot send to yourself'));
r2(getUrl('home'), 'd', Lang::T('Cannot send to yourself'));
}
if (Balance::transfer($user['id'], $username, $balance)) {
//sender
@ -75,10 +75,10 @@ if (_post('send') == 'balance') {
Message::sendBalanceNotification($user, $target, $balance, ($user['balance'] - $balance), Lang::getNotifText('balance_send'), $config['user_notification_payment']);
Message::sendBalanceNotification($target, $user, $balance, ($target['balance'] + $balance), Lang::getNotifText('balance_received'), $config['user_notification_payment']);
Message::sendTelegram("#u$user[username] send balance to #u$target[username] \n" . Lang::moneyFormat($balance));
r2(U . 'home', 's', Lang::T('Sending balance success'));
r2(getUrl('home'), 's', Lang::T('Sending balance success'));
}
} else {
r2(U . 'home', 'd', Lang::T('Failed, balance is not available'));
r2(getUrl('home'), 'd', Lang::T('Failed, balance is not available'));
}
} else if (_post('send') == 'plan') {
if ($user['status'] != 'Active') {
@ -90,13 +90,12 @@ if (_post('send') == 'balance') {
foreach ($actives as $active) {
$router = ORM::for_table('tbl_routers')->where('name', $active['routers'])->find_one();
if ($router) {
r2(U . "order/send/$router[id]/$active[plan_id]&u=" . trim(_post('username')), 's', Lang::T('Review package before recharge'));
r2(getUrl('order/send/$router[id]/$active[plan_id]&u=') . trim(_post('username')), 's', Lang::T('Review package before recharge'));
}
}
r2(U . 'home', 'w', Lang::T('Your friend do not have active package'));
r2(getUrl('home'), 'w', Lang::T('Your friend do not have active package'));
}
$_bill = User::_billing();
$ui->assign('_bills', $_bill);
// Sync plan to router
if (isset($_GET['sync']) && !empty($_GET['sync'])) {
@ -128,7 +127,7 @@ if (isset($_GET['sync']) && !empty($_GET['sync'])) {
}
}
}
r2(U . 'home', 's', $log);
r2(getUrl('home'), 's', $log);
}
if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
@ -136,7 +135,7 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
}
if (!empty(App::getTokenValue(_get('stoken')))) {
r2(U . "voucher/invoice/");
r2(getUrl('voucher/invoice/'));
die();
}
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['recharge'])->where('username', $user['username'])->findOne();
@ -147,17 +146,17 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
$routers = ORM::for_table('tbl_routers')->where('name', $bill['routers'])->find_one();
$router = $routers['id'];
}
r2(U. "order/gateway/$router/$bill[plan_id]");
r2(getUrl("order/gateway/$router/$bill[plan_id]"));
}
} else if (!empty(_get('extend'))) {
if ($user['status'] != 'Active') {
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
}
if (!$config['extend_expired']) {
r2(U . 'home', 'e', "cannot extend");
r2(getUrl('home'), 'e', "cannot extend");
}
if (!empty(App::getTokenValue(_get('stoken')))) {
r2(U . 'home', 'e', "You already extend");
r2(getUrl('home'), 'e', "You already extend");
}
$id = _get('extend');
$tur = ORM::for_table('tbl_user_recharges')->where('customer_id', $user['id'])->where('id', $id)->find_one();
@ -172,7 +171,7 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
// is already extend
$last = file_get_contents($path);
if ($last == $m) {
r2(U . 'home', 'e', "You already extend for this month");
r2(getUrl('home'), 'e', "You already extend for this month");
}
}
if ($tur['status'] != 'on') {
@ -197,17 +196,17 @@ 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']));
r2(U . 'home', 's', "Extend until $expiration");
r2(getUrl('home'), 's', "Extend until $expiration");
} else {
r2(U . 'home', 'e', "Plan is not expired");
r2(getUrl('home'), 'e', "Plan is not expired");
}
} else {
r2(U . 'home', 'e', "Plan Not Found or Not Active");
r2(getUrl('home'), 'e', "Plan Not Found or Not Active");
}
} else if (isset($_GET['deactivate']) && !empty($_GET['deactivate'])) {
$bill = ORM::for_table('tbl_user_recharges')->where('id', $_GET['deactivate'])->where('username', $user['username'])->findOne();
@ -228,9 +227,9 @@ if (isset($_GET['recharge']) && !empty($_GET['recharge'])) {
$bill->save();
_log('User ' . $bill['username'] . ' Deactivate ' . $bill['namebp'], 'Customer', $bill['customer_id']);
Message::sendTelegram('User u' . $bill['username'] . ' Deactivate ' . $bill['namebp']);
r2(U . 'home', 's', 'Success deactivate ' . $bill['namebp']);
r2(getUrl('home'), 's', 'Success deactivate ' . $bill['namebp']);
} else {
r2(U . 'home', 'e', 'No Active Plan');
r2(getUrl('home'), 'e', 'No Active Plan');
}
}
@ -245,10 +244,10 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && $_c['hs_auth_m
require_once $dvc;
if ($_GET['mikrotik'] == 'login') {
(new $p['device'])->connect_customer($user, $_SESSION['nux-ip'], $_SESSION['nux-mac'], $bill['routers']);
r2(U . 'home', 's', Lang::T('Login Request successfully'));
r2(getUrl('home'), 's', Lang::T('Login Request successfully'));
} else if ($_GET['mikrotik'] == 'logout') {
(new $p['device'])->disconnect_customer($user, $bill['routers']);
r2(U . 'home', 's', Lang::T('Logout Request successfully'));
r2(getUrl('home'), 's', Lang::T('Logout Request successfully'));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -269,16 +268,16 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSI
$ui->assign('logged', $_GET['logged']);
if ($_app_stage != 'demo') {
if ($_GET['mikrotik'] == 'login') {
r2(U . 'home&hchap=true', 's', Lang::T('Login Request successfully'));
r2(getUrl('home&hchap=true'), 's', Lang::T('Login Request successfully'));
}
$getmsg = $_GET['msg'];
///get auth notification from mikrotik
if ($getmsg == 'Connected') {
$msg .= Lang::T($getmsg);
r2(U . 'home&logged=1', 's', $msg);
r2(getUrl('home&logged=1'), 's', $msg);
} else if ($getmsg) {
$msg .= Lang::T($getmsg);
r2(U . 'home', 's', $msg);
r2(getUrl('home'), 's', $msg);
}
}
}
@ -296,60 +295,39 @@ if (!empty($_SESSION['nux-mac']) && !empty($_SESSION['nux-ip'] && !empty($_SESSI
$ui->assign('logged', $_GET['logged']);
if ($_app_stage != 'demo') {
if ($_GET['mikrotik'] == 'login') {
r2(U . 'home&hchap=true', 's', Lang::T('Login Request successfully'));
r2(getUrl('home&hchap=true'), 's', Lang::T('Login Request successfully'));
}
$getmsg = $_GET['msg'];
///get auth notification from mikrotik
if ($getmsg == 'Connected') {
$msg .= Lang::T($getmsg);
r2(U . 'home&logged=1', 's', $msg);
r2(getUrl('home&logged=1'), 's', $msg);
} else if ($getmsg) {
$msg .= Lang::T($getmsg);
r2(U . 'home', 's', $msg);
r2(getUrl('home'), 's', $msg);
}
}
}
$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('widgets', $widgets);
$ui->assign('code', alphanumeric(_get('code'), "-"));
$abills = User::getAttributes("Bill");
$ui->assign('abills', $abills);
run_hook('view_customer_dashboard'); #HOOK
$ui->display('customer/dashboard.tpl');

View File

@ -0,0 +1,27 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
*
**/
_admin();
$ui->assign('_title', Lang::T('Invoice Lists'));
$ui->assign('_system_menu', 'reports');
$action = $routes['1'];
$ui->assign('_admin', $admin);
if (empty($action)) {
$action = 'list';
}
switch ($action) {
case 'list':
$ui->assign('xheader', '<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">');
$ui->assign('invoices', Invoice::getAll());
$ui->display('admin/invoices/list.tpl');
break;
default:
$ui->display('admin/404.tpl');
}

View File

@ -11,7 +11,7 @@ if ($maintenance_mode == true) {
}
if (User::getID()) {
r2(U . 'home');
r2(getUrl('home'));
}
if (isset($routes['1'])) {
@ -27,7 +27,7 @@ switch ($do) {
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
_msglog('e', Lang::T('Invalid or Expired CSRF Token'));
r2(U . 'login');
r2(getUrl('login'));
}
run_hook('customer_login'); #HOOK
if ($username != '' and $password != '') {
@ -54,15 +54,15 @@ switch ($do) {
} else {
_msglog('e', Lang::T('Invalid Username or Password'));
_log($username . ' ' . Lang::T('Failed Login'), 'User');
r2(U . 'login');
r2(getUrl('login'));
}
} else {
_msglog('e', Lang::T('Invalid Username or Password'));
r2(U . 'login');
r2(getUrl('login'));
}
} else {
_msglog('e', Lang::T('Invalid Username or Password'));
r2(U . 'login');
r2(getUrl('login'));
}
break;
@ -72,7 +72,7 @@ switch ($do) {
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
_msglog('e', Lang::T('Invalid or Expired CSRF Token'));
r2(U . 'login');
r2(getUrl('login'));
}
$voucher = Text::alphanumeric(_post('voucher_only'), "-_.,");
$tur = ORM::for_table('tbl_user_recharges')
@ -103,7 +103,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -112,7 +112,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
_alert(Lang::T("Voucher activation success, now you can login"), 'danger', $config['voucher_redirect']);
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, you are connected to internet"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, you are connected to internet"));
}
} else {
_alert(Lang::T('Internet Plan Expired'), 'danger', "login");
@ -148,7 +148,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -157,7 +157,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
_alert(Lang::T("Voucher activation success, now you can login"), 'danger', $config['voucher_redirect']);
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, you are connected to internet"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, you are connected to internet"));
}
} else {
_alert(Lang::T('Internet Plan Expired'), 'danger', "login");
@ -190,11 +190,11 @@ switch ($do) {
if ($d->save()) {
$user = ORM::for_table('tbl_customers')->where('username', $username)->find_one($d->id());
if (!$user) {
r2(U . 'login', 'e', Lang::T('Voucher activation failed'));
r2(getUrl('login'), 'e', Lang::T('Voucher activation failed'));
}
} else {
_alert(Lang::T('Login Successful'), 'success', "dashboard");
r2(U . 'login', 'e', Lang::T('Voucher activation failed') . '.');
r2(getUrl('login'), 'e', Lang::T('Voucher activation failed') . '.');
}
}
if ($v1['status'] == 0) {
@ -222,7 +222,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -231,26 +231,26 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, you are connected to internet"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, you are connected to internet"));
}
} catch (Exception $e) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
}
}
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
// if failed to recharge, restore old password
$user->password = $oldPass;
$user->save();
r2(U . 'login', 'e', Lang::T("Failed to activate voucher"));
r2(getUrl('login'), 'e', Lang::T("Failed to activate voucher"));
}
} else {
// used voucher
@ -269,7 +269,7 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} else {
new Exception(Lang::T("Devices Not Found"));
@ -278,30 +278,30 @@ switch ($do) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
} catch (Exception $e) {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, now you can login"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
}
} else {
if (!empty($config['voucher_redirect'])) {
r2($config['voucher_redirect'], 's', Lang::T("Voucher activation success, you are connected to internet"));
} else {
r2(U . "login", 's', Lang::T("Voucher activation success, now you can login"));
r2(getUrl('login'), 's', Lang::T("Voucher activation success, now you can login"));
}
}
} else {
// voucher used by other customer
r2(U . 'login', 'e', Lang::T('Voucher Not Valid'));
r2(getUrl('login'), 'e', Lang::T('Voucher Not Valid'));
}
}
} else {
_msglog('e', Lang::T('Invalid Username or Password'));
r2(U . 'login');
r2(getUrl('login'));
}
}
default:

View File

@ -80,12 +80,45 @@ 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');
if (!empty($keep)) {
ORM::raw_execute("DELETE FROM tbl_logs WHERE UNIX_TIMESTAMP(date) < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
r2(U . "logs/list/", 's', "Delete logs older than $keep days");
r2(getUrl('logs/list/'), 's', "Delete logs older than $keep days");
}
if ($q != '') {
$query = ORM::for_table('tbl_logs')->where_like('description', '%' . $q . '%')->order_by_desc('id');
@ -97,14 +130,14 @@ 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'));
$keep = _post('keep');
if (!empty($keep)) {
ORM::raw_execute("DELETE FROM radpostauth WHERE UNIX_TIMESTAMP(authdate) < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))", [], 'radius');
r2(U . "logs/radius/", 's', "Delete logs older than $keep days");
r2(getUrl('logs/radius/'), 's', "Delete logs older than $keep days");
}
if ($q != '') {
$query = ORM::for_table('radpostauth', 'radius')->where_like('username', '%' . $q . '%')->order_by_desc('id');
@ -116,10 +149,37 @@ 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(U . 'logs/list/', 's', '');
r2(getUrl('logs/list/'), 's', '');
}

View File

@ -14,7 +14,7 @@ switch ($action) {
case 'view':
$mail = ORM::for_table('tbl_customers_inbox')->where('customer_id', $user['id'])->find_one($routes['2']);
if(!$mail){
r2(U. 'mail', 'e', Lang::T('Message Not Found'));
r2(getUrl('mail'), 'e', Lang::T('Message Not Found'));
}
if($mail['date_read'] == null){
$mail->date_read = date('Y-m-d H:i:s');
@ -34,9 +34,9 @@ switch ($action) {
case 'delete':
if($routes['2']){
if(ORM::for_table('tbl_customers_inbox')->where('customer_id', $user['id'])->where('id', $routes['2'])->find_one()->delete()){
r2(U. 'mail', 's', Lang::T('Mail Deleted Successfully'));
r2(getUrl('mail'), 's', Lang::T('Mail Deleted Successfully'));
}else{
r2(U. 'home', 'e', Lang::T('Failed to Delete Message'));
r2(getUrl('home'), 'e', Lang::T('Failed to Delete Message'));
}
break;
}

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,13 +45,23 @@ 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(U . 'map/customer', 'e', 'action not defined');
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':
@ -55,40 +57,79 @@ EOT;
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
// Get form data
$id_customer = $_POST['id_customer'];
$message = $_POST['message'];
$via = $_POST['via'];
$id_customer = $_POST['id_customer'] ?? '';
$message = $_POST['message'] ?? '';
$via = $_POST['via'] ?? '';
$subject = $_POST['subject'] ?? '';
// Check if fields are empty
if ($id_customer == '' or $message == '' or $via == '') {
r2(U . 'message/send', 'e', Lang::T('All field is required'));
} else {
// Get customer details from the database
$c = ORM::for_table('tbl_customers')->find_one($id_customer);
// Validate subject based on the selected channel
if (($via === 'all' || $via === 'email' || $via === 'inbox') && empty($subject)) {
r2(getUrl('message/send'), 'e', LANG::T('Subject is required to send message using') . ' ' . $via . '.');
}
// Replace placeholders in the message with actual values
$message = str_replace('[[name]]', $c['fullname'], $message);
$message = str_replace('[[user_name]]', $c['username'], $message);
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
if (empty($id_customer) || empty($message) || empty($via)) {
r2(getUrl('message/send'), 'e', Lang::T('Customer, Message, and Channel are required'));
}
$customer = ORM::for_table('tbl_customers')->find_one($id_customer);
if (!$customer) {
r2(getUrl('message/send'), 'e', Lang::T('Customer not found'));
}
//Send the message
if ($via == 'sms' || $via == 'both') {
$smsSent = Message::sendSMS($c['phonenumber'], $message);
}
// Replace placeholders in message and subject
$currentMessage = str_replace(
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
$message
);
if ($via == 'wa' || $via == 'both') {
$waSent = Message::sendWhatsapp($c['phonenumber'], $message);
}
$currentSubject = str_replace(
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
$subject
);
if (isset($smsSent) || isset($waSent)) {
r2(U . 'message/send', 's', Lang::T('Message Sent Successfully'));
if (strpos($message, '[[payment_link]]') !== false) {
$token = User::generateToken($customer['id'], 1);
if (!empty($token['token'])) {
$tur = ORM::for_table('tbl_user_recharges')
->where('customer_id', $customer['id'])
->find_one();
if ($tur) {
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
$currentMessage = str_replace('[[payment_link]]', $url, $currentMessage);
}
} else {
r2(U . 'message/send', 'e', Lang::T('Failed to send message'));
$currentMessage = str_replace('[[payment_link]]', '', $currentMessage);
}
}
// Send the message through the selected channels
$smsSent = $waSent = $emailSent = $inboxSent = false;
if ($via === 'sms' || $via === 'both' || $via === 'all') {
$smsSent = Message::sendSMS($customer['phonenumber'], $currentSubject);
}
if ($via === 'wa' || $via === 'both' || $via === 'all') {
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentSubject);
}
if ($via === 'email' || $via === 'all') {
$emailSent = Message::sendEmail($customer['email'], $currentSubject, $currentMessage);
}
if ($via === 'inbox' || $via === 'all') {
$inboxSent = Message::addToInbox($customer['id'], $currentSubject, $currentMessage, 'Admin');
}
// Check if any message was sent successfully
if ($smsSent || $waSent || $emailSent || $inboxSent) {
r2(getUrl('message/send'), 's', Lang::T('Message Sent Successfully'));
} else {
r2(getUrl('message/send'), 'e', Lang::T('Failed to send message'));
}
break;
case 'send_bulk':
@ -96,143 +137,362 @@ 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';
$service = $_REQUEST['service'] ?? '';
$subject = $_REQUEST['subject'] ?? '';
if (empty($group) || empty($message) || empty($via) || empty($service)) {
die(json_encode(['status' => 'error', 'message' => LANG::T('All fields are required')]));
}
if (in_array($via, ['all', 'email', 'inbox']) && empty($subject)) {
die(json_encode(['status' => 'error', 'message' => LANG::T('Subject is required to send message using') . ' ' . $via . '.']));
}
// 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' => LANG::T('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'],
['tbl_customers.username', 'username'],
['tbl_customers.email', 'email'],
['tbl_customers.service_type', 'service_type'],
]);
$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;
$totalEmailSent = 0;
$totalEmailFailed = 0;
$totalInboxSent = 0;
$totalInboxFailed = 0;
$batchStatus = [];
//$subject = $config['CompanyName'] . ' ' . Lang::T('Notification Message');
$form = 'Admin';
if (_req('send') == 'now') {
// Check if fields are empty
if ($group == '' || $message == '' || $via == '') {
r2(U . 'message/send_bulk', 'e', Lang::T('All fields are required'));
foreach ($customers as $customer) {
$currentMessage = str_replace(
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
$message
);
$currentSubject = str_replace(
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
$subject
);
$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'],
'channel' => 'Test Channel',
'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' || $via === 'all') {
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'
];
}
}
}
if ($via === 'wa' || $via == 'both' || $via === 'all') {
if (Message::sendWhatsapp($customer['phonenumber'], $currentMessage)) {
$totalWhatsappSent++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
} else {
$totalWhatsappFailed++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
}
}
// Introduce a delay between each batch
if ($batchIndex < $totalBatches - 1) {
sleep($delay);
if ($via === 'email' || $via === 'all') {
if (Message::sendEmail($customer['email'], $currentSubject, $currentMessage)) {
$totalEmailSent++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['email'], 'status' => 'Email Sent', 'message' => $currentMessage];
} else {
$totalEmailFailed++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => $customer['email'], 'status' => 'Email Failed', 'message' => $currentMessage];
}
}
if ($via === 'inbox' || $via === 'all') {
if (Message::addToInbox($customer['customer_id'], $currentSubject, $currentMessage, $form)) {
$totalInboxSent++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => 'Inbox', 'status' => 'Inbox Message Sent', 'message' => $currentMessage];
} else {
$totalInboxFailed++;
$batchStatus[] = ['name' => $customer['fullname'], 'channel' => 'Inbox', 'status' => 'Inbox Message 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 + $totalEmailSent + $totalInboxSent,
'totalFailed' => $totalSMSFailed + $totalWhatsappFailed + $totalEmailFailed + $totalInboxFailed,
'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'] ?? '';
$subject = $_POST['subject'] ?? '';
$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;
}
if ($via === 'all' || $via === 'email' || $via === 'inbox' && empty($subject)) {
die(json_encode(['status' => 'error', 'message' => LANG::T('Subject is required to send message using') . ' ' . $via . '.']));
}
// Prepare to send messages
$sentCount = 0;
$failedCount = 0;
$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(U . 'message/send_sms', 'e', 'action not defined');
r2(getUrl('message/send_sms'), 'e', 'action not defined');
}

View File

@ -34,7 +34,7 @@ switch ($action) {
break;
case 'balance':
if (strpos($user['email'], '@') === false) {
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
r2(getUrl('accounts/profile'), 'e', Lang::T("Please enter your email address"));
}
$ui->assign('_title', 'Top Up');
$ui->assign('_system_menu', 'balance');
@ -44,7 +44,7 @@ switch ($action) {
break;
case 'package':
if (strpos($user['email'], '@') === false) {
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
r2(getUrl('accounts/profile'), 'e', Lang::T("Please enter your email address"));
}
$ui->assign('_title', 'Order Plan');
$ui->assign('_system_menu', 'package');
@ -144,12 +144,12 @@ switch ($action) {
r_find_unpaid'); #HOOK
if ($d) {
if (empty($d['pg_url_payment'])) {
r2(U . "order/buy/" . $trx['routers_id'] . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
r2(getUrl('order/buy/') . $trx['routers_id'] . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
} else {
r2(U . "order/view/" . $d['id'] . '/check/', 's', Lang::T("You have unpaid transaction"));
r2(getUrl('order/view/') . $d['id'] . '/check/', 's', Lang::T("You have unpaid transaction"));
}
} else {
r2(U . "order/package/", 's', Lang::T("You have no unpaid transaction"));
r2(getUrl('order/package/'), 's', Lang::T("You have no unpaid transaction"));
}
break;
case 'view':
@ -160,15 +160,15 @@ switch ($action) {
run_hook('customer_view_payment'); #HOOK
// jika tidak ditemukan, berarti punya orang lain
if (empty($trx)) {
r2(U . "order/package", 'w', Lang::T("Payment not found"));
r2(getUrl('order/package'), 'w', Lang::T("Payment not found"));
}
// jika url kosong, balikin ke buy, kecuali cancel
if ($trx['status'] == 1 && empty($trx['pg_url_payment']) && $routes['3'] != 'cancel') {
r2(U . "order/buy/" . (($trx['routers_id'] == 0) ? $trx['routers'] : $trx['routers_id']) . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
r2(getUrl('order/buy/') . (($trx['routers_id'] == 0) ? $trx['routers'] : $trx['routers_id']) . '/' . $trx['plan_id'], 'w', Lang::T("Checking payment"));
}
if ($routes['3'] == 'check') {
if (!file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $trx['gateway'] . '.php')) {
r2(U . 'order/view/' . $trxid, 'e', Lang::T("No Payment Gateway Available"));
r2(getUrl('order/view/') . $trxid, 'e', Lang::T("No Payment Gateway Available"));
}
run_hook('customer_check_payment_status'); #HOOK
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $trx['gateway'] . '.php';
@ -185,7 +185,7 @@ switch ($action) {
->find_one($trxid);
}
if (empty($trx)) {
r2(U . "order/package", 'e', Lang::T("Transaction Not found"));
r2(getUrl('order/package'), 'e', Lang::T("Transaction Not found"));
}
$router = ORM::for_table('tbl_routers')->where('name', $trx['routers'])->find_one();
@ -202,10 +202,10 @@ switch ($action) {
break;
case 'pay':
if ($config['enable_balance'] != 'yes') {
r2(U . "order/package", 'e', Lang::T("Balance not enabled"));
r2(getUrl('order/package'), 'e', Lang::T("Balance not enabled"));
}
if (!empty(App::getTokenValue($_GET['stoken']))) {
r2(U . "voucher/invoice/");
r2(getUrl('voucher/invoice/'));
die();
}
if ($user['status'] != 'Active') {
@ -213,7 +213,7 @@ switch ($action) {
}
$plan = ORM::for_table('tbl_plans')->find_one($routes[3]);
if (!$plan) {
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
r2(getUrl('order/package'), 'e', Lang::T("Plan Not found"));
}
if ($plan['is_radius'] == '1') {
$router_name = 'radius';
@ -247,21 +247,21 @@ switch ($action) {
// if success, then get the balance
Balance::min($user['id'], $total_cost);
App::setToken($_GET['stoken'], "success");
r2(U . "voucher/invoice/", 's', Lang::T("Success to buy package"));
r2(getUrl('voucher/invoice/'), 's', Lang::T("Success to buy package"));
} else {
r2(U . "order/package", 'e', Lang::T("Failed to buy package"));
r2(getUrl('order/package'), 'e', Lang::T("Failed to buy package"));
Message::sendTelegram("Buy Package with Balance Failed\n\n#u$c[username] #buy \n" . $plan['name_plan'] .
"\nRouter: " . $router_name .
"\nPrice: " . $total_cost);
}
} else {
r2(U . "order/gateway/$routes[2]/$routes[3]", 'e', Lang::T("Insufficient balance"));
r2(getUrl('order/gateway/$routes[2]/$routes[3]'), 'e', Lang::T("Insufficient balance"));
}
break;
case 'send':
if ($config['enable_balance'] != 'yes') {
r2(U . "order/package", 'e', Lang::T("Balance not enabled"));
r2(getUrl('order/package'), 'e', Lang::T("Balance not enabled"));
}
if ($user['status'] != 'Active') {
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
@ -270,10 +270,10 @@ switch ($action) {
$ui->assign('_system_menu', 'package');
$plan = ORM::for_table('tbl_plans')->find_one($routes['3']);
if (empty($plan)) {
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
r2(getUrl('order/package'), 'e', Lang::T("Plan Not found"));
}
if (!$plan['enabled']) {
r2(U . "home", 'e', 'Plan is not exists');
r2(getUrl('home'), 'e', 'Plan is not exists');
}
if ($plan['is_radius'] == '1') {
$routes['2'] = 0;
@ -312,13 +312,13 @@ switch ($action) {
}
if (!$target) {
r2(U . 'home', 'd', Lang::T('Username not found'));
r2(getUrl('home'), 'd', Lang::T('Username not found'));
}
if ($user['balance'] < $plan['price']) {
r2(U . 'home', 'd', Lang::T('insufficient balance'));
r2(getUrl('home'), 'd', Lang::T('insufficient balance'));
}
if ($user['username'] == $target['username']) {
r2(U . "order/pay/$routes[2]/$routes[3]", 's', '^_^ v');
r2(getUrl('order/pay/$routes[2]/$routes[3]'), 's', '^_^ v');
}
$active = ORM::for_table('tbl_user_recharges')
->where('username', _post('username'))
@ -326,7 +326,7 @@ switch ($action) {
->find_one();
if ($active && $active['plan_id'] != $plan['id']) {
r2(U . "order/package", 'e', Lang::T("Target has active plan, different with current plant.") . " [ <b>$active[namebp]</b> ]");
r2(getUrl('order/package'), 'e', Lang::T("Target has active plan, different with current plant.") . " [ <b>$active[namebp]</b> ]");
}
$result = Package::rechargeUser($target['id'], $router_name, $plan['id'], $user['username'], 'Balance');
if (!empty($result)) {
@ -371,7 +371,7 @@ switch ($action) {
$d->trx_invoice = $result;
$d->status = 2;
$d->save();
r2(U . "order/view/$trx_id", 's', Lang::T("Success to send package"));
r2(getUrl("order/view/$trx_id"), 's', Lang::T("Success to send package"));
} else {
$errorMessage = "Send Package with Balance Failed\n\n#u$user[username] #send \n" . $plan['name_plan'] .
"\nRouter: " . $router_name .
@ -381,7 +381,7 @@ switch ($action) {
$errorMessage .= "\nTax: " . $tax;
}
r2(U . "order/package", 'e', Lang::T("Failed to Send package"));
r2(getUrl('order/package'), 'e', Lang::T("Failed to Send package"));
Message::sendTelegram($errorMessage);
}
}
@ -395,7 +395,7 @@ switch ($action) {
$ui->assign('_title', Lang::T('Select Payment Gateway'));
$ui->assign('_system_menu', 'package');
if (strpos($user['email'], '@') === false) {
r2(U . 'accounts/profile', 'e', Lang::T("Please enter your email address"));
r2(getUrl('accounts/profile'), 'e', Lang::T("Please enter your email address"));
}
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
@ -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'])) {
@ -503,7 +507,7 @@ switch ($action) {
if (count($pgs) == 0) {
sendTelegram("Payment Gateway not set, please set it in Settings");
_log(Lang::T("Payment Gateway not set, please set it in Settings"));
r2(U . "home", 'e', Lang::T("Failed to create Transaction.."));
r2(getUrl('home'), 'e', Lang::T("Failed to create Transaction.."));
}
if (count($pgs) > 0) {
$ui->assign('pgs', $pgs);
@ -516,7 +520,7 @@ switch ($action) {
$ui->assign('custom', '1');
$ui->assign('amount', _post('amount'));
} else {
r2(U . "order/balance", 'e', Lang::T("Please enter amount"));
r2(getUrl('order/balance'), 'e', Lang::T("Please enter amount"));
}
}
@ -530,14 +534,14 @@ switch ($action) {
} else {
sendTelegram("Payment Gateway not set, please set it in Settings");
_log(Lang::T("Payment Gateway not set, please set it in Settings"));
r2(U . "home", 'e', Lang::T("Failed to create Transaction.."));
r2(getUrl('home'), 'e', Lang::T("Failed to create Transaction.."));
}
case 'buy':
$gateway = _post('gateway');
$discount = _post('discount') ?: 0;
if ($gateway == 'balance') {
unset($_SESSION['gateway']);
r2(U . 'order/pay/' . $routes[2] . '/' . $routes[3]);
r2(getUrl('order/pay/') . $routes[2] . '/' . $routes[3]);
}
if (empty($gateway) && !empty($_SESSION['gateway'])) {
$gateway = $_SESSION['gateway'];
@ -548,7 +552,7 @@ switch ($action) {
_alert(Lang::T('This account status') . ' : ' . Lang::T($user['status']), 'danger', "");
}
if (empty($gateway)) {
r2(U . 'order/gateway/' . $routes[2] . '/' . $routes[3], 'w', Lang::T("Please select Payment Gateway"));
r2(getUrl('order/gateway/') . $routes[2] . '/' . $routes[3], 'w', Lang::T("Please select Payment Gateway"));
}
run_hook('customer_buy_plan'); #HOOK
include $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $gateway . '.php';
@ -560,7 +564,7 @@ switch ($action) {
$amount = (float) $amount;
if ($amount <= 0) {
r2(U . "order/gateway/" . $routes[2] . '/' . $routes[3], 'w', Lang::T("Please enter amount"));
r2(getUrl('order/gateway/') . $routes[2] . '/' . $routes[3], 'w', Lang::T("Please enter amount"));
}
$d = ORM::for_table('tbl_payment_gateway')
@ -569,7 +573,7 @@ switch ($action) {
->find_one();
if ($d) {
if ($d['pg_url_payment']) {
r2(U . "order/view/" . $d['id'], 'w', Lang::T("You already have unpaid transaction, cancel it or pay it."));
r2(getUrl('order/view/') . $d['id'], 'w', Lang::T("You already have unpaid transaction, cancel it or pay it."));
} else {
if ($gateway == $d['gateway']) {
$id = $d['id'];
@ -606,7 +610,7 @@ switch ($action) {
$router['name'] = 'balance';
}
if (empty($router) || empty($plan)) {
r2(U . "order/package", 'e', Lang::T("Plan Not found"));
r2(getUrl('order/package'), 'e', Lang::T("Plan Not found"));
}
$d = ORM::for_table('tbl_payment_gateway')
->where('username', $user['username'])
@ -614,7 +618,7 @@ switch ($action) {
->find_one();
if ($d) {
if ($d['pg_url_payment']) {
r2(U . "order/view/" . $d['id'], 'w', Lang::T("You already have unpaid transaction, cancel it or pay it."));
r2(getUrl('order/view/') . $d['id'], 'w', Lang::T("You already have unpaid transaction, cancel it or pay it."));
} else {
if ($gateway == $d['gateway']) {
$id = $d['id'];
@ -693,11 +697,11 @@ switch ($action) {
break;
}
if (!$id) {
r2(U . "order/package/" . $d['id'], 'e', Lang::T("Failed to create Transaction.."));
r2(getUrl('order/package/') . $d['id'], 'e', Lang::T("Failed to create Transaction.."));
} else {
call_user_func($gateway . '_create_transaction', $d, $user);
}
break;
default:
r2(U . "order/package/", 's', '');
r2(getUrl('order/package/'), 's', '');
}

View File

@ -26,7 +26,7 @@ if (strpos($action, "-reset") !== false) {
} else {
file_put_contents($path, Http::getData('https://raw.githubusercontent.com/hotspotbilling/phpnuxbill/master/pages_template/' . $action . '.html'));
}
r2(U . 'pages/' . $action);
r2(getUrl('pages/') . $action);
} else if (strpos($action, "-post") === false) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
@ -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");
@ -78,10 +78,10 @@ if (strpos($action, "-reset") !== false) {
file_put_contents("$PAGES_PATH/vouchers/" . _post('template_name') . '.html', $html);
}
}
r2(U . 'pages/' . $action, 's', Lang::T("Saving page success"));
r2(getUrl('pages/') . $action, 's', Lang::T("Saving page success"));
} else {
r2(U . 'pages/' . $action, 'e', Lang::T("Failed to save page, make sure i can write to folder pages, <i>chmod 664 pages/*.html<i>"));
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

@ -16,7 +16,7 @@ switch ($action) {
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $pg . '.php')) {
deleteFile($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR, $pg);
}
r2(U . 'paymentgateway', 's', Lang::T('Payment Gateway Deleted'));
r2(getUrl('paymentgateway'), 's', Lang::T('Payment Gateway Deleted'));
case 'audit':
$pg = alphanumeric($routes[2]);
@ -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') {
@ -61,7 +61,7 @@ switch ($action) {
$d->value = $pgs;
$d->save();
}
r2(U . 'paymentgateway', 's', Lang::T('Payment Gateway saved successfully'));
r2(getUrl('paymentgateway'), 's', Lang::T('Payment Gateway saved successfully'));
}
if (file_exists($PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR . $action . '.php')) {
@ -70,18 +70,18 @@ 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 {
if (!empty($action)) {
r2(U . 'paymentgateway', 'w', Lang::T('Payment Gateway Not Found'));
r2(getUrl('paymentgateway'), 'w', Lang::T('Payment Gateway Not Found'));
} else {
$files = scandir($PAYMENTGATEWAY_PATH);
foreach ($files as $file) {
@ -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'])) {
@ -66,7 +68,7 @@ switch ($action) {
$log .= "PLAN NOT FOUND : $tur[username], $tur[namebp], $tur[type], $tur[routers]<br>";
}
}
r2(U . 'plan/list', 's', $log);
r2(getUrl('plan/list'), 's', $log);
case 'recharge':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
@ -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;
@ -127,13 +133,13 @@ switch ($action) {
if ($using == 'balance' && $config['enable_balance'] == 'yes') {
if (!$cust) {
r2(U . 'plan/recharge', 'e', Lang::T('Customer not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Customer not found'));
}
if (!$plan) {
r2(U . 'plan/recharge', 'e', Lang::T('Plan not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Plan not found'));
}
if ($cust['balance'] < $total_cost) {
r2(U . 'plan/recharge', 'e', Lang::T('insufficient balance'));
r2(getUrl('plan/recharge'), 'e', Lang::T('insufficient balance'));
}
$gateway = 'Recharge Balance';
}
@ -158,9 +164,10 @@ 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(U . 'plan/recharge', 'e', $msg);
r2(getUrl('plan/recharge'), 'e', $msg);
}
break;
@ -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;
@ -217,13 +224,13 @@ switch ($action) {
if ($using == 'balance' && $config['enable_balance'] == 'yes') {
//$plan = ORM::for_table('tbl_plans')->find_one($planId);
if (!$cust) {
r2(U . 'plan/recharge', 'e', Lang::T('Customer not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Customer not found'));
}
if (!$plan) {
r2(U . 'plan/recharge', 'e', Lang::T('Plan not found'));
r2(getUrl('plan/recharge'), 'e', Lang::T('Plan not found'));
}
if ($cust['balance'] < $total_cost) {
r2(U . 'plan/recharge', 'e', Lang::T('insufficient balance'));
r2(getUrl('plan/recharge'), 'e', Lang::T('insufficient balance'));
}
$gateway = 'Recharge Balance';
}
@ -239,13 +246,13 @@ 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(U . 'plan/recharge', 'e', "Failed to recharge account");
r2(getUrl('plan/recharge'), 'e', "Failed to recharge account");
}
} else {
r2(U . 'plan/recharge', 'e', $msg);
r2(getUrl('plan/recharge'), 'e', $msg);
}
break;
@ -257,13 +264,26 @@ switch ($action) {
$c = ORM::for_table('tbl_customers')->where('username', $in['username'])->find_one();
if ($c) {
Message::sendInvoice($c, $in);
r2(U . 'plan/view/' . $id, 's', "Success send to customer");
r2(getUrl('plan/view/') . $id, 's', "Success send to customer");
}
r2(U . 'plan/view/' . $id, 'd', "Customer not found");
r2(getUrl('plan/view/') . $id, 'd', "Customer not found");
}
Package::createInvoice($in);
$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,9 +332,9 @@ 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(U . 'plan/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('plan/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -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
@ -339,7 +359,7 @@ switch ($action) {
}
$d->delete();
_log('[' . $admin['username'] . ']: ' . 'Delete Plan for Customer ' . $c['username'] . ' [' . $in['plan_name'] . '][' . Lang::moneyFormat($in['price']) . ']', $admin['user_type'], $admin['id']);
r2(U . 'plan/list', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('plan/list'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -405,9 +425,9 @@ switch ($action) {
}
$d->save();
_log('[' . $admin['username'] . ']: ' . 'Edit Plan for Customer ' . $d['username'] . ' to [' . $d['namebp'] . '][' . Lang::moneyFormat($p['price']) . ']', $admin['user_type'], $admin['id']);
r2(U . 'plan/list', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('plan/list'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'plan/edit/' . $id, 'e', $msg);
r2(getUrl('plan/edit/') . $id, 'e', $msg);
}
break;
@ -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':
@ -544,7 +564,7 @@ switch ($action) {
$jml++;
}
}
r2(U . 'plan/voucher', 's', "$jml " . Lang::T('Data Deleted Successfully'));
r2(getUrl('plan/voucher'), 's', "$jml " . Lang::T('Data Deleted Successfully'));
}
case 'print-voucher':
$from_id = _post('from_id');
@ -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,16 +816,16 @@ 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) {
r2(U . 'plan/voucher-view/' . $d->id(), 's', Lang::T('Create Vouchers Successfully'));
r2(getUrl('plan/voucher-view/') . $d->id(), 's', Lang::T('Create Vouchers Successfully'));
}
r2(U . 'plan/voucher', 's', Lang::T('Create Vouchers Successfully'));
r2(getUrl('plan/voucher'), 's', Lang::T('Create Vouchers Successfully'));
} else {
r2(U . 'plan/add-voucher/' . $id, 'e', $msg);
r2(getUrl('plan/add-voucher/') . $id, 'e', $msg);
}
break;
@ -855,11 +880,11 @@ switch ($action) {
$voucher = ORM::for_table('tbl_voucher')
->find_one($id);
if (!in_array($voucher['generated_by'], $sales)) {
r2(U . 'plan/voucher/', 'e', Lang::T('Voucher Not Found'));
r2(getUrl('plan/voucher/'), 'e', Lang::T('Voucher Not Found'));
}
}
if (!$voucher) {
r2(U . 'plan/voucher/', 'e', Lang::T('Voucher Not Found'));
r2(getUrl('plan/voucher/'), 'e', Lang::T('Voucher Not Found'));
}
$plan = ORM::for_table('tbl_plans')->find_one($voucher['id_plan']);
if ($voucher && $plan) {
@ -891,21 +916,21 @@ 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(U . 'plan/voucher/', 'e', Lang::T('Voucher Not Found'));
r2(getUrl('plan/voucher/'), 'e', Lang::T('Voucher Not Found'));
}
break;
case 'voucher-delete':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$id = $routes['2'];
$id = $routes['2'];
run_hook('delete_voucher'); #HOOK
$d = ORM::for_table('tbl_voucher')->find_one($id);
if ($d) {
$d->delete();
r2(U . 'plan/voucher', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('plan/voucher'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -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,12 +961,12 @@ 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(U . 'plan/refill', 'e', "Failed to refill account");
r2(getUrl('plan/refill'), 'e', "Failed to refill account");
}
} else {
r2(U . 'plan/refill', 'e', Lang::T('Voucher Not Valid'));
r2(getUrl('plan/refill'), 'e', Lang::T('Voucher Not Valid'));
}
break;
case 'deposit':
@ -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,9 +1012,9 @@ switch ($action) {
if (!empty($svoucher)) {
App::setVoucher($svoucher, $trxId);
}
$ui->display('invoice.tpl');
$ui->display('admin/plan/invoice.tpl');
} else {
r2(U . 'plan/refill', 'e', "Failed to refill account");
r2(getUrl('plan/refill'), 'e', "Failed to refill account");
}
} else if (!empty($user) && !empty($plan)) {
$p = ORM::for_table('tbl_plans')->find_one($plan);
@ -1000,12 +1025,12 @@ switch ($action) {
if (!empty($svoucher)) {
App::setVoucher($svoucher, $trxId);
}
$ui->display('invoice.tpl');
$ui->display('admin/plan/invoice.tpl');
} else {
r2(U . 'plan/refill', 'e', "Failed to refill account");
r2(getUrl('plan/refill'), 'e', "Failed to refill account");
}
} else {
r2(U . 'plan/refill', 'e', "All field is required");
r2(getUrl('plan/refill'), 'e', "All field is required");
}
break;
case 'extend':
@ -1013,7 +1038,7 @@ switch ($action) {
$days = $routes[3];
$svoucher = $_GET['svoucher'];
if (App::getVoucherValue($svoucher)) {
r2(U . 'plan', 's', "Extend already done");
r2(getUrl('plan'), 's', "Extend already done");
}
$tur = ORM::for_table('tbl_user_recharges')->find_one($id);
$status = $tur['status'];
@ -1045,23 +1070,22 @@ switch ($action) {
$tur->status = "on";
$tur->save();
} else {
r2(U . 'plan', 's', "Plan not found");
r2(getUrl('plan'), 's', "Plan not found");
}
} else {
r2(U . 'plan', 's', "Customer not found");
r2(getUrl('plan'), 's', "Customer not found");
}
Message::sendTelegram("#u$tur[username] #extend #" . $p['type'] . " \n" . $p['name_plan'] .
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']);
r2(U . 'plan', 's', "Extend until $expiration");
_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(U . 'plan', 's', "Customer is not expired yet");
r2(getUrl('plan'), 's', "Customer is not expired yet");
}
break;
default:
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/plan.js"></script>');
$ui->assign('_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

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

View File

@ -25,7 +25,7 @@ if (file_exists($cache) && time() - filemtime($cache) < (24 * 60 * 60)) {
$json = json_decode($txt, true);
if (empty($json['plugins']) && empty($json['payment_gateway'])) {
unlink($cache);
r2(U . 'pluginmanager');
r2(getUrl('pluginmanager'));
}
} else {
$data = Http::getData($plugin_repository);
@ -34,24 +34,25 @@ if (file_exists($cache) && time() - filemtime($cache) < (24 * 60 * 60)) {
}
switch ($action) {
case 'refresh':
if (file_exists($cache)) unlink($cache);
r2(U . "pluginmanager", 's', 'Refresh success');
if (file_exists($cache))
unlink($cache);
r2(getUrl('pluginmanager'), 's', 'Refresh success');
break;
case 'dlinstall':
if ($_app_stage == 'demo') {
r2(U . "pluginmanager", 'e', 'Demo Mode cannot install as it Security risk');
if ($_app_stage == 'Demo') {
r2(getUrl('pluginmanager'), 'e', 'Demo Mode cannot install as it Security risk');
}
if (!is_writeable($CACHE_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder cache/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder cache/ is not writable');
}
if (!is_writeable($PLUGIN_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder plugin/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder plugin/ is not writable');
}
if (!is_writeable($DEVICE_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder devices/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder devices/ is not writable');
}
if (!is_writeable($UI_PATH . DIRECTORY_SEPARATOR . 'themes')) {
r2(U . "pluginmanager", 'e', 'Folder themes/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder themes/ is not writable');
}
$cache = $CACHE_PATH . DIRECTORY_SEPARATOR . 'installer' . DIRECTORY_SEPARATOR;
if (!file_exists($cache)) {
@ -97,7 +98,7 @@ switch ($action) {
}
//Cleaning
File::deleteFolder($cache);
r2(U . "pluginmanager", 's', 'Installation success');
r2(getUrl('pluginmanager'), 's', 'Installation success');
} else if (_post('gh_url', '') != '') {
$ghUrl = _post('gh_url', '');
if (!empty($config['github_token']) && !empty($config['github_username'])) {
@ -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;
@ -155,23 +156,27 @@ switch ($action) {
}
}
File::deleteFolder($cache);
r2(U . "pluginmanager", 's', 'Installation success');
r2(getUrl('pluginmanager'), 's', 'Installation success');
} else {
r2(U . 'pluginmanager', 'e', 'Nothing Installed');
r2(getUrl('pluginmanager'), 'e', 'Nothing Installed');
}
break;
case 'delete':
if ($_app_stage == 'Demo') {
r2(getUrl('pluginmanager'), 'e', 'You cannot perform this action in Demo mode');
}
if (!is_writeable($CACHE_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder cache/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder cache/ is not writable');
}
if (!is_writeable($PLUGIN_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder plugin/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder plugin/ is not writable');
}
set_time_limit(-1);
$tipe = $routes['2'];
$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) {
@ -199,12 +204,12 @@ switch ($action) {
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
}
if (!file_exists($folder)) {
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
r2(getUrl('pluginmanager'), 'e', 'Extracted Folder is unknown');
}
scanAndRemovePath($folder, $PLUGIN_PATH . DIRECTORY_SEPARATOR);
File::deleteFolder($folder);
unlink($file);
r2(U . "pluginmanager", 's', 'Plugin ' . $plugin . ' has been deleted');
r2(getUrl('pluginmanager'), 's', 'Plugin ' . $plugin . ' has been deleted');
break;
}
}
@ -212,17 +217,21 @@ 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(U . "pluginmanager", 'e', 'Folder cache/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder cache/ is not writable');
}
if (!is_writeable($PLUGIN_PATH)) {
r2(U . "pluginmanager", 'e', 'Folder plugin/ is not writable');
r2(getUrl('pluginmanager'), 'e', 'Folder plugin/ is not writable');
}
set_time_limit(-1);
$tipe = $routes['2'];
$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) {
@ -250,12 +259,12 @@ switch ($action) {
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
}
if (!file_exists($folder)) {
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
r2(getUrl('pluginmanager'), 'e', 'Extracted Folder is unknown');
}
File::copyFolder($folder, $PLUGIN_PATH . DIRECTORY_SEPARATOR, ['README.md', 'LICENSE']);
File::deleteFolder($folder);
unlink($file);
r2(U . "pluginmanager", 's', 'Plugin ' . $plugin . ' has been installed');
r2(getUrl('pluginmanager'), 's', 'Plugin ' . $plugin . ' has been installed');
break;
}
}
@ -287,12 +296,12 @@ switch ($action) {
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
}
if (!file_exists($folder)) {
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
r2(getUrl('pluginmanager'), 'e', 'Extracted Folder is unknown');
}
File::copyFolder($folder, $PAYMENTGATEWAY_PATH . DIRECTORY_SEPARATOR, ['README.md', 'LICENSE']);
File::deleteFolder($folder);
unlink($file);
r2(U . "paymentgateway", 's', 'Payment Gateway ' . $plugin . ' has been installed');
r2(getUrl('paymentgateway'), 's', 'Payment Gateway ' . $plugin . ' has been installed');
break;
}
}
@ -324,12 +333,12 @@ switch ($action) {
$folder = $CACHE_PATH . File::pathFixer('/' . $plugin . '-master/');
}
if (!file_exists($folder)) {
r2(U . "pluginmanager", 'e', 'Extracted Folder is unknown');
r2(getUrl('pluginmanager'), 'e', 'Extracted Folder is unknown');
}
File::copyFolder($folder, $DEVICE_PATH . DIRECTORY_SEPARATOR, ['README.md', 'LICENSE']);
File::deleteFolder($folder);
unlink($file);
r2(U . "settings/devices", 's', 'Device ' . $plugin . ' has been installed');
r2(getUrl('settings/devices'), 's', 'Device ' . $plugin . ' has been installed');
break;
}
}
@ -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="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,9 +48,9 @@ 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(U . 'pool/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('pool/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -65,7 +64,7 @@ switch ($action) {
}
$d->delete();
r2(U . 'pool/list', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('pool/list'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -78,7 +77,7 @@ switch ($action) {
$log .= 'DONE: ' . $pool['pool_name'] . ': ' . $pool['range_ip'] . '<br>';
}
}
r2(U . 'pool/list', 's', $log);
r2(getUrl('pool/list'), 's', $log);
break;
case 'add-post':
$name = _post('name');
@ -108,9 +107,9 @@ switch ($action) {
(new MikrotikPppoe())->add_pool($b);
}
$b->save();
r2(U . 'pool/list', 's', Lang::T('Data Created Successfully'));
r2(getUrl('pool/list'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'pool/add', 'e', $msg);
r2(getUrl('pool/add'), 'e', $msg);
}
break;
@ -143,13 +142,12 @@ switch ($action) {
(new MikrotikPppoe())->update_pool($old, $d);
}
r2(U . 'pool/list', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('pool/list'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'pool/edit/' . $id, 'e', $msg);
r2(getUrl('pool/edit/') . $id, 'e', $msg);
}
case 'port':
$ui->assign('xfooter', '<script type="text/javascript" src="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,9 +176,9 @@ 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(U . 'pool/port', 'e', Lang::T('Account Not Found'));
r2(getUrl('pool/port'), 'e', Lang::T('Account Not Found'));
}
break;
@ -191,7 +189,7 @@ switch ($action) {
if ($d) {
$d->delete();
r2(U . 'pool/port', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('pool/port'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -204,7 +202,7 @@ switch ($action) {
$log .= 'DONE: ' . $pool['port_name'] . ': ' . $pool['range_port'] . '<br>';
}
}
r2(U . 'pool/list', 's', $log);
r2(getUrl('pool/list'), 's', $log);
break;
case 'add-port-post':
$name = _post('name');
@ -231,21 +229,21 @@ switch ($action) {
$b->range_port = $port_range;
$b->routers = $routers;
$b->save();
r2(U . 'pool/port', 's', Lang::T('Data Created Successfully'));
r2(getUrl('pool/port'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'pool/add-port', 'e', $msg);
r2(getUrl('pool/add-port'), 'e', $msg);
}
break;
case 'edit-port-post':
$name = _post('name');
$name = _post('name');
$public_ip = _post('public_ip');
$range_port = _post('range_port');
$routers = _post('routers');
run_hook('edit_port'); #HOOK
$msg = '';
$msg = '';
$msg = '';
if (Validator::Length($name, 30, 2) == false) {
$msg .= 'Name should be between 3 to 30 characters' . '<br>';
}
@ -261,20 +259,18 @@ switch ($action) {
}
if ($msg == '') {
$d->port_name = $name;
$d->port_name = $name;
$d->public_ip = $public_ip;
$d->range_port = $range_port;
$d->routers = $routers;
$d->save();
r2(U . 'pool/port', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('pool/port'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'pool/edit-port/' . $id, 'e', $msg);
r2(getUrl('pool/edit-port/') . $id, 'e', $msg);
}
break;
default:
r2(U . 'pool/list/', 's', '');
r2(getUrl('pool/list/'), 's', '');
}

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

@ -57,7 +57,7 @@ switch ($do) {
// Expire after 10 minutes
if (file_exists($otpPath) && time() - filemtime($otpPath) > 1200) {
unlink($otpPath);
r2(U . 'register', 's', 'Verification code expired');
r2(getUrl('register'), 's', 'Verification code expired');
} else if (file_exists($otpPath)) {
$code = file_get_contents($otpPath);
if ($code != $otp_code) {
@ -75,7 +75,7 @@ switch ($do) {
unlink($otpPath);
}
} else {
r2(U . 'register', 's', 'No Verification code');
r2(getUrl('register'), 's', 'No Verification code');
}
}
@ -113,14 +113,15 @@ 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>';
if ($config['reg_nofify_admin'] == 'yes') {
sendTelegram($config['CompanyName'] . ' - ' . Lang::T('New User Registration') . "\n\nFull Name: " . $fullname . "\nUsername: " . $username . "\nEmail: " . $email . "\nPhone Number: " . $phone_number . "\nAddress: " . $address);
}
r2(U . 'login', 's', Lang::T('Register Success! You can login now'));
r2(getUrl('login'), 's', Lang::T('Register Success! You can login now'));
} else {
$ui->assign('username', $username);
$ui->assign('fullname', $fullname);
@ -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;
@ -159,7 +197,7 @@ switch ($do) {
if (!empty($phone_number)) {
$d = ORM::for_table('tbl_customers')->where('username', $phone_number)->find_one();
if ($d) {
r2(U . 'register', 's', Lang::T('Account already exists'));
r2(getUrl('register'), 's', Lang::T('Account already exists'));
}
if (!file_exists($otpPath)) {
mkdir($otpPath);
@ -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) {
@ -241,7 +246,7 @@ switch ($action) {
$total += $v;
$array['data'][] = $v;
}
if($total>0){
if ($total > 0) {
$result['datas'][] = $array;
}
}
@ -253,23 +258,34 @@ switch ($action) {
die();
case 'by-date':
case 'activation':
$q = (_post('q') ? _post('q') : _get('q'));
$q = trim(_post('q') ?: _get('q'));
$keep = _post('keep');
if (!empty($keep)) {
ORM::raw_execute("DELETE FROM tbl_transactions WHERE date < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL $keep DAY))");
r2(U . "logs/list/", 's', "Delete logs older than $keep days");
ORM::raw_execute("DELETE FROM tbl_transactions WHERE date < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL ? DAY))", [$keep]);
r2(getUrl('reports/activation/'), 's', "Deleted logs older than $keep days");
}
if ($q != '') {
$query = ORM::for_table('tbl_transactions')->where_like('invoice', '%' . $q . '%')->order_by_desc('id');
$query = ORM::for_table('tbl_transactions')
->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
->select('tbl_transactions.*')
->select('tbl_customers.fullname', 'fullname')
->order_by_desc('tbl_transactions.id');
if ($q !== '') {
$query->where_like('invoice', "%$q%");
}
try {
$d = Paginator::findMany($query, ['q' => $q]);
} else {
$query = ORM::for_table('tbl_transactions')->order_by_desc('id');
$d = Paginator::findMany($query);
} catch (Exception $e) {
r2(getUrl('reports/activation/'), 'e', 'Database query failed: ' . $e->getMessage());
$d = [];
}
$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 +293,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':
@ -286,6 +302,10 @@ switch ($action) {
$stype = _post('stype');
$d = ORM::for_table('tbl_transactions');
$d->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
->select('tbl_transactions.*')
->select('tbl_customers.fullname', 'fullname')
->order_by_desc('tbl_transactions.id');
if ($stype != '') {
$d->where('type', $stype);
}
@ -293,7 +313,7 @@ switch ($action) {
$d->where_gte('recharged_on', $fdate);
$d->where_lte('recharged_on', $tdate);
$d->order_by_desc('id');
$x = $d->find_many();
$x = $d->find_many();
$dr = ORM::for_table('tbl_transactions');
if ($stype != '') {
@ -310,7 +330,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':
@ -343,16 +363,21 @@ switch ($action) {
$query = ORM::for_table('tbl_transactions')
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) >= " . strtotime("$sd $ts"))
->whereRaw("UNIX_TIMESTAMP(CONCAT(`recharged_on`,' ',`recharged_time`)) <= " . strtotime("$ed $te"))
->order_by_desc('id');
->left_outer_join('tbl_customers', 'tbl_transactions.username = tbl_customers.username')
->select('tbl_transactions.*')
->select('tbl_customers.fullname', 'fullname')
->order_by_desc('tbl_transactions.id');
if (count($tps) > 0) {
$query->where_in('type', $tps);
}
if (count($mts) > 0) {
if (count($mts) != count($methods)) {
foreach ($mts as $mt) {
$query->where_like('method', "$mt - %");
}
$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 +409,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,9 +38,9 @@ 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(U . 'routers/list', 'e', Lang::T('Account Not Found'));
r2(getUrl('routers/list'), 'e', Lang::T('Account Not Found'));
}
break;
@ -65,7 +50,7 @@ switch ($action) {
$d = ORM::for_table('tbl_routers')->find_one($id);
if ($d) {
$d->delete();
r2(U . 'routers/list', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('routers/list'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -109,9 +94,9 @@ switch ($action) {
$d->enabled = $enabled;
$d->save();
r2(U . 'routers/edit/' . $d->id(), 's', Lang::T('Data Created Successfully'));
r2(getUrl('routers/edit/') . $d->id(), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'routers/add', 'e', $msg);
r2(getUrl('routers/add'), 'e', $msg);
}
break;
@ -197,14 +182,13 @@ switch ($action) {
$p->set('routers', $name);
$p->save();
}
r2(U . 'routers/list', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('routers/list'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'routers/edit/' . $id, 'e', $msg);
r2(getUrl('routers/edit/') . $id, 'e', $msg);
}
break;
default:
$ui->assign('xfooter', '<script type="text/javascript" src="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

@ -2,7 +2,7 @@
$query = isset($_GET['query']) ? trim($_GET['query']) : '';
if (!empty($query)) {
if (!empty($query)) {
$results = ORM::for_table('tbl_customers')
->where_like('username', "%$query%")
->find_many();

View File

@ -33,7 +33,7 @@ switch ($action) {
}
}
}
r2(U . 'services/hotspot', 's', $log);
r2(getUrl('services/hotspot'), 's', $log);
} else if ($routes['2'] == 'pppoe') {
$plans = ORM::for_table('tbl_plans')->where('type', 'PPPOE')->find_many();
$log = '';
@ -49,11 +49,10 @@ switch ($action) {
}
}
}
r2(U . 'services/pppoe', 's', $log);
r2(getUrl('services/pppoe'), 's', $log);
}
r2(U . 'services/hotspot', 'w', 'Unknown command');
r2(getUrl('services/hotspot'), 'w', 'Unknown command');
case 'hotspot':
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/hotspot.js"></script>');
$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,9 +190,9 @@ 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(U . 'services/hotspot', 'e', Lang::T('Account Not Found'));
r2(getUrl('services/hotspot'), 'e', Lang::T('Account Not Found'));
}
break;
@ -214,7 +213,7 @@ switch ($action) {
}
$d->delete();
r2(U . 'services/hotspot', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('services/hotspot'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -307,9 +306,9 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/edit/' . $d->id(), 's', Lang::T('Data Created Successfully'));
r2(getUrl('services/edit/') . $d->id(), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'services/add', 'e', $msg);
r2(getUrl('services/add'), 'e', $msg);
}
break;
@ -420,15 +419,14 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/hotspot', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('services/hotspot'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'services/edit/' . $id, 'e', $msg);
r2(getUrl('services/edit/') . $id, 'e', $msg);
}
break;
case 'pppoe':
$ui->assign('_title', Lang::T('PPPOE Plans'));
$ui->assign('xfooter', '<script type="text/javascript" src="ui/lib/c/pppoe.js"></script>');
$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,9 +576,9 @@ 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(U . 'services/pppoe', 'e', Lang::T('Account Not Found'));
r2(getUrl('services/pppoe'), 'e', Lang::T('Account Not Found'));
}
break;
@ -602,7 +600,7 @@ switch ($action) {
}
$d->delete();
r2(U . 'services/pppoe', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('services/pppoe'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -700,9 +698,9 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/pppoe', 's', Lang::T('Data Created Successfully'));
r2(getUrl('services/pppoe'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'services/pppoe-add', 'e', $msg);
r2(getUrl('services/pppoe-add'), 'e', $msg);
}
break;
@ -801,9 +799,9 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/pppoe', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('services/pppoe'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'services/pppoe-edit/' . $id, 'e', $msg);
r2(getUrl('services/pppoe-edit/') . $id, 'e', $msg);
}
break;
case 'balance':
@ -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'];
@ -841,7 +839,7 @@ switch ($action) {
if ($d) {
run_hook('delete_balance'); #HOOK
$d->delete();
r2(U . 'services/balance', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('services/balance'), 's', Lang::T('Data Deleted Successfully'));
}
break;
case 'balance-edit-post':
@ -877,9 +875,9 @@ switch ($action) {
$d->prepaid = 'yes';
$d->save();
r2(U . 'services/balance', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('services/balance'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'services/balance-edit/' . $id, 'e', $msg);
r2(getUrl('services/balance-edit/') . $id, 'e', $msg);
}
break;
case 'balance-add-post':
@ -914,14 +912,13 @@ switch ($action) {
$d->prepaid = 'yes';
$d->save();
r2(U . 'services/balance', 's', Lang::T('Data Created Successfully'));
r2(getUrl('services/balance'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'services/balance-add', 'e', $msg);
r2(getUrl('services/balance-add'), 'e', $msg);
}
break;
case 'vpn':
$ui->assign('_title', Lang::T('VPN Plans'));
$ui->assign('xfooter', '<script type="text/javascript" src="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,9 +1068,9 @@ 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(U . 'services/vpn', 'e', Lang::T('Account Not Found'));
r2(getUrl('services/vpn'), 'e', Lang::T('Account Not Found'));
}
break;
@ -1095,7 +1092,7 @@ switch ($action) {
}
$d->delete();
r2(U . 'services/vpn', 's', Lang::T('Data Deleted Successfully'));
r2(getUrl('services/vpn'), 's', Lang::T('Data Deleted Successfully'));
}
break;
@ -1193,9 +1190,9 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/vpn', 's', Lang::T('Data Created Successfully'));
r2(getUrl('services/vpn'), 's', Lang::T('Data Created Successfully'));
} else {
r2(U . 'services/vpn-add', 'e', $msg);
r2(getUrl('services/vpn-add'), 'e', $msg);
}
break;
@ -1294,11 +1291,11 @@ switch ($action) {
new Exception(Lang::T("Devices Not Found"));
}
}
r2(U . 'services/vpn', 's', Lang::T('Data Updated Successfully'));
r2(getUrl('services/vpn'), 's', Lang::T('Data Updated Successfully'));
} else {
r2(U . 'services/vpn-edit/' . $id, 'e', $msg);
r2(getUrl('services/vpn-edit/') . $id, 'e', $msg);
}
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(U . "settings/app", 's', 'Test Whatsapp has been send<br>Result: ' . $result);
r2(getUrl('settings/app'), 's', 'Test Whatsapp has been send<br>Result: ' . $result);
}
if (!empty(_get('testSms'))) {
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(U . "settings/app", 's', 'Test SMS has been send<br>Result: ' . $result);
r2(getUrl('settings/app'), 's', 'Test SMS has been send<br>Result: ' . $result);
}
if (!empty(_get('testEmail'))) {
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(U . "settings/app", 's', 'Test Email has been send');
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(U . "settings/app", 's', 'Test Telegram has been send<br>Result: ' . $result);
r2(getUrl('settings/app'), 's', 'Test Telegram has been send<br>Result: ' . $result);
}
$UPLOAD_URL_PATH = str_replace($root_path, '', $UPLOAD_PATH);
$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 {
@ -105,7 +117,7 @@ switch ($action) {
} else {
$favicon = $UPLOAD_URL_PATH . DIRECTORY_SEPARATOR . 'favicon.default.png';
}
$ui->assign('login_logo', $login_logo);
$ui->assign('wallpaper', $wallpaper);
$ui->assign('favicon', $favicon);
@ -134,7 +146,8 @@ switch ($action) {
$r = ORM::for_table('tbl_routers')->find_many();
$ui->assign('r', $r);
if (function_exists("shell_exec")) {
$php = trim(shell_exec('which php'));
$which = stripos(php_uname('s'), "Win") === 0 ? 'where' : 'which';
$php = trim(shell_exec("$which php"));
if (empty($php)) {
$php = 'php';
}
@ -165,35 +178,43 @@ 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");
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'settings/app', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('settings/app'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$company = _post('CompanyName');
$custom_tax_rate = filter_var(_post('custom_tax_rate'), FILTER_SANITIZE_SPECIAL_CHARS);
if (preg_match('/[^0-9.]/', $custom_tax_rate)) {
r2(U . 'settings/app', 'e', 'Special characters are not allowed in tax rate');
r2(getUrl('settings/app'), 'e', 'Special characters are not allowed in tax rate');
die();
}
run_hook('save_settings'); #HOOK
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(U . 'settings/app', 'e', 'PHP GD is not installed');
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
}
if ($_POST['general'] && $company == '') {
r2(U . 'settings/app', 'e', Lang::T('All field is required'));
r2(getUrl('settings/app'), 'e', Lang::T('All field is required'));
} else {
if ($radius_enable) {
try {
@ -204,7 +225,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();
}
}
@ -215,6 +236,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) {
@ -229,102 +332,10 @@ switch ($action) {
}
_log('[' . $admin['username'] . ']: ' . Lang::T('Settings Saved Successfully'), $admin['user_type'], $admin['id']);
r2(U . 'settings/app', 's', Lang::T('Settings Saved Successfully'));
r2(getUrl('settings/app'), 's', Lang::T('Settings Saved Successfully'));
}
break;
case 'login-page-post':
// Login page post
$login_page_title = _post('login_page_head');
$login_page_description = _post('login_page_description');
$login_Page_template = _post('login_Page_template');
$login_page_type = _post('login_page_type');
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . '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(U . 'settings/app', 'e', 'Please fill all required fields');
return;
}
if (strlen($login_page_title) > 25) {
r2(U . 'settings/app', 'e', 'Login page title must not exceed 25 characters');
return;
}
if (strlen($login_page_description) > 100) {
r2(U . '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(U . '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(U . '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(U . '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(U . '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");
@ -351,16 +362,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(U . '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');
@ -368,7 +382,7 @@ switch ($action) {
$lan = _post('lan');
run_hook('save_localisation'); #HOOK
if ($tzone == '' or $date_format == '' or $lan == '') {
r2(U . 'settings/app', 'e', Lang::T('All field is required'));
r2(getUrl('settings/localisation'), 'e', Lang::T('All field is required'));
} else {
$d = ORM::for_table('tbl_appconfig')->where('setting', 'timezone')->find_one();
$d->value = $tzone;
@ -453,7 +467,7 @@ switch ($action) {
$d->value = $lan;
$d->save();
_log('[' . $admin['username'] . ']: ' . 'Settings Saved Successfully', $admin['user_type'], $admin['id']);
r2(U . 'settings/localisation', 's', 'Settings Saved Successfully');
r2(getUrl('settings/localisation'), 's', 'Settings Saved Successfully');
}
break;
@ -471,11 +485,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')
@ -526,7 +540,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':
@ -537,17 +551,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
@ -566,9 +580,9 @@ 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(U . 'settings/users', 'e', Lang::T('Account Not Found'));
r2(getUrl('settings/users'), 'e', Lang::T('Account Not Found'));
}
break;
case 'users-edit':
@ -576,7 +590,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'];
}
@ -622,9 +636,9 @@ 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(U . 'settings/users', 'e', Lang::T('Account Not Found'));
r2(getUrl('settings/users'), 'e', Lang::T('Account Not Found'));
}
break;
@ -632,18 +646,20 @@ 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(U . 'settings/users', 'e', 'Sorry You can\'t delete yourself');
r2(getUrl('settings/users'), 'e', 'Sorry You can\'t delete yourself');
}
$d = ORM::for_table('tbl_users')->find_one($id);
if ($d) {
run_hook('delete_admin'); #HOOK
$d->delete();
r2(U . 'settings/users', 's', Lang::T('User deleted Successfully'));
r2(getUrl('settings/users'), 's', Lang::T('User deleted Successfully'));
} else {
r2(U . 'settings/users', 'e', Lang::T('Account Not Found'));
r2(getUrl('settings/users'), 'e', Lang::T('Account Not Found'));
}
break;
@ -651,9 +667,12 @@ 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(U . 'settings/users-add', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('settings/users-add'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$username = _post('username');
$fullname = _post('fullname');
@ -712,16 +731,19 @@ switch ($action) {
}
_log('[' . $admin['username'] . ']: ' . "Created $user_type <b>$username</b>", $admin['user_type'], $admin['id']);
r2(U . 'settings/users', 's', Lang::T('Account Created Successfully'));
r2(getUrl('settings/users'), 's', Lang::T('Account Created Successfully'));
} else {
r2(U . 'settings/users-add', 'e', $msg);
r2(getUrl('settings/users-add'), 'e', $msg);
}
break;
case 'users-edit-post':
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(U . 'settings/users-edit/', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('settings/users-edit/'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$username = _post('username');
$fullname = _post('fullname');
@ -812,7 +834,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')) {
@ -822,9 +844,10 @@ 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(U . 'settings/app', 'e', 'PHP GD is not installed');
r2(getUrl('settings/app'), 'e', 'PHP GD is not installed');
}
}
@ -858,9 +881,9 @@ switch ($action) {
$d->save();
_log('[' . $admin['username'] . ']: $username ' . Lang::T('User Updated Successfully'), $admin['user_type'], $admin['id']);
r2(U . 'settings/users-view/' . $id, 's', 'User Updated Successfully');
r2(getUrl('settings/users-view/') . $id, 's', 'User Updated Successfully');
} else {
r2(U . 'settings/users-edit/' . $id, 'e', $msg);
r2(getUrl('settings/users-edit/') . $id, 'e', $msg);
}
break;
@ -868,14 +891,17 @@ 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)) {
r2(U . 'settings/change-password', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('settings/change-password'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
if ($password != '') {
$d = ORM::for_table('tbl_users')->where('username', $admin['username'])->find_one();
@ -886,10 +912,10 @@ switch ($action) {
$npass = _post('npass');
$cnpass = _post('cnpass');
if (!Validator::Length($npass, 15, 5)) {
r2(U . 'settings/change-password', 'e', 'New Password must be 6 to 14 character');
r2(getUrl('settings/change-password'), 'e', 'New Password must be 6 to 14 character');
}
if ($npass != $cnpass) {
r2(U . 'settings/change-password', 'e', 'Both Password should be same');
r2(getUrl('settings/change-password'), 'e', 'Both Password should be same');
}
$npass = Password::_crypt($npass);
@ -899,15 +925,15 @@ switch ($action) {
_msglog('s', Lang::T('Password changed successfully, Please login again'));
_log('[' . $admin['username'] . ']: Password changed successfully', $admin['user_type'], $admin['id']);
r2(U . 'admin');
r2(getUrl('admin'));
} else {
r2(U . 'settings/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('settings/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
} else {
r2(U . 'settings/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('settings/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
} else {
r2(U . 'settings/change-password', 'e', Lang::T('Incorrect Current Password'));
r2(getUrl('settings/change-password'), 'e', Lang::T('Incorrect Current Password'));
}
break;
@ -925,18 +951,21 @@ 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");
}
$csrf_token = _post('csrf_token');
if (!Csrf::check($csrf_token)) {
r2(U . 'settings/notifications', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('settings/notifications'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
file_put_contents($UPLOAD_PATH . "/notifications.json", json_encode($_POST));
r2(U . 'settings/notifications', 's', Lang::T('Settings Saved Successfully'));
r2(getUrl('settings/notifications'), 's', Lang::T('Settings Saved Successfully'));
break;
case 'dbstatus':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
@ -952,11 +981,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");
}
@ -977,6 +1009,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");
}
@ -1024,10 +1059,11 @@ switch ($action) {
} catch (Throwable $e) {
} catch (Exception $e) {
}
if (file_exists($_FILES['json']['tmp_name'])) unlink($_FILES['json']['tmp_name']);
r2(U . "settings/dbstatus", 's', "Restored $suc success $fal failed");
if (file_exists($_FILES['json']['tmp_name']))
unlink($_FILES['json']['tmp_name']);
r2(getUrl('settings/dbstatus'), 's', "Restored $suc success $fal failed");
} else {
r2(U . "settings/dbstatus", 'e', 'Upload failed');
r2(getUrl('settings/dbstatus'), 'e', 'Upload failed');
}
break;
case 'language':
@ -1042,16 +1078,19 @@ 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(U . 'settings/language', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('settings/language'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
file_put_contents($lan_file, json_encode($_POST, JSON_PRETTY_PRINT));
r2(U . 'settings/language', 's', Lang::T('Translation saved Successfully'));
r2(getUrl('settings/language'), 's', Lang::T('Translation saved Successfully'));
break;
case 'maintenance':
@ -1061,9 +1100,12 @@ 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(U . 'settings/maintenance', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('settings/maintenance'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
$status = isset($_POST['maintenance_mode']) ? 1 : 0; // Checkbox returns 1 if checked, otherwise 0
$force_logout = isset($_POST['maintenance_mode_logout']) ? 1 : 0; // Checkbox returns 1 if checked, otherwise 0
@ -1088,13 +1130,13 @@ switch ($action) {
}
}
r2(U . "settings/maintenance", 's', Lang::T('Settings Saved Successfully'));
r2(getUrl('settings/maintenance'), 's', Lang::T('Settings Saved Successfully'));
}
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_c', $config);
$ui->assign('_title', Lang::T('Maintenance Mode Settings'));
$ui->display('maintenance-mode.tpl');
$ui->display('admin/settings/maintenance-mode.tpl');
break;
case 'miscellaneous':
@ -1103,9 +1145,12 @@ 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(U . 'settings/miscellaneous', 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
r2(getUrl('settings/miscellaneous'), 'e', Lang::T('Invalid or Expired CSRF Token') . ".");
}
foreach ($_POST as $key => $value) {
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
@ -1120,15 +1165,15 @@ switch ($action) {
}
}
r2(U . "settings/miscellaneous", 's', Lang::T('Settings Saved Successfully'));
r2(getUrl('settings/miscellaneous'), 's', Lang::T('Settings Saved Successfully'));
}
$csrf_token = Csrf::generateAndStoreToken();
$ui->assign('csrf_token', $csrf_token);
$ui->assign('_c', $config);
$ui->assign('_title', Lang::T('Miscellaneous Settings'));
$ui->display('app-miscellaneous.tpl');
$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);
@ -30,12 +66,12 @@ switch ($action) {
$v1->used_date = date('Y-m-d H:i:s');
$v1->user = $user['username'];
$v1->save();
r2(U . "voucher/list-activated", 's', Lang::T('Activation Vouchers Successfully'));
r2(getUrl('voucher/list-activated'), 's', Lang::T('Activation Vouchers Successfully'));
} else {
r2(U . 'voucher/activation', 'e', "Failed to refill account");
r2(getUrl('voucher/activation'), 'e', "Failed to refill account");
}
} else {
r2(U . 'voucher/activation', 'e', Lang::T('Voucher Not Valid'));
r2(getUrl('voucher/activation'), 'e', Lang::T('Voucher Not Valid'));
}
break;
@ -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(U . 'voucher/list-activated', 'e', Lang::T('Not Found'));
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_7days'] !== '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_3days'] !== '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(
@ -238,7 +244,7 @@ class MikrotikPppoe
function add_pool($pool){
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$mikrotik = $this->info($pool['routers']);
@ -253,7 +259,7 @@ class MikrotikPppoe
function update_pool($old_pool, $new_pool){
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$mikrotik = $this->info($new_pool['routers']);
@ -278,7 +284,7 @@ class MikrotikPppoe
function remove_pool($pool){
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$mikrotik = $this->info($pool['routers']);
@ -323,7 +329,7 @@ class MikrotikPppoe
function getClient($ip, $user, $pass)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$iport = explode(":", $ip);
@ -333,7 +339,7 @@ class MikrotikPppoe
function removePpoeUser($client, $username)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$printRequest = new RouterOS\Request('/ppp/secret/print');
@ -370,7 +376,7 @@ class MikrotikPppoe
function removePpoeActive($client, $username)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$onlineRequest = new RouterOS\Request('/ppp/active/print');
@ -386,7 +392,7 @@ class MikrotikPppoe
function getIpHotspotUser($client, $username)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$printRequest = new RouterOS\Request(
@ -399,7 +405,7 @@ class MikrotikPppoe
function addIpToAddressList($client, $ip, $listName, $comment = '')
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$addRequest = new RouterOS\Request('/ip/firewall/address-list/add');
@ -414,7 +420,7 @@ class MikrotikPppoe
function removeIpFromAddressList($client, $ip)
{
global $_app_stage;
if ($_app_stage == 'demo') {
if ($_app_stage == 'Demo') {
return null;
}
$printRequest = new RouterOS\Request(

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

@ -1,188 +1,211 @@
{
"2023.8.9": [
"ALTER TABLE `tbl_customers` ADD `balance` decimal(15,2) NOT NULL DEFAULT 0.00 COMMENT 'For Money Deposit' AFTER `email`;",
"CREATE TABLE `tbl_customers_meta` (`id` int(11) NOT NULL, `customer_id` int(11) NOT NULL,`meta_key` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `meta_value` longtext COLLATE utf8mb4_general_ci) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
"ALTER TABLE `tbl_customers_meta` ADD PRIMARY KEY (`id`);",
"ALTER TABLE `tbl_customers_meta` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;"
],
"2023.8.14": [
"ALTER TABLE `tbl_customers` ADD `pppoe_password` varchar(45) NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;",
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_customers` ADD `auto_renewal` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auto renewall using balance' AFTER `balance`;"
],
"2023.8.23": [
"ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';"
],
"2023.8.28": [
"ALTER TABLE `tbl_user_recharges` ADD `recharged_time` time NOT NULL DEFAULT '00:00:00' AFTER `recharged_on`;",
"ALTER TABLE `tbl_transactions` ADD `recharged_time` time NOT NULL DEFAULT '00:00:00' AFTER `recharged_on`;"
],
"2023.9.5": [
"DROP TABLE `tbl_language`;",
"ALTER TABLE `tbl_plans` ADD `pool_expired` varchar(40) NOT NULL DEFAULT '' AFTER `pool`;"
],
"2023.9.27": [
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance','Radius') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance','Radius') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
],
"2023.9.28": [
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
],
"2023.10.1": [
"ALTER TABLE `tbl_plans` ADD `is_radius` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '1 is radius' AFTER `routers`; "
],
"2023.10.24": [
"ALTER TABLE `nas` ADD `routers` VARCHAR(32) NOT NULL DEFAULT '' AFTER `description`;"
],
"2023.12.15": [
"ALTER TABLE `tbl_customers` ADD `service_type` ENUM('Hotspot','PPPoE','Others') DEFAULT 'Others' COMMENT 'For selecting user type' AFTER `balance`;"
],
"2024.1.11": [
"ALTER TABLE `tbl_plans` ADD `allow_purchase` ENUM('yes','no') DEFAULT 'yes' COMMENT 'allow to show package in buy package page' AFTER `enabled`;"
],
"2024.2.7": [
"ALTER TABLE `tbl_voucher` ADD `generated_by` INT NOT NULL DEFAULT '0' COMMENT 'id admin' AFTER `status`;",
"ALTER TABLE `tbl_users` ADD `root` INT NOT NULL DEFAULT '0' COMMENT 'for sub account' AFTER `id`;"
],
"2024.2.12": [
"ALTER TABLE `tbl_users` CHANGE `user_type` `user_type` ENUM('SuperAdmin','Admin','Report','Agent','Sales') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
],
"2024.2.15": [
"ALTER TABLE `tbl_users` CHANGE `password` `password` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_users` ADD `phone` VARCHAR(32) NOT NULL DEFAULT '' AFTER `password`, ADD `email` VARCHAR(128) NOT NULL DEFAULT '' AFTER `phone`, ADD `city` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kota' AFTER `email`, ADD `subdistrict` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kecamatan' AFTER `city`, ADD `ward` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kelurahan' AFTER `subdistrict`;"
],
"2024.2.16": [
"ALTER TABLE `tbl_customers` ADD `created_by` INT NOT NULL DEFAULT '0' AFTER `auto_renewal`;"
],
"2024.2.19": [
"CREATE TABLE `tbl_customers_fields` (`id` INT PRIMARY KEY AUTO_INCREMENT, `customer_id` INT NOT NULL, `field_name` VARCHAR(255) NOT NULL, `field_value` VARCHAR(255) NOT NULL, FOREIGN KEY (customer_id) REFERENCES tbl_customers(id));"
],
"2024.2.20": [
"ALTER TABLE `tbl_plans` ADD `list_expired` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'address list' AFTER `pool_expired`;",
"ALTER TABLE `tbl_bandwidth` ADD `burst` VARCHAR(128) NOT NULL DEFAULT '' AFTER `rate_up_unit`;"
],
"2024.2.20.1": [
"DROP TABLE IF EXISTS `tbl_customers_meta`;"
],
"2024.2.23": [
"ALTER TABLE `tbl_transactions` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;",
"ALTER TABLE `tbl_user_recharges` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;"
],
"2024.3.3": [
"ALTER TABLE `tbl_plans` CHANGE `validity_unit` `validity_unit` ENUM('Mins','Hrs','Days','Months','Period') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
],
"2024.3.12": [
"ALTER TABLE `tbl_plans` CHANGE `allow_purchase` `prepaid` ENUM('yes','no') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'yes' COMMENT 'is prepaid';"
],
"2024.3.14": [
"ALTER TABLE `tbl_transactions` ADD `note` VARCHAR(256) NOT NULL DEFAULT '' COMMENT 'for note' AFTER `type`;"
],
"2024.3.19": [
"ALTER TABLE `tbl_customers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates' AFTER `email`;"
],
"2024.3.19.1": [
"ALTER TABLE `tbl_customers` ADD `account_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' AFTER `coordinates`;"
],
"2024.3.19.2": [
"ALTER TABLE `tbl_plans` ADD `plan_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' ;"
],
"2023.3.20": [
"ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';"
],
"2024.4.5": [
"ALTER TABLE `tbl_payment_gateway` ADD `trx_invoice` VARCHAR(25) NOT NULL DEFAULT '' COMMENT 'from tbl_transactions' AFTER `paid_date`;"
],
"2024.5.17": [
"ALTER TABLE `tbl_customers` ADD `status` ENUM('Active','Banned','Disabled') NOT NULL DEFAULT 'Active' AFTER `auto_renewal`;"
],
"2024.5.18": [
"ALTER TABLE `tbl_customers` CHANGE `status` `status` ENUM('Active','Banned','Disabled','Inactive','Limited','Suspended') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'Active';"
],
"2024.5.20": [
"ALTER TABLE `tbl_customers` ADD `city` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `address`, ADD `district` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `city`, ADD `state` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `district`, ADD `zip` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `state`;"
],
"2024.6.5": [
"ALTER TABLE `tbl_plans` ADD `price_old` VARCHAR(40) NOT NULL DEFAULT '' AFTER `price`;",
"ALTER TABLE `tbl_plans` ADD `device` VARCHAR(32) NOT NULL DEFAULT '' AFTER `plan_type`;"
],
"2024.6.10": [
"ALTER TABLE `tbl_pool` ADD `local_ip` VARCHAR(40) NOT NULL DEFAULT '' AFTER `pool_name`;"
],
"2024.6.11": [
"ALTER TABLE `tbl_plans` ADD `plan_expired` INT NOT NULL DEFAULT '0' AFTER `pool`;",
"ALTER TABLE `tbl_plans` DROP `pool_expired`, DROP `list_expired`;"
],
"2024.6.19": [
"ALTER TABLE `tbl_plans` ADD `expired_date` TINYINT(1) NOT NULL DEFAULT '20' AFTER `plan_expired`;"
],
"2024.6.21": [
"ALTER TABLE `tbl_plans` ADD `on_login` TEXT NULL DEFAULT NULL AFTER `device`;",
"ALTER TABLE `tbl_plans` ADD `on_logout` TEXT NULL DEFAULT NULL AFTER `on_login`;"
],
"2024.7.6": [
"CREATE TABLE IF NOT EXISTS `rad_acct` ( `id` bigint NOT NULL, `acctsessionid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `realm` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasid` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
"ALTER TABLE `rad_acct` ADD PRIMARY KEY (`id`), ADD KEY `username` (`username`), ADD KEY `framedipaddress` (`framedipaddress`), ADD KEY `acctsessionid` (`acctsessionid`), ADD KEY `nasipaddress` (`nasipaddress`);",
"ALTER TABLE `rad_acct` MODIFY `id` bigint NOT NULL AUTO_INCREMENT;"
],
"2024.7.24": [
"ALTER TABLE `tbl_voucher` ADD `used_date` DATETIME NULL DEFAULT NULL AFTER `status`;",
"UPDATE `tbl_voucher` SET `used_date`=now() WHERE `status`=1;"
],
"2024.8.1": [
"ALTER TABLE `tbl_payment_gateway` CHANGE `gateway_trx_id` `gateway_trx_id` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';",
"ALTER TABLE `tbl_payment_gateway` CHANGE `pg_url_payment` `pg_url_payment` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';"
],
"2024.8.2": [
"CREATE TABLE IF NOT EXISTS `tbl_customers_inbox` (`id` int UNSIGNED NOT NULL AUTO_INCREMENT, `customer_id` int NOT NULL, `date_created` datetime NOT NULL, `date_read` datetime DEFAULT NULL, `subject` varchar(64) COLLATE utf8mb4_general_ci NOT NULL, `body` TEXT NULL DEFAULT NULL, `from` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'System' COMMENT 'System or Admin or Else',`admin_id` int NOT NULL DEFAULT '0' COMMENT 'other than admin is 0', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
],
"2024.8.5": [
"ALTER TABLE `tbl_customers` ADD `pppoe_username` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;",
"ALTER TABLE `tbl_customers` ADD `pppoe_ip` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `pppoe_password`;"
],
"2024.8.5.1": [
"ALTER TABLE `tbl_routers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' AFTER `description`;",
"ALTER TABLE `tbl_routers` ADD `coverage` VARCHAR(8) NOT NULL DEFAULT '0' AFTER `coordinates`;"
],
"2024.8.6": [
"ALTER TABLE `rad_acct` ADD `acctinputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `framedipaddress`;",
"ALTER TABLE `rad_acct` ADD `acctoutputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `acctinputoctets`;"
],
"2024.8.7": [
"ALTER TABLE `tbl_customers` CHANGE `coordinates` `coordinates` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates';"
],
"2024.8.28": [
"ALTER TABLE `tbl_routers` ADD `status` ENUM('Online', 'Offline') DEFAULT 'Online' AFTER `coordinates`;",
"ALTER TABLE `tbl_routers` ADD `last_seen` DATETIME AFTER `status`;"
],
"2024.9.13": [
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_customers` CHANGE `service_type` `service_type` ENUM('Hotspot','PPPoE','VPN','Others') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'Others' COMMENT 'For selecting user type';",
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"CREATE TABLE IF NOT EXISTS `tbl_port_pool` ( `id` int(10) NOT NULL AUTO_INCREMENT , `public_ip` varchar(40) NOT NULL, `port_name` varchar(40) NOT NULL, `range_port` varchar(40) NOT NULL, `routers` varchar(40) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
],
"2024.10.10": [
"ALTER TABLE `tbl_users` ADD `login_token` VARCHAR(40) AFTER `last_login`;"
],
"2024.10.17": [
"CREATE TABLE IF NOT EXISTS `tbl_meta` ( `id` int UNSIGNED NOT NULL AUTO_INCREMENT, `tbl` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Table name', `tbl_id` int NOT NULL COMMENT 'table value id', `name` varchar(32) COLLATE utf8mb4_general_ci NOT NULL, `value` mediumtext COLLATE utf8mb4_general_ci, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='This Table to add additional data for any table';"
],
"2024.10.30": [
"ALTER TABLE `tbl_users` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/admin.default.png' AFTER `root`;",
"ALTER TABLE `tbl_users` ADD `data` TEXT NULL DEFAULT NULL COMMENT 'to put additional data' AFTER `status`;"
],
"2024.10.31": [
"ALTER TABLE `tbl_customers` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/user.default.jpg' AFTER `password`;"
],
"2024.12.5.1": [
"ALTER TABLE `tbl_transactions` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;",
"ALTER TABLE `tbl_payment_gateway` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;"
],
"2024.12.16": [
"CREATE TABLE `tbl_coupons` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `code` VARCHAR(50) NOT NULL UNIQUE, `type` ENUM('fixed', 'percent') NOT NULL, `value` DECIMAL(10,2) NOT NULL, `description` TEXT NOT NULL, `max_usage` INT NOT NULL DEFAULT 1,`usage_count` INT NOT NULL DEFAULT 0,`status` ENUM('active', 'inactive') NOT NULL, `min_order_amount` DECIMAL(10,2) NOT NULL, `max_discount_amount` DECIMAL(10,2) NOT NULL, `start_date` DATE NOT NULL,`end_date` DATE NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);"
],
"2024.12.20": [
"ALTER TABLE `tbl_voucher` ADD `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;"
]
}
"2023.8.9": [
"ALTER TABLE `tbl_customers` ADD `balance` decimal(15,2) NOT NULL DEFAULT 0.00 COMMENT 'For Money Deposit' AFTER `email`;",
"CREATE TABLE `tbl_customers_meta` (`id` int(11) NOT NULL, `customer_id` int(11) NOT NULL,`meta_key` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `meta_value` longtext COLLATE utf8mb4_general_ci) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
"ALTER TABLE `tbl_customers_meta` ADD PRIMARY KEY (`id`);",
"ALTER TABLE `tbl_customers_meta` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;"
],
"2023.8.14": [
"ALTER TABLE `tbl_customers` ADD `pppoe_password` varchar(45) NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;",
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_customers` ADD `auto_renewal` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auto renewall using balance' AFTER `balance`;"
],
"2023.8.23": [
"ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';"
],
"2023.8.28": [
"ALTER TABLE `tbl_user_recharges` ADD `recharged_time` time NOT NULL DEFAULT '00:00:00' AFTER `recharged_on`;",
"ALTER TABLE `tbl_transactions` ADD `recharged_time` time NOT NULL DEFAULT '00:00:00' AFTER `recharged_on`;"
],
"2023.9.5": [
"DROP TABLE `tbl_language`;",
"ALTER TABLE `tbl_plans` ADD `pool_expired` varchar(40) NOT NULL DEFAULT '' AFTER `pool`;"
],
"2023.9.27": [
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance','Radius') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance','Radius') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
],
"2023.9.28": [
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
],
"2023.10.1": [
"ALTER TABLE `tbl_plans` ADD `is_radius` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '1 is radius' AFTER `routers`; "
],
"2023.10.24": [
"ALTER TABLE `nas` ADD `routers` VARCHAR(32) NOT NULL DEFAULT '' AFTER `description`;"
],
"2023.12.15": [
"ALTER TABLE `tbl_customers` ADD `service_type` ENUM('Hotspot','PPPoE','Others') DEFAULT 'Others' COMMENT 'For selecting user type' AFTER `balance`;"
],
"2024.1.11": [
"ALTER TABLE `tbl_plans` ADD `allow_purchase` ENUM('yes','no') DEFAULT 'yes' COMMENT 'allow to show package in buy package page' AFTER `enabled`;"
],
"2024.2.7": [
"ALTER TABLE `tbl_voucher` ADD `generated_by` INT NOT NULL DEFAULT '0' COMMENT 'id admin' AFTER `status`;",
"ALTER TABLE `tbl_users` ADD `root` INT NOT NULL DEFAULT '0' COMMENT 'for sub account' AFTER `id`;"
],
"2024.2.12": [
"ALTER TABLE `tbl_users` CHANGE `user_type` `user_type` ENUM('SuperAdmin','Admin','Report','Agent','Sales') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
],
"2024.2.15": [
"ALTER TABLE `tbl_users` CHANGE `password` `password` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_users` ADD `phone` VARCHAR(32) NOT NULL DEFAULT '' AFTER `password`, ADD `email` VARCHAR(128) NOT NULL DEFAULT '' AFTER `phone`, ADD `city` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kota' AFTER `email`, ADD `subdistrict` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kecamatan' AFTER `city`, ADD `ward` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'kelurahan' AFTER `subdistrict`;"
],
"2024.2.16": [
"ALTER TABLE `tbl_customers` ADD `created_by` INT NOT NULL DEFAULT '0' AFTER `auto_renewal`;"
],
"2024.2.19": [
"CREATE TABLE `tbl_customers_fields` (`id` INT PRIMARY KEY AUTO_INCREMENT, `customer_id` INT NOT NULL, `field_name` VARCHAR(255) NOT NULL, `field_value` VARCHAR(255) NOT NULL, FOREIGN KEY (customer_id) REFERENCES tbl_customers(id));"
],
"2024.2.20": [
"ALTER TABLE `tbl_plans` ADD `list_expired` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'address list' AFTER `pool_expired`;",
"ALTER TABLE `tbl_bandwidth` ADD `burst` VARCHAR(128) NOT NULL DEFAULT '' AFTER `rate_up_unit`;"
],
"2024.2.20.1": ["DROP TABLE IF EXISTS `tbl_customers_meta`;"],
"2024.2.23": [
"ALTER TABLE `tbl_transactions` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;",
"ALTER TABLE `tbl_user_recharges` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;"
],
"2024.3.3": [
"ALTER TABLE `tbl_plans` CHANGE `validity_unit` `validity_unit` ENUM('Mins','Hrs','Days','Months','Period') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;"
],
"2024.3.12": [
"ALTER TABLE `tbl_plans` CHANGE `allow_purchase` `prepaid` ENUM('yes','no') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'yes' COMMENT 'is prepaid';"
],
"2024.3.14": [
"ALTER TABLE `tbl_transactions` ADD `note` VARCHAR(256) NOT NULL DEFAULT '' COMMENT 'for note' AFTER `type`;"
],
"2024.3.19": [
"ALTER TABLE `tbl_customers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates' AFTER `email`;"
],
"2024.3.19.1": [
"ALTER TABLE `tbl_customers` ADD `account_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' AFTER `coordinates`;"
],
"2024.3.19.2": [
"ALTER TABLE `tbl_plans` ADD `plan_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' ;"
],
"2023.3.20": [
"ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';"
],
"2024.4.5": [
"ALTER TABLE `tbl_payment_gateway` ADD `trx_invoice` VARCHAR(25) NOT NULL DEFAULT '' COMMENT 'from tbl_transactions' AFTER `paid_date`;"
],
"2024.5.17": [
"ALTER TABLE `tbl_customers` ADD `status` ENUM('Active','Banned','Disabled') NOT NULL DEFAULT 'Active' AFTER `auto_renewal`;"
],
"2024.5.18": [
"ALTER TABLE `tbl_customers` CHANGE `status` `status` ENUM('Active','Banned','Disabled','Inactive','Limited','Suspended') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'Active';"
],
"2024.5.20": [
"ALTER TABLE `tbl_customers` ADD `city` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `address`, ADD `district` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `city`, ADD `state` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `district`, ADD `zip` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `state`;"
],
"2024.6.5": [
"ALTER TABLE `tbl_plans` ADD `price_old` VARCHAR(40) NOT NULL DEFAULT '' AFTER `price`;",
"ALTER TABLE `tbl_plans` ADD `device` VARCHAR(32) NOT NULL DEFAULT '' AFTER `plan_type`;"
],
"2024.6.10": [
"ALTER TABLE `tbl_pool` ADD `local_ip` VARCHAR(40) NOT NULL DEFAULT '' AFTER `pool_name`;"
],
"2024.6.11": [
"ALTER TABLE `tbl_plans` ADD `plan_expired` INT NOT NULL DEFAULT '0' AFTER `pool`;",
"ALTER TABLE `tbl_plans` DROP `pool_expired`, DROP `list_expired`;"
],
"2024.6.19": [
"ALTER TABLE `tbl_plans` ADD `expired_date` TINYINT(1) NOT NULL DEFAULT '20' AFTER `plan_expired`;"
],
"2024.6.21": [
"ALTER TABLE `tbl_plans` ADD `on_login` TEXT NULL DEFAULT NULL AFTER `device`;",
"ALTER TABLE `tbl_plans` ADD `on_logout` TEXT NULL DEFAULT NULL AFTER `on_login`;"
],
"2024.7.6": [
"CREATE TABLE IF NOT EXISTS `rad_acct` ( `id` bigint NOT NULL, `acctsessionid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `realm` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasid` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
"ALTER TABLE `rad_acct` ADD PRIMARY KEY (`id`), ADD KEY `username` (`username`), ADD KEY `framedipaddress` (`framedipaddress`), ADD KEY `acctsessionid` (`acctsessionid`), ADD KEY `nasipaddress` (`nasipaddress`);",
"ALTER TABLE `rad_acct` MODIFY `id` bigint NOT NULL AUTO_INCREMENT;"
],
"2024.7.24": [
"ALTER TABLE `tbl_voucher` ADD `used_date` DATETIME NULL DEFAULT NULL AFTER `status`;",
"UPDATE `tbl_voucher` SET `used_date`=now() WHERE `status`=1;"
],
"2024.8.1": [
"ALTER TABLE `tbl_payment_gateway` CHANGE `gateway_trx_id` `gateway_trx_id` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';",
"ALTER TABLE `tbl_payment_gateway` CHANGE `pg_url_payment` `pg_url_payment` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';"
],
"2024.8.2": [
"CREATE TABLE IF NOT EXISTS `tbl_customers_inbox` (`id` int UNSIGNED NOT NULL AUTO_INCREMENT, `customer_id` int NOT NULL, `date_created` datetime NOT NULL, `date_read` datetime DEFAULT NULL, `subject` varchar(64) COLLATE utf8mb4_general_ci NOT NULL, `body` TEXT NULL DEFAULT NULL, `from` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'System' COMMENT 'System or Admin or Else',`admin_id` int NOT NULL DEFAULT '0' COMMENT 'other than admin is 0', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
],
"2024.8.5": [
"ALTER TABLE `tbl_customers` ADD `pppoe_username` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;",
"ALTER TABLE `tbl_customers` ADD `pppoe_ip` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `pppoe_password`;"
],
"2024.8.5.1": [
"ALTER TABLE `tbl_routers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' AFTER `description`;",
"ALTER TABLE `tbl_routers` ADD `coverage` VARCHAR(8) NOT NULL DEFAULT '0' AFTER `coordinates`;"
],
"2024.8.6": [
"ALTER TABLE `rad_acct` ADD `acctinputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `framedipaddress`;",
"ALTER TABLE `rad_acct` ADD `acctoutputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `acctinputoctets`;"
],
"2024.8.7": [
"ALTER TABLE `tbl_customers` CHANGE `coordinates` `coordinates` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates';"
],
"2024.8.28": [
"ALTER TABLE `tbl_routers` ADD `status` ENUM('Online', 'Offline') DEFAULT 'Online' AFTER `coordinates`;",
"ALTER TABLE `tbl_routers` ADD `last_seen` DATETIME AFTER `status`;"
],
"2024.9.13": [
"ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"ALTER TABLE `tbl_customers` CHANGE `service_type` `service_type` ENUM('Hotspot','PPPoE','VPN','Others') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'Others' COMMENT 'For selecting user type';",
"ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
"CREATE TABLE IF NOT EXISTS `tbl_port_pool` ( `id` int(10) NOT NULL AUTO_INCREMENT , `public_ip` varchar(40) NOT NULL, `port_name` varchar(40) NOT NULL, `range_port` varchar(40) NOT NULL, `routers` varchar(40) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;"
],
"2024.10.10": [
"ALTER TABLE `tbl_users` ADD `login_token` VARCHAR(40) AFTER `last_login`;"
],
"2024.10.17": [
"CREATE TABLE IF NOT EXISTS `tbl_meta` ( `id` int UNSIGNED NOT NULL AUTO_INCREMENT, `tbl` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Table name', `tbl_id` int NOT NULL COMMENT 'table value id', `name` varchar(32) COLLATE utf8mb4_general_ci NOT NULL, `value` mediumtext COLLATE utf8mb4_general_ci, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='This Table to add additional data for any table';"
],
"2024.10.30": [
"ALTER TABLE `tbl_users` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/admin.default.png' AFTER `root`;",
"ALTER TABLE `tbl_users` ADD `data` TEXT NULL DEFAULT NULL COMMENT 'to put additional data' AFTER `status`;"
],
"2024.10.31": [
"ALTER TABLE `tbl_customers` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/user.default.jpg' AFTER `password`;"
],
"2024.12.5.1": [
"ALTER TABLE `tbl_transactions` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;",
"ALTER TABLE `tbl_payment_gateway` ADD `user_id` INT(11) NOT NULL DEFAULT 0 AFTER `username`;"
],
"2024.12.16": [
"CREATE TABLE `tbl_coupons` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `code` VARCHAR(50) NOT NULL UNIQUE, `type` ENUM('fixed', 'percent') NOT NULL, `value` DECIMAL(10,2) NOT NULL, `description` TEXT NOT NULL, `max_usage` INT NOT NULL DEFAULT 1,`usage_count` INT NOT NULL DEFAULT 0,`status` ENUM('active', 'inactive') NOT NULL, `min_order_amount` DECIMAL(10,2) NOT NULL, `max_discount_amount` DECIMAL(10,2) NOT NULL, `start_date` DATE NOT NULL,`end_date` DATE NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);"
],
"2024.12.20": [
"ALTER TABLE `tbl_voucher` ADD `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;"
],
"2025.1.23": [
"ALTER TABLE `rad_acct` ADD `acctsessiontime` BIGINT(12) NOT NULL DEFAULT '0' AFTER `framedipaddress`;"
],
"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;"
],
"2025.3.10": [
"CREATE TABLE IF NOT EXISTS `tbl_invoices` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `number` VARCHAR(50) NOT NULL, `customer_id` INT NOT NULL, `fullname` VARCHAR(100) NOT NULL, `email` VARCHAR(100) NOT NULL, `address` TEXT, `status` ENUM('Unpaid', 'Paid', 'Cancelled') NOT NULL DEFAULT 'Unpaid', `due_date` DATETIME NOT NULL, `filename` VARCHAR(255), `amount` DECIMAL(10, 2) NOT NULL, `data` JSON NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP);"
]
}

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

@ -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>

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