Compare commits

...

351 Commits

Author SHA1 Message Date
ca551406c7 Merge branch 'master' into stage
Some checks failed
SQL Syntax Check for db.sql / Check db.sql SQL Syntax (pull_request) Failing after 18s
PHPLint / build (pull_request) Successful in 26s
Welcome New Contributor / run (pull_request_target) Successful in 22s
# Conflicts:
#	guest/guest_post.php
2025-10-21 13:07:25 -04:00
Johnny
0d5bfdafdf Merge pull request #1242 from itflow-org/develop
Develop to Master
2025-10-08 17:39:41 -04:00
johnnyq
fbf3346052 Update Changelog 2025-10-08 17:30:19 -04:00
johnnyq
3ff206f84d Add .htaccess in cron 2025-10-08 17:25:03 -04:00
johnnyq
a3b0fce961 Fix login_microsoft 2025-10-08 17:19:17 -04:00
johnnyq
8130280b35 Fix edit from contact details modal 2025-10-08 17:12:23 -04:00
johnnyq
fea3020d9a Add powered by ITFlow in Guest Section 2025-10-08 15:29:06 -04:00
johnnyq
1eb9d163fa Updated Changelog 2025-10-08 14:50:34 -04:00
johnnyq
e3e7c2e38b Add Signature in ticket reply POST dont add a signature if Internal 2025-10-08 14:48:13 -04:00
johnnyq
27e1d6a9cd remove net terms in quote add jquery to guest header 2025-10-08 01:18:55 -04:00
johnnyq
2ec4cdc4fb Ceated inc_all_guest.php and modulaized the guest header.php removed guest footer and used the global footer.php as they were very similar 2025-10-08 01:00:48 -04:00
johnnyq
35a7506c26 Copy crons from /scripts to /cron, added custom directories for api/v1/, /setup, /cron and /scripts 2025-10-07 13:55:54 -04:00
johnnyq
16242be74e Update Client Nav to use Absolute links and updated more of the inc_alls to use Document Server Root 2025-10-07 13:44:08 -04:00
johnnyq
3fcbe440d3 Fix Missing Missing Country code in Guest View Ticket 2025-10-07 12:34:26 -04:00
johnnyq
4ef0755039 Update Guest header and footer to use Server Document Root var 2025-10-06 17:07:19 -04:00
johnnyq
a4ed906dd1 Update modal footer and header to user SERVER Document root with absolute path intead of relative 2025-10-06 15:13:52 -04:00
johnnyq
416a8d9a94 Fix to properly redirect to the setup page if config_enable_setup is not set or is 1 2025-10-06 14:19:49 -04:00
wrongecho
d8803aaac2 prevent open redirects upon agent login 2025-10-06 16:32:42 +01:00
wrongecho
01f6615ca0 rm test 2025-10-06 16:13:08 +01:00
johnnyq
fd93ee3263 Allow HTML for signatures 2025-10-04 19:20:34 -04:00
johnnyq
32bfd298a1 Added Project Edit Function for Ticket 2025-10-03 17:37:25 -04:00
johnnyq
5de2e7a3bd If imap Encryption is blank then use notls in the ticket mail parser 2025-10-03 11:36:27 -04:00
johnnyq
6e8c133a99 Fix Regressions in Vendor Templates updated path from ../user/post to ../agent/post 2025-10-03 11:25:48 -04:00
johnnyq
956f18430b Fix Microsoft SSO Login in Client Portal, fix ticket templates due to regression from changing from user to agent 2025-10-03 11:12:48 -04:00
johnnyq
76c9933baf Update imap and smtp providers to allow empty string if empty do not execute mail queue 2025-10-02 14:28:43 -04:00
johnnyq
6c6a988c2b Fix custom Favicon 2025-10-02 11:42:30 -04:00
Johnny
d829e39b66 Merge pull request #1240 from itflow-org/develop
Develop to Master for 25.10 release
2025-10-01 15:28:06 -04:00
johnnyq
0e401df3c0 Setting 0 for the Payment Provider threshold disables the invoice amount check 2025-10-01 15:23:00 -04:00
johnnyq
9072c37e95 Set payment provider default Threshold to 9999.00 when adding a payment provider 2025-10-01 15:06:14 -04:00
johnnyq
2eff11efbf Added Custom Link option to Reports Nav 2025-10-01 14:35:18 -04:00
johnnyq
6a7a02d220 Add Custom Links Admin Nav option 2025-10-01 13:48:52 -04:00
johnnyq
d6349bbc5c Mention new installs already configuring the new cron jobs 2025-10-01 11:37:13 -04:00
johnnyq
9a2f887db1 Update Changelog increase app version to 25.10 2025-10-01 11:34:43 -04:00
johnnyq
71d30ff95f Enforce Payment Provider Max Threshold for Stripe Paymented in Guest Invoice and Unpaid Invoices 2025-09-30 12:14:24 -04:00
johnnyq
3135247936 Add Gibraltar to the countries list array 2025-09-25 17:35:02 -04:00
johnnyq
0d629221fe Add Software keys and software key assignment for contacts and assets table to the database schema 2025-09-25 17:24:42 -04:00
johnnyq
181ea4b487 Remove dead router.php require 2025-09-25 13:43:47 -04:00
johnnyq
fa769665df Update report links in dashboard 2025-09-25 13:38:51 -04:00
johnnyq
00f5198bed Update appNotidfy links to use Absolute paths, updated gitignore 2025-09-25 13:31:16 -04:00
johnnyq
785a291614 Remove unessesary vars in edit ticket asset modal 2025-09-24 14:56:50 -04:00
johnnyq
92209c7125 Updated edit ticket asset to new ajax-modal and add additonal assets field 2025-09-24 14:51:34 -04:00
johnnyq
690007be5c More Relative to Absolute web asset paths updates 2025-09-24 13:09:25 -04:00
johnnyq
e6bcf0e12f Started updating href paths to absolute paths instead of relative paths as itflow should be installed in document root anyway and not a sub-directory 2025-09-24 12:56:01 -04:00
wrongecho
ca6a903b8f Keepalive
- Fix directory path
- Remove duplicate file
- Add to client portal
2025-09-24 08:39:45 +01:00
johnnyq
50f790dd6c Fix MFA Enforcement 2025-09-23 20:30:25 -04:00
johnnyq
ed6aa843b7 Fix Activity log link 2025-09-23 19:41:02 -04:00
johnnyq
52a27699f1 fix mfa model 2025-09-23 19:39:49 -04:00
johnnyq
dba08714bf moved user preferences and user settings into /agent/user/ directory 2025-09-23 19:17:14 -04:00
johnnyq
edabc5c33f rename /user/ to /agent/ and update links to use agent/ instead 2025-09-23 18:04:23 -04:00
johnnyq
6b6c70f1df added extra ../ to href for css and js in header and footer to allow code to be placed deeper within the directory structure moved reports to /user/reports, this should also fix the new custom directories 2025-09-23 17:05:12 -04:00
johnnyq
93061eb695 Add Assigned Agent column to Recurring Tickets 2025-09-23 15:43:45 -04:00
johnnyq
1f9133c188 Added Viewing Archived Users and the Ability to Restore alonf with reassigning tickets of that user during the archive process 2025-09-23 15:36:45 -04:00
wrongecho
e7dcc6df3c Tickets API - Add ability to resolve ticket in post request with only a ticket ID and client ID via resolve.php endpoint 2025-09-23 09:28:27 +01:00
wrongecho
fbd58b4723 Bugfix: Change working directory back after 2025-09-23 09:27:04 +01:00
wrongecho
e992138456 Better logic for the index/root page:
- If app user, send to their start page
- If a client contact, send back to the client area
- If not logged in at all, make them
2025-09-23 08:45:06 +01:00
wrongecho
058f79d0a1 Fix file paths in cron notifs to new structure 2025-09-23 08:38:29 +01:00
johnnyq
5c448c05a9 Update .gitignore and add custom directories to /admin /client /guest /user Example Documentation coming soon 2025-09-22 19:33:40 -04:00
wrongecho
e966cd3068 New tickets v2 - Exclude leads 2025-09-22 12:04:11 +01:00
wrongecho
6d3351b2f7 Payment providers bug-fixing 2025-09-22 11:53:49 +01:00
wrongecho
61a1d61901 Bugfix - remove capital in html field name - Threshold => threshold 2025-09-22 11:23:18 +01:00
johnnyq
4ff3231451 Fix Incorrect Asset Name in Logging and Flash Alert when editing an asset in a ticket, remove ACH and Add Bank Transfer to Setup 2025-09-20 15:56:50 -04:00
johnnyq
ce832d2805 Fix Broken Restore from Backup on Setup Page 2025-09-19 16:02:55 -04:00
Johnny
b11730303e Merge pull request #1238 from itflow-org/develop
Develop
2025-09-19 14:00:02 -04:00
johnnyq
565f9ab314 Update Changelog and App Version 2025-09-19 13:47:36 -04:00
Johnny
9435434cf9 Merge pull request #1237 from git-kup/develop
Update invoice.php - removed extra unnecessary wording
2025-09-17 00:03:41 -04:00
git-kup
a58ca6f66d Update invoice.php - removed extra unnecessary wording
it always bugged me that the alert message is so bloated. i almost never go to the mail queue after sending each invoice it just doesn't make any sense
2025-09-16 19:36:25 -04:00
johnnyq
c769bbc405 Created new Mail Queuer to take advantage of OAUTH2 for M365 and Google Workspaces not enabled by default 2025-09-16 15:43:54 -04:00
Johnny
0379143829 Merge pull request #1236 from cynicalgeek/develop
Update to use payment_methods table
2025-09-16 11:46:30 -04:00
cynicalgeek
ee235cf231 Update to use payment_methods table 2025-09-16 01:38:11 -07:00
johnnyq
04b29d43df Update Agent ticket access path in emails to new /user/ path 2025-09-15 17:28:58 -04:00
johnnyq
dc0715da57 Added SMTP Provider and the ability to share OAUTH keys with IMAP for M365 Mail Auth 2025-09-15 17:23:00 -04:00
johnnyq
902323a75b Fix Broken gloabl search when in Admin section 2025-09-12 17:32:44 -04:00
johnnyq
3a5b18f3dd Added Beta support for Microsoft IMAP OAUTH2 must use new mail parser for it to work cron/ticket_email_parser.php 2025-09-12 15:56:44 -04:00
johnnyq
ce7d84aa2f Reintroduce Webklex IMAP for ticket processing as PHP-IMAP is no longer being developed. This is optional for now and considered beta can be found in cron/ticket_email_parser.php 2025-09-10 14:27:46 -04:00
johnnyq
981fb9585d Updated All Exports to include your company name if exporting all and if exporting just from a client prepend the client name to file, introduced a sanitize_filename function and used it for the exports to always get a clean file name that works on every OS 2025-09-10 12:50:10 -04:00
johnnyq
23b2dcba70 Renamed post file user back to users 2025-09-09 17:47:16 -04:00
johnnyq
e4a437f54c added escape parameters to fputcsv to satisfy php 8.4 Depracations 2025-09-09 17:45:09 -04:00
johnnyq
d4167f9595 Fix Missing first row on interface export, started adding escape parameters to fputcsv to satisfy php 8.4 Depracations and fixed export all quotes 2025-09-09 16:54:18 -04:00
johnnyq
88475a2b76 Fix Mispelling causing Adding new certificate to break 2025-09-09 12:50:27 -04:00
johnnyq
c26ce4b7dc Spruced up UI and eliminated checkbox to grant consent on Stripe consent in client Portal instead button acts a consent 2025-09-08 17:36:09 -04:00
johnnyq
5960e7cbd9 Fix Broken Add Payment link in reccuring invoice in the client portal 2025-09-08 17:23:51 -04:00
johnnyq
68872ab9fb Merge branch 'develop' of github.com:itflow-org/itflow into develop 2025-09-08 13:06:49 -04:00
johnnyq
64f12b42b8 Fix Footer path in used edit causing edit user not to function, also removed the old ajax used edit modal 2025-09-08 13:06:33 -04:00
wrongecho
8c0d542d7d Better logic handling for the default page redirects 2025-09-08 15:40:59 +01:00
wrongecho
c016b67c3a Fix typo in sql query field, matching develop back to master because we screwed up the flow 2025-09-08 15:26:57 +01:00
Johnny
49d127e957 Merge pull request #1235 from expxx/patch-1
fix: spelling error with `vendor_descripion` -> `vendor_description`
2025-09-07 21:04:14 -04:00
Cam
e7353c4757 fix: spelling error with vendor_descripion 2025-09-07 18:26:00 -06:00
Johnny
3106685972 Merge pull request #1234 from itflow-org/develop
v25.09.1
2025-09-07 11:44:10 -04:00
johnnyq
2b7017fae2 Fix dark mode for AI Ticket Summary 2025-09-07 11:38:03 -04:00
johnnyq
da0b01e23f Center Generating Summary in AI Summary 2025-09-07 11:30:43 -04:00
johnnyq
d450ea4beb Fix Installer Database not installing due to bad file name 2025-09-07 11:06:26 -04:00
johnnyq
9210734911 Also take in consideration of the ticket source, category and priority 2025-09-06 19:41:16 -04:00
johnnyq
ebae80bb7e Ticket Summeries are now in HTML form wit ha breakdown of the Main Issue, Actions Taken and Resolution or Next Steps. It also takes into consideration of the current ticket status and Who replyied for the ticket replys / comments 2025-09-06 19:31:44 -04:00
johnnyq
dcade3a5c7 Update the AI Ticket Summary Prompt 2025-09-06 17:57:58 -04:00
johnnyq
f51c3e9e3f Text Wrap client tags so table doesnt go off the page with clients with many tags 2025-09-05 19:29:21 -04:00
johnnyq
b34298e45b Hide Add Credit button in Client Top Head Dropdown also hide credits in client listing if no credit balance 2025-09-05 19:17:02 -04:00
johnnyq
9fa78897bc Fix extra spacing between icon and Assigned Contact Name iin ticket details 2025-09-05 19:05:06 -04:00
johnnyq
9642babb7e Fix issue with AI Settings displaying in Admin Menu if the module invoice/accouting module is turned off 2025-09-05 19:00:17 -04:00
johnnyq
d2d1aed393 Fix long Invoice/Quote notes overflowing into the invoice/quote footer by removing a rowspan class, adjusted the page margins from 15 to 10 and adjusted the invoice/quote notes to use font size 9 instead of 10 2025-09-05 18:48:35 -04:00
Johnny
58d43063ec Merge pull request #1233 from itflow-org/develop
25.09 Release
2025-09-05 13:24:54 -04:00
johnnyq
6a5ce1dce3 Add note about updating from the webUI 2025-09-05 13:09:02 -04:00
johnnyq
f3c7c0761b Add note about backing up before updating 2025-09-05 13:05:46 -04:00
johnnyq
27b3124f77 Update .gitignore to ignore the new custom/* 2025-09-05 12:57:19 -04:00
johnnyq
aa516529e7 Another attempt at fix automatic recurring payments expense 2025-09-04 17:56:00 -04:00
johnnyq
2283ad0eb2 Fix Adding Expense on recurring stripe payments in cron 2025-09-04 17:44:34 -04:00
johnnyq
cece9ad46c Another attempt to fix reccuring payment migration to match the correct client_id; 2025-09-04 16:29:55 -04:00
johnnyq
d37da2e8ff Fix Recurring Payment Migration for stripe 2025-09-04 16:07:38 -04:00
johnnyq
9d9bfc65d5 Fix Active side nav for api keys 2025-09-03 18:46:01 -04:00
johnnyq
d7d6e4ac6f Fix paths for db and uploads dir in debug 2025-09-03 18:42:32 -04:00
johnnyq
46d93f986e Further enhanced the Saved Payments Section by splitting cards and add card into 2 columns 2025-09-03 17:45:11 -04:00
johnnyq
2549a97a7e Added card icons to Saved Payments in Client Portal and spruced up ui 2025-09-03 17:40:09 -04:00
johnnyq
fc2cca5fdb Added card icon 2025-09-03 17:14:53 -04:00
johnnyq
ef1ec56270 Allow the Client to easily Pay an invoice from the client portal with a saved card 2025-09-03 17:09:17 -04:00
johnnyq
cf63a2e7d1 Fix TinyMCE License in client portal 2025-09-02 14:57:09 -04:00
johnnyq
ed9b99e2f0 Fix unpaid invoices on client portal but also comment it out until ready for next release 2025-09-02 14:52:38 -04:00
johnnyq
33340b80f9 Updated Changelog, Increased app version 2025-09-02 13:14:18 -04:00
johnnyq
ec94bb3d2a Add back Pay Invoice with saved card, currently only pays the full amount, would like to integrate into add payment modal and add payment post eventually 2025-09-01 16:19:33 -04:00
johnnyq
bc2fe2bec8 Fixed stripe Migration table name spelling 2025-08-30 11:19:28 -04:00
Marcus Hill
a87a206c04 Add better error handling for dodgy whois results 2025-08-30 16:16:55 +01:00
Marcus Hill
45083e19b0 Fix if undefined 2025-08-30 15:58:19 +01:00
Marcus Hill
de627c19c5 No csrf for client side, yet 2025-08-30 15:51:14 +01:00
Marcus Hill
595e57dcdd Rewording 2025-08-30 15:51:05 +01:00
Marcus Hill
dadcc69900 Typo 2025-08-30 15:35:31 +01:00
Marcus Hill
2c160d1ac9 Take you back to dashboard rather than a redirect 2025-08-30 15:31:49 +01:00
johnnyq
292b074d8c Remove legacy Stripe Enabled from invoice Commented out Pay Via Saved card this will have to wait for next release 2025-08-29 13:44:22 -04:00
johnnyq
2beff45811 Removed some simple tinyMCE inits and combined AI button into one 2025-08-29 12:51:14 -04:00
johnnyq
469c36da46 Remove AI Enable References through the UI this will be handled differently 2025-08-29 12:26:56 -04:00
johnnyq
8afe2d52a0 Converted and combine ajax add for tags and categories, fixed quick add links 2025-08-28 16:57:08 -04:00
johnnyq
51273e9151 Remove unused / unfinished browser extension from user preferences 2025-08-28 15:39:00 -04:00
johnnyq
1517c96841 Remove Budget from reports as it totally unfinished 2025-08-28 14:38:49 -04:00
johnnyq
b7afbd3500 Revert Bump down fontawesome-free 7.0.0 to 5.15.4, did not like the way the new icons looked with the app at all 2025-08-28 14:25:14 -04:00
johnnyq
39d6c42c71 Bump and Migrate logic chartjs 2.9.4 to 4.5.0, bump stripe-php from 17.2.1 to 17.6.0, fontawesome-free from 5.15.4 to 7.0.0, fullcalendar from 6.1.17 to 6.1.19, TinyMCE from 7.9.1 to 8.0.2, bootsatrap js bundle from 4.6.1 to 4.6.2, DataTables from 2.3.1 to 2.3.3 2025-08-28 13:57:42 -04:00
johnnyq
9f50c9355a Move reports out of the sub directory for now as it broke requires 2025-08-28 11:46:20 -04:00
johnnyq
fd73f132bc Fixed a few table names in Stripe Migration, Removed legacy stripe and AI settings 2025-08-28 11:44:29 -04:00
johnnyq
1f99e592b7 Remove old Autop Pay Logic from recurring Invoices Listing 2025-08-27 20:09:12 -04:00
johnnyq
a90c4eded8 Remove Old Auto Pay Modal for recurring Invoices 2025-08-27 20:05:47 -04:00
johnnyq
1638538fb4 Remove Old Auto Pay from recurring Invoice in favor of the saved cards. 2025-08-27 20:04:49 -04:00
johnnyq
da0892f9e3 Remove Stripe check and add payment provider check in Client Portal Recurring Invoices to create auto payments 2025-08-27 18:23:58 -04:00
johnnyq
66cab6c1ab Fixed Guest Pay removed old stripe vars in place of new payment provider vars 2025-08-27 17:38:41 -04:00
johnnyq
535078b0ab Fix Bad vars in PAyment Providers listing, add check to see if payment provider already exists 2025-08-27 16:27:54 -04:00
johnnyq
d584c6b1e8 Commented out Credit lines as its not ready for next release running into way to many pitfalls will focus more on next release 2025-08-27 16:02:00 -04:00
johnnyq
530a76a9a2 Fix undefined vars in export invoice / quote pdf fix regression company logo path in export invoice / quote 2025-08-27 11:53:58 -04:00
johnnyq
2a0181fd92 Convert Invoice and Quote Guest URL to Copy to Clipboard link instead of open links 2025-08-26 12:17:45 -04:00
johnnyq
86c5b9a55d Removed Budge from the side nav as this was never finished and doesnt really do anything, will work on it at a later date 2025-08-25 23:51:48 -04:00
johnnyq
0a6b890250 Fix broken php start tag 2025-08-25 17:33:40 -04:00
johnnyq
af7f1f30ad Credentials: Fix broken page if no clients exist, which also fixes dont show share modal if GET client_id is not set 2025-08-25 17:32:15 -04:00
johnnyq
f6c0f840fe Asset: Multiple Bug fixes, Fix broken asset list if no assets exist, fix bulk Asset Ticket creation to use the client_id of the associated asset, allow to assign to all open projects. Fix Transfer client where client_id is not defined in client overview section 2025-08-25 16:59:15 -04:00
johnnyq
0e54359db8 Assets: Fix Description not displaying, Fix issue with unitialized OS var, fix client asset uri using asset_uri_2 2025-08-25 14:07:55 -04:00
johnnyq
373f1574bd Update function path for customAction to use the new path /custom and not /xcustom 2025-08-25 12:58:57 -04:00
johnnyq
6f5ffef22f Link Fixes 2025-08-25 12:50:01 -04:00
johnnyq
451206525e Remove function code for old way ajax-modal, moved modals over to modal_header/footer and removed _new 2025-08-25 00:07:51 -04:00
johnnyq
41f957ea3b Migrated admin modals to modals/ENTITY removed ajax_ and _modal prefixes from file names and use new ajax-modal syntax 2025-08-24 23:50:38 -04:00
johnnyq
91b0080428 Finished Consolidating the remaining user modals to modals/ENTITY removed ajax_ and _modal prefixes from file names and use new ajax-modal syntax 2025-08-24 00:38:48 -04:00
johnnyq
170d6dd7e5 Consolidate alot of modals to modals/ENTITY removed ajax_ and _modal prefixes from file names and use new ajax-modal syntax 2025-08-23 18:48:21 -04:00
johnnyq
15ba385398 Consolidate asset modals to modals/asset removed ajax_ and _modal prefixes from file names and use new ajax-modal syntax 2025-08-22 21:54:26 -04:00
johnnyq
0b93d58a32 Consolidate account modals to modals/account removed ajax_ and _modal prefixes from file names and use new ajax-modal syntax 2025-08-22 18:51:39 -04:00
johnnyq
67078c4552 Consolidate product modals to modals/product removed ajax_ and _modal prefixes from file names and use new ajax-modal syntax 2025-08-22 18:22:04 -04:00
johnnyq
ab17de7efd Consolidate client modals to modals/client and use new ajax-modal syntax 2025-08-22 18:06:37 -04:00
johnnyq
1145260619 Consolidate trip modals to modals/trip removed ajax_ and _modal prefixes from file names and use new ajax-modal syntax 2025-08-22 17:49:46 -04:00
johnnyq
066952ed04 Consolidate vendor modals to modals/vendor removed ajax_ and _modal prefixes from file names and use new ajax-modal syntax 2025-08-22 17:36:07 -04:00
johnnyq
6ccf7a0fb8 Set Invoice Credit, Consolidated all expense modals to modals/expense removed ajax_ and _modal prefixes from file name create new modal header and footer to include another parent directory eventually will consolidate all, this was just an example 2025-08-22 16:59:31 -04:00
johnnyq
7f49ecefb1 Flash alert if no comment was entered in a ticket reply via the client portal 2025-08-21 18:35:16 -04:00
johnnyq
43324391ff Fix Regressed See More... in User Activies page 2025-08-21 18:15:28 -04:00
johnnyq
89af8216b8 Fix Ticket Kanban CSS Path and category query 2025-08-21 18:13:31 -04:00
johnnyq
be9e30e6d8 Fix Show Add Payment dropdown if client has credits or saved payment methods 2025-08-21 18:04:49 -04:00
johnnyq
85cfd6fb7c Fix Regression Broken Quick add modals 2025-08-21 17:54:10 -04:00
johnnyq
e694e20025 Use mysqli escape string instead of sanitizeInput on stripe to payment provider migration just incase 2025-08-20 14:18:34 -04:00
johnnyq
7618dd335a Update Stripe to Payment Provider Migration to not transfer over clients with a NULL or blank stripe payment method 2025-08-20 14:12:39 -04:00
johnnyq
eca09253d3 Migrate over Payment Methods from Categories to its own table payment_methods 2025-08-20 13:53:38 -04:00
johnnyq
96c32b3275 Added Stripe Config Migration to a database Update 2025-08-20 13:30:46 -04:00
wrongecho
94a4fec3c6 Fix popping future tickets numbering 2025-08-20 16:43:45 +01:00
Marcus Hill
a8386d3937 Allow reporting on average time to first response 2025-08-16 21:44:17 +01:00
Marcus Hill
4b7aa4bece Powered by 2025-08-16 16:37:24 +01:00
Marcus Hill
4c604f4009 Use HTML for the logo instead of pdf->image; scales much nicer 2025-08-16 16:25:51 +01:00
Marcus Hill
98cf2f7a5a Use freeserif font instead of helvetica to properly display foreign characters 2025-08-16 15:54:28 +01:00
Marcus Hill
804467b1ba Use freeserif font instead of helvetica to properly display foreign characters 2025-08-16 15:52:54 +01:00
Marcus Hill
9fbf775299 Fix file icon in file rename modal 2025-08-16 15:30:10 +01:00
Marcus Hill
5c93753b7e Rm extra unnecessary line in overdue invoice email 2025-08-16 15:17:48 +01:00
Marcus Hill
27016eaa28 Recurring tickets - add ability to bulk recurr 2025-08-16 14:31:36 +01:00
Marcus Hill
859281784a Recurring tickets - fix include path 2025-08-16 14:31:01 +01:00
Marcus Hill
7ae7b8904c Invoice.php - Fix undefined error 2025-08-16 14:30:37 +01:00
Marcus Hill
559e0c6350 Comment direct link to guest pay, we should not encourage agents to do this 2025-08-16 13:47:47 +01:00
Marcus Hill
d942badec8 Fix primary contact text not showing 2025-08-16 13:42:29 +01:00
johnnyq
f443ae9203 convert even more ajax modals to new syntax 2025-08-16 02:06:42 -04:00
johnnyq
d37dc37c2d convert more ajax modals to new syntax 2025-08-16 01:40:40 -04:00
johnnyq
236d895498 Reworked the ajax-modal function to be more simplified and use less data attributes 2025-08-16 01:02:16 -04:00
johnnyq
3bb0ef6f39 Left Align Billing Header 2025-08-15 22:55:40 -04:00
johnnyq
a0ced96d1d Use a WAY MORE Efficient query when filling out filter selection boxes by using EXIST instead of DISTINCT selections 2025-08-15 20:39:23 -04:00
johnnyq
4ef1e91c78 When Show Archived is selected in clients show Drop down filters for them as well in Idustry and Referral 2025-08-15 19:32:09 -04:00
johnnyq
2333fa2d9b Product Category Dropdown Filter only show categories thats are assigned to the products not all the categories 2025-08-15 19:26:42 -04:00
johnnyq
37bef2d824 Check Stock if product type is a product before adding the line item 2025-08-15 19:12:45 -04:00
johnnyq
6427235aec Show Name Description Tax Price and Stock in Product Auto Complete in Invoice 2025-08-15 18:25:23 -04:00
johnnyq
535c8e9f7b Remove Stock if Inventory item is selected and added to an invoice and return stock when Invoice Item related to a product is deleted 2025-08-15 17:32:44 -04:00
johnnyq
890e166d1c Thicken the left border on ticket replies for more prominent type of reply 2025-08-13 13:35:29 -04:00
johnnyq
337ceb7477 Fix Client Net terms displaying default Net terms instead of On Receipt if selected 2025-08-11 21:51:05 -04:00
johnnyq
f82f3e9b8c Allow adding more than 9 stock 2025-08-11 21:40:42 -04:00
johnnyq
3c709955e3 Add UI elements for product location, used logic to seperate product from service, also added the ability to add stock for a product and linking stock to an expense with notation, still need to work on taking away from stock 2025-08-11 21:18:55 -04:00
johnnyq
595e6090c7 Added Stock Ledger to tables and additonal fields for product including type, code and location 2025-08-11 18:19:45 -04:00
johnnyq
1d98eb5160 Update Project Details Header to use card group class and update client ui to not show header button texts in mobile view, added no text wrap to location and contact column 2025-08-08 16:09:46 -04:00
johnnyq
7172c92f02 Update Database updates patch in update_cli 2025-08-08 15:09:03 -04:00
johnnyq
874d9ddbc3 Cleaned up ticket details UI used card Group for top, removed responses in ticket replies section, uncarded the ticket commend editor giving much more room, moved summarize to 3 dots 2025-08-07 20:38:23 -04:00
johnnyq
73b84a9690 Removed dynamic depth path_prefix due to potential security issues with it 2025-08-06 18:12:13 -04:00
johnnyq
c78f50ab8c cap the path_prefix to 3 depth directories max 2025-08-06 18:03:51 -04:00
johnnyq
8407cd3ea5 updated router.php to automatically determine relative path based off directory depth and use var path_prefix instead of prepend_uri var these vars are only used in header and footer php files for now 2025-08-06 18:02:25 -04:00
johnnyq
9caa42975d rename get_settings.php to load_global_settings.php and update all requires 2025-08-06 17:48:23 -04:00
johnnyq
44fdb6c24f Broke up the check_login.php require into several files seperated by function and then required them in the check_login 2025-08-06 17:16:30 -04:00
Johnny
c8984d1bc9 Merge pull request #1231 from itflow-org/ticket-fr
Implement ticket first response date/time tracking
2025-08-06 16:19:27 -04:00
johnnyq
be66a1690f Fix setup redirect 2025-08-06 12:52:02 -04:00
wrongecho
13838ec8d6 Implement ticket first response date/time tracking 2025-08-06 10:45:28 +01:00
wrongecho
2d72a6fd12 Implement ticket first response date/time tracking 2025-08-06 10:38:52 +01:00
johnnyq
e24ec04a75 Fix Post errors when repeat and Email Event is not selected 2025-08-05 18:39:24 -04:00
johnnyq
9a0aa9ce97 Fix Calendar not showing when no client is selected 2025-08-05 18:36:23 -04:00
johnnyq
643a4ce666 add router.php to inc_all_client.php 2025-08-05 18:18:46 -04:00
johnnyq
189098541e Remove unused top nav ticket modal 2025-08-05 18:17:14 -04:00
johnnyq
e8a1d4c66c Update mfa enforcement page to use new asset pathes 2025-08-05 14:05:09 -04:00
johnnyq
08dd6147f2 Migrate Dark/light mode to a user settings instead of global setting, created new include called router.php to dynamically prepend ../../ to asset pathes based off currect directory depth 2025-08-05 13:32:19 -04:00
johnnyq
7e55808a05 add some redirect to login.php, fix ticket post 2025-08-04 18:16:13 -04:00
johnnyq
6b39c1a280 Finished moving to the redirect, flash_alert and get_field_by_id and maing sure validate_csrf is on top and some other cleanups for guest and client posts 2025-08-04 18:07:26 -04:00
johnnyq
a38d0054df Finished moving to the redirect, flash_alert and get_field_by_id and maing sure validate_csrf is on top and some other cleanups for user posts 2025-08-04 17:41:32 -04:00
johnnyq
e39072d3bc More work on moving to the redirect, flash_alert and get_field_by_id and maing sure validate_csrf is on top and some other cleanups, along with more work on credits 2025-08-04 17:01:19 -04:00
johnnyq
b4e5e3fda7 Fix injection with role id affecting authenticated admin accounts only 2025-08-04 12:06:05 -04:00
johnnyq
e8af173b4f Fix Client Billing column lweft align the label right align the amounts 2025-08-04 11:36:30 -04:00
johnnyq
fef0d8761f More user post function migration and cleanup 2025-08-04 11:27:24 -04:00
wrongecho
76b48378ab Hide invite contact letter options for now 2025-08-04 11:45:54 +01:00
johnnyq
a7e042b5f4 Removed logging comments as its implied in asset post also if returning 1 field for logging use getFieldById Function flipped it so it validate CSRF before role permission check, replaced header redirect with new redirect function and elimnasted the extra exit() afer redirect as its done in the function, also used the flash_alert function 2025-08-02 12:51:40 -04:00
johnnyq
ab8f51622f Started Removing logging comments as its implied in user posts also if returning 1 field for logging use getFieldById Function flipped it so it validate CSRF before role permission check 2025-08-01 18:30:32 -04:00
johnnyq
fae7e76721 Remove logging comments as its implied in admin also if returning 1 field for logging use getFieldById Function 2025-08-01 18:08:02 -04:00
johnnyq
012b0f1a77 Update Admin posts to use new functions flash_alert and getFieldById which greatly cleans up and reduces the code also removed logging comments as implied by the function logAction 2025-08-01 17:21:35 -04:00
johnnyq
cc5b1c8040 Add new function called flash_alert() to repace the alerting in posts, updated accounts post to use new function 2025-08-01 15:51:29 -04:00
johnnyq
2cf932600e Fix broken link for edit role 2025-08-01 15:32:43 -04:00
johnnyq
d2dac1915d Update admin, user, guest and client post to use new redirect function 2025-08-01 15:25:52 -04:00
johnnyq
5d52b6c4ec Update logic for Apply Credit on invoice Post 2025-08-01 15:17:50 -04:00
johnnyq
7cfba7f2ef Added redirect() function for page redirects and updated credit system fields to include credit_note, credit_invoice_id and type for better reporting 2025-08-01 13:37:23 -04:00
johnnyq
41a8e41463 Move reports back and rename them appropriately update broken links 2025-07-31 18:48:33 -04:00
johnnyq
07e2c41eb7 Fix client uri regression broken asset details page update echos to use php short hand for easier code reading 2025-07-31 16:10:33 -04:00
johnnyq
9fe0d8417b Feature: Added Client URI field to Assets which is exposed in the client portal can be used for remote desktop links etc 2025-07-31 15:54:07 -04:00
johnnyq
905908bcf1 broken link updates 2025-07-30 20:59:20 -04:00
johnnyq
8e87c25de1 Update more broken links from file diretory consolidation and started migrating some js and ajax and css from root to /user /admin etc that pertain to the section 2025-07-30 20:08:28 -04:00
johnnyq
5a76780cd9 Move db.sql back to fix develop platform 2025-07-29 21:40:50 -04:00
johnnyq
10fd911dcc Update links on the setup page as well 2025-07-29 18:59:42 -04:00
johnnyq
2333d81276 Update more broken links from file dirextory consolidation 2025-07-29 18:34:51 -04:00
johnnyq
9ee76213e1 Update more broken links from file dirextory consolidation 2025-07-29 14:32:43 -04:00
johnnyq
4ec1441189 Cleanup some broken links from file dirextory consolidation 2025-07-28 23:29:46 -04:00
johnnyq
38e2e50a29 More progress on consolidating files into user and admin 2025-07-28 22:48:44 -04:00
johnnyq
95950700d8 Moved user items to user directory 2025-07-28 17:57:06 -04:00
johnnyq
0494bfc1cf Moved admin_ to /admin, user_ to user report_ to /reports each have their own post includes modals directories created seperate headers and footer. Also did the same for xcustom, more work to me done 2025-07-28 13:32:28 -04:00
johnnyq
4906e06bf1 Initialized migrating modals into entity folders and modals out of ajax and start working on moving admin items to new admin directory 2025-07-27 21:24:14 -04:00
johnnyq
699546f531 In Client Import Clarify CSV as Primary Contact Name instead of just Contact Name same with Location name use Primary Location Name, makes it much clearer when importing clients 2025-07-25 14:21:55 -04:00
johnnyq
d5ec601d75 add eg info@company.com for further clarification on company setup 2025-07-25 13:36:07 -04:00
johnnyq
5f26f68a04 Clarify Email during company setup, instead of just enter email enter company email for the placeholder https://youtu.be/Ul10HXaPwNk?t=2079 2025-07-25 13:34:05 -04:00
johnnyq
219fe59aa2 Use Badge Pill for rounded Badges in client side nav 2025-07-25 12:44:43 -04:00
johnnyq
d115d57d1a Update sanitize_input Function to handle encoding issues such as Vietnamese. Instead of Blindly just converting to UTF-8 check the input to see if its already UTF-8 2025-07-25 12:35:13 -04:00
johnnyq
6307099bc1 Clarified Importing contacts when inporting clients in the client import modal window 2025-07-25 12:18:17 -04:00
johnnyq
e0a5efdfaf Updated Contact Asset Counts to use more optimied query and added asset counts to client listing 2025-07-24 17:28:10 -04:00
johnnyq
e014e516e6 Beta Support for Dark Mode has been added to the theme options 2025-07-24 14:58:06 -04:00
johnnyq
4d71eb5de0 Remove Credit once Applied, also get total credits applied 2025-07-24 13:24:59 -04:00
johnnyq
0db6853fde Updated Invoice, Quote Recurring Invoice UI to include a table border and added hover effects to line items 2025-07-24 13:02:57 -04:00
johnnyq
197dd9f299 Initial Work on Client Account Credit System, DB Structure, Credit Balance Calculation, added Apply Credit and add Credit with Expire dates, Added DB Structure for Discount Codes, UI Rewrite on client top header now using grouped cards, more dark mode work as well 2025-07-24 11:31:45 -04:00
johnnyq
99aaeefe8e Move the adminlte css to be the last css to fix select2 being white in dark-mode 2025-07-23 17:32:51 -04:00
johnnyq
712804d2a8 Preparing for Dark mode theme: Started Fixing models so they look better in dark mode 2025-07-23 17:02:20 -04:00
johnnyq
a393284acd Remove Building Icon next to company name in main side bar nav 2025-07-23 13:24:48 -04:00
johnnyq
719a0e5efa Also Apply previous commit it editing recurring expenses 2025-07-23 13:20:06 -04:00
johnnyq
6221fd67e1 Fix recurring expense next run date logic for monthly and annual frequencies 2025-07-23 11:47:31 -04:00
johnnyq
56c7c76ac6 Add option to filter footer 500 records per page 2025-07-22 16:15:09 -04:00
johnnyq
a5aae51b1a Fix issue in some of the client overview entities archived will not remain selected after query, fixed issue where clients would not show in overview if in archived 2025-07-22 16:13:04 -04:00
johnnyq
14691fa367 Fix Archived Query for domains to not show archived clients 2025-07-22 15:51:57 -04:00
johnnyq
38b2a94231 Client Overview do not count Archived Clients entities and in services do not show services of archived clients 2025-07-22 15:46:17 -04:00
johnnyq
4c031c8f83 Increase the width for totals on invoices and Quotes PDF Exports 2025-07-21 21:31:39 -04:00
johnnyq
b100498dd7 Add Client Bulk Actions: Edit Tags, Referral, Industry, Set Hourly Rate, Send Bulk Email, Archive. Also added restore option to restore a client 2025-07-21 18:27:31 -04:00
johnnyq
50a8e67917 Commented out deleting old Payment Provider Settings from DB until next release 2025-07-21 13:29:29 -04:00
wrongecho
dfe0a0b1c7 Add default netid for asset int csv import to prevent errors 2025-07-21 09:18:30 +01:00
wrongecho
52dac2252a Add default purchase date for csv import to prevent error when undefined 2025-07-21 08:11:57 +01:00
johnnyq
5aee84e84f Add Credit card icon to the pay button 2025-07-15 18:27:09 -04:00
johnnyq
3e7d7f3801 Make Client Portal Dashboard Cards Linkable, added upaid invoices with ui for option to enter card manually, pay with saved card or pay entire balance 2025-07-15 18:23:20 -04:00
johnnyq
184aba4de4 Finished UI work for AI Providers and Models, AI is set to use the new AI Provider and Models 2025-07-15 16:19:23 -04:00
johnnyq
dd482b246f Use getFallBack function on MAC Address if no MAC is present show a - placeholder 2025-07-15 13:56:54 -04:00
johnnyq
717e6351e7 Add Option for Mac Address in Additonal Column and allow to filter by MAC 2025-07-15 13:54:25 -04:00
johnnyq
c36b1cf50f Added Income Category Filter to Invoices 2025-07-15 13:34:05 -04:00
johnnyq
896da66caf Remove the Archive button from invoices as we dont archive invoices 2025-07-15 13:26:13 -04:00
johnnyq
f72b7763c1 Added Bulk Actions to Invoices Assign Income Category 2025-07-15 13:24:28 -04:00
johnnyq
46c4a62046 Converted add payment to a dropdown under invoice to allow for the standard manaual add payment and to pay with a saved card or by entering a card manully 2025-07-14 17:36:03 -04:00
wrongecho
da3a1d2ce4 Allow linking closed tickets to a project 2025-07-14 15:13:36 +01:00
johnnyq
35d6b51770 use table-sm for tickets to reduce padding and margin and remove ticket_compact_view 2025-07-10 16:11:02 -04:00
johnnyq
35cbfdd736 Rework Categories into a select box for tickets, which now makes it easier to see what category your viewing and one less click as the form auto submits upon selection 2025-07-10 16:04:52 -04:00
johnnyq
a7b2522c87 Fix Invoice Link in ticket details when billed 2025-07-09 20:10:08 -04:00
johnnyq
fe8baf2ca4 Fix pointer not switch to a finger when hovering over add contact in ticket details 2025-07-09 20:06:22 -04:00
johnnyq
300aef413c Fixed Email Notify not showing for recurring invoice 2025-07-09 19:32:24 -04:00
johnnyq
467c681117 Add Saved Payment Options to recurring invoices and details for agents 2025-07-09 19:23:12 -04:00
johnnyq
31a89c0641 Update Recurring Payment Auto pay in cron 2025-07-09 17:05:56 -04:00
johnnyq
5f94543d9b Add Easy Select Auto Pay option in Client Portal Recurring Invoices, fix guest pay 2025-07-09 15:58:13 -04:00
johnnyq
10fd74b4c4 Remove unused model 2025-07-09 01:46:56 -04:00
johnnyq
9bc705bfa1 Updated Guest pay and Client Add Delete Payments, adjustedsetting saved payment per recurring invoice 2025-07-09 01:45:56 -04:00
johnnyq
307d9892b2 No need for a sql delete on recurring payments when deleting a saved payment method as SQL cascadely deletes the recurring payment if saved payment method is deleted 2025-07-08 14:10:27 -04:00
johnnyq
93f4ea51fd Rework Payment Provider AI Provider Tables and logic add cascading deletes 2025-07-08 14:07:10 -04:00
johnnyq
a6f83493f8 Fix Payment Method Additons in setup 2025-07-07 23:22:35 -04:00
johnnyq
7e17fdf851 Add new UI for Saved Payments for payment providers 2025-07-07 18:25:23 -04:00
johnnyq
c76da10747 Add Payment Method to UI and switch Add Payment modals to use the new table 2025-07-07 16:37:51 -04:00
wrongecho
7c558ff842 Add location create endpoint 2025-07-07 13:47:17 +01:00
johnnyq
a011dc4dea POST code for AI and Payment Providers 2025-07-06 17:01:28 -04:00
johnnyq
fa9acef279 Fixed IMAP test button with error details 2025-07-06 12:47:26 -04:00
johnnyq
ed0936059b Convert new lines to brs for contact notes 2025-07-06 11:50:12 -04:00
johnnyq
77597fd7a9 Update wording on Add Payment Processing, we plan on using the income/expense account to be named after provider name and category be processing fees and the vendor named after the provider name if payment processing expense is enabled 2025-07-04 18:07:59 -04:00
johnnyq
8d1bf3d245 UI add modal and listing work on Payment Providers 2025-07-04 17:48:54 -04:00
johnnyq
e7503e8f55 Initial DB work to Generalize Payment Providers, allow multiple payment providers and client payment methods, also to move payment methods to its own table instead of in categories, started work on ai provider ui models 2025-07-04 16:52:30 -04:00
johnnyq
9b6be66623 Initial work on Adding Payment Methods for Online Payments in Client on Agent Side and initial work on AI Providers with multiple model support assigned to various sections and custom prompts 2025-07-04 15:42:26 -04:00
johnnyq
6bc7862232 UI Dashb oard resize some Dashboard cards 2025-07-03 18:14:20 -04:00
Johnny
69a8a9246d Merge pull request #1226 from MydsiIversen/Document-enhancement-clientside
Enhanced the document function on the client side
2025-07-03 17:42:24 -04:00
johnnyq
b87b801b04 Fix broken Table header Link buttons in contact details 2025-07-03 16:24:51 -04:00
johnnyq
dba04a407a Fix Table Header Link Buttons in Asset Details 2025-07-03 16:19:55 -04:00
johnnyq
e281a39512 Add title Icon for Bulk Set Status 2025-07-03 16:08:15 -04:00
johnnyq
e0364183e2 Disable Certain Bulk Actions that are directly related to a client in client overview section instead are shown in the client section 2025-07-03 16:03:57 -04:00
Johnny
6af61cbb3c Merge pull request #1225 from itflow-org/bulk-assign-phyloc
Add physical location modal - not working
2025-07-03 15:11:42 -04:00
johnnyq
67dc012caa Small wording hints on file uploads instead of upload File its just upload and added the title of the upload modal from upload files to upload file(s) 2025-07-03 14:44:30 -04:00
johnnyq
8753655c9c Do not allow client portal logins with Contact users of a client that is archived 2025-07-03 14:18:14 -04:00
wrongecho
873dc1f76d Fix contact location if not provided in api call 2025-07-01 11:50:20 +01:00
wrongecho
d781130b49 Merge branch 'develop' of https://github.com/itflow-org/itflow into develop 2025-07-01 10:52:05 +01:00
wrongecho
effbf241e6 Fix default rate and net terms if not provided in api call 2025-07-01 10:51:56 +01:00
Mads Iversen
396a67b198 - Enhanced the document retrieval process by including the document description in the SQL query.
- Implemented file upload capabilities, allowing users to attach files to documents with support for various formats (PDF, Word, text).
- Created modals for adding new documents and uploading files, improving user experience.
- Added functions for formatting file sizes and retrieving appropriate icons based on file extensions.
- Updated the document display logic to handle attached files, providing inline previews for PDFs and images.
2025-06-30 14:41:12 +02:00
Marcus Hill
1939e06a24 Start using the new modal when creating tickets. This allows us to select both the client and contact, along with a client-specific asset, location and vendor 2025-06-28 17:56:55 +01:00
Marcus Hill
3f21e73b2e Comment ticket collabs, still broken 2025-06-28 15:55:13 +01:00
Marcus Hill
bc9529c488 Client Portal - Allow client choose asset during ticket creation 2025-06-28 15:49:17 +01:00
Marcus Hill
19af05ebee Client Portal - Add company logo to header banner 2025-06-28 14:28:25 +01:00
Marcus Hill
eb11e5b2f8 Bugfix: Company logo not being removed properly from db 2025-06-28 14:27:51 +01:00
Marcus Hill
b85137096d Add physical location modal - not working 2025-06-28 13:42:00 +01:00
Marcus Hill
77e9d72f92 Add physical location modal - not working 2025-06-28 13:37:44 +01:00
Marcus Hill
ec344cbaa7 Allow entering multiple ticket watchers at once 2025-06-28 12:30:42 +01:00
johnnyq
4cb2f42d71 Added Bulk Action Set IP to DHCP in Asset Interface 2025-06-27 01:45:25 -04:00
johnnyq
98723b76cf Added Bulk Actions Set Network, Type and Delete to Asset Interfaces 2025-06-27 01:31:23 -04:00
johnnyq
45ac7f084b Add Bulk Delete Function Also when deletign a ticket delete the watchers and attachments and remove the ticket id from uploads/tickets/ticket_id 2025-06-26 23:16:08 -04:00
johnnyq
3626777bd1 Fix Item name and Description Alignment in PDF Exports 2025-06-26 19:12:25 -04:00
johnnyq
5fb1070567 Show your Assigned Assets on the Client Portal Home Page 2025-06-26 18:45:23 -04:00
johnnyq
f0bcf975b6 Add Assets to the client Portal viewable by Primary and technical Contacts 2025-06-26 18:18:36 -04:00
johnnyq
0ca28dc630 Fix Document API to not insert a new document upon update as we changed the way documents and document versions are handled 2025-06-26 17:30:34 -04:00
johnnyq
8196706538 Ticket API: Allow HTML into ticket Content and allow inserting asset_id 2025-06-26 17:13:27 -04:00
johnnyq
411a6efeb5 Fix Searching Closed Tickets 2025-06-26 17:05:38 -04:00
johnnyq
ffad0cdec9 Fix broken Documents in the Client Portal due to regression with splitting document templates 2025-06-26 16:45:00 -04:00
johnnyq
ca519fe4e1 Fix Adding a vendor from a template and adding country code to vendor template 2025-06-26 16:27:11 -04:00
johnnyq
6b0855cff8 Fix Exports while in Client Overview some were failing due to missing client_id var and some were not logging the client_id if in the client section 2025-06-26 15:49:49 -04:00
wrongecho
e8a3309822 Fix project search not filtering completed projects 2025-06-26 13:13:25 +01:00
wrongecho
ce8c4dfc4c Add debug check for php-zip 2025-06-25 20:40:08 +01:00
wrongecho
a1b99c46c7 Client Portal - Don't allow tech contacts to edit themselves 2025-06-25 09:44:57 +01:00
wrongecho
1ee50fdfde Ticket merging - Hyperlink the new parent ticket in the internal comment for quicker access 2025-06-25 09:23:02 +01:00
wrongecho
05850f61d2 Fix php debug message 2025-06-25 09:11:19 +01:00
johnnyq
12d8c8e959 Fix Overlapping logo with company information in PDF Invoice and Quote 2025-06-23 19:43:41 -04:00
johnnyq
2b4933bd5c Do not Update ticket status when scheduling or cancelling a tickets schedule 2025-06-23 18:15:05 -04:00
Marcus Hill
500e713764 clarify error message wording 2025-06-22 10:01:10 +01:00
Marcus Hill
2ca50776ec Ticket
- Move ticket metadata/details into their own card (source, category, time, closure details, etc)
- Start adding support for ticket_quote
2025-06-22 10:00:53 +01:00
Marcus Hill
bd31513e5d Add db fields for ticket_quote 2025-06-22 09:59:23 +01:00
Johnny
6df04390bb Merge pull request #1224 from itflow-org/develop
25.6.1 Release
2025-06-22 01:52:33 -04:00
johnnyq
49511276fd Bump Release to 25.06.1 - updated changelog 2025-06-21 18:42:19 -04:00
johnnyq
c4e91df1ed Fix Regression that broke setup, set company currency default to USD if not set 2025-06-21 18:33:48 -04:00
2998 changed files with 196488 additions and 27619 deletions

18
.gitignore vendored
View File

@@ -26,4 +26,22 @@ xcustom/*
!xcustom/readme.php
post/xcustom
!post/xcustom/readme.php
admin/custom/*
!admin/custom/readme.php
agent/custom/*
!agent/custom/readme.php
client/custom/*
!client/custom/readme.php
guest/custom/*
!guest/custom/readme.php
cron/custom/*
!cron/custom/readme.php
scripts/custom/*
!scripts/custom/readme.php
setup/custom/*
!setup/custom/readme.php
api/v1/custom/*
!api/v1/custom/readme.php
.zed

View File

@@ -2,6 +2,197 @@
This file documents all notable changes made to ITFlow.
## [25.10.1]
- Deprecation Notice: `/scripts/cron_mail_queue.php` , `/scripts/cron_ticket_email_parser.php` , `/scripts/cron.php` `/scripts/cron_domain_refresher.php`, `/scripts/cron_certificate_refresher.php` are being phased out. Please transition to `/cron/mail_queue.php` , `/cron/ticket_email_parser.php`, `/cron/cron.php`, `/cron/domain_refresher.php`, `/cron/certificate_refresher.php` These older scripts will be removed in the November 25.11 release—update accordingly. 25.10.1 installs have the script already configured.
### Fixes
- Fix regression missing custom Favicon.
- Update SMTP and IMAP provider to allow for empty strings, empty means disabled.
- Fix Client portal Microsoft SSO Logins.
- Fix regression in Vendor Templates.
- Fix refression in some broken links from user to agent.
- Fix Project edit.
- Prevent open redirects upon agent login.
- Fix regression on switching to Webklex IMAP to allow for no SSL/TLS in IMAP.
- Fix Setup Redirect not behaving properly when setup hasnt been performed.
- Added Server Document Root Var to several includes, headers, footers files to allow includes from deeper directory strutures such as the new custom directories.
- Fix edit contact in contact details.
- Add .htaccess to /cron/.
### Added / Changed
- Support for HTML Signatures.
- Add Edit Project Functionality in a ticket.
- Added more custom locations: /cron/custom/, /scripts/custom/, /api/v1/custom/, /setup/custom/.
- Copied `/scripts/cron.php` `/scripts/cron_domain_refresher.php`, `/scripts/cron_certificate_refresher.php` to `/cron/cron.php`, `/cron/domain_refresher.php`, `/cron/certificate_refresher.php`. See Above!
- Signatures is now handled in post ticket reply on Public Comments only.
## [25.10]
### Breaking Changes
- Renamed `/user/` directory to `/agent/`.
- Deprecation Notice: `/scripts/cron_mail_queue.php` and `/scripts/cron_ticket_email_parser.php` are being phased out. Please transition to `/cron/mail_queue.php` and `/cron/ticket_email_parser.php`. These older scripts will be removed in the November release—update accordingly. New Installs via the script will have this already configured.
- Custom is working now. Custom code should be placed in /admin/custom/ , /agent/custom/ , /client/custom/ /guest/custom/
We will provide example code with directory structure for each custom directory a week after this release.
### Fixes
- Resolved issue with "Restore from Setup" not functioning correctly.
- Corrected asset name display in logs and flash messages when editing an asset in a ticket.
- Fixed Payment Provider Threshold not being applied.
- Fixed issue where Threshold setting was not saving properly.
- Various minor fixes for Payment Provider issues.
- Removed leads from the client selection list in the "New Ticket" modal.
- Fixed issues with the MFA modal.
- Resolved MFA enforcement bugs.
- Fixed KeepAlive functionality to maintain user sessions longer.
- Fixed multiple broken links caused by the `/user/` to `/agent/` path migration.
- Fixed Custom code directories.
### Added / Changed
- Removed "ACH" as a payment method; added "Bank Transfer" instead.
- Replaced relative paths with absolute paths for web assets.
- Tickets can now be resolved via the API.
- Added a filter for Archived Users and an option to restore them.
- Introduced a modal when archiving users, allowing reassignment of open and recurring tickets to another agent.
- Improved logic for determining the index/root page.
- Added "Assigned Agent" column for recurring tickets.
- Introduced "Additional Assets" option when editing assets in tickets; modal now uses the updated AJAX method.
- Added Gibraltar to the list of supported countries.
- Added Custom Link Option for the Admin Nav.
- Added Custom Link Option for the Reports Nav.
### Other notes
- Major releases will happen on the first week of every Month.
## [25.09.2]
### Fixes
- Fix Payment Method Select box in Revenue.
- Remove Extra Feeback Wording When Invoice Sends.
- Updated all CSV exports to use escape parameters.
- Fix Missing First row on Asset interface export.
- Fix Edit User not working due to incorrect modal footer path.
- Fix Add Certificate breaking due spelling on function.
- Update all CSV Exports to include company name or client name depending on when its being exported from.
- Introduced new function sanitize_filename and implmented it in all exports.
- Spruced up UI/UX Saved Paymented section in Client Portal.
- Fix add Payment Link in client portal recurring invoice section.
- Better Logic handling for default page redirect.
### Features
- Introduced new Beta mail parser cron using webklex imap library instead of php-imap as this is deprecated --Not Enabled on existing installs, only new installs.
- Introduced Beta support for OAUTH2 Authentication for Microsoft 365 and Google Workspaces for both incoming ticket parsing and outgoing email but must use new mail parser and mail queue for this to work, and requires changing the cron jobs: scripts/cron_mail_queue.php to cron/mail_queue.php and scripts/cron_ticket_email_parser.php to cron/ticket_email_parser.php.
---
## [25.09.1]
### Fixes
- **Web Installer**: Resolved issue with broken installer caused by incorrect database schema file name.
- Hide the "Add Credit" button as the feature is not fully implemented yet.
- Corrected long invoice/quote notes that were overlapping with the footer in PDF exports.
- Fixed AI settings not appearing in the Admin Menu when the Billing module was disabled.
- Enabled wrapping of client tags when they are too long.
- Fixed an issue where AI was not functioning correctly.
- Removed extra spacing between the contact name and icon in the Ticket Details contact card.
### Features
- Redesigned **AI Ticket Summary**, now divided into 3 sections: Main Issue, Actions Taken, and Resolution/Next Steps.
- Updated the **AI Ticket Summary** prompt to include ticket status, reply author, source, category, and priority.
---
## [25.09]
***BACK UP*** before updating.
---
### Breaking Changes and Notes
- We strongly recommend updating from the command line, however if performed via the webui and after performed it will return a 404. thats normal as the directory structure has changed, just close your browser then log back in then go back to update to perform the many database updates.
- This is a major release with significant changes. While the community has done a great job identifying bugs, some may still remain — continued testing is encouraged.
- All AI settings will be **reset** and must be reconfigured using the new AI provider backend.
- The `xcustom` directory has been renamed to `custom`. All custom libraries and post-processing scripts should now be placed here.
---
### Added / Changed
- Numerous UI improvements and refinements across the application.
- Enhanced visual clarity by thickening the left border on ticket comments to help identify comment types.
- Ticket details UI redesigned to use less space at the top of the screen.
- Introduced tracking for the **first response date/time** on tickets.
- New reporting feature: **Average time to first response** on tickets.
- Stripe integration rebuilt using the new **payment provider backend**.
- Clients can now save and manage **multiple payment methods**.
- Support for selecting saved cards for **recurring invoices** in both the client and agent portals.
- Initial database structure and logic added for **credit management** (feature not yet enabled).
- Major **backend directory restructuring**.
- Introduced **stock/inventory management**, including a stock ledger backend.
- Stock quantities now update automatically when invoice items are added or removed.
- Invoice autocomplete now includes: **name, description, price, tax, stock levels**, and links `product_id` to `item_id`.
- Added a **category filter** to invoices.
- Linked stock to related expenses.
- New product fields: **location, code, and type**.
- Products now separated into two types: **Service** and **Product**.
- **Dark mode** introduced.
- Projects: Now support linking **closed tickets**.
- Clients: Added bulk actions for tags, referral source, industry, hourly rate, email, archive, and restore.
- Invoices: Bulk action added to **assign categories**.
- Assets: New `client_uri` field, visible in both the agent and client portals.
- Client Portal: Clients can now **select an asset** during ticket creation.
- Client Portal: Company logo now **displays in the header**.
- Client Portal: Dashboard cards are now **clickable** for more detail.
- Assets: Option added to include **MAC Address** in additional columns.
- Asset Interface: Bulk actions added — set DHCP, network type, and delete.
- API:
- Added `/location` endpoint.
- Ticket content now supports **HTML formatting**.
- New option to filter and display **500 records per page** in the footer.
- Payment methods are now treated as a **separate entity** instead of being grouped under categories.
- Updated libraries:
- **TinyMCE**
- **Chart.js** (major upgrade)
- **DataTables**
- **Bootstrap**
- **FullCalendar**
- **php-stripe**
---
### Fixed
- Several security vulnerabilities patched.
- Ticket status is no longer updated when scheduling.
- Client Portal: Tech contacts can no longer edit their own details.
- Fixed overlapping logo issue in Invoice/Quote PDF exports.
- Refactored `check_login.php` into multiple files for modular login functionality.
- Removed redundant logging comments for redirects.
- Renamed `get_settings.php` to `load_global_settings.php`.
- Simplified syntax for `ajax-modal` and updated usage throughout the app.
- Fixed issue where primary contact text wasnt displaying.
- Corrected client **Net Terms** display.
- Fixed logic for recurring expense **next run date**.
- Resolved broken **IMAP test button**.
- Archived clients can no longer log into the portal.
- Searching closed tickets no longer reverts to open tickets.
- Fixed project search filter not showing completed projects.
- Fixed issue where company logo was not being removed correctly.
- Resolved API bugs:
- Default rate and net terms.
- Contact location.
- Document endpoint.
---
### Developer Updates
- Replaced legacy code with newer functions like `redirect()`, `getFieldById()`, and `flash_alert()`.
- Significantly improved performance of queries used for filter selection boxes.
## [25.06.1]
### Fixed
- Fixed a regression in setup causing it to crash and never complete, due to missing default for currency.
## [25.06]
### Breaking CHANGES

108
admin/ai_model.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
// Default Column Sortby Filter
$sort = "ai_model_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT * FROM ai_models LEFT JOIN ai_providers ON ai_model_ai_provider_id = ai_provider_id ORDER BY $sort $order");
$num_rows = mysqli_num_rows($sql);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot mr-2"></i>AI Models</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addAIModelModal"><i class="fas fa-plus mr-2"></i>Add Model</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
<tr>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_model_name&order=<?php echo $disp; ?>">
Model <?php if ($sort == 'ai_model_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_name&order=<?php echo $disp; ?>">
Provider <?php if ($sort == 'ai_provider_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_model_use_case&order=<?php echo $disp; ?>">
Use Case<?php if ($sort == 'ai_model_use_case') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark">Prompt</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$provider_id = intval($row['ai_provider_id']);
$provider_name = nullable_htmlentities($row['ai_provider_name']);
$model_id = intval($row['ai_model_id']);
$model_name = nullable_htmlentities($row['ai_model_name']);
$use_case = nullable_htmlentities($row['ai_model_use_case']);
$prompt = nl2br(nullable_htmlentities($row['ai_model_prompt']));
?>
<tr>
<td>
<a class="text-dark text-bold ajax-modal" href="#"
data-modal-url="modals/ai/ai_model_edit.php?id=<?= $model_id ?>">
<?php echo $model_name; ?>
</a>
</td>
<td><?php echo $provider_name; ?></td>
<td><?php echo $use_case; ?></td>
<td><?php echo $prompt; ?></td>
<td>
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/ai/ai_model_edit.php?id=<?= $model_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_ai_model=<?php echo $model_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>
</td>
</tr>
<?php
}
if ($num_rows == 0) {
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "modals/ai/ai_model_add.php";
require_once "../includes/footer.php";

109
admin/ai_provider.php Normal file
View File

@@ -0,0 +1,109 @@
<?php
// Default Column Sortby Filter
$sort = "ai_provider_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT * FROM ai_providers ORDER BY $sort $order");
$num_rows = mysqli_num_rows($sql);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot mr-2"></i>AI Providers</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addAIProviderModal"><i class="fas fa-plus mr-2"></i>Add Provider</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
<tr>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_name&order=<?php echo $disp; ?>">
Provider <?php if ($sort == 'ai_provider_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_api_url&order=<?php echo $disp; ?>">
URL <?php if ($sort == 'ai_provider_api_url') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_api_key&order=<?php echo $disp; ?>">
Key <?php if ($sort == 'ai_provider_api_key') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark">Models</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$provider_id = intval($row['ai_provider_id']);
$provider_name = nullable_htmlentities($row['ai_provider_name']);
$url = nullable_htmlentities($row['ai_provider_api_url']);
$key = nullable_htmlentities($row['ai_provider_api_key']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('ai_model_id') AS ai_model_count FROM ai_models WHERE ai_model_ai_provider_id = $provider_id"));
$ai_model_count = intval($row['ai_model_count']);
?>
<tr>
<td>
<a class="text-dark text-bold ajax-modal" href="#"
data-modal-url="modals/ai/ai_provider_edit.php?id=<?= $provider_id ?>">
<?php echo $provider_name; ?>
</a>
</td>
<td><?php echo $url; ?></td>
<td><?php echo $key; ?></td>
<td><?php echo $ai_model_count; ?></td>
<td>
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/ai/ai_provider_edit.php?id=<?= $provider_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_ai_provider=<?php echo $provider_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>
</td>
</tr>
<?php
}
if ($num_rows == 0) {
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "modals/ai/ai_provider_add.php";
require_once "../includes/footer.php";

View File

@@ -162,15 +162,15 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</form>
</div>
<?php require_once "includes/filter_footer.php";
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<script src="js/bulk_actions.js"></script>
<script src="../js/bulk_actions.js"></script>
<?php
require_once "modals/admin_api_key_add_modal.php";
require_once "modals/api/api_key_add.php";
require_once "includes/footer.php";
require_once "../includes/footer.php";

View File

@@ -183,10 +183,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</tbody>
</table>
</div>
<?php require_once "includes/filter_footer.php";
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<?php
require_once "includes/footer.php";
require_once "../includes/footer.php";

View File

@@ -266,7 +266,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
if (empty($client_name)) {
$client_name_display = "-";
} else {
$client_name_display = "<a href='client_overview.php?client_id=$client_id'>$client_name</a>";
$client_name_display = "<a href='../agent/client_overview.php?client_id=$client_id'>$client_name</a>";
}
$log_entity_id = intval($row['log_entity_id']);
@@ -292,11 +292,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</tbody>
</table>
</div>
<?php require_once "includes/filter_footer.php";
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<?php
require_once "includes/footer.php";
require_once "../includes/footer.php";

View File

@@ -34,5 +34,5 @@ require_once "includes/inc_all_admin.php";
</div>
<?php
require_once "includes/footer.php";
require_once "../includes/footer.php";

View File

@@ -34,7 +34,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
if (!isset($_GET['archived'])) {
?>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addCategoryModal"><i
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/category/category_add.php?category=<?= nullable_htmlentities($category) ?>"><i
class="fas fa-plus mr-2"></i>New <?php echo nullable_htmlentities($category); ?> Category</button>
</div>
<?php
@@ -77,13 +77,6 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
} else {
echo 'btn-default';
} ?>">Referral</a>
<a href="?category=Payment Method"
class="btn <?php if ($category == 'Payment Method') {
echo 'btn-primary';
} else {
echo 'btn-default';
} ?>">Payment
Method</a>
<a href="?category=Ticket"
class="btn <?php if ($category == 'Ticket') {
echo 'btn-primary';
@@ -126,11 +119,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
?>
<tr>
<td>
<a class="text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_category_edit.php"
data-ajax-id="<?php echo $category_id; ?>"
>
<a class="text-dark ajax-modal" href="#"
data-modal-url="modals/category/category_edit.php?id=<?= $category_id ?>">
<?php echo $category_name; ?>
</a>
</td>
@@ -155,11 +145,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php
} else {
?>
<a class="dropdown-item" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_category_edit.php"
data-ajax-id="<?php echo $category_id; ?>"
>
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/category/category_edit.php?id=<?= $category_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<a class="dropdown-item text-danger confirm-link"
@@ -183,11 +170,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</tbody>
</table>
</div>
<?php require_once "includes/filter_footer.php";
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<?php
require_once "modals/admin_category_add_modal.php";
require_once "includes/footer.php";
require_once "../includes/footer.php";

8
admin/custom/readme.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
/*
- Custom Pages -
If you wish to add custom pages to ITFlow, add them to this directory"
Link to Documentation for File Directory Structure and examples
*/

View File

@@ -96,16 +96,17 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$custom_link_location_display = "Top Nav";
} elseif ($custom_link_location == 3) {
$custom_link_location_display = "Client Portal Nav";
} elseif ($custom_link_location == 4) {
$custom_link_location_display = "Admin Nav";
} elseif ($custom_link_location == 5) {
$custom_link_location_display = "Reports Nav";
}
?>
<tr>
<td>
<a href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_custom_link_edit.php"
data-ajax-id="<?php echo $custom_link_id; ?>"
>
<a class="ajax-modal" href="#"
data-modal-url="modals/custom_link/custom_link_edit.php?id=<?= $custom_link_id ?>">
<i class="fa fa-fw fa-<?php echo $custom_link_icon; ?> mr-2"></i><?php echo $custom_link_name;?>
</a>
</td>
@@ -118,7 +119,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" data-toggle="ajax-modal" data-ajax-url="ajax/ajax_custom_link_edit.php" data-ajax-id="<?php echo $custom_link_id; ?>">
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/custom_link/custom_link_edit.php?id=<?= $custom_link_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
@@ -138,11 +139,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</tbody>
</table>
</div>
<?php require_once "includes/filter_footer.php";
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<?php
require_once "modals/admin_custom_link_add_modal.php";
require_once "includes/footer.php";
require_once "modals/custom_link/custom_link_add.php";
require_once "../includes/footer.php";

View File

@@ -2776,7 +2776,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
//Dropping patch panel as a patch panel can be documented as an asset with interfaces.
mysqli_query($mysqli, "DROP TABLE `patch_panel_ports`");
mysqli_query($mysqli, "DROP TABLE `patch_panels`");
mysqli_query($mysqli, "RENAME TABLE `events` TO `calendar_events`");
mysqli_query($mysqli, "RENAME TABLE `event_attendees` TO `calendar_event_attendees`");
@@ -2957,7 +2957,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
ALTER TABLE `calendar_events`
ADD FOREIGN KEY (`event_calendar_id`) REFERENCES `calendars`(`calendar_id`) ON DELETE CASCADE
");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.0.3'");
}
@@ -2974,7 +2974,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
ALTER TABLE `certificate_history`
ADD FOREIGN KEY (`certificate_history_certificate_id`) REFERENCES `certificates`(`certificate_id`) ON DELETE CASCADE
");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.0.4'");
}
@@ -3335,14 +3335,14 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
ADD FOREIGN KEY (`vendor_id`) REFERENCES `vendors`(`vendor_id`) ON DELETE CASCADE,
ADD FOREIGN KEY (`file_id`) REFERENCES `files`(`file_id`) ON DELETE CASCADE
");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.0.5'");
}
if (CURRENT_DATABASE_VERSION == '2.0.5') {
// CONVERT All tables TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci
$tables = [
'accounts', 'api_keys', 'app_logs', 'asset_credentials', 'asset_custom', 'asset_documents',
'asset_files', 'asset_history', 'asset_interface_links', 'asset_interfaces', 'asset_notes', 'assets',
@@ -3381,14 +3381,14 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
}
if (CURRENT_DATABASE_VERSION == '2.0.7') {
mysqli_query($mysqli, "ALTER TABLE `files` DROP `file_hash`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.0.8'");
}
if (CURRENT_DATABASE_VERSION == '2.0.8') {
mysqli_query($mysqli, "ALTER TABLE `files` DROP `file_has_thumbnail`");
mysqli_query($mysqli, "ALTER TABLE `files` DROP `file_has_preview`");
mysqli_query($mysqli, "ALTER TABLE `files` DROP `file_asset_id`");
@@ -3397,7 +3397,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
}
if (CURRENT_DATABASE_VERSION == '2.0.9') {
mysqli_query($mysqli, "ALTER TABLE `contacts` ADD `contact_phone_country_code` VARCHAR(10) DEFAULT 1 AFTER `contact_email`");
mysqli_query($mysqli, "ALTER TABLE `contacts` ADD `contact_mobile_country_code` VARCHAR(10) DEFAULT 1 AFTER `contact_extension`");
@@ -3425,7 +3425,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
}
if (CURRENT_DATABASE_VERSION == '2.1.2') {
// Update country_code to NULL for `contacts` table
mysqli_query($mysqli, "ALTER TABLE `contacts` MODIFY `contact_phone_country_code` VARCHAR(10) DEFAULT NULL");
mysqli_query($mysqli, "ALTER TABLE `contacts` MODIFY `contact_mobile_country_code` VARCHAR(10) DEFAULT NULL");
@@ -3460,7 +3460,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
if (CURRENT_DATABASE_VERSION == '2.1.3') {
mysqli_query($mysqli, "ALTER TABLE `client_stripe` ADD `stripe_pm_details` VARCHAR(200) DEFAULT NULL AFTER `stripe_pm`");
mysqli_query($mysqli, "ALTER TABLE `client_stripe` ADD `stripe_pm_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `stripe_pm_details`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.1.4'");
}
@@ -3670,13 +3670,375 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.1.9'");
}
// if (CURRENT_DATABASE_VERSION == '2.1.9') {
// // Insert queries here required to update to DB version 2.2.0
if (CURRENT_DATABASE_VERSION == '2.1.9') {
mysqli_query($mysqli, "ALTER TABLE `companies` MODIFY `company_currency` VARCHAR(200) DEFAULT 'USD'");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.0'");
}
if (CURRENT_DATABASE_VERSION == '2.2.0') {
mysqli_query($mysqli, "ALTER TABLE `tickets` ADD `ticket_quote_id` INT(11) NOT NULL DEFAULT 0 AFTER `ticket_asset_id`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.1'");
}
if (CURRENT_DATABASE_VERSION == '2.2.1') {
mysqli_query($mysqli, "CREATE TABLE `ai_providers` (
`ai_provider_id` INT(11) NOT NULL AUTO_INCREMENT,
`ai_provider_name` VARCHAR(200) NOT NULL,
`ai_provider_api_url` VARCHAR(200) NOT NULL,
`ai_provider_api_key` VARCHAR(200) DEFAULT NULL,
`ai_provider_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`ai_provider_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`ai_provider_id`)
)");
mysqli_query($mysqli, "
CREATE TABLE `ai_models` (
`ai_model_id` INT(11) NOT NULL AUTO_INCREMENT,
`ai_model_name` VARCHAR(200) NOT NULL,
`ai_model_prompt` TEXT DEFAULT NULL,
`ai_model_use_case` VARCHAR(200) DEFAULT NULL,
`ai_model_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`ai_model_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP,
`ai_model_ai_provider_id` INT(11) NOT NULL,
PRIMARY KEY (`ai_model_id`),
FOREIGN KEY (`ai_model_ai_provider_id`)
REFERENCES `ai_providers`(`ai_provider_id`)
ON DELETE CASCADE
)
");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.2'");
}
if (CURRENT_DATABASE_VERSION == '2.2.2') {
mysqli_query($mysqli, "CREATE TABLE `payment_methods` (
`payment_method_id` INT(11) NOT NULL AUTO_INCREMENT,
`payment_method_name` VARCHAR(200) NOT NULL,
`payment_method_description` VARCHAR(250) DEFAULT NULL,
`payment_method_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`payment_method_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`payment_method_id`)
)");
mysqli_query($mysqli, "CREATE TABLE `payment_providers` (
`payment_provider_id` INT(11) NOT NULL AUTO_INCREMENT,
`payment_provider_name` VARCHAR(200) NOT NULL,
`payment_provider_description` VARCHAR(250) DEFAULT NULL,
`payment_provider_public_key` VARCHAR(250) DEFAULT NULL,
`payment_provider_private_key` VARCHAR(250) DEFAULT NULL,
`payment_provider_threshold` DECIMAL(15,2) DEFAULT NULL,
`payment_provider_active` TINYINT(1) NOT NULL DEFAULT 1,
`payment_provider_account` INT(11) NOT NULL,
`payment_provider_expense_vendor` INT(11) NOT NULL DEFAULT 0,
`payment_provider_expense_category` INT(11) NOT NULL DEFAULT 0,
`payment_provider_expense_percentage_fee` DECIMAL(4,4) DEFAULT NULL,
`payment_provider_expense_flat_fee` DECIMAL(15,2) DEFAULT NULL,
`payment_provider_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`payment_provider_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`payment_provider_id`)
)");
mysqli_query($mysqli, "CREATE TABLE `client_saved_payment_methods` (
`saved_payment_id` INT(11) NOT NULL AUTO_INCREMENT,
`saved_payment_provider_method` VARCHAR(200) NOT NULL,
`saved_payment_description` VARCHAR(200) DEFAULT NULL,
`saved_payment_client_id` INT(11) NOT NULL,
`saved_payment_provider_id` INT(11) NOT NULL,
`saved_payment_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`saved_payment_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`saved_payment_id`),
FOREIGN KEY (`saved_payment_client_id`) REFERENCES clients(`client_id`) ON DELETE CASCADE,
FOREIGN KEY (`saved_payment_provider_id`) REFERENCES payment_providers(`payment_provider_id`) ON DELETE CASCADE
)");
mysqli_query($mysqli, "CREATE TABLE `client_payment_provider` (
`client_id` INT(11) NOT NULL,
`payment_provider_id` INT(11) NOT NULL,
`payment_provider_client` VARCHAR(200) NOT NULL,
`client_payment_provider_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`client_id`, `payment_provider_id`),
FOREIGN KEY (`client_id`) REFERENCES clients(`client_id`) ON DELETE CASCADE,
FOREIGN KEY (`payment_provider_id`) REFERENCES payment_providers(`payment_provider_id`) ON DELETE CASCADE
)");
mysqli_query($mysqli, "ALTER TABLE `recurring_payments` ADD `recurring_payment_saved_payment_id` INT(11) DEFAULT NULL AFTER `recurring_payment_recurring_invoice_id`");
mysqli_query($mysqli, "ALTER TABLE `recurring_payments` ADD CONSTRAINT `fk_recurring_saved_payment` FOREIGN KEY (`recurring_payment_saved_payment_id`) REFERENCES `client_saved_payment_methods`(`saved_payment_id`) ON DELETE CASCADE");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.3'");
}
if (CURRENT_DATABASE_VERSION == '2.2.3') {
mysqli_query($mysqli, "CREATE TABLE `credits` (
`credit_id` INT(11) NOT NULL AUTO_INCREMENT,
`credit_amount` DECIMAL(15,2) NOT NULL,
`credit_reference` VARCHAR(250) DEFAULT NULL,
`credit_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(),
`credit_created_by` INT(11) NOT NULL,
`credit_expire_at` DATE DEFAULT NULL,
`credit_client_id` INT(11) NOT NULL,
PRIMARY KEY (`credit_id`)
)");
mysqli_query($mysqli, "ALTER TABLE `invoices` ADD `invoice_credit_amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00 AFTER `invoice_discount_amount`");
mysqli_query($mysqli, "CREATE TABLE `discount_codes` (
`discount_code_id` INT(11) NOT NULL AUTO_INCREMENT,
`discount_code_description` VARCHAR(250) DEFAULT NULL,
`discount_code_amount` DECIMAL(15,2) NOT NULL,
`discount_code` VARCHAR(200) NOT NULL,
`discount_code_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(),
`discount_code_created_by` INT(11) NOT NULL,
`discount_code_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP,
`discount_code_archived_at` DATETIME NULL DEFAULT NULL,
`discount_code_expire_at` DATE DEFAULT NULL,
PRIMARY KEY (`discount_code_id`)
)");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.4'");
}
if (CURRENT_DATABASE_VERSION == '2.2.4') {
mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_theme_dark` TINYINT(1) NOT NULL DEFAULT 0 AFTER `config_theme`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.5'");
}
if (CURRENT_DATABASE_VERSION == '2.2.5') {
mysqli_query($mysqli, "ALTER TABLE `assets` ADD `asset_uri_client` VARCHAR(500) NULL DEFAULT NULL AFTER `asset_uri_2`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.6'");
}
if (CURRENT_DATABASE_VERSION == '2.2.6') {
mysqli_query($mysqli, "ALTER TABLE `credits` DROP `credit_reference`");
mysqli_query($mysqli, "ALTER TABLE `credits` ADD `credit_type` ENUM('prepaid', 'manual', 'refund', 'promotion', 'usage') NOT NULL DEFAULT 'manual' AFTER `credit_amount`");
mysqli_query($mysqli, "ALTER TABLE `credits` ADD `credit_note` TEXT NULL DEFAULT NULL AFTER `credit_type`");
mysqli_query($mysqli, "ALTER TABLE `credits` ADD `credit_invoice_id` INT(11) NULL DEFAULT NULL AFTER `credit_expire_at`");
mysqli_query($mysqli, "ALTER TABLE `credits` ADD INDEX (`credit_client_id`)");
mysqli_query($mysqli, "ALTER TABLE `credits` ADD INDEX (`credit_invoice_id`)");
mysqli_query($mysqli, "ALTER TABLE `credits` ADD INDEX (`credit_created_at`)");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.7'");
}
if (CURRENT_DATABASE_VERSION == '2.2.7') {
mysqli_query($mysqli, "ALTER TABLE `user_settings` ADD `user_config_theme_dark` TINYINT(1) NOT NULL DEFAULT 0 AFTER `user_config_signature`");
mysqli_query($mysqli, "ALTER TABLE `settings` DROP `config_theme_dark`");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.8'");
}
if (CURRENT_DATABASE_VERSION == '2.2.8') {
mysqli_query($mysqli, "ALTER TABLE `products` ADD `product_type` ENUM('service', 'product') NOT NULL DEFAULT 'service' AFTER `product_name`");
mysqli_query($mysqli, "ALTER TABLE `products` ADD `product_code` VARCHAR(200) DEFAULT NULL AFTER `product_description`");
mysqli_query($mysqli, "ALTER TABLE `products` ADD `product_location` VARCHAR(250) DEFAULT NULL AFTER `product_code`");
mysqli_query($mysqli, "CREATE TABLE `product_stock` (
`stock_id` INT(11) NOT NULL AUTO_INCREMENT,
`stock_qty` INT(11) NOT NULL,
`stock_note` TEXT DEFAULT NULL,
`stock_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(),
`stock_expense_id` INT(11) DEFAULT NULL,
`stock_item_id` INT(11) DEFAULT NULL,
`stock_product_id` INT(11) NOT NULL,
PRIMARY KEY (`stock_id`)
)");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.9'");
}
if (CURRENT_DATABASE_VERSION == '2.2.9') {
// Migrate Stripe Settings over to new Tables
// Get Current Stripe Settings
$sql_stripe_settings = mysqli_query($mysqli, "SELECT * FROM settings WHERE company_id = 1");
$row = mysqli_fetch_array($sql_stripe_settings);
$config_stripe_enable = intval($row['config_stripe_enable']);
if ($config_stripe_enable === 1) {
$config_stripe_publishable = mysqli_real_escape_string($mysqli, $row['config_stripe_publishable']);
$config_stripe_secret = mysqli_real_escape_string($mysqli, $row['config_stripe_secret']);
$config_stripe_account = intval($row['config_stripe_account']);
$config_stripe_expense_vendor = intval($row['config_stripe_expense_vendor']);
$config_stripe_expense_category = intval($row['config_stripe_expense_category']);
$config_stripe_percentage_fee = floatval($row['config_stripe_percentage_fee']);
$config_stripe_flat_fee = floatval($row['config_stripe_flat_fee']);
mysqli_query($mysqli,"INSERT INTO payment_providers SET
payment_provider_name = 'Stripe',
payment_provider_public_key = '$config_stripe_publishable',
payment_provider_private_key = '$config_stripe_secret',
payment_provider_account = $config_stripe_account,
payment_provider_expense_vendor = $config_stripe_expense_vendor,
payment_provider_expense_category = $config_stripe_expense_category,
payment_provider_expense_percentage_fee = $config_stripe_percentage_fee,
payment_provider_expense_flat_fee = $config_stripe_flat_fee"
);
$provider_id = mysqli_insert_id($mysqli);
// Migrate Clients and Payment Method over
$sql_stripe_clients = mysqli_query($mysqli, "SELECT * FROM client_stripe WHERE stripe_pm IS NOT NULL AND stripe_pm != ''");
while ($row = mysqli_fetch_array($sql_stripe_clients)) {
$client_id = intval($row['client_id']);
$stripe_id = mysqli_real_escape_string($mysqli, $row['stripe_id']);
$stripe_pm = mysqli_real_escape_string($mysqli, $row['stripe_pm']);
$stripe_pm_details = mysqli_real_escape_string($mysqli, $row['stripe_pm_details'] ?? 'Saved Card');
mysqli_query($mysqli,"INSERT INTO client_payment_provider SET
client_id = $client_id,
payment_provider_id = $provider_id,
payment_provider_client = '$stripe_id'"
);
mysqli_query($mysqli,"INSERT INTO client_saved_payment_methods SET
saved_payment_provider_method = '$stripe_pm',
saved_payment_description = '$stripe_pm_details',
saved_payment_client_id = $client_id,
saved_payment_provider_id = $provider_id"
);
}
}
// Get Stripe provider id
$res = mysqli_query($mysqli, "
SELECT payment_provider_id
FROM payment_providers
WHERE payment_provider_name = 'Stripe'
ORDER BY payment_provider_id DESC
LIMIT 1
");
$stripe = mysqli_fetch_assoc($res);
$stripe_provider_id = intval($stripe['payment_provider_id']);
// Correct mapping: RP -> Recurring Invoice -> Client -> Client's Stripe saved method
mysqli_query($mysqli, "
UPDATE recurring_payments rp
INNER JOIN recurring_invoices ri
ON ri.recurring_invoice_id = rp.recurring_payment_recurring_invoice_id
INNER JOIN client_saved_payment_methods spm
ON spm.saved_payment_client_id = ri.recurring_invoice_client_id
AND spm.saved_payment_provider_id = $stripe_provider_id
SET
rp.recurring_payment_method = 'Credit Card',
rp.recurring_payment_saved_payment_id = spm.saved_payment_id
WHERE rp.recurring_payment_method = 'Stripe'
");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.0'");
}
if (CURRENT_DATABASE_VERSION == '2.3.0') {
// Migrate Payment Methods from Categories Table to new payment_methods table
$sql_categories = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Payment Method' AND category_name != 'Stripe' AND category_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_categories)) {
$category_name = sanitizeInput($row['category_name']);
mysqli_query($mysqli,"INSERT INTO payment_methods SET payment_method_name = '$category_name'");
}
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.1'");
}
if (CURRENT_DATABASE_VERSION == '2.3.1') {
// Delete all Recurring Payments that are Stripe
mysqli_query($mysqli, "DELETE FROM recurring_payments WHERE recurring_payment_method = 'Stripe'");
// Delete Stripe Specific ITFlow Client Stripe Client Relationship Table
mysqli_query($mysqli, "DROP TABLE client_stripe");
// Delete Unused Stripe and AI Settings now in their own tables
mysqli_query($mysqli, "ALTER TABLE `settings`
DROP `config_stripe_enable`,
DROP `config_stripe_publishable`,
DROP `config_stripe_secret`,
DROP `config_stripe_account`,
DROP `config_stripe_expense_vendor`,
DROP `config_stripe_expense_category`,
DROP `config_stripe_percentage_fee`,
DROP `config_stripe_flat_fee`,
DROP `config_ai_enable`,
DROP `config_ai_provider`,
DROP `config_ai_model`,
DROP `config_ai_url`,
DROP `config_ai_api_key`
");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.2'");
}
if (CURRENT_DATABASE_VERSION == '2.3.2') {
mysqli_query($mysqli, "ALTER TABLE settings
ADD `config_imap_provider` ENUM('standard_imap','google_oauth','microsoft_oauth') NULL DEFAULT NULL AFTER `config_mail_from_name`,
ADD `config_mail_oauth_client_id` VARCHAR(255) NULL AFTER `config_imap_provider`,
ADD `config_mail_oauth_client_secret` VARCHAR(255) NULL AFTER `config_mail_oauth_client_id`,
ADD `config_mail_oauth_tenant_id` VARCHAR(255) NULL AFTER `config_mail_oauth_client_secret`,
ADD `config_mail_oauth_refresh_token` TEXT NULL AFTER `config_mail_oauth_tenant_id`,
ADD `config_mail_oauth_access_token` TEXT NULL AFTER `config_mail_oauth_refresh_token`,
ADD `config_mail_oauth_access_token_expires_at` DATETIME NULL AFTER `config_mail_oauth_access_token`
");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.3'");
}
if (CURRENT_DATABASE_VERSION == '2.3.3') {
mysqli_query($mysqli, "ALTER TABLE settings
ADD `config_smtp_provider` ENUM('standard_smtp','google_oauth','microsoft_oauth') NULL DEFAULT NULL AFTER `config_start_page`
");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.4'");
}
if (CURRENT_DATABASE_VERSION == '2.3.4') {
// Add Software Keys
mysqli_query($mysqli, "CREATE TABLE `software_keys` (
`software_key_id` INT(11) NOT NULL AUTO_INCREMENT,
`software_key` VARCHAR(400) NOT NULL,
`software_key_software_id` INT(11) NOT NULL,
PRIMARY KEY (`software_key_id`),
FOREIGN KEY (`software_key_software_id`) REFERENCES `software`(`software_id`) ON DELETE CASCADE
)");
// Software Key Assignments to Contacts
mysqli_query($mysqli, "CREATE TABLE `software_key_contact_assignments` (
`software_key_id` INT(11) NOT NULL,
`contact_id` INT(11) NOT NULL,
`software_key_assigned_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`software_key_id`, `contact_id`),
FOREIGN KEY (`software_key_id`) REFERENCES `software_keys`(`software_key_id`) ON DELETE CASCADE,
FOREIGN KEY (`contact_id`) REFERENCES `contacts`(`contact_id`) ON DELETE CASCADE
)");
// Software Key Assignments to Assets
mysqli_query($mysqli, "CREATE TABLE `software_key_asset_assignments` (
`software_key_id` INT(11) NOT NULL,
`asset_id` INT(11) NOT NULL,
`software_key_assigned_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`software_key_id`, `asset_id`),
FOREIGN KEY (`software_key_id`) REFERENCES `software_keys`(`software_key_id`) ON DELETE CASCADE,
FOREIGN KEY (`asset_id`) REFERENCES `assets`(`asset_id`) ON DELETE CASCADE
)");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.5'");
}
if (CURRENT_DATABASE_VERSION == '2.3.5') {
mysqli_query($mysqli, "ALTER TABLE `settings` CHANGE `config_smtp_provider` `config_smtp_provider` VARCHAR(200) DEFAULT NULL");
mysqli_query($mysqli, "ALTER TABLE `settings` CHANGE `config_imap_provider` `config_imap_provider` VARCHAR(200) DEFAULT NULL");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.6'");
}
// if (CURRENT_DATABASE_VERSION == '2.3.5') {
// // Insert queries here required to update to DB version 2.3.5
// // Then, update the database to the next sequential version
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.0'");
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.6'");
// }
} else {
// Up-to-date
}

View File

@@ -1,8 +1,8 @@
<?php
require_once "includes/inc_all_admin.php";
require_once "includes/database_version.php";
require_once "config.php";
require_once "../includes/database_version.php";
require_once "../config.php";
$checks = [];
@@ -53,6 +53,7 @@ $extensions = [
'php-curl' => 'curl',
'php-mbstring' => 'mbstring',
'php-gd' => 'gd',
'php-zip' => 'zip',
];
foreach ($extensions as $name => $ext) {
@@ -245,7 +246,7 @@ $filePermissions[] = [
$uploadsStats = [];
// Define the uploads directory path
$uploadsDir = __DIR__ . '/uploads'; // Adjust the path if needed
$uploadsDir = __DIR__ . '/../uploads'; // Adjust the path if needed
if (is_dir($uploadsDir)) {
// Function to recursively count files and calculate total size
@@ -348,7 +349,7 @@ if ($tablesResult) {
$dbComparison = [];
// Path to the db.sql file
$dbSqlFile = __DIR__ . '/db.sql';
$dbSqlFile = __DIR__ . '/../db.sql';
if (file_exists($dbSqlFile)) {
// Read the db.sql file
@@ -765,5 +766,5 @@ $mysqli->close();
<?php
require_once "includes/footer.php";
require_once "../includes/footer.php";

View File

@@ -79,7 +79,7 @@
<tr>
<td>
<a class="text-bold" href="admin_document_template_details.php?document_template_id=<?php echo $document_template_id; ?>"><i class="fas fa-fw fa-file-alt text-dark"></i> <?php echo $document_template_name; ?></a>
<a class="text-bold" href="document_template_details.php?document_template_id=<?php echo $document_template_id; ?>"><i class="fas fa-fw fa-file-alt text-dark"></i> <?php echo $document_template_name; ?></a>
<div class="mt-1 text-secondary"><?php echo $document_template_description; ?></div>
</td>
<td>
@@ -93,12 +93,9 @@
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"
data-toggle="ajax-modal"
<a class="dropdown-item ajax-modal" href="#"
data-modal-size="xl"
data-ajax-url="ajax/ajax_document_template_edit.php"
data-ajax-id="<?php echo $document_template_id; ?>"
>
data-modal-url="modals/document_template/document_template_edit.php?id=<?= $document_template_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
@@ -120,12 +117,12 @@
</table>
<br>
</div>
<?php require_once "includes/filter_footer.php"; ?>
<?php require_once "../includes/filter_footer.php"; ?>
</div>
</div>
<?php require_once "modals/admin_document_template_add_modal.php"; ?>
<?php require_once "includes/footer.php"; ?>
<?php require_once "modals/document_template/document_template_add.php"; ?>
<?php require_once "../includes/footer.php"; ?>
<script>
$(document).ready(function(){

View File

@@ -4,7 +4,7 @@ require_once "includes/inc_all_admin.php";
//Initialize the HTML Purifier to prevent XSS
require "plugins/htmlpurifier/HTMLPurifier.standalone.php";
require "../plugins/htmlpurifier/HTMLPurifier.standalone.php";
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one
@@ -29,13 +29,13 @@ $document_template_updated_at = nullable_htmlentities($row['document_template_up
<ol class="breadcrumb d-print-none">
<li class="breadcrumb-item">
<a href="clients.php">Home</a>
<a href="../">Home</a>
</li>
<li class="breadcrumb-item">
<a href="admin_user.php">Admin</a>
<a href="users.php">Admin</a>
</li>
<li class="breadcrumb-item">
<a href="admin_document_template.php">Document Templates</a>
<a href="document_template.php">Document Templates</a>
</li>
<li class="breadcrumb-item active"><i class="fas fa-file mr-2"></i><?php echo $document_template_name; ?></li>
</ol>
@@ -46,12 +46,9 @@ $document_template_updated_at = nullable_htmlentities($row['document_template_up
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file mr-2"></i><?php echo $document_template_name; ?></h3>
<div class="card-tools">
<button type="button" class="btn btn-primary"
data-toggle="ajax-modal"
<button type="button" class="btn btn-primary ajax-modal"
data-modal-size="xl"
data-ajax-url="ajax/ajax_document_template_edit.php"
data-ajax-id="<?php echo $document_template_id; ?>"
>
data-modal-url="modals/document_template/document_template_edit.php?id=<?= $document_template_id ?>">
<i class="fas fa-edit mr-2"></i>Edit
</button>
</div>
@@ -61,7 +58,7 @@ $document_template_updated_at = nullable_htmlentities($row['document_template_up
</div>
</div>
<script src="js/pretty_content.js"></script>
<script src="../js/pretty_content.js"></script>
<?php
require_once "includes/footer.php";
require_once "../includes/footer.php";

View File

@@ -13,7 +13,7 @@ require_once "includes/inc_all_admin.php";
<h4>Client Portal SSO via Microsoft Entra</h4>
<div class="form-group">
<label>Identity Provider <small class='text-secondary'>(Currently only works with Microsft Entra)</small></label>
<label>Identity Provider <small class='text-secondary'>(Currently only works with Microsoft Entra ID/AAD)</small></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
@@ -55,4 +55,4 @@ require_once "includes/inc_all_admin.php";
</div>
</div>
<?php require_once "includes/footer.php";
<?php require_once "../includes/footer.php";

View File

@@ -0,0 +1,16 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/config.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/functions.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/check_login.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/page_title.php';
if (!isset($session_is_admin) || !$session_is_admin) {
exit(WORDING_ROLECHECK_FAILED . "<br>Tell your admin: Your role does not have admin access.");
}
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/top_nav.php';
require_once 'includes/side_nav.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/inc_wrapper.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/inc_alert_feedback.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/filter_header.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/app_version.php';

View File

@@ -1,6 +1,6 @@
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-<?php echo nullable_htmlentities($config_theme); ?> d-print-none">
<a class="brand-link pb-1 mt-1" href="clients.php">
<a class="brand-link pb-1 mt-1" href="/agent/<?php echo $config_start_page ?>">
<p class="h6">
<i class="nav-icon fas fa-arrow-left ml-3 mr-2"></i>
<span class="brand-text">
@@ -16,19 +16,19 @@
<ul class="nav nav-pills nav-sidebar flex-column mt-2" data-widget="treeview" data-accordion="false">
<!-- ACCESS Section -->
<li class="nav-item">
<a href="admin_user.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "admin_user.php") {echo "active";} ?>">
<a href="users.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "users.php") {echo "active";} ?>">
<i class="nav-icon fas fa-users"></i>
<p>Users</p>
</a>
</li>
<li class="nav-item">
<a href="admin_role.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "admin_role.php") {echo "active";} ?>">
<a href="roles.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "roles.php") {echo "active";} ?>">
<i class="nav-icon fas fa-user-shield"></i>
<p>Roles</p>
</a>
</li>
<li class="nav-item">
<a href="admin_api.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "admin_api.php") {echo "active";} ?>">
<a href="api_keys.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "api_keys.php") {echo "active";} ?>">
<i class="nav-icon fas fa-key"></i>
<p>API Keys</p>
</a>
@@ -36,35 +36,66 @@
<li class="nav-header">TAGS & CATEGORIES</li>
<li class="nav-item">
<a href="admin_tag.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_tag.php' ? 'active' : ''); ?>">
<a href="tag.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'tag.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-tags"></i>
<p>Tags</p>
</a>
</li>
<li class="nav-item">
<a href="admin_category.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_category.php' ? 'active' : ''); ?>">
<a href="category.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'category.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-list-ul"></i>
<p>Categories</p>
</a>
</li>
<?php if ($config_module_enable_accounting) { ?>
<li class="nav-item">
<a href="admin_tax.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_tax.php' ? 'active' : ''); ?>">
<a href="tax.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'tax.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-balance-scale"></i>
<p>Taxes</p>
</a>
</li>
<li class="nav-item">
<a href="payment_method.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'payment_method.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-hand-holding-usd"></i>
<p>Payment Methods</p>
</a>
</li>
<li class="nav-item">
<a href="payment_provider.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'payment_provider.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-credit-card"></i>
<p>Payment Providers</p>
</a>
</li>
<li class="nav-item">
<a href="saved_payment_method.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'saved_payment_method.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-credit-card"></i>
<p>Saved Payments</p>
</a>
</li>
<?php } ?>
<li class="nav-item">
<a href="ai_provider.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'ai_provider.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-robot"></i>
<p>AI Providers</p>
</a>
</li>
<li class="nav-item">
<a href="ai_model.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'ai_model.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-robot"></i>
<p>AI Models</p>
</a>
</li>
<?php if ($config_module_enable_ticketing) { ?>
<li class="nav-item">
<a href="admin_ticket_status.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_ticket_status.php' ? 'active' : ''); ?>">
<a href="ticket_status.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'ticket_status.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-info-circle"></i>
<p>Ticket Statuses</p>
</a>
</li>
<?php } ?>
<li class="nav-item">
<a href="admin_custom_link.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_custom_link.php' ? 'active' : ''); ?>">
<a href="custom_link.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'custom_link.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-external-link-alt"></i>
<p>Custom Links</p>
</a>
@@ -74,31 +105,31 @@
<li class="nav-header">TEMPLATES</li>
<li class="nav-item">
<a href="admin_project_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['admin_project_template.php', 'admin_project_template_details.php']) ? 'active' : ''); ?>">
<a href="project_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['project_template.php', 'project_template_details.php']) ? 'active' : ''); ?>">
<i class="nav-icon fas fa-project-diagram"></i>
<p>Project Templates</p>
</a>
</li>
<li class="nav-item">
<a href="admin_ticket_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['admin_ticket_template.php', 'admin_ticket_template_details.php']) ? 'active' : ''); ?>">
<a href="ticket_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['ticket_template.php', 'ticket_template_details.php']) ? 'active' : ''); ?>">
<i class="nav-icon fas fa-life-ring"></i>
<p>Ticket Templates</p>
</a>
</li>
<li class="nav-item">
<a href="admin_vendor_template.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_vendor_template.php' ? 'active' : ''); ?>">
<a href="vendor_template.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'vendor_template.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-building"></i>
<p>Vendor Templates</p>
</a>
</li>
<li class="nav-item">
<a href="admin_software_template.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_software_template.php' ? 'active' : ''); ?>">
<a href="software_template.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'software_template.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-rocket"></i>
<p>License Templates</p>
</a>
</li>
<li class="nav-item">
<a href="admin_document_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['admin_document_template.php', 'admin_document_template_details.php']) ? 'active' : ''); ?>">
<a href="document_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['document_template.php', 'document_template_details.php']) ? 'active' : ''); ?>">
<i class="nav-icon fas fa-file"></i>
<p>Document Templates</p>
</a>
@@ -108,44 +139,44 @@
<li class="nav-header">MAINTENANCE</li>
<li class="nav-item">
<a href="admin_mail_queue.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_mail_queue.php' ? 'active' : ''); ?>">
<a href="mail_queue.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'mail_queue.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-mail-bulk"></i>
<p>Mail Queue</p>
</a>
</li>
<li class="nav-item">
<a href="admin_audit_log.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_audit_log.php' ? 'active' : ''); ?>">
<a href="audit_log.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'audit_log.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-history"></i>
<p>Audit Logs</p>
</a>
</li>
<li class="nav-item">
<a href="admin_app_log.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_app_log.php' ? 'active' : ''); ?>">
<a href="app_log.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'app_log.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-history"></i>
<p>App Logs</p>
</a>
</li>
<li class="nav-item">
<a href="admin_backup.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_backup.php' ? 'active' : ''); ?>">
<a href="backup.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'backup.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-cloud-upload-alt"></i>
<p>Backup</p>
</a>
</li>
<li class="nav-item">
<a href="admin_debug.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_debug.php' ? 'active' : ''); ?>">
<a href="debug.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'debug.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-bug"></i>
<p>Debug</p>
</a>
</li>
<li class="nav-item">
<a href="admin_update.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_update.php' ? 'active' : ''); ?>">
<a href="update.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'update.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-download"></i>
<p>Update</p>
</a>
</li>
<!-- SETTINGS Section -->
<li class="nav-item has-treeview mt-2 <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['admin_settings_company.php', 'admin_settings_localization.php', 'admin_settings_theme.php', 'admin_settings_security.php', 'admin_settings_mail.php', 'admin_settings_notification.php', 'admin_settings_default.php', 'admin_settings_invoice.php', 'admin_settings_quote.php', 'admin_settings_online_payment.php', 'admin_settings_online_payment_clients.php', 'admin_settings_project.php', 'admin_settings_ticket.php', 'admin_settings_ai.php', 'admin_identity_provider.php', 'admin_settings_telemetry.php', 'admin_settings_module.php']) ? 'menu-open' : ''); ?>">
<li class="nav-item has-treeview mt-2 <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['settings_company.php', 'settings_localization.php', 'settings_theme.php', 'settings_security.php', 'settings_mail.php', 'settings_notification.php', 'settings_default.php', 'settings_invoice.php', 'settings_quote.php', 'settings_online_payment.php', 'settings_online_payment_clients.php', 'settings_project.php', 'settings_ticket.php', 'settings_ai.php', 'identity_provider.php', 'settings_telemetry.php', 'settings_module.php']) ? 'menu-open' : ''); ?>">
<a href="#" class="nav-link">
<p>
SETTINGS
@@ -154,118 +185,128 @@
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="admin_settings_company.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_company.php' ? 'active' : ''); ?>">
<a href="settings_company.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_company.php' ? 'active' : ''); ?>">
<i class="nav-icon fa fa-briefcase"></i>
<p>Company Details</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_localization.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_localization.php' ? 'active' : ''); ?>">
<a href="settings_localization.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_localization.php' ? 'active' : ''); ?>">
<i class="nav-icon fa fa-globe"></i>
<p>Localization</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_theme.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_theme.php' ? 'active' : ''); ?>">
<a href="settings_theme.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_theme.php' ? 'active' : ''); ?>">
<i class="nav-icon fa fa-paint-brush"></i>
<p>Theme</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_security.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_security.php' ? 'active' : ''); ?>">
<a href="settings_security.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_security.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-shield-alt"></i>
<p>Security</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_mail.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_mail.php' ? 'active' : ''); ?>">
<a href="settings_mail.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_mail.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-envelope"></i>
<p>Mail</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_notification.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_notification.php' ? 'active' : ''); ?>">
<a href="settings_notification.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_notification.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-bell"></i>
<p>Notifications</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_default.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_default.php' ? 'active' : ''); ?>">
<a href="settings_default.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_default.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-cogs"></i>
<p>Defaults</p>
</a>
</li>
<?php if ($config_module_enable_accounting) { ?>
<li class="nav-item">
<a href="admin_settings_invoice.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_invoice.php' ? 'active' : ''); ?>">
<a href="settings_invoice.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_invoice.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-file-invoice"></i>
<p>Invoice</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_quote.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_quote.php' ? 'active' : ''); ?>">
<a href="settings_quote.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_quote.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-comment-dollar"></i>
<p>Quote</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_online_payment.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_online_payment.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-credit-card"></i>
<p>Online Payment</p>
</a>
</li>
<?php if ($config_stripe_enable) { ?>
<li class="nav-item">
<a href="admin_settings_online_payment_clients.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_online_payment_clients.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-credit-card"></i>
<p>Payment/Stripe Clients</p>
</a>
</li>
<?php } ?>
<?php } ?>
<?php if ($config_module_enable_ticketing) { ?>
<li class="nav-item">
<a href="admin_settings_project.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_project.php' ? 'active' : ''); ?>">
<a href="settings_project.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_project.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-project-diagram"></i>
<p>Project</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_ticket.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_ticket.php' ? 'active' : ''); ?>">
<a href="settings_ticket.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_ticket.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-life-ring"></i>
<p>Ticket</p>
</a>
</li>
<?php } ?>
<li class="nav-item">
<a href="admin_settings_ai.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_ai.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-robot"></i>
<p>AI</p>
</a>
</li>
<!-- Currently the only integration is the client portal SSO -->
<?php if ($config_client_portal_enable) { ?>
<li class="nav-item">
<a href="admin_identity_provider.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_identity_provider.php' ? 'active' : ''); ?>">
<a href="identity_provider.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'identity_provider.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-fingerprint"></i>
<p>Identity Provider</p>
</a>
</li>
<?php } ?>
<li class="nav-item">
<a href="admin_settings_telemetry.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_telemetry.php' ? 'active' : ''); ?>">
<a href="settings_telemetry.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_telemetry.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-satellite-dish"></i>
<p>Telemetry</p>
</a>
</li>
<li class="nav-item">
<a href="admin_settings_module.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_module.php' ? 'active' : ''); ?>">
<a href="settings_module.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_module.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-cube"></i>
<p>Modules</p>
</a>
</li>
</ul>
</li>
<?php
$sql_custom_links = mysqli_query($mysqli, "SELECT * FROM custom_links
WHERE custom_link_location = 4 AND custom_link_archived_at IS NULL
ORDER BY custom_link_order ASC, custom_link_name ASC"
);
while ($row = mysqli_fetch_array($sql_custom_links)) {
$custom_link_name = nullable_htmlentities($row['custom_link_name']);
$custom_link_uri = sanitize_url($row['custom_link_uri']);
$custom_link_icon = nullable_htmlentities($row['custom_link_icon']);
$custom_link_new_tab = intval($row['custom_link_new_tab']);
if ($custom_link_new_tab == 1) {
$target = "target='_blank' rel='noopener noreferrer'";
} else {
$target = "";
}
?>
<li class="nav-item">
<a href="<?php echo $custom_link_uri; ?>" <?php echo $target; ?> class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == basename($custom_link_uri)) { echo "active"; } ?>">
<i class="fas fa-<?php echo $custom_link_icon; ?> nav-icon"></i>
<p><?php echo $custom_link_name; ?></p>
<i class="fas fa-angle-right nav-icon float-right"></i>
</a>
</li>
<?php } ?>
</ul>
</nav>
<!-- /.sidebar-menu -->

4
admin/index.php Normal file
View File

@@ -0,0 +1,4 @@
<?php
header('Location: users.php');

View File

@@ -174,12 +174,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<td><?php echo $email_status_display; ?></td>
<td><?php echo $email_attempts; ?></td>
<td class="text-center">
<a class="btn btn-sm btn-secondary" href="#"
data-toggle = "ajax-modal"
data-modal-size = "lg"
data-ajax-url = "ajax/ajax_admin_mail_queue_message_view.php"
data-ajax-id = "<?php echo $email_id; ?>"
>
<a class="btn btn-sm btn-secondary ajax-modal" href="#"
data-modal-size="lg"
data-modal-url="modals/mail_queue/mail_queue_message_view.php?id=<?= $email_id ?>">
<i class="fas fa-fw fa-eye"></i>
</a>
@@ -205,11 +202,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div>
</form>
<?php require_once "includes/filter_footer.php"; ?>
<?php require_once "../includes/filter_footer.php"; ?>
</div>
</div>
<script src="js/bulk_actions.js"></script>
<script src="../js/bulk_actions.js"></script>
<?php
require_once "includes/footer.php";
require_once "../includes/footer.php";

View File

@@ -0,0 +1,73 @@
<div class="form-group">
<div class="modal" id="addAIModelModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Add AI Model</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="form-group">
<label>Provider <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
</div>
<select class="form-control select2" name="provider" required>
<option value="">- Select an AI Provider -</option>
<?php
$sql_ai_providers = mysqli_query($mysqli, "SELECT * FROM ai_providers");
while ($row = mysqli_fetch_array($sql_ai_providers)) {
$ai_provider_id = intval($row['ai_provider_id']);
$ai_provider_name = nullable_htmlentities($row['ai_provider_name']);
?>
<option value="<?php echo $ai_provider_id; ?>"><?php echo $ai_provider_name; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Model Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
</div>
<input type="text" class="form-control" name="model" placeholder="ex gpt-4">
</div>
</div>
<div class="form-group">
<label>Use Case <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-th-list"></i></span>
</div>
<select class="form-control select2" name="use_case">
<option>General</option>
<option>Tickets</option>
<option>Documentation</option>
</select>
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="8" name="prompt" placeholder="Enter a model prompt:"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ai_model" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,90 @@
<?php
require_once '../../../includes/modal_header.php';
$model_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM ai_models WHERE ai_model_id = $model_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$ai_model_ai_provider_id = intval($row['ai_model_ai_provider_id']);
$model_id = intval($row['ai_model_id']);
$model_name = nullable_htmlentities($row['ai_model_name']);
$use_case = nullable_htmlentities($row['ai_model_use_case']);
$prompt = nullable_htmlentities($row['ai_model_prompt']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Editing: <strong><?php echo $model_name; ?></strong></h5>
<button type="button" class="close text-light" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="model_id" value="<?php echo $model_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Provider <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
</div>
<select class="form-control select2" name="provider" required>
<option value="">- Select an AI Provider -</option>
<?php
$sql_ai_providers = mysqli_query($mysqli, "SELECT * FROM ai_providers");
while ($row = mysqli_fetch_array($sql_ai_providers)) {
$ai_provider_id = intval($row['ai_provider_id']);
$ai_provider_name = nullable_htmlentities($row['ai_provider_name']);
?>
<option <?php if ($ai_provider_id = $ai_model_ai_provider_id) { echo "selected"; } ?> value="<?php echo $ai_provider_id; ?>"><?php echo $ai_provider_name; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Model Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
</div>
<input type="text" class="form-control" name="model" value="<?php echo $model_name; ?>" placeholder="ex gpt-4">
</div>
</div>
<div class="form-group">
<label>Use Case <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-th-list"></i></span>
</div>
<select class="form-control select2" name="use_case">
<option <?php if ($use_case == 'General') { echo "selected"; } ?>>General</option>
<option <?php if ($use_case == 'Tickets') { echo "selected"; } ?>>Tickets</option>
<option <?php if ($use_case == 'Documentation') { echo "selected"; } ?>>Documentation</option>
</select>
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="8" name="prompt" placeholder="Enter a model prompt:"><?php echo $prompt; ?></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ai_model" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,54 @@
<div class="form-group">
<div class="modal" id="addAIProviderModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>New AI Provider</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="form-group">
<label>Provider Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
</div>
<input type="text" class="form-control" name="provider" placeholder="ex OpenAI">
</div>
</div>
<div class="form-group">
<label>URL <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
</div>
<input type="url" class="form-control" name="url" placeholder="ex https://ai.company.ext/api">
</div>
</div>
<div class="form-group">
<label>API Key</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="api_key" placeholder="Enter API key here">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ai_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,69 @@
<?php
require_once '../../../includes/modal_header.php';
$provider_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM ai_providers WHERE ai_provider_id = $provider_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$provider_name = nullable_htmlentities($row['ai_provider_name']);
$url = nullable_htmlentities($row['ai_provider_api_url']);
$key = nullable_htmlentities($row['ai_provider_api_key']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Editing: <strong><?php echo $provider_name; ?></strong></h5>
<button type="button" class="close text-light" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="provider_id" value="<?php echo $provider_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Provider Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
</div>
<input type="text" class="form-control" name="provider" value="<?php echo $provider_name; ?>" placeholder="ex OpenAI">
</div>
</div>
<div class="form-group">
<label>URL <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
</div>
<input type="url" class="form-control" name="url" value="<?php echo $url; ?>" placeholder="ex https://ai.company.ext/api">
</div>
</div>
<div class="form-group">
<label>API Key</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="api_key" value="<?php echo $key; ?>" placeholder="Enter API key here">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ai_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -4,15 +4,15 @@ $decryptPW = randomString(160);
?>
<div class="modal" id="addApiKeyModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-key mr-2"></i>New Key</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body bg-white">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
@@ -111,7 +111,7 @@ $decryptPW = randomString(160);
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_api_key" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -0,0 +1,48 @@
<?php
require_once '../../../includes/modal_header.php';
$category = nullable_htmlentities($_GET['category']);
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul mr-2"></i>New <strong><?= nullable_htmlentities($category) ?></strong> Category</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="type" value="<?php echo ($category); ?>">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list-ul"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Category name" maxlength="200" required autofocus>
</div>
</div>
<div class="form-group">
<label>Color <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
</div>
<input type="color" class="form-control col-3" name="color" required>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_category" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$category_id = intval($_GET['id']);
@@ -14,7 +14,7 @@ $category_type = nullable_htmlentities($row['category_type']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul mr-2"></i>Editing category: <strong><?php echo $category_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -23,7 +23,7 @@ ob_start();
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="category_id" value="<?php echo $category_id; ?>">
<input type="hidden" name="type" value="<?php echo $category_type; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
@@ -46,11 +46,11 @@ ob_start();
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_category" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -1,7 +1,7 @@
<div class="modal" id="createCustomFieldModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-th-list mr-2"></i>Create <?php echo nullable_htmlentities($table); ?> field</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -10,7 +10,7 @@
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="table" value="<?php echo nullable_htmlentities($table); ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Label <strong class="text-danger">*</strong></label>
@@ -31,7 +31,7 @@
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="create_custom_field" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,7 +1,7 @@
<div class="modal" id="editCustomFieldModal<?php echo $custom_field_id; ?>" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-th-list mr-2"></i>Editing custom field: <strong><?php echo $custom_field_label; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -9,7 +9,7 @@
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="custom_field_id" value="<?php echo $custom_field_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Label <strong class="text-danger">*</strong></label>
@@ -30,7 +30,7 @@
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_custom_field" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,7 +1,7 @@
<div class="modal" id="addLinkModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt mr-2"></i>New Custom Link</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -9,7 +9,7 @@
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
@@ -66,12 +66,14 @@
<option value="1">Main Side Nav</option>
<option value="2">Top Nav (Icon Required)</option>
<option value="3">Client Portal Nav</option>
<option value="4">Admin Nav</option>
<option value="5">Reports Nav</option>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$custom_link_id = intval($_GET['id']);
@@ -17,7 +17,7 @@ $custom_link_location = intval($row['custom_link_location']);
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt mr-2"></i>Editing link: <strong><?php echo $custom_link_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -27,7 +27,7 @@ ob_start();
<input type="hidden" name="custom_link_id" value="<?php echo $custom_link_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
@@ -81,19 +81,21 @@ ob_start();
<span class="input-group-text"><i class="fa fa-fw fa-home"></i></span>
</div>
<select class="form-control select2" name="location" required>
<option value="1" <?php if ($custom_link_location == 1) { echo "selected"; } ?> >Main Side Nav</option>
<option value="2" <?php if ($custom_link_location == 2) { echo "selected"; } ?> >Top Nav (Icon Required)</option>
<option value="3" <?php if ($custom_link_location == 3) { echo "selected"; } ?> >Client Portal Nav</option>
<option value="1" <?php if ($custom_link_location === 1) { echo "selected"; } ?> >Main Side Nav</option>
<option value="2" <?php if ($custom_link_location === 2) { echo "selected"; } ?> >Top Nav (Icon Required)</option>
<option value="3" <?php if ($custom_link_location === 3) { echo "selected"; } ?> >Client Portal Nav</option>
<option value="4" <?php if ($custom_link_location === 4) { echo "selected"; } ?> >Admin Nav</option>
<option value="5" <?php if ($custom_link_location === 5) { echo "selected"; } ?> >Reports Nav</option>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -1,21 +1,19 @@
<div class="modal" id="addDocumentTemplateModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt mr-2"></i>Creating Document Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<input type="text" class="form-control" name="name" placeholder="Template name" maxlength="200">
</div>
<?php if ($config_ai_enable == 1) { ?>
<!-- Prompt for AI -->
<div class="form-group">
<label>Enter a prompt for the type of IT documentation you want to generate:</label>
<div class="input-group mb-3">
@@ -27,7 +25,6 @@
</div>
</div>
</div>
<?php } ?>
<!-- TinyMCE Content -->
<div class="form-group">
@@ -40,7 +37,7 @@
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_document_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$document_template_id = intval($_GET['id']);
@@ -14,7 +14,7 @@ $document_template_content = nullable_htmlentities($row['document_template_conte
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt mr-2"></i>Editing template: <strong><?php echo $document_template_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -22,7 +22,7 @@ ob_start();
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="document_template_id" value="<?php echo $document_template_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $document_template_name; ?>" placeholder="Name" required>
@@ -37,11 +37,11 @@ ob_start();
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_document_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
if (!isset($session_is_admin) || !$session_is_admin) {
exit(WORDING_ROLECHECK_FAILED . "<br>Tell your admin: Your role does not have admin access.");
@@ -9,7 +9,7 @@ if (!isset($session_is_admin) || !$session_is_admin) {
$email_id = intval($_GET['id']);
//Initialize the HTML Purifier to prevent XSS
require "../plugins/htmlpurifier/HTMLPurifier.standalone.php";
require "../../../plugins/htmlpurifier/HTMLPurifier.standalone.php";
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one
@@ -43,13 +43,13 @@ if ($email_status == 0) {
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class='fas fa-fw fa-envelope-open mr-2'></i><strong><?php echo $email_subject; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<button type="button" class="close text-light" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body bg-white">
<div class="modal-body">
<div class="row">
<div class="col-md-1">
<span class="text-secondary">From:</span>
@@ -73,7 +73,7 @@ ob_start();
</div>
</div>
<script src="../js/pretty_content.js"></script>
<script src="../../js/pretty_content.js"></script>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,37 @@
<div class="modal" id="addPaymentMethodModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Creating: <strong>Payment Method</strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Payment method name" maxlength="200" required autofocus>
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="3" name="description" placeholder="Enter a description..."></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_payment_method" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,51 @@
<?php
require_once '../../../includes/modal_header.php';
$payment_method_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM payment_methods WHERE payment_method_id = $payment_method_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$payment_method_id = intval($row['payment_method_id']);
$payment_method_name = nullable_htmlentities($row['payment_method_name']);
$payment_method_description = nullable_htmlentities($row['payment_method_description']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Editing: <strong><?php echo $payment_method_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
</div>
<input type="text" class="form-control" name="name" value="<?php echo $payment_method_name; ?>" placeholder="Payment method name" maxlength="200" required autofocus>
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="3" name="description" placeholder="Enter a description..."><?php echo $payment_method_description; ?></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_payment_method" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,103 @@
<div class="form-group">
<div class="modal" id="addPaymentProviderModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Add Payment Provider</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="alert alert-info">
An income account named after the provider will always be created and used for income of paid invoices.<br>
If "Enable Expense" option is enabled, a matching vendor will also be automatically created for expense tracking. Additionally, an expense category named "Payment Processing" will be created.
</div>
<div class="form-group">
<label>Provider <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
</div>
<select class="form-control select2" name="provider">
<option>Stripe</option>
</select>
</div>
</div>
<div class="form-group">
<label>Publishable key <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
</div>
<input type="text" class="form-control" name="public_key" placeholder="Publishable API Key (pk_...)">
</div>
</div>
<div class="form-group">
<label>Secret key <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="private_key" placeholder="Secret API Key (sk_...)">
</div>
</div>
<div class="form-group">
<label>Threshold</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,2}" name="threshold" placeholder="1000.00">
</div>
<small class="form-text text-muted">Will not show as an option at Checkout if invoice amount is above this number, 0 disables the threshold check.</small>
</div>
<hr>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="enable_expense" checked value="1" id="enableExpenseSwitch">
<label class="custom-control-label" for="enableExpenseSwitch">Enable Expense</label>
</div>
</div>
<div class="form-group">
<label>Percentage Fee to expense</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-percent"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,2}" name="percentage_fee" placeholder="Enter Percentage">
</div>
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
</div>
<div class="form-group">
<label>Flat Fee to expense</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,3}" name="flat_fee" placeholder="0.030">
</div>
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_payment_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Add</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,107 @@
<?php
require_once '../../../includes/modal_header.php';
$provider_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM payment_providers WHERE payment_provider_id = $provider_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$provider_name = nullable_htmlentities($row['payment_provider_name']);
$public_key = nullable_htmlentities($row['payment_provider_public_key']);
$private_key = nullable_htmlentities($row['payment_provider_private_key']);
$account_id = nullable_htmlentities($row['payment_provider_account']);
$threshold = floatval($row['payment_provider_threshold']);
$vendor_id = nullable_htmlentities($row['payment_provider_expense_vendor']);
$category_id = nullable_htmlentities($row['payment_provider_expense_category']);
$percent_fee = floatval($row['payment_provider_expense_percentage_fee']) * 100;
$flat_fee = floatval($row['payment_provider_expense_flat_fee']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Editing: <strong><?php echo $provider_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="provider_id" value="<?php echo $provider_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Publishable key <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
</div>
<input type="text" class="form-control" name="public_key" placeholder="Publishable API Key (pk_...)" value="<?php echo $public_key; ?>">
</div>
</div>
<div class="form-group">
<label>Secret key <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="private_key" placeholder="Secret API Key (sk_...)" value="<?php echo $private_key; ?>">
</div>
</div>
<div class="form-group">
<label>Threshold</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,2}" name="threshold" placeholder="1000.00" value="<?php echo $threshold; ?>">
</div>
<small class="form-text text-muted">Will not show as an option at Checkout if above this number</small>
</div>
<hr>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="enable_expense" <?php if ($vendor_id) { echo "checked"; } ?> value="1" id="enableEditExpenseSwitch">
<label class="custom-control-label" for="enableEditExpenseSwitch">Enable Expense</label>
</div>
<small>(Category: Payment Processing -- Vendor: <?php echo $provider_name; ?></small>
</div>
<div class="form-group">
<label>Percentage Fee to expense</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-percent"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,2}" name="percentage_fee" value="<?php echo $percent_fee; ?>" placeholder="Enter Percentage">
</div>
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
</div>
<div class="form-group">
<label>Flat Fee to expense</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,3}" name="flat_fee" value="<?php echo $flat_fee; ?>" placeholder="0.030">
</div>
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_payment_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -1,7 +1,7 @@
<div class="modal" id="addProjectTemplateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram mr-2"></i>Creating Project Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -9,7 +9,7 @@
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Project Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
@@ -31,7 +31,7 @@
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,7 +1,7 @@
<div class="modal" id="editProjectTemplateModal<?php echo $project_template_id; ?>" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram mr-2"></i>Editing Project Template: <strong><?php echo $project_template_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -10,7 +10,7 @@
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="project_template_id" value="<?php echo $project_template_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Project Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
@@ -33,7 +33,7 @@
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,7 +1,7 @@
<div class="modal" id="addProjectTemplateTicketTemplateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-life-ring mr-2"></i>Adding Ticket Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -9,7 +9,7 @@
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="project_template_id" value="<?php echo $project_template_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Ticket Template <strong class="text-danger">*</strong></label>
@@ -54,7 +54,7 @@
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_ticket_template_to_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Add</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,7 +1,7 @@
<div class="modal" id="addRoleModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield mr-2"></i>Add new role</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -9,7 +9,7 @@
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="tab-content">
<div class="form-group">
@@ -48,7 +48,7 @@
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_role" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$role_id = intval($_GET['id']);
@@ -35,7 +35,7 @@ if (empty($user_names_string)) {
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield mr-2"></i>Editing role:
<strong><?php echo $role_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
@@ -45,7 +45,7 @@ ob_start();
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="role_id" value="<?php echo $role_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
@@ -143,11 +143,11 @@ ob_start();
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_role" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -1,14 +1,14 @@
<div class="modal" id="addSoftwareTemplateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>New License Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
@@ -73,7 +73,7 @@
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"></textarea>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_software_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$software_template_id = intval($_GET['id']);
@@ -17,7 +17,7 @@ $software_notes = nullable_htmlentities($row['software_template_notes']);
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>Editing template: <strong><?php echo $software_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -25,7 +25,7 @@ ob_start();
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="software_template_id" value="<?php echo $software_template_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
@@ -89,11 +89,11 @@ ob_start();
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?php echo $software_notes; ?></textarea>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_software_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,94 @@
<?php
require_once '../../../includes/modal_header.php';
$type_display = '';
if (isset($_GET['type'])) {
$type = intval($_GET['type']);
if ($type === 1) {
$type_display = "Client";
} elseif($type === 2) {
$type_display = "Location";
} elseif ($type === 3) {
$type_display = "Contact";
} elseif ($type === 4) {
$type_display = "Credential";
}
}
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-tag mr-2"></i>New <strong><?= $type_display ?></strong> Tag</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="type" value="<?php echo $type; ?>">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Tag name" maxlength="200" required autofocus>
</div>
</div>
<?php if (isset($_GET['type'])) { ?>
<input type="hidden" name="type" value="<?= $type ?>">
<?php } else { ?>
<div class="form-group">
<label>Type <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-th"></i></span>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Type -</option>
<option value="1">Client Tag</option>
<option value="2">Location Tag</option>
<option value="3">Contact Tag</option>
<option value="4">Credential Tag</option>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Color <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
</div>
<input type="color" class="form-control col-3" name="color" required>
</div>
</div>
<div class="form-group">
<label>Icon</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
</div>
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_tag" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$tag_id = intval($_GET['id']);
@@ -15,7 +15,7 @@ $tag_icon = nullable_htmlentities($row['tag_icon']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-tag mr-2"></i>Editing tag: <strong><?php echo $tag_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -23,7 +23,7 @@ ob_start();
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="tag_id" value="<?php echo $tag_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
@@ -72,11 +72,11 @@ ob_start();
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_tag" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -1,7 +1,7 @@
<div class="modal" id="addTaxModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header text-white">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-balance-scale mr-2"></i>New Tax</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
@@ -10,7 +10,7 @@
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<input type="text" class="form-control" name="name" placeholder="Tax name" maxlength="200" required autofocus>
@@ -20,7 +20,7 @@
<input type="number" min="0" step="any" class="form-control col-md-4" name="percent">
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_tax" class="btn btn-primary text-bold"><i class="fa fa-check mr- 2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$tax_id = intval($_GET['id']);
@@ -13,7 +13,7 @@ $tax_percent = floatval($row['tax_percent']);
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-balance-scale mr-2"></i>Editing tax: <strong><?php echo $tax_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -23,7 +23,7 @@ ob_start();
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="tax_id" value="<?php echo $tax_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
@@ -36,11 +36,11 @@ ob_start();
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_tax" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -1,7 +1,7 @@
<div class="modal" id="addTicketStatusModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle mr-2"></i>New Ticket Status</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -9,7 +9,7 @@
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
@@ -32,7 +32,7 @@
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$ticket_status_id = intval($_GET['id']);
@@ -15,7 +15,7 @@ $ticket_status_active = intval($row['ticket_status_active']);
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle mr-2"></i>Editing Ticket Status: <strong><?php echo $ticket_status_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -23,7 +23,7 @@ ob_start();
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="ticket_status_id" value="<?php echo $ticket_status_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
@@ -69,11 +69,11 @@ ob_start();
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -1,14 +1,14 @@
<div class="modal" id="addTicketTemplateModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring mr-2"></i>Creating Ticket Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
@@ -31,7 +31,7 @@
</div>
<div class="form-group">
<textarea class="form-control tinymceTicket<?php if($config_ai_enable) { echo "AI"; } ?>" name="details"></textarea>
<textarea class="form-control tinymceTicket" name="details"></textarea>
</div>
<div class="form-group">
@@ -65,7 +65,7 @@
</div>
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_ticket_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,8 +1,8 @@
<div class="modal" id="editTicketTemplateModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring mr-2"></i>Editing Ticket Template: <?php echo $ticket_template_name; ?></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -10,7 +10,7 @@
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="ticket_template_id" value="<?php echo $ticket_template_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
@@ -33,7 +33,7 @@
</div>
<div class="form-group">
<textarea class="form-control tinymceTicket<?php if($config_ai_enable) { echo "AI"; } ?>" name="details"><?php echo $ticket_template_details; ?></textarea>
<textarea class="form-control tinymceTicket" name="details"><?php echo $ticket_template_details; ?></textarea>
</div>
<div class="form-group">
@@ -47,7 +47,7 @@
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_ticket_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$task_template_id = intval($_GET['id']);
@@ -17,7 +17,7 @@ ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-tasks mr-2"></i>Editing task</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -26,7 +26,7 @@ ob_start();
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="task_template_id" value="<?php echo $task_template_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
@@ -50,7 +50,7 @@ ob_start();
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_ticket_template_task" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
@@ -59,4 +59,4 @@ ob_start();
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

View File

@@ -1,7 +1,7 @@
<div class="modal" id="addUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-plus mr-2"></i>New User</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -9,7 +9,7 @@
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
@@ -146,7 +146,7 @@
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -0,0 +1,83 @@
<?php
require_once '../../../includes/modal_header.php';
$user_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE users.user_id = $user_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$user_name = nullable_htmlentities($row['user_name']);
$user_email = nullable_htmlentities($row['user_email']);
$user_avatar = nullable_htmlentities($row['user_avatar']);
$user_initials = nullable_htmlentities(initials($user_name));
$sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets
WHERE ticket_assigned_to = $user_id AND ticket_resolved_at IS NULL AND ticket_closed_at IS NULL");
$ticket_count = mysqli_num_rows($sql_related_tickets);
// Related Recurring Tickets Query
$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM recurring_tickets WHERE recurring_ticket_assigned_to = $user_id");
$recurring_ticket_count = mysqli_num_rows($sql_related_recurring_tickets);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-slash mr-2"></i>Archiving user:
<strong><?php echo $user_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
<div class="modal-body">
<center class="mb-3">
<?php if (!empty($user_avatar)) { ?>
<img class="img-fluid" src="<?php echo "../uploads/users/$user_id/$user_avatar"; ?>">
<?php } else { ?>
<span class="fa-stack fa-4x">
<i class="fa fa-circle fa-stack-2x text-secondary"></i>
<span class="fa fa-stack-1x text-white"><?php echo $user_initials; ?></span>
</span>
<?php } ?>
</center>
<div class="form-group">
<label>Reassign <?= $ticket_count ?> Open Tickets and <?= $recurring_ticket_count ?> Recurring Tickets To:</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
</div>
<select class="form-control select2" name="ticket_assign" required>
<option value="0">No one</option>
<?php
$sql_users = mysqli_query($mysqli, "SELECT * FROM users WHERE user_type = 1 AND user_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_users)) {
$user_id_select = intval($row['user_id']);
$user_name_select = nullable_htmlentities($row['user_name']);
?>
<option value="<?= $user_id_select ?>"><?= $user_name_select ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="archive_user" class="btn btn-danger text-bold"><i class="fas fa-archive mr-2"></i>Archive</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../../../includes/modal_footer.php";

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$user_id = intval($_GET['id']);
@@ -28,7 +28,7 @@ while ($row = mysqli_fetch_assoc($user_client_access_sql)) {
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-edit mr-2"></i>Editing user:
<strong><?php echo $user_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
@@ -38,7 +38,7 @@ ob_start();
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
@@ -57,7 +57,7 @@ ob_start();
<center class="mb-3">
<?php if (!empty($user_avatar)) { ?>
<img class="img-fluid" src="<?php echo "uploads/users/$user_id/$user_avatar"; ?>">
<img class="img-fluid" src="<?php echo "../uploads/users/$user_id/$user_avatar"; ?>">
<?php } else { ?>
<span class="fa-stack fa-4x">
<i class="fa fa-circle fa-stack-2x text-secondary"></i>
@@ -194,11 +194,11 @@ ob_start();
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="edit_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once "../../../includes/modal_footer.php";

View File

@@ -1,17 +1,17 @@
<div class="modal" id="exportUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Users to CSV</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body bg-white">
<div class="modal-body">
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="export_users_csv" class="btn btn-primary text-bold"><i class="fas fa-fw fa-download mr-2"></i>Download CSV</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,7 +1,7 @@
<div class="modal" id="userInviteModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-plus"></i>Invite User</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -9,7 +9,7 @@
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<div class="form-group">
<label>Email <strong class="text-danger">*</strong></label>
@@ -35,7 +35,7 @@
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="invite_user" class="btn btn-primary text-bold"><i class="fas fa-paper-plane mr-2"></i>Send Invite</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -0,0 +1,87 @@
<?php
require_once '../../../includes/modal_header.php';
$user_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_id = $user_id AND user_archived_at IS NOT NULL LIMIT 1");
$row = mysqli_fetch_array($sql);
$user_name = str_replace(" (archived)", "", $row['user_name']); //Removed (archived) from user_name
$user_name = nullable_htmlentities($user_name);
$user_email = nullable_htmlentities($row['user_email']);
$user_avatar = nullable_htmlentities($row['user_avatar']);
$user_initials = initials($user_name);
$user_role_id = intval($row['user_role_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-redo-alt mr-2"></i>Restoring user:
<strong><?php echo $user_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
<div class="modal-body">
<center class="mb-3">
<?php if (!empty($user_avatar)) { ?>
<img class="img-fluid" src="<?php echo "../uploads/users/$user_id/$user_avatar"; ?>">
<?php } else { ?>
<span class="fa-stack fa-4x">
<i class="fa fa-circle fa-stack-2x text-secondary"></i>
<span class="fa fa-stack-1x text-white"><?php echo $user_initials; ?></span>
</span>
<?php } ?>
</center>
<div class="form-group">
<label>Set a New Password</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
</div>
<input type="password" class="form-control" data-toggle="password" name="new_password"
placeholder="Enter a new password" autocomplete="new-password" required>
<div class="input-group-append">
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
</div>
</div>
</div>
<div class="form-group">
<label>Role <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
</div>
<select class="form-control select2" name="role" required>
<?php
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE role_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_user_roles)) {
$role_id = intval($row['role_id']);
$role_name = nullable_htmlentities($row['role_name']);
?>
<option <?php if ($role_id == $user_role_id) {echo "selected";} ?> value="<?php echo $role_id; ?>"><?php echo $role_name; ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="restore_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Restore</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../../../includes/modal_footer.php";

View File

@@ -1,7 +1,7 @@
<div class="modal" id="addVendorTemplateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-building mr-2"></i>New Vendor Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -11,7 +11,7 @@
<input type="hidden" name="client_id" value="<?php if (isset($_GET['client_id'])) { echo $client_id; } else { echo 0; } ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
@@ -158,7 +158,7 @@
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" name="add_vendor_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create Template</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>

View File

@@ -1,6 +1,6 @@
<?php
require_once '../includes/ajax_header.php';
require_once '../../../includes/modal_header.php';
$vendor_template_id = intval($_GET['id']);
@@ -10,7 +10,8 @@ $vendor_name = nullable_htmlentities($row['vendor_template_name']);
$vendor_description = nullable_htmlentities($row['vendor_template_description']);
$vendor_account_number = nullable_htmlentities($row['vendor_template_account_number']);
$vendor_contact_name = nullable_htmlentities($row['vendor_template_contact_name']);
$vendor_phone = formatPhoneNumber($row['vendor_template_phone']);
$vendor_phone_country_code = intval($row['vendor_template_phone_country_code']);
$vendor_phone = formatPhoneNumber($row['vendor_template_phone'], $vendor_phone_country_code);
$vendor_extension = nullable_htmlentities($row['vendor_template_extension']);
$vendor_email = nullable_htmlentities($row['vendor_template_email']);
$vendor_website = nullable_htmlentities($row['vendor_template_website']);
@@ -23,7 +24,7 @@ $vendor_notes = nullable_htmlentities($row['vendor_template_notes']);
ob_start();
?>
<div class="modal-header">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-building mr-2"></i>Editing vendor template: <strong><?php echo $vendor_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
@@ -31,7 +32,7 @@ ob_start();
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="vendor_template_id" value="<?php echo $vendor_template_id; ?>">
<div class="modal-body bg-white">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
@@ -133,17 +134,20 @@ ob_start();
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div>
<input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4" value="<?php echo $vendor_phone_country_code; ?>">
<input type="tel" class="form-control" name="phone" value="<?php echo $vendor_phone; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_phone" value="1">
</div>
</div>
</div>
</div>
</div>
<div class="col-4">
<input type="text" class="form-control" name="extension" placeholder="Prompts" maxlength="200" value="<?php echo $vendor_extension; ?>">
<div class="input-group">
<input type="text" class="form-control" name="extension" placeholder="Prompts" maxlength="200" value="<?php echo $vendor_extension; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_phone" value="1">
</div>
</div>
</div>
</div>
</div>
@@ -240,11 +244,11 @@ ob_start();
</div>
</div>
<div class="modal-footer bg-white">
<div class="modal-footer">
<button type="submit" class="btn btn-primary text-bold" name="edit_vendor_template"><i class="fa fa-check mr-2"></i>Update Template</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../includes/ajax_footer.php";
require_once '../../../includes/modal_footer.php';

102
admin/payment_method.php Normal file
View File

@@ -0,0 +1,102 @@
<?php
// Default Column Sortby Filter
$sort = "payment_method_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT * FROM payment_methods ORDER BY $sort $order");
$num_rows = mysqli_num_rows($sql);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card mr-2"></i>Payment Methods</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addPaymentMethodModal"><i class="fas fa-plus mr-2"></i>Add Payment Method</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
<tr>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_method_name&order=<?php echo $disp; ?>">
Method <?php if ($sort == 'payment_method_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_method_description&order=<?php echo $disp; ?>">
Description <?php if ($sort == 'payment_method_description') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_method_created_at&order=<?php echo $disp; ?>">
Created at <?php if ($sort == 'payment_method_created_at') { echo $order_icon; } ?>
</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$payment_method_id = intval($row['payment_method_id']);
$payment_method_name = nullable_htmlentities($row['payment_method_name']);
$payment_method_description = nullable_htmlentities($row['payment_method_description']);
$payment_method_created_at = nullable_htmlentities($row['payment_method_created_at']);
?>
<tr>
<td>
<a class="text-dark text-bold ajax-modal" href="#"
data-modal-url="modals/payment_method/payment_method_edit.php?id=<?= $payment_method_id ?>">
<?php echo $payment_method_name; ?>
</a>
</td>
<td><?php echo $payment_method_description; ?></td>
<td><?php echo $payment_method_created_at; ?></td>
<td>
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/payment_method/payment_method_edit.php?id=<?= $payment_method_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_payment_method=<?php echo $payment_method_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>
</td>
</tr>
<?php
}
if ($num_rows == 0) {
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "modals/payment_method/payment_method_add.php";
require_once "../includes/footer.php";

139
admin/payment_provider.php Normal file
View File

@@ -0,0 +1,139 @@
<?php
// Default Column Sortby Filter
$sort = "payment_provider_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT * FROM payment_providers
LEFT JOIN accounts ON payment_provider_account = account_id
LEFT JOIN vendors ON payment_provider_expense_vendor = vendor_id
LEFT JOIN categories ON payment_provider_expense_category = category_id
ORDER BY $sort $order"
);
$num_rows = mysqli_num_rows($sql);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card mr-2"></i>Payment Providers</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addPaymentProviderModal"><i class="fas fa-plus mr-2"></i>Add Provider</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
<tr>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_provider_name&order=<?php echo $disp; ?>">
Provider <?php if ($sort == 'payment_provider_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=account_name&order=<?php echo $disp; ?>">
Expense / Income Account <?php if ($sort == 'account_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_provider_threshold&order=<?php echo $disp; ?>">
Threshold <?php if ($sort == 'payment_provider_threshold') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=vendor_name&order=<?php echo $disp; ?>">
Expense Vendor <?php if ($sort == 'vendor_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=category_name&order=<?php echo $disp; ?>">
Expense Category <?php if ($sort == 'category_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark">Expensed Fee</a>
</th>
<th>
<a class="text-dark">Saved Payment Methods</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$provider_id = intval($row['payment_provider_id']);
$provider_name = nullable_htmlentities($row['payment_provider_name']);
$provider_description = nullable_htmlentities($row['payment_provider_description']);
$account_name = nullable_htmlentities($row['account_name']);
$threshold = floatval($row['payment_provider_threshold']);
$vendor_name = nullable_htmlentities($row['vendor_name']);
$category = nullable_htmlentities($row['category_name']);
$percent_fee = floatval($row['payment_provider_expense_percentage_fee']) * 100;
$flat_fee = floatval($row['payment_provider_expense_flat_fee']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('saved_payment_id') AS saved_payment_count FROM client_saved_payment_methods WHERE saved_payment_provider_id = $provider_id"));
$saved_payment_count = intval($row['saved_payment_count']);
?>
<tr>
<td>
<a class="text-dark text-bold ajax-modal" href="#"
data-modal-url="modals/payment_provider/payment_provider_edit.php?id=<?= $provider_id ?>">
<?php echo $provider_name; ?>
</a>
<span class="text-secondary"><?php echo $provider_description; ?></span>
</td>
<td><?php echo $account_name; ?></td>
<td><?php echo numfmt_format_currency($currency_format, $threshold, $session_company_currency); ?></td>
<td><?php echo $vendor_name; ?></td>
<td><?php echo $category; ?></td>
<td><?php echo $percent_fee; ?>% + <?php echo numfmt_format_currency($currency_format, $flat_fee, $session_company_currency); ?></td>
<td><?php echo $saved_payment_count; ?></td>
<td>
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/payment_provider/payment_provider_edit.php?id=<?= $provider_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<!-- <a class="dropdown-item text-danger confirm-link" href="post.php?disable_payment_provider=--><?php //echo $provider_id; ?><!--&csrf_token=--><?php //echo $_SESSION['csrf_token'] ?><!--">-->
<!-- <i class="fas fa-fw fa-thumbs-down mr-2"></i>Disable-->
<!-- </a>-->
<!-- <a class="dropdown-item text-danger confirm-link" href="post.php?delete_payment_provider=--><?php //echo $provider_id; ?><!--&csrf_token=--><?php //echo $_SESSION['csrf_token'] ?><!--">-->
<!-- <i class="fas fa-fw fa-trash mr-2"></i>Delete-->
<!-- </a>-->
</div>
</div>
</td>
</tr>
<?php
}
if ($num_rows == 0) {
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "modals/payment_provider/payment_provider_add.php";
require_once "../includes/footer.php";

42
admin/post.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
/*
* ITFlow - Admin GET/POST request handler
*/
require_once "../config.php";
require_once "../functions.php";
require_once "../includes/check_login.php";
// Define a variable that we can use to only allow running post files via inclusion (prevents people/bots poking them)
define('FROM_POST_HANDLER', true);
// Determine which files we should load
// Parse URL & get the path
$path = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_PATH);
// Get the base name (the page name)
$module = explode(".", basename($path))[0];
// Strip off any _details bits
$module = str_ireplace('_details', '', $module);
// Dynamically load admin-related module POST logic
if (isset($session_is_admin) && $session_is_admin) {
// As (almost) every admin setting is only changed from 1 page, we can dynamically load the relevant logic inside this single admin check IF statement
// To add a new admin POST request handler, add a file named after the admin page
// e.g. changes made on the page http://itflow/admin_ticket_statues.php will load the page admin/post/admin_ticket_statues.php to handle the changes
include_once "post/$module.php";
}
// Logout is the same for user and admin
require_once "../post/logout.php";
// TODO: Find a home for these
require_once "../post/ai.php";
require_once "../post/misc.php";

65
admin/post/ai_model.php Normal file
View File

@@ -0,0 +1,65 @@
<?php
/*
* ITFlow - GET/POST request handler for AI Models ('ai_model')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_ai_model'])) {
validateCSRFToken($_POST['csrf_token']);
$provider_id = intval($_POST['provider']);
$model = sanitizeInput($_POST['model']);
$prompt = sanitizeInput($_POST['prompt']);
$use_case = sanitizeInput($_POST['use_case']);
mysqli_query($mysqli,"INSERT INTO ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case', ai_model_ai_provider_id = $provider_id");
$ai_model_id = mysqli_insert_id($mysqli);
logAction("AI Model", "Create", "$session_name created AI Model $model");
flash_alert("AI Model <strong>$model</strong> created");
redirect();
}
if (isset($_POST['edit_ai_model'])) {
validateCSRFToken($_POST['csrf_token']);
$model_id = intval($_POST['model_id']);
$model = sanitizeInput($_POST['model']);
$prompt = sanitizeInput($_POST['prompt']);
$use_case = sanitizeInput($_POST['use_case']);
mysqli_query($mysqli,"UPDATE ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case' WHERE ai_model_id = $model_id");
logAction("AI Model", "Edit", "$session_name edited AI Model $model");
flash_alert("AI Model <strong>$model</strong> edited");
redirect();
}
if (isset($_GET['delete_ai_model'])) {
validateCSRFToken($_GET['csrf_token']);
$model_id = intval($_GET['delete_ai_model']);
$model_name = sanitizeInput(getFieldById('ai_models', $model_id, 'ai_model_name'));
mysqli_query($mysqli,"DELETE FROM ai_models WHERE ai_model_id = $model_id");
logAction("AI Model", "Delete", "$session_name deleted AI Model $model_name");
flash_alert("AI Model <strong>$model_name</strong> deleted", 'error');
redirect();
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* ITFlow - GET/POST request handler for AI Providers ('ai_provider')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_ai_provider'])) {
validateCSRFToken($_POST['csrf_token']);
$provider = sanitizeInput($_POST['provider']);
$url = sanitizeInput($_POST['url']);
$model = sanitizeInput($_POST['model']);
$api_key = sanitizeInput($_POST['api_key']);
mysqli_query($mysqli,"INSERT INTO ai_providers SET ai_provider_name = '$provider', ai_provider_api_url = '$url', ai_provider_api_key = '$api_key'");
$ai_provider_id = mysqli_insert_id($mysqli);
logAction("AI Provider", "Create", "$session_name created AI Provider $provider");
flash_alert("AI Model <strong>$provider</strong> created");
redirect();
}
if (isset($_POST['edit_ai_provider'])) {
validateCSRFToken($_POST['csrf_token']);
$provider_id = intval($_POST['provider_id']);
$provider = sanitizeInput($_POST['provider']);
$url = sanitizeInput($_POST['url']);
$api_key = sanitizeInput($_POST['api_key']);
mysqli_query($mysqli,"UPDATE ai_providers SET ai_provider_name = '$provider', ai_provider_api_url = '$url', ai_provider_api_key = '$api_key' WHERE ai_provider_id = $provider_id");
logAction("AI Provider", "Edit", "$session_name edited AI Provider $provider");
flash_alert("AI Model <strong>$provider</strong> edited");
redirect();
}
if (isset($_GET['delete_ai_provider'])) {
validateCSRFToken($_GET['csrf_token']);
$provider_id = intval($_GET['delete_ai_provider']);
$provider_name = sanitizeInput(getFieldById('ai_providers', $provider_id, 'ai_provider_name'));
mysqli_query($mysqli,"DELETE FROM ai_providers WHERE ai_provider_id = $provider_id");
logAction("AI Provider", "Delete", "$session_name deleted AI Provider $provider_name", 'error');
flash_alert("AI Provider <strong>$provider_name</strong> deleted", 'error');
redirect();
}

View File

@@ -23,12 +23,11 @@ if (isset($_POST['add_api_key'])) {
$api_key_id = mysqli_insert_id($mysqli);
// Logging
logAction("API Key", "Create", "$session_name created API key $name set to expire on $expire", $client_id, $api_key_id);
$_SESSION['alert_message'] = "API Key <strong>$name</strong> created";
flash_alert("API Key <strong>$name</strong> created");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -45,13 +44,11 @@ if (isset($_GET['delete_api_key'])) {
mysqli_query($mysqli,"DELETE FROM api_keys WHERE api_key_id = $api_key_id");
// Logging
logAction("API Key", "Delete", "$session_name deleted API key $name", $client_id);
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "API Key <strong>$name</strong> deleted";
flash_alert("API Key <strong>$name</strong> deleted", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -75,18 +72,16 @@ if (isset($_POST['bulk_delete_api_keys'])) {
mysqli_query($mysqli, "DELETE FROM api_keys WHERE api_key_id = $api_key_id");
// Logging
logAction("API Key", "Delete", "$session_name deleted API key $name", $client_id);
}
// Logging
logAction("API Key", "Bulk Delete", "$session_name deleted $count API key(s)");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Deleted <strong>$count</strong> API keys(s)";
flash_alert("Deleted <strong>$count</strong> API keys(s)", 'error');
}
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -6,9 +6,10 @@
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
require_once "includes/app_version.php";
require_once "../includes/app_version.php";
if (isset($_GET['download_backup'])) {
validateCSRFToken($_GET['csrf_token']);
$timestamp = date('YmdHis');
@@ -122,7 +123,7 @@ if (isset($_GET['download_backup'])) {
file_put_contents($sqlFile, $sqlContent);
// === 4. Zip the uploads folder
$zipFolder("uploads", $uploadsZip);
$zipFolder("../uploads", $uploadsZip);
// === 5. Create version.txt
$commitHash = trim(shell_exec('git log -1 --format=%H')) ?: 'N/A';
@@ -175,7 +176,7 @@ if (isset($_GET['download_backup'])) {
fclose($fp);
logAction("System", "Backup Download", "$session_name downloaded full backup.");
$_SESSION['alert_message'] = "Full backup downloaded.";
flash_alert("Full backup downloaded.");
exit;
}
@@ -192,10 +193,8 @@ if (isset($_POST['backup_master_key'])) {
if (password_verify($password, $row['user_password'])) {
$site_encryption_master_key = decryptUserSpecificKey($row['user_specific_encryption_ciphertext'], $password);
// Logging
logAction("Master Key", "Download", "$session_name retrieved the master encryption key");
// App Notify
appNotify("Master Key", "$session_name retrieved the master encryption key");
echo "==============================";
@@ -204,13 +203,11 @@ if (isset($_POST['backup_master_key'])) {
echo "<br>==============================";
} else {
// Log the failure
logAction("Master Key", "Download", "$session_name attempted to retrieve the master encryption key but failed");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Incorrect password.";
flash_alert("Incorrect password.", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
}

View File

@@ -8,35 +8,33 @@ defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_category'])) {
require_once 'post/admin/admin_category_model.php';
require_once 'category_model.php';
mysqli_query($mysqli,"INSERT INTO categories SET category_name = '$name', category_type = '$type', category_color = '$color'");
$category_id = mysqli_insert_id($mysqli);
// Logging
logAction("Category", "Create", "$session_name created category $type $name", 0, $category_id);
$_SESSION['alert_message'] = "Category $type <strong>$name</strong> created";
flash_alert("Category $type <strong>$name</strong> created");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
if (isset($_POST['edit_category'])) {
require_once 'post/admin/admin_category_model.php';
require_once 'category_model.php';
$category_id = intval($_POST['category_id']);
mysqli_query($mysqli,"UPDATE categories SET category_name = '$name', category_type = '$type', category_color = '$color' WHERE category_id = $category_id");
// Logging
logAction("Category", "Edit", "$session_name edited category $type $name", 0, $category_id);
$_SESSION['alert_message'] = "Category $type <strong>$name</strong> edited";
flash_alert("Category $type <strong>$name</strong> edited");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -52,13 +50,11 @@ if (isset($_GET['archive_category'])) {
mysqli_query($mysqli,"UPDATE categories SET category_archived_at = NOW() WHERE category_id = $category_id");
// Logging
logAction("Category", "Archive", "$session_name archived category $category_type $category_name", 0, $category_id);
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Category $category_type <strong>$category_name</strong> archived";
flash_alert("Category $category_type <strong>$category_name</strong> archived", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -74,12 +70,11 @@ if (isset($_GET['unarchive_category'])) {
mysqli_query($mysqli,"UPDATE categories SET category_archived_at = NULL WHERE category_id = $category_id");
// Logging
logAction("Category", "Unarchive", "$session_name unarchived category $category_type $category_name", 0, $category_id);
$_SESSION['alert_message'] = "Category $category_type <strong>$category_name</strong> unarchived";
flash_alert("Category $category_type <strong>$category_name</strong> unarchived");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -95,12 +90,10 @@ if (isset($_GET['delete_category'])) {
mysqli_query($mysqli,"DELETE FROM categories WHERE category_id = $category_id");
// Logging
logAction("Category", "Delete", "$session_name deleted category $category_type $category_name");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Category $category_type <strong>$category_name</strong> deleted";
flash_alert("Category $category_type <strong>$category_name</strong> deleted", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -8,7 +8,7 @@ defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if(isset($_POST['create_custom_field'])){
require_once 'post/admin/admin_custom_field_model.php';
require_once 'custom_field_model.php';
$table = sanitizeInput($_POST['table']);
@@ -16,48 +16,42 @@ if(isset($_POST['create_custom_field'])){
$custom_field_id = mysqli_insert_id($mysqli);
// Logging
logAction("Custom Field", "Create", "$session_name created custom field $label", 0, $custom_field_id);
$_SESSION['alert_message'] = "Custom field <strong>$label</strong> created";
flash_alert("Custom field <strong>$label</strong> created");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
if(isset($_POST['edit_custom_field'])){
require_once 'post/admin/admin_custom_field_model.php';
require_once 'custom_field_model.php';
$custom_field_id = intval($_POST['custom_field_id']);
mysqli_query($mysqli,"UPDATE custom_fields SET custom_field_label = '$label', custom_field_type = '$type' WHERE custom_field_id = $custom_field_id");
// Logging
logAction("Custom Field", "Edit", "$session_name edited custom field $label", 0, $custom_field_id);
$_SESSION['alert_message'] = "Custom field <strong>$label</strong> edited";
flash_alert("Custom field <strong>$label</strong> edited");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
if(isset($_GET['delete_custom_field'])){
$custom_field_id = intval($_GET['delete_custom_field']);
// Get Custom Field Label for logging
$sql = mysqli_query($mysqli,"SELECT custom_field_label FROM custom_fields WHERE custom_field_id = $custom_field_id");
$row = mysqli_fetch_array($sql);
$custom_field_label = sanitizeInput($row['custom_field_label']);
$label = sanitizeInput(getFieldById('custom_fields', $custom_field_id, 'custom_field_label'));
mysqli_query($mysqli,"DELETE FROM custom_fields WHERE custom_field_id = $custom_field_id");
// Logging
logAction("Custom Field", "Delete", "$session_name deleted custom field $label");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Custom field <strong>$label</strong> deleted";
flash_alert("Custom field <strong>$label</strong> deleted", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -19,12 +19,11 @@ if (isset($_POST['add_custom_link'])) {
$custom_link_id = mysqli_insert_id($mysqli);
// Logging
logAction("Custom Link", "Create", "$session_name created custom link $name -> $uri", 0, $custom_link_id);
$_SESSION['alert_message'] = "Custom link <strong>$name</strong> created";
flash_alert("Custom link <strong>$name</strong> created");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -40,16 +39,16 @@ if (isset($_POST['edit_custom_link'])) {
mysqli_query($mysqli,"UPDATE custom_links SET custom_link_name = '$name', custom_link_uri = '$uri', custom_link_new_tab = $new_tab, custom_link_icon = '$icon', custom_link_order = $order, custom_link_location = $location WHERE custom_link_id = $custom_link_id");
// Logging
logAction("Custom Link", "Edit", "$session_name edited custom link $name -> $uri", 0, $custom_link_id);
$_SESSION['alert_message'] = "Custom Link <strong>$name</strong> edited";
flash_alert("Custom Link <strong>$name</strong> edited");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
if (isset($_GET['delete_custom_link'])) {
$custom_link_id = intval($_GET['delete_custom_link']);
// Get Custom Link name and uri for logging
@@ -60,12 +59,10 @@ if (isset($_GET['delete_custom_link'])) {
mysqli_query($mysqli,"DELETE FROM custom_links WHERE custom_link_id = $custom_link_id");
// Logging
logAction("Custom Link", "Delete", "$session_name deleted custom link $custom_link_name -> $custom_link_uri");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Custom Link <strong>$name</strong> deleted";
flash_alert("Custom Link <strong>$name</strong> deleted", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -10,17 +10,15 @@ if (isset($_POST['add_document_template'])) {
$description = sanitizeInput($_POST['description']);
$content = mysqli_real_escape_string($mysqli,$_POST['content']);
// Document create query
mysqli_query($mysqli,"INSERT INTO document_templates SET document_template_name = '$name', document_template_description = '$description', document_template_content = '$content', document_template_created_by = $session_user_id");
$document_template_id = mysqli_insert_id($mysqli);
// Logging
logAction("Document Template", "Create", "$session_name created document template $name", 0, $document_template_id);
$_SESSION['alert_message'] = "Document template <strong>$name</strong> created";
flash_alert("Document template <strong>$name</strong> created");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -34,12 +32,11 @@ if (isset($_POST['edit_document_template'])) {
// Document edit query
mysqli_query($mysqli,"UPDATE document_templates SET document_template_name = '$name', document_template_description = '$description', document_template_content = '$content', document_template_updated_by = $session_user_id WHERE document_template_id = $document_template_id");
// Logging
logAction("Document Template", "Edit", "$session_name edited document template $name", 0, $document_template_id);
$_SESSION['alert_message'] = "Document Template <strong>$name</strong> edited";
flash_alert("Document Template <strong>$name</strong> edited");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -47,19 +44,14 @@ if (isset($_GET['delete_document_template'])) {
$document_template_id = intval($_GET['delete_document_template']);
// Get Document Template Name for logging
$sql = mysqli_query($mysqli,"SELECT document_template_name FROM document_templates WHERE document_template_id = $document_template_id");
$row = mysqli_fetch_array($sql);
$document_template_name = sanitizeInput($row['document_template_name']);
$document_template_name = sanitizeInput(getFieldById('document_templates', $document_template_id, 'document_template_name'));
mysqli_query($mysqli,"DELETE FROM document_templates WHERE document_template_id = $document_template_id");
//Logging
logAction("Document Template", "Delete", "$session_name deleted document template $document_template_name");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Document Template <strong>$document_template_name</strong> deleted";
flash_alert("Document Template <strong>$document_template_name</strong> deleted", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -11,11 +11,10 @@ if (isset($_POST['edit_identity_provider'])) {
mysqli_query($mysqli,"UPDATE settings SET config_azure_client_id = '$azure_client_id', config_azure_client_secret = '$azure_client_secret' WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited identity provider settings");
$_SESSION['alert_message'] = "Identity Provider Settings updated";
flash_alert("Identity Provider Settings updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -8,12 +8,11 @@ if (isset($_GET['send_failed_mail'])) {
mysqli_query($mysqli,"UPDATE email_queue SET email_status = 0, email_attempts = 3 WHERE email_id = $email_id");
// Logging
logAction("Email", "Send", "$session_name attempted to force send email id: $email_id in the mail queue", 0, $email_id);
$_SESSION['alert_message'] = "Email Force Sent, give it a minute to resend";
flash_alert("Email Force Sent, give it a minute to resend");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -23,13 +22,11 @@ if (isset($_GET['cancel_mail'])) {
mysqli_query($mysqli,"UPDATE email_queue SET email_status = 2, email_attempts = 99, email_failed_at = NOW() WHERE email_id = $email_id");
// Logging
logAction("Email", "Send", "$session_name canceled send email id: $email_id in the mail queue", 0, $email_id);
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Email cancelled and marked as failed.";
flash_alert("Email cancelled and marked as failed.", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -47,19 +44,18 @@ if (isset($_POST['bulk_cancel_emails'])) {
$email_id = intval($email_id);
mysqli_query($mysqli,"UPDATE email_queue SET email_status = 2, email_attempts = 99, email_failed_at = NOW() WHERE email_id = $email_id");
// Logging
logAction("Email", "Cancel", "$session_name cancelled email id: $email_id in the mail queue", 0, $email_id);
}
// Logging
logAction("Email", "Bulk Cancel", "$session_name cancelled $count email(s) in the mail queue");
$_SESSION['alert_message'] = "Cancelled <strong>$count</strong> email(s)";
flash_alert("Cancelled <strong>$count</strong> email(s)", 'error');
}
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
if (isset($_POST['bulk_delete_emails'])) {
@@ -76,18 +72,16 @@ if (isset($_POST['bulk_delete_emails'])) {
$email_id = intval($email_id);
mysqli_query($mysqli,"DELETE FROM email_queue WHERE email_id = $email_id");
// Logging
logAction("Email", "Delete", "$session_name deleted email id: $email_id from the mail queue");
}
// Logging
logAction("Email", "Bulk Delete", "$session_name deleted $count email(s) from the mail queue");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Deleted <strong>$count</strong> email(s)";
flash_alert("Deleted <strong>$count</strong> email(s)", 'error');
}
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* ITFlow - GET/POST request handler for AI Providers ('ai_providers')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_payment_method'])) {
validateCSRFToken($_POST['csrf_token']);
$name = sanitizeInput($_POST['name']);
$description = sanitizeInput($_POST['description']);
mysqli_query($mysqli,"INSERT INTO payment_methods SET payment_method_name = '$name', payment_method_description = '$description'");
logAction("Payment Method", "Create", "$session_name created Payment Method $name");
flash_alert("Payment Method <strong>$name</strong> created");
redirect();
}
if (isset($_POST['edit_payment_method'])) {
validateCSRFToken($_POST['csrf_token']);
$payment_method_id = intval($_POST['payment_method_id']);
$name = sanitizeInput($_POST['name']);
$description = sanitizeInput($_POST['description']);
mysqli_query($mysqli,"UPDATE payment_methods SET payment_method_name = '$name', payment_method_description = '$description' WHERE payment_method_id = $payment_method_id");
logAction("Payment Method", "Edit", "$session_name edited Payment Method $name");
flash_alert("Payment Method <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['delete_payment_method'])) {
$payment_method_id = intval($_GET['delete_payment_method']);
$payment_method_name = sanitizeInput(getFieldById('payment_methods', $payment_method_is, 'payment_method_name'));
mysqli_query($mysqli,"DELETE FROM payment_methods WHERE payment_method_id = $payment_method_id");
logAction("Payment Method", "Delete", "$session_name deleted Payment Method $payment_method_name");
flash_alert("Payment Method <strong>$payment_method_name</strong> deleted", 'error');
redirect();
}

View File

@@ -0,0 +1,115 @@
<?php
/*
* ITFlow - GET/POST request handler for AI Providers ('ai_providers')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_payment_provider'])) {
validateCSRFToken($_POST['csrf_token']);
$provider = sanitizeInput($_POST['provider']);
$public_key = sanitizeInput($_POST['public_key']);
$private_key = sanitizeInput($_POST['private_key']);
$threshold = floatval($_POST['threshold']);
$enable_expense = intval($_POST['enable_expense'] ?? 0);
$percentage_fee = floatval($_POST['percentage_fee']) / 100 ?? 0;
$flat_fee = floatval($_POST['flat_fee']) ?? 0;
// Check to ensure provider isn't added twice
$sql = "SELECT 1 FROM payment_providers WHERE payment_provider_name = '$provider' LIMIT 1";
$result = mysqli_query($mysqli, $sql);
if (mysqli_num_rows($result) > 0) {
flash_alert("Payment Provider <strong>$provider</strong> already exists", 'error');
redirect();
}
// Check for Stripe Account, if not create it
$sql_account = mysqli_query($mysqli,"SELECT account_id FROM accounts WHERE account_name = '$provider' AND account_archived_at IS NULL LIMIT 1");
if (mysqli_num_rows($sql_account) == 0) {
$account_id = mysqli_insert_id($mysqli);
} else {
$row = mysqli_fetch_array($sql_account);
$account_id = intval($row['account_id']);
}
// Expense defaults
$category_id = 0;
$vendor_id = 0;
if ($enable_expense) {
// Category
$sql_category = mysqli_query($mysqli,"SELECT category_id FROM categories WHERE category_name = 'Payment Processing' AND category_type = 'Expense' AND category_archived_at IS NULL LIMIT 1");
if (mysqli_num_rows($sql_category) == 0) {
mysqli_query($mysqli,"INSERT INTO categories SET category_name = 'Processing Fee', category_type = 'Payment Processing', category_color = 'gray'");
$category_id = mysqli_insert_id($mysqli);
} else {
$row = mysqli_fetch_array($sql_category);
$category_id = intval($row['category_id']);
}
// Vendor
$sql_vendor = mysqli_query($mysqli,"SELECT vendor_id FROM vendors WHERE vendor_name = '$provider' AND vendor_client_id = 0 AND vendor_archived_at IS NULL LIMIT 1");
if (mysqli_num_rows($sql_vendor) == 0) {
mysqli_query($mysqli,"INSERT INTO vendors SET vendor_name = '$provider', vendor_description = 'Payment Processor Provider', vendor_client_id = 0");
$vendor_id = mysqli_insert_id($mysqli);
} else {
$row = mysqli_fetch_array($sql_vendor);
$vendor_id = intval($row['vendor_id']);
}
}
mysqli_query($mysqli,"INSERT INTO payment_providers SET payment_provider_name = '$provider', payment_provider_public_key = '$public_key', payment_provider_private_key = '$private_key', payment_provider_threshold = $threshold, payment_provider_account = $account_id, payment_provider_expense_vendor = $vendor_id, payment_provider_expense_category = $category_id, payment_provider_expense_percentage_fee = $percentage_fee, payment_provider_expense_flat_fee = $flat_fee");
$provider_id = mysqli_insert_id($mysqli);
logAction("Payment Provider", "Create", "$session_name created AI Provider $provider");
flash_alert("Payment provider <strong>$provider</strong> created");
redirect();
}
if (isset($_POST['edit_payment_provider'])) {
validateCSRFToken($_POST['csrf_token']);
$provider_id = intval($_POST['provider_id']);
$description = sanitizeInput($_POST['description']);
$public_key = sanitizeInput($_POST['public_key']);
$private_key = sanitizeInput($_POST['private_key']);
$threshold = floatval($_POST['threshold']);
$enable_expense = intval($_POST['enable_expense'] ?? 0);
$percentage_fee = floatval($_POST['percentage_fee']) / 100;
$flat_fee = floatval($_POST['flat_fee']);
mysqli_query($mysqli,"UPDATE payment_providers SET payment_provider_public_key = '$public_key', payment_provider_private_key = '$private_key', payment_provider_threshold = $threshold, payment_provider_expense_percentage_fee = $percentage_fee, payment_provider_expense_flat_fee = $flat_fee WHERE payment_provider_id = $provider_id");
logAction("Payment Provider", "Edit", "$session_name edited Payment Provider $provider");
flash_alert("Payment Provider <strong>$provider</strong> edited");
redirect();
}
if (isset($_GET['delete_payment_provider'])) {
validateCSRFToken($_GET['csrf_token']);
$provider_id = intval($_GET['delete_payment_provider']);
$provider_name = sanitizeInput(getFieldById('payment_providers', $provider_id, 'provider_name'));
// Delete provider
mysqli_query($mysqli,"DELETE FROM payment_providers WHERE payment_provider_id = $provider_id");
logAction("Payment Provider", "Delete", "$session_name deleted Payment Provider $provider_name");
flash_alert("Payment Provider <strong>$provider_name</strong> deleted", 'error');
redirect();
}

View File

@@ -11,12 +11,11 @@ if (isset($_POST['add_project_template'])) {
$project_template_id = mysqli_insert_id($mysqli);
// Logging
logAction("Project Template", "Create", "$session_name created project template $name", 0, $project_template_id);
$_SESSION['alert_message'] = "Project Template <strong>$name</strong> created";
flash_alert("Project Template <strong>$name</strong> created");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -28,12 +27,12 @@ if (isset($_POST['edit_project_template'])) {
mysqli_query($mysqli, "UPDATE project_templates SET project_template_name = '$name', project_template_description = '$description' WHERE project_template_id = $project_template_id");
// Logging
logAction("Project Template", "Edit", "$session_name edited project template $name", 0, $project_template_id);
$_SESSION['alert_message'] = "Project Template <strong>$name</strong> edited";
flash_alert("Project Template <strong>$name</strong> edited");
redirect();
header("Location: " . $_SERVER["HTTP_REFERER"]);
}
if (isset($_POST['edit_ticket_template_order'])) {
@@ -44,7 +43,8 @@ if (isset($_POST['edit_ticket_template_order'])) {
mysqli_query($mysqli, "UPDATE project_template_ticket_templates SET ticket_template_order = $order WHERE ticket_template_id = $ticket_template_id AND project_template_id = $project_template_id");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
if (isset($_POST['add_ticket_template_to_project_template'])) {
@@ -55,12 +55,12 @@ if (isset($_POST['add_ticket_template_to_project_template'])) {
mysqli_query($mysqli, "INSERT INTO project_template_ticket_templates SET project_template_id = $project_template_id, ticket_template_id = $ticket_template_id, ticket_template_order = $order");
// Logging
logAction("Project Template", "Edit", "$session_name added ticket template to project_template", 0, $project_template_id);
$_SESSION['alert_message'] = "Ticket template added";
flash_alert("Ticket template added");
redirect();
header("Location: " . $_SERVER["HTTP_REFERER"]);
}
if (isset($_POST['remove_ticket_template_from_project_template'])) {
@@ -71,34 +71,29 @@ if (isset($_POST['remove_ticket_template_from_project_template'])) {
mysqli_query($mysqli, "DELETE FROM project_template_ticket_templates WHERE project_template_id = $project_template_id AND ticket_template_id = $ticket_template_id");
// Logging
logAction("Project Template", "Edit", "$session_name removed ticket template from project template", 0, $project_template_id);
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Ticket template removed";
flash_alert("Ticket template removed", 'error');
redirect();
header("Location: " . $_SERVER["HTTP_REFERER"]);
}
if (isset($_GET['delete_project_template'])) {
$project_template_id = intval($_GET['delete_project_template']);
// Get project template name
$sql = mysqli_query($mysqli, "SELECT * FROM project_templates WHERE project_template_id = $project_template_id");
$row = mysqli_fetch_array($sql);
$project_template_name = sanitizeInput($row['project_template_name']);
$project_template_name = sanitizeInput(getFieldById('project_templates', $project_template_id, 'project_template_name'));
mysqli_query($mysqli, "DELETE FROM project_templates WHERE project_template_id = $project_template_id");
// Remove Associated Ticket Templates
mysqli_query($mysqli, "DELETE FROM project_template_ticket_templates WHERE project_template_id = $project_template_id");
// Logging
logAction("Project Template", "Delete", "$session_name deleted project template $project_template_name and its associated ticket templates and tasks");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Project Template <strong>$project_template_name</strong> and its associated ticket templates and tasks deleted";
flash_alert("Project Template <strong>$project_template_name</strong> and its associated ticket templates and tasks deleted", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -18,12 +18,11 @@ if (isset($_POST['add_role'])) {
$role_id = mysqli_insert_id($mysqli);
// Logging
logAction("User Role", "Create", "$session_name created user role $name", 0, $role_id);
$_SESSION['alert_message'] = "User Role <strong$name</strong> created";
flash_alert("User Role <strong$name</strong> created");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -31,8 +30,7 @@ if (isset($_POST['edit_role'])) {
validateCSRFToken($_POST['csrf_token']);
// Update role metadata
$role_id = sanitizeInput($_POST['role_id']);
$role_id = intval($_POST['role_id']);
$name = sanitizeInput($_POST['role_name']);
$description = sanitizeInput($_POST['role_description']);
$admin = intval($_POST['role_is_admin']);
@@ -53,12 +51,12 @@ if (isset($_POST['edit_role'])) {
}
// Logging
logAction("User Role", "Edit", "$session_name edited user role $name", 0, $role_id);
$_SESSION['alert_message'] = "User Role <strong>$name</strong> edited";
flash_alert("User Role <strong>$name</strong> edited");
redirect();
header("Location: " . $_SERVER["HTTP_REFERER"]);
}
if (isset($_GET['archive_role'])) {
@@ -71,19 +69,19 @@ if (isset($_GET['archive_role'])) {
$sql_role_user_count = mysqli_query($mysqli, "SELECT COUNT(user_id) FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
$role_user_count = mysqli_fetch_row($sql_role_user_count)[0];
if ($role_user_count != 0) {
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Role must not in use to archive it";
header("Location: " . $_SERVER["HTTP_REFERER"]);
exit();
flash_alert("Role must not in use to archive it", 'error');
redirect();
}
mysqli_query($mysqli, "UPDATE user_roles SET role_archived_at = NOW() WHERE role_id = $role_id");
// Logging
$role_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT role_name FROM user_roles WHERE role_id = $role_id LIMIT 1"));
$role_name = sanitizeInput($role_details['role_name']);
$role_name = sanitizeInput(getFieldById('roles', $role_id, 'role_name'));
logAction("User Role", "Archive", "$session_name archived user role $role_name", 0, $role_id);
$_SESSION['alert_message'] = "User Role archived";
header("Location: " . $_SERVER["HTTP_REFERER"]);
flash_alert("User Role <strong>$role_name</strong> archived", 'error');
redirect();
}

View File

@@ -0,0 +1,70 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_GET['delete_saved_payment'])) {
validateCSRFToken($_GET['csrf_token']);
$saved_payment_id = intval($_GET['delete_saved_payment']);
$sql = mysqli_query($mysqli, "
SELECT
client_saved_payment_methods.saved_payment_id,
client_saved_payment_methods.saved_payment_client_id,
client_saved_payment_methods.saved_payment_provider_id,
client_saved_payment_methods.saved_payment_provider_method,
client_saved_payment_methods.saved_payment_description,
client_payment_provider.payment_provider_client,
payment_providers.payment_provider_name,
payment_providers.payment_provider_private_key
FROM client_saved_payment_methods
LEFT JOIN client_payment_provider
ON client_payment_provider.client_id = client_saved_payment_methods.saved_payment_client_id
AND client_payment_provider.payment_provider_id = client_saved_payment_methods.saved_payment_provider_id
LEFT JOIN payment_providers
ON payment_providers.payment_provider_id = client_saved_payment_methods.saved_payment_provider_id
WHERE client_saved_payment_methods.saved_payment_id = $saved_payment_id"
);
$row = mysqli_fetch_array($sql);
$client_id = intval($row['saved_payment_client_id']);
$provider_id = intval($row['saved_payment_provider_id']);
$payment_provider_name = nullable_htmlentities($row['payment_provider_name']);
$saved_payment_description = nullable_htmlentities($row['saved_payment_description']);
$provider_client = nullable_htmlentities($row['payment_provider_client']);
$payment_method = $row['saved_payment_provider_method'];
$private_key = $row['payment_provider_private_key'];
// Separate logic for each Payment Provider
if ($payment_provider_name == 'Stripe') {
try {
// Initialize stripe
require_once 'plugins/stripe-php/init.php';
$stripe = new \Stripe\StripeClient($private_key);
// Detach PM
$stripe->paymentMethods->detach($payment_method, []);
} catch (Exception $e) {
$error = $e->getMessage();
error_log("Stripe payment error - encountered exception when removing payment method info for $payment_method: $error");
logApp("Stripe", "error", "Exception removing payment method for $payment_method: $error");
}
}
// Remove payment method from ITFlow
mysqli_query($mysqli, "DELETE FROM client_saved_payment_methods WHERE saved_payment_id = $saved_payment_id");
// SQL Cascade delete will Remove All Associated Auto Payment Methods on recurring invoices in the recurring payments table.
logAction("Payment Provider", "Update", "$session_name deleted saved payment method $saved_payment_description (PM: $payment_method)", $client_id);
flash_alert("Payment method <strong>$saved_payment_description</strong> removed", 'error');
redirect();
}

View File

@@ -18,11 +18,10 @@ if (isset($_POST['edit_ai_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_ai_enable = $ai_enable, config_ai_provider = '$provider', config_ai_model = '$model', config_ai_url = '$url', config_ai_api_key = '$api_key' WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited AI settings");
$_SESSION['alert_message'] = "AI Settings updated";
flash_alert("AI Settings updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -28,13 +28,13 @@ if (isset($_POST['edit_company'])) {
$file_tmp_path = $_FILES['file']['tmp_name'];
// directory in which the uploaded file will be moved
$upload_file_dir = "uploads/settings/";
$upload_file_dir = "../uploads/settings/";
$dest_path = $upload_file_dir . $new_file_name;
move_uploaded_file($file_tmp_path, $dest_path);
// Delete old file
unlink("uploads/settings/$existing_file_name");
unlink("../uploads/settings/$existing_file_name");
// Set Logo
mysqli_query($mysqli,"UPDATE companies SET company_logo = '$new_file_name' WHERE company_id = 1");
@@ -44,12 +44,11 @@ if (isset($_POST['edit_company'])) {
mysqli_query($mysqli,"UPDATE companies SET company_name = '$name', company_address = '$address', company_city = '$city', company_state = '$state', company_zip = '$zip', company_country = '$country', company_phone_country_code = '$phone_country_code', company_phone = '$phone', company_email = '$email', company_website = '$website', company_tax_id = '$tax_id' WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited company details");
$_SESSION['alert_message'] = "Company <strong>$name</strong> edited";
flash_alert("Company <strong>$name</strong> edited");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -59,14 +58,14 @@ if (isset($_GET['remove_company_logo'])) {
$row = mysqli_fetch_array($sql);
$company_logo = $row['company_logo']; // FileSystem Operation Logo is already sanitized
unlink("uploads/settings/$company_logo");
unlink("../uploads/settings/$company_logo");
mysqli_query($mysqli,"UPDATE companies SET company_logo = NULL WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name deleted company logo");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Removed company logo";
flash_alert("Removed company logo", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -19,10 +19,10 @@ if (isset($_POST['edit_default_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_start_page = '$start_page', config_default_expense_account = $expense_account, config_default_payment_account = $payment_account, config_default_payment_method = '$payment_method', config_default_expense_payment_method = '$expense_payment_method', config_default_transfer_from_account = $transfer_from_account, config_default_transfer_to_account = $transfer_to_account, config_default_calendar = $calendar, config_default_net_terms = $net_terms, config_default_hourly_rate = $hourly_rate WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited default settings");
$_SESSION['alert_message'] = "Default settings edited";
flash_alert("Default settings edited");
redirect();
header("Location: " . $_SERVER["HTTP_REFERER"]);
}

View File

@@ -21,11 +21,10 @@ if (isset($_POST['edit_invoice_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_invoice_prefix = '$config_invoice_prefix', config_invoice_next_number = $config_invoice_next_number, config_invoice_footer = '$config_invoice_footer', config_invoice_show_tax_id = $config_invoice_show_tax_id, config_invoice_late_fee_enable = $config_invoice_late_fee_enable, config_invoice_late_fee_percent = $config_invoice_late_fee_percent, config_invoice_paid_notification_email = '$config_invoice_paid_notification_email', config_recurring_invoice_prefix = '$config_recurring_invoice_prefix', config_recurring_invoice_next_number = $config_recurring_invoice_next_number WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited invoice settings");
$_SESSION['alert_message'] = "Invoice Settings edited";
flash_alert("Invoice Settings edited");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -14,11 +14,10 @@ if (isset($_POST['edit_localization'])) {
mysqli_query($mysqli,"UPDATE settings SET config_timezone = '$timezone' WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited localization settings");
$_SESSION['alert_message'] = "Company localization updated";
flash_alert("Company localization updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -0,0 +1,186 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_mail_smtp_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_smtp_provider = sanitizeInput($_POST['config_smtp_provider']);
$config_smtp_host = sanitizeInput($_POST['config_smtp_host']);
$config_smtp_port = intval($_POST['config_smtp_port'] ?? 0);
$config_smtp_encryption = sanitizeInput($_POST['config_smtp_encryption']);
$config_smtp_username = sanitizeInput($_POST['config_smtp_username']);
$config_smtp_password = sanitizeInput($_POST['config_smtp_password']);
// Shared OAuth fields
$config_mail_oauth_client_id = sanitizeInput($_POST['config_mail_oauth_client_id']);
$config_mail_oauth_client_secret = sanitizeInput($_POST['config_mail_oauth_client_secret']);
$config_mail_oauth_tenant_id = sanitizeInput($_POST['config_mail_oauth_tenant_id']);
$config_mail_oauth_refresh_token = sanitizeInput($_POST['config_mail_oauth_refresh_token']);
$config_mail_oauth_access_token = sanitizeInput($_POST['config_mail_oauth_access_token']);
mysqli_query($mysqli, "
UPDATE settings SET
config_smtp_provider = '$config_smtp_provider',
config_smtp_host = '$config_smtp_host',
config_smtp_port = $config_smtp_port,
config_smtp_encryption = '$config_smtp_encryption',
config_smtp_username = '$config_smtp_username',
config_smtp_password = '$config_smtp_password',
config_mail_oauth_client_id = '$config_mail_oauth_client_id',
config_mail_oauth_client_secret = '$config_mail_oauth_client_secret',
config_mail_oauth_tenant_id = '$config_mail_oauth_tenant_id',
config_mail_oauth_refresh_token = '$config_mail_oauth_refresh_token',
config_mail_oauth_access_token = '$config_mail_oauth_access_token'
WHERE company_id = 1
");
logAction("Settings", "Edit", "$session_name edited SMTP settings");
flash_alert("SMTP Mail Settings updated");
redirect();
}
if (isset($_POST['edit_mail_imap_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_imap_provider = sanitizeInput($_POST['config_imap_provider']);
$config_imap_host = sanitizeInput($_POST['config_imap_host']);
$config_imap_port = intval($_POST['config_imap_port'] ?? 0);
$config_imap_encryption = sanitizeInput($_POST['config_imap_encryption']);
$config_imap_username = sanitizeInput($_POST['config_imap_username']);
$config_imap_password = sanitizeInput($_POST['config_imap_password']);
// Shared OAuth fields
$config_mail_oauth_client_id = sanitizeInput($_POST['config_mail_oauth_client_id']);
$config_mail_oauth_client_secret = sanitizeInput($_POST['config_mail_oauth_client_secret']);
$config_mail_oauth_tenant_id = sanitizeInput($_POST['config_mail_oauth_tenant_id']);
$config_mail_oauth_refresh_token = sanitizeInput($_POST['config_mail_oauth_refresh_token']);
$config_mail_oauth_access_token = sanitizeInput($_POST['config_mail_oauth_access_token']);
mysqli_query($mysqli, "
UPDATE settings SET
config_imap_provider = '$config_imap_provider',
config_imap_host = '$config_imap_host',
config_imap_port = $config_imap_port,
config_imap_encryption = '$config_imap_encryption',
config_imap_username = '$config_imap_username',
config_imap_password = '$config_imap_password',
config_mail_oauth_client_id = '$config_mail_oauth_client_id',
config_mail_oauth_client_secret = '$config_mail_oauth_client_secret',
config_mail_oauth_tenant_id = '$config_mail_oauth_tenant_id',
config_mail_oauth_refresh_token = '$config_mail_oauth_refresh_token',
config_mail_oauth_access_token = '$config_mail_oauth_access_token'
WHERE company_id = 1
");
logAction("Settings", "Edit", "$session_name edited IMAP settings");
flash_alert("IMAP Mail Settings updated");
redirect();
}
if (isset($_POST['edit_mail_from_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_mail_from_email = sanitizeInput(filter_var($_POST['config_mail_from_email'], FILTER_VALIDATE_EMAIL));
$config_mail_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_mail_from_name']));
$config_invoice_from_email = sanitizeInput(filter_var($_POST['config_invoice_from_email'], FILTER_VALIDATE_EMAIL));
$config_invoice_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_invoice_from_name']));
$config_quote_from_email = sanitizeInput(filter_var($_POST['config_quote_from_email'], FILTER_VALIDATE_EMAIL));
$config_quote_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_quote_from_name']));
$config_ticket_from_email = sanitizeInput(filter_var($_POST['config_ticket_from_email'], FILTER_VALIDATE_EMAIL));
$config_ticket_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_ticket_from_name']));
mysqli_query($mysqli,"UPDATE settings SET config_mail_from_email = '$config_mail_from_email', config_mail_from_name = '$config_mail_from_name', config_invoice_from_email = '$config_invoice_from_email', config_invoice_from_name = '$config_invoice_from_name', config_quote_from_email = '$config_quote_from_email', config_quote_from_name = '$config_quote_from_name', config_ticket_from_email = '$config_ticket_from_email', config_ticket_from_name = '$config_ticket_from_name' WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited mail from settings");
flash_alert("Mail From Settings updated");
redirect();
}
if (isset($_POST['test_email_smtp'])) {
validateCSRFToken($_POST['csrf_token']);
$test_email = intval($_POST['test_email']);
if($test_email == 1) {
$email_from = sanitizeInput($config_mail_from_email);
$email_from_name = sanitizeInput($config_mail_from_name);
} elseif ($test_email == 2) {
$email_from = sanitizeInput($config_invoice_from_email);
$email_from_name = sanitizeInput($config_invoice_from_name);
} elseif ($test_email == 3) {
$email_from = sanitizeInput($config_quote_from_email);
$email_from_name = sanitizeInput($config_quote_from_name);
} else {
$email_from = sanitizeInput($config_ticket_from_email);
$email_from_name = sanitizeInput($config_ticket_from_name);
}
$email_to = sanitizeInput($_POST['email_to']);
$subject = "Test email from ITFlow";
$body = "This is a test email from ITFlow. If you are reading this, it worked!";
$data = [
[
'from' => $email_from,
'from_name' => $email_from_name,
'recipient' => $email_to,
'recipient_name' => 'Chap',
'subject' => $subject,
'body' => $body
]
];
$mail = addToMailQueue($data);
if ($mail === true) {
flash_alert("Test email queued! <a class='text-bold text-light' href='admin_mail_queue.php'>Check Admin > Mail queue</a>");
} else {
flash_alert("Failed to add test mail to queue", 'error');
}
redirect();
}
if (isset($_POST['test_email_imap'])) {
validateCSRFToken($_POST['csrf_token']);
// Setup your IMAP connection parameters
$hostname = "{" . $config_imap_host . ":" . $config_imap_port . "/" . $config_imap_encryption . "/novalidate-cert}INBOX";
$username = $config_imap_username;
$password = $config_imap_password;
try {
$inbox = @imap_open($hostname, $username, $password);
if ($inbox) {
imap_close($inbox);
flash_alert("Connected successfully");
} else {
throw new Exception(imap_last_error());
}
} catch (Exception $e) {
flash_alert("<strong>IMAP connection failed:</strong> " . $e->getMessage(), 'error');
}
redirect();
}

View File

@@ -19,11 +19,10 @@ if (isset($_POST['edit_module_settings'])) {
mysqli_query($mysqli, "UPDATE settings SET config_whitelabel_enabled = 0, config_whitelabel_key = '' WHERE company_id = 1");
}
// Logging
logAction("Settings", "Edit", "$session_name edited module settings");
$_SESSION['alert_message'] = "Module Settings updated";
flash_alert("Module Settings updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -14,11 +14,10 @@ if (isset($_POST['edit_notification_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_send_invoice_reminders = $config_send_invoice_reminders, config_recurring_auto_send_invoice = $config_recurring_auto_send_invoice, config_enable_cron = $config_enable_cron, config_enable_alert_domain_expire = $config_enable_alert_domain_expire, config_ticket_client_general_notifications = $config_ticket_client_general_notifications WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited notification settings");
$_SESSION['alert_message'] = "Notification Settings updated";
flash_alert("Notification Settings updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -17,15 +17,14 @@ if (isset($_POST['edit_online_payment_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_stripe_enable = $config_stripe_enable, config_stripe_publishable = '$config_stripe_publishable', config_stripe_secret = '$config_stripe_secret', config_stripe_account = $config_stripe_account, config_stripe_expense_vendor = $config_stripe_expense_vendor, config_stripe_expense_category = $config_stripe_expense_category, config_stripe_percentage_fee = $config_stripe_percentage_fee, config_stripe_flat_fee = $config_stripe_flat_fee WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited online payment settings");
if ($config_stripe_enable && $config_stripe_account == 0) {
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Stripe payment account must be specified!";
flash_alert("Stripe payment account must be specified!", 'error');
} else {
$_SESSION['alert_message'] = "Online Payment Settings updated";
flash_alert("Online Payment Settings updated");
}
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -3,12 +3,12 @@
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_GET['stripe_remove_pm'])) {
validateCSRFToken($_GET['csrf_token']);
if (!$config_stripe_enable) {
$_SESSION['alert_message'] = "Stripe not enabled";
header("Location: " . $_SERVER["HTTP_REFERER"]);
exit();
flash_alert("Stripe not enabled", 'error');
redirect();
}
$client_id = intval($_GET['client_id']);
@@ -16,7 +16,7 @@ if (isset($_GET['stripe_remove_pm'])) {
try {
// Initialize stripe
require_once 'plugins/stripe-php/init.php';
require_once '../plugins/stripe-php/init.php';
$stripe = new \Stripe\StripeClient($config_stripe_secret);
// Detach PM
@@ -39,14 +39,16 @@ if (isset($_GET['stripe_remove_pm'])) {
mysqli_query($mysqli, "DELETE FROM recurring_payments WHERE recurring_payment_method = 'Stripe' AND recurring_payment_recurring_invoice_id = $recurring_invoice_id");
}
// Logging & Redirect
logAction("Stripe", "Update", "$session_name deleted saved Stripe payment method (PM: $payment_method)", $client_id);
$_SESSION['alert_message'] = "Payment method removed";
header("Location: " . $_SERVER["HTTP_REFERER"]);
flash_alert("Payment method removed", 'error');
redirect();
}
if (isset($_GET['stripe_reset_customer'])) {
validateCSRFToken($_GET['csrf_token']);
$client_id = intval($_GET['client_id']);
@@ -62,9 +64,10 @@ if (isset($_GET['stripe_reset_customer'])) {
mysqli_query($mysqli, "DELETE FROM recurring_payments WHERE recurring_payment_method = 'Stripe' AND recurring_payment_recurring_invoice_id = $recurring_invoice_id");
}
// Logging
logAction("Stripe", "Delete", "$session_name reset Stripe settings for client", $client_id);
$_SESSION['alert_message'] = "Reset client Stripe settings";
header("Location: " . $_SERVER["HTTP_REFERER"]);
}
flash_alert("Reset client Stripe settings", 'error');
redirect();
}

View File

@@ -11,11 +11,10 @@ if (isset($_POST['edit_project_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_project_prefix = '$config_project_prefix', config_project_next_number = $config_project_next_number WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited project settings");
$_SESSION['alert_message'] = "Project Settings updated";
flash_alert("Project Settings updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -16,11 +16,10 @@ if (isset($_POST['edit_quote_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_quote_prefix = '$config_quote_prefix', config_quote_next_number = $config_quote_next_number, config_quote_footer = '$config_quote_footer', config_quote_notification_email = '$config_quote_notification_email' WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited Quote settings");
$_SESSION['alert_message'] = "Quote Settings updated";
flash_alert("Quote Settings updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -19,10 +19,10 @@ if (isset($_POST['edit_security_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_login_message = '$config_login_message', config_login_key_required = '$config_login_key_required', config_login_key_secret = '$config_login_key_secret', config_login_remember_me_expire = $config_login_remember_me_expire, config_log_retention = $config_log_retention WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited security settings");
$_SESSION['alert_message'] = "Security settings updated";
flash_alert("Security settings updated");
redirect();
header("Location: " . $_SERVER["HTTP_REFERER"]);
}

View File

@@ -10,11 +10,10 @@ if (isset($_POST['edit_telemetry_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_telemetry = $config_telemetry WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited telemetry settings");
$_SESSION['alert_message'] = "Telemetry Settings updated";
flash_alert("Telemetry Settings updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -10,12 +10,12 @@ if (isset($_POST['edit_theme_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_theme = '$theme' WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited theme settings");
logAction("Settings", "Edit", "$session_name edited theme settings $dark_mode");
$_SESSION['alert_message'] = "Changed theme to <strong>$theme</strong>";
flash_alert("Changed theme to <strong>$theme</strong>");
redirect();
header("Location: " . $_SERVER["HTTP_REFERER"]);
}
if (isset($_POST['edit_favicon_settings'])) {
@@ -28,12 +28,12 @@ if (isset($_POST['edit_favicon_settings'])) {
$file_tmp_path = $_FILES['file']['tmp_name'];
// Delete old file
if(file_exists("uploads/favicon.ico")) {
unlink("uploads/favicon.ico");
if(file_exists("../uploads/favicon.ico")) {
unlink("../uploads/favicon.ico");
}
// directory in which the uploaded file will be moved
$upload_file_dir = "uploads/";
$upload_file_dir = "../uploads/";
//Force File Name
$new_file_name = "favicon.ico";
$dest_path = $upload_file_dir . $new_file_name;
@@ -42,11 +42,10 @@ if (isset($_POST['edit_favicon_settings'])) {
}
}
// Logging
logAction("Settings", "Edit", "$session_name changed the favicon");
$_SESSION['alert_message'] = "Favicon Updated";
flash_alert("Favicon Updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

View File

@@ -21,11 +21,10 @@ if (isset($_POST['edit_ticket_settings'])) {
mysqli_query($mysqli,"UPDATE settings SET config_ticket_prefix = '$config_ticket_prefix', config_ticket_next_number = $config_ticket_next_number, config_ticket_email_parse = $config_ticket_email_parse, config_ticket_email_parse_unknown_senders = $config_ticket_email_parse_unknown_senders, config_ticket_autoclose_hours = $config_ticket_autoclose_hours, config_ticket_new_ticket_notification_email = '$config_ticket_new_ticket_notification_email', config_ticket_default_billable = $config_ticket_default_billable, config_ticket_default_view = $config_ticket_default_view, config_ticket_moving_columns = $config_ticket_moving_columns, config_ticket_ordering = $config_ticket_ordering, config_ticket_timer_autostart = $config_ticket_timer_autostart WHERE company_id = 1");
// Logging
logAction("Settings", "Edit", "$session_name edited ticket settings");
$_SESSION['alert_message'] = "Ticket Settings updated";
flash_alert("Ticket Settings updated");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
}

View File

@@ -17,12 +17,11 @@ if (isset($_POST['add_software_template'])) {
$software_template_id = mysqli_insert_id($mysqli);
// Logging
logAction("Software Template", "Create", "$session_name created software template $name", 0, $software_template_id);
$_SESSION['alert_message'] = "Software template <strong>$name</strong> created";
flash_alert("Software template <strong>$name</strong> created");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -38,12 +37,11 @@ if (isset($_POST['edit_software_template'])) {
mysqli_query($mysqli,"UPDATE software_templates SET software_template_name = '$name', software_template_version = '$version', software_template_description = '$description', software_template_type = '$type', software_template_license_type = '$license_type', software_template_notes = '$notes' WHERE software_template_id = $software_template_id");
// Logging
logAction("Software Template", "Edit", "$session_name edited software template $name", 0, $software_template_id);
$_SESSION['alert_message'] = "Software template <strong>$name</strong> edited";
flash_alert("Software template <strong>$name</strong> edited");
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}
@@ -58,12 +56,10 @@ if (isset($_GET['delete_software_template'])) {
mysqli_query($mysqli,"DELETE FROM software_templates WHERE software_template_id = $software_template_id");
//Logging
logAction("Software Template", "Delete", "$session_name deleted software template $software_template_name");
$_SESSION['alert_type'] = "error";
$_SESSION['alert_message'] = "Software Template <strong>$software_template_name</strong> deleted";
flash_alert("Software Template <strong>$software_template_name</strong> deleted", 'error');
header("Location: " . $_SERVER["HTTP_REFERER"]);
redirect();
}

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