153 Commits

Author SHA1 Message Date
Johnny
939b07422d Merge pull request #1220 from itflow-org/develop
Merge Develop into Master for 25.05 Release
2025-05-29 14:30:25 -04:00
wrongecho
88369d480a Reword changelog 2025-05-29 18:42:25 +01:00
wrongecho
cf083e94e6 Fix 2025-05-29 18:40:51 +01:00
wrongecho
11e8f5acfc Bump app version 2025-05-29 18:37:39 +01:00
wrongecho
77b3a89eb2 Reword changelog 2025-05-29 18:37:16 +01:00
johnnyq
f572f4265a Updated Changelog 2025-05-29 13:31:40 -04:00
johnnyq
0b66c8e1be Better naming of Indetity Providers instead of Integrations to make way for more Indentity Provider like Google and custom SSO for the future 2025-05-29 13:29:27 -04:00
johnnyq
a8328a3f56 Add more text-nowrap to more table headers 2025-05-29 11:45:45 -04:00
johnnyq
8b42b17121 Added more text no wrap to table headers 2025-05-29 00:18:37 -04:00
johnnyq
db418ce662 Mobile UI/UX - Hide long New Button names and use plus icon only in mobile view, proper form group spacing in mobile as well 2025-05-28 23:39:44 -04:00
johnnyq
96fe566e08 Add location country even when client id uri is not set 2025-05-28 22:03:55 -04:00
johnnyq
a00f26d8a4 UI/UX Draggables now switch to a hand on hover, updated the UI in invoice quote and reccuring invoice so buttons do not wrap and are grouped. Seperated the Dragable button using a button link, did the same for tasks in tickets 2025-05-28 21:22:21 -04:00
johnnyq
eeef63d1c3 Updated Changelog 2025-05-28 19:36:52 -04:00
johnnyq
0b88ea85ae Display Country in Invoices, Quotes, Recurring Invoices, Clients, locations, client top head, and allow searching via country in Locations and Clients 2025-05-28 19:27:11 -04:00
johnnyq
c564118156 Fix Dupe GET VARS in tickets Removed extra Rebuild URL as this has been done in the filter header for some time now 2025-05-28 18:34:50 -04:00
johnnyq
01a7dc2068 Allow both Client and Contact to be sorted in same column in ticket list 2025-05-28 18:29:02 -04:00
johnnyq
bb44ecec3f Trips UI/UX - Move Client at the end column for consistency 2025-05-28 18:13:44 -04:00
johnnyq
b7b24d7de6 UI/UX Expenses - Combined Category and Description Column with secondary text seperation and expanded Truncation of Description from 50 chars to 60 chars 2025-05-28 18:08:08 -04:00
johnnyq
dcca93e34f Only show 8 notifications at a time instead of 10 2025-05-28 17:43:03 -04:00
johnnyq
4124188505 Ticket UI/UX allow the ticket toolbar to be a little more Mobile friendly 2025-05-28 17:39:32 -04:00
johnnyq
eb5d59623b Don't show Checkbox columns when ticket is closed, compact ticket list now matches round pills for status and priority 2025-05-28 17:02:50 -04:00
Marcus Hill
8631c06731 Stripe - Remove the locally stored payment identifiers (expiry/last 4) from the database when the client removes the stripe pm 2025-05-27 19:29:03 +01:00
Marcus Hill
40eb40fd86 Cron mail queue - fix $config_smtp_encryption being set to None in IF statement 2025-05-27 14:46:50 +01:00
Marcus Hill
c486682a0e Cron mail queue - fix app_log_type not being one of available enums 2025-05-27 14:05:09 +01:00
Marcus Hill
336da073f1 Admin mail settings
- Disable the IMAP test button as it doesn't work yet
- Remove word 'successfully' when testing SMTP as we're only adding the message to the queue, it wasn't sent yet
2025-05-27 14:00:25 +01:00
Marcus Hill
d2e187a239 Update security supported versions 2025-05-26 20:34:01 +01:00
johnnyq
f69de29353 Get a more accurate count of Tables rows in Debug using count instead of relying on show table status as this is not accurate all the time. 2025-05-24 13:45:27 -04:00
johnnyq
61de8bc792 Redirect to login when itflow restore completes 2025-05-24 13:30:49 -04:00
johnnyq
d31f19707b DB Dump 2025-05-24 13:23:41 -04:00
johnnyq
811312466f If Restore from Backup is chosen show message must configure database first if DB and config dont exist 2025-05-24 13:09:16 -04:00
johnnyq
5ecfb3e962 Update setup to include welcome as an option and do not hide the side nav items if db is already created. 2025-05-24 12:40:33 -04:00
johnnyq
77be5af4e5 Update setup to include restore option but place it in its own nav section for seperation. Also if DB and config is configured skip and do not show checks or database in the side nav 2025-05-24 12:28:34 -04:00
johnnyq
c512a716d2 Fix extra spacing in modal footer in notifications 2025-05-23 19:09:36 -04:00
johnnyq
14f5630caf Fix Invoice Header Button 2025-05-23 18:34:25 -04:00
johnnyq
8532bdc172 More UI updates to Guest Pay 2025-05-23 18:25:09 -04:00
johnnyq
9d74bf8e19 Use cards in Guest Pay Invoice 2025-05-23 17:45:06 -04:00
johnnyq
f75445b4d0 Limit Stripe Payments to just Credit Cards 2025-05-23 17:18:22 -04:00
johnnyq
e04fa1b696 Add stripe_pm_created_at 2025-05-23 17:13:05 -04:00
johnnyq
e07dfb5f67 db structure 2025-05-23 15:42:47 -04:00
johnnyq
3d1af05fc2 Updated DB to store Payment details 2025-05-23 15:41:52 -04:00
johnnyq
0e38925d74 Update Changelog 2025-05-22 18:00:08 -04:00
johnnyq
c0f3343412 Client Portal Add Recurrung Invoices with option to Enable or Disable Auto Pay per recurring Invoice if Stripe is enabled and Client has a payment method. Also when removing saved auto payment methods delete all recurring payments for that client that are Stripe method also added this to the admin settings 2025-05-22 17:58:51 -04:00
johnnyq
6a368840fa Bump stripe-php from 16.4.0 to 17.2.1 2025-05-22 12:37:35 -04:00
johnnyq
5361391b3b Update changelog with the bumps 2025-05-22 12:24:49 -04:00
johnnyq
b80662bb24 Bump FullCalendar from 6.1.15 to 6.1.17 2025-05-22 12:16:16 -04:00
johnnyq
4c272b6b8d Bump DataTables from 2.2.2 to 2.3.1 2025-05-22 12:08:47 -04:00
johnnyq
96abdef3ad Bump TCPDF from 6.8.2 to 6.9.4 2025-05-22 12:04:48 -04:00
johnnyq
0b04bc79e9 Bump tinyMCE from 7.7.1 to 7.9.0 2025-05-22 11:57:54 -04:00
johnnyq
cefbbdc3a8 Bump phpMailer from 6.9.2 to 6.10.0 2025-05-22 11:46:09 -04:00
johnnyq
83ffe05a99 Update Changelog 2025-05-22 11:38:24 -04:00
johnnyq
b6f73083ef SMTP Option Encryption None now works as intended 2025-05-22 11:37:14 -04:00
johnnyq
693736023e Update changelog 2025-05-21 21:02:55 -04:00
johnnyq
fed87c93ab Migrated contact link models to the new ajax models this fixes the issue of the overlapping var contact_name and improves page load and performance in contact details 2025-05-21 12:18:42 -04:00
johnnyq
f53b77b556 Migrated asset link models to the new ajax models this fixes the issue of the overlapping var asset_name and improves page load and performance in asset details 2025-05-21 11:51:18 -04:00
wrongecho
b858d82b0b Show archived categories properly 2025-05-20 14:51:12 +01:00
wrongecho
ccb2af6d17 Fix category name/type logging when archiving/deleting a category 2025-05-20 14:50:27 +01:00
wrongecho
8d937ac8f5 Fix add asset modal icon not showing 2025-05-20 11:39:35 +01:00
wrongecho
2786fb65ed Don't show archived ticket categories in the tickets.php filter options 2025-05-20 11:39:09 +01:00
wrongecho
025532f579 Fix quote top navbar options not showing following perms work 2025-05-20 11:29:26 +01:00
wrongecho
5bd03be1ad Ticket tasks - set maxlength html attribute 2025-05-14 16:05:43 +01:00
wrongecho
40086f1ce0 Quotes / Invoicing - More role/perms enforcement 2025-05-14 11:07:25 +01:00
wrongecho
be66ad9a4c Quotes / Invoicing
- Ability to manually mark a quote as invoiced (weird css fix for this, we can remove the custom css if we make the parent button just a dropdown, but don't want to introduce extra clicks)
- When converting a quote to an invoice, show the new invoice number in the quote history
- Quotes can now be sent from the main Send dropdown, instead of having to use the send button in the options menu / main quotes.php page
2025-05-14 10:41:32 +01:00
Marcus Hill
0df5c01bb7 Project - Require CSRF token to delete a project 2025-05-11 12:25:13 +01:00
Marcus Hill
b85fa38b67 Project - Show client abbreviation in open ticket link modal 2025-05-11 12:19:34 +01:00
Marcus Hill
546246d7c5 Project - Allow editing client after creation 2025-05-11 12:14:20 +01:00
Marcus Hill
d5536e78f4 Ajax contacts - Enforce client access restrictions when getting client contacts 2025-05-11 12:01:23 +01:00
Marcus Hill
908738b7ca Ajax active clients - enforce client access restrictions (e.g. when changing ticket client) 2025-05-11 11:57:16 +01:00
Marcus Hill
797e02bffa Hide Credentials in side nav if no perms to view 2025-05-11 11:48:47 +01:00
Marcus Hill
d856685782 Merge branch 'develop' of https://github.com/itflow-org/itflow into develop 2025-05-11 11:46:00 +01:00
Marcus Hill
1400983d8c Projects/Tickets
- Hide new project button for users that only have support read access
- Hide new ticket button for users that only have support read access
- Enforce client access restrictions for viewing project details based off project client
- Prevent selecting the 'Select tickets' text when linking ticket
2025-05-11 11:45:47 +01:00
johnnyq
2a43c5d868 Remove DB Check 2025-05-08 16:46:25 -04:00
johnnyq
a67675c649 Remove 500 Records per page option to reduce Resource strain, 100 records per page is max 2025-05-08 16:08:28 -04:00
johnnyq
fc344ef636 add notification paging 2025-05-07 19:23:11 -04:00
johnnyq
2ffb2be083 Update the backup code to be a full backup zip file download of uploads and db dump along with version meta data file. Also allow to restore a single file in setup currently hidden 2025-05-07 15:37:57 -04:00
johnnyq
069772f27d Add Upload uploads.zip file to restore files as well 2025-05-07 14:54:25 -04:00
johnnyq
241ec50802 Add hidden option to restore dumped ITFlow Database during Setup 2025-05-07 14:32:51 -04:00
johnnyq
b943c9cd89 Remove Influencer as a referral 2025-05-06 19:50:51 -04:00
johnnyq
80625f8c3f Remove Unused vars in setup, added jpeg to the allow extension for user avatars 2025-05-06 19:36:31 -04:00
johnnyq
58435d3460 Add Next Button if Database is already configured 2025-05-06 17:18:54 -04:00
johnnyq
7a7ac4a47f Fix Edit Blank Recurring ticket in Asset Details 2025-05-06 13:16:56 -04:00
Marcus Hill
429dfa5ca4 Allow file upload extensions: .bat, .stk 2025-05-04 21:38:11 +01:00
wrongecho
e1f212d30d Start recording ticket source (API, Email, Portal, Agent) 2025-04-28 14:51:38 +01:00
wrongecho
670450bcfb Ticket statuses - Allow ordering from admin settings, this can replace the need to move the Kanban columns 2025-04-23 10:22:33 +01:00
Johnny
83e15e9e4a Merge pull request #1216 from itflow-org/develop
Develop to Master 25.03.6
2025-04-21 17:28:14 -04:00
johnnyq
b309081d75 Allow to search by project reference number 2025-04-21 17:16:35 -04:00
johnnyq
f1a7b35aa6 Update Changelog and App Version fix date to 2025-04-21 17:00:32 -04:00
Marcus Hill
469c5ef06d Update client pdf export
- Fix HTML formatting for the cover div, other div styling is still broken
- Adjust layout of cover info and add MSP logo
- Add software purchase and expiry dates
2025-04-19 16:30:00 +01:00
Marcus Hill
07cbe561bd Add stupidly bigger update warning to update page. Add reminder note to check ITFlow backup (one in every ten page loads) 2025-04-19 15:14:40 +01:00
Johnny
b69a70cfc3 Merge pull request #1213 from itflow-org/develop
Develop to Master - 25.03.5 Release
2025-04-18 19:08:18 -04:00
johnnyq
923001928c Update Changelog and version 2025-04-18 18:08:40 -04:00
johnnyq
75ed461c67 Asset and Contact Links now goto the details page instead of the details modal 2025-04-16 18:51:53 -04:00
johnnyq
691aebce91 Revert Fix 2025-04-13 15:15:18 -04:00
johnnyq
846947ff49 Change the button handlebar class 2025-04-13 15:11:37 -04:00
johnnyq
65e107d154 Totally remove Dragula in Favor of the modern SortableJS library, updated the Kanban 2025-04-13 15:01:52 -04:00
johnnyq
19b809b699 Added SortableJS Library, and updated Invoice, Quote and Recurring to use it. Added Grab Bar Icons next to action buttons. Will now sort in Mobile much more efficiently, update ajax vars for recurring invoice 2025-04-13 13:29:16 -04:00
johnnyq
60fe02bb47 Comment 2025-04-13 11:57:26 -04:00
johnnyq
3e708059c6 Fix not showing File folders instead of Document Folders when creating a document. 2025-04-13 11:55:14 -04:00
johnnyq
424104bb66 Default Date between max date to 9999-12-31 instead of current date for filtered listings, this fixes the issue if you post date an entity it would not show in the listing by default unless you selected a a great to date in the filter 2025-04-12 12:08:30 -04:00
johnnyq
62696b9ebe Fix Mobile Country Code in contact list 2025-04-12 11:58:32 -04:00
Johnny
87403e8c2d Merge pull request #1209 from itflow-org/index-redirect
Redirect the blank index page to the start page
2025-04-12 11:54:05 -04:00
wrongecho
7a5a607ff6 Redirect the blank index page to the start page 2025-04-11 14:53:02 +01:00
wrongecho
a195774726 Update README.md
Add JetBrains to sponsors, as they provide FOSS licenses of PhpStorm to us
2025-04-11 14:31:01 +01:00
johnnyq
8d0da7b55b Fix Entity Linking in Asset and contact details 2025-04-09 14:00:51 -04:00
Johnny
58c315cd09 Merge pull request #1207 from TamirSlo/fix-dashboard-db-update-1-9-7
Fix Dashboard following DB Update 1.9.7
2025-04-08 17:00:40 -04:00
Johnny
dd6c4602db Merge pull request #1194 from ssteeltm/develop
fix: missing kanban ticket settings
2025-04-08 16:59:07 -04:00
Tamir Slobodskoy
d413e0c8ff Fix Copy Trip Modal 2025-04-08 05:27:56 +01:00
Tamir Slobodskoy
b356658635 Fix Dashboard following DB Update 1.9.7 2025-04-08 05:02:46 +01:00
Johnny
d92f0fc49b Merge pull request #1206 from itflow-org/develop
Develop to Master for 25.03.4 Release
2025-04-07 13:31:20 -04:00
johnnyq
f206a28cf7 Update Changelog and App version to 25.03.4 2025-04-07 13:07:03 -04:00
johnnyq
70cb0ac635 Add the ability to remove additional assets from the ticket details screen 2025-04-07 12:58:55 -04:00
johnnyq
a0ece18876 Allow to remove additional assets in a ticket 2025-04-07 11:59:56 -04:00
wrongecho
4a22b03952 Global search - assets
When global search returns an asset, include a hyperlink to the asset details as part of the asset name
2025-04-04 15:12:32 +01:00
wrongecho
5ebf797c90 rm asset_mac - no longer in assets table 2025-04-04 15:08:04 +01:00
wrongecho
a20759f1f2 rm asset_mac - no longer in assets table 2025-04-04 15:07:47 +01:00
wrongecho
c273cab36e Portal - View all ticket bugfix
View all tickets should display the ticket_number rather than the database ID
2025-04-04 14:50:36 +01:00
johnnyq
8306a04eda Add Purchase Reference to Copy Asset 2025-04-03 15:07:56 -04:00
johnnyq
f078203136 Fix Database Export, was not properly exporting utf8, html data and such. Also disable foreign key contraint check in the export then renable it in the end. This fixes the issue with importing the database into phpmyadmin or using the mysql command 2025-04-03 15:01:47 -04:00
johnnyq
15e89c3c4e Fix Bulk Assign ticket to only show ITflow users and not client users 2025-04-01 17:23:44 -04:00
Johnny
595c4f1440 Merge pull request #1205 from itflow-org/develop
Develop to Master for 25.03.3 release
2025-04-01 11:45:41 -04:00
johnnyq
91a523dc23 Update Changelog 2025-04-01 11:40:24 -04:00
johnnyq
8567c97c09 Merge branch 'develop' of github.com:itflow-org/itflow into develop 2025-04-01 11:33:16 -04:00
johnnyq
3621e99c61 Update Changelog and app version 2025-04-01 11:33:04 -04:00
Johnny
d99b9cbe68 Merge pull request #1204 from itflow-org/fix-assign2
Ticket assign
2025-04-01 10:56:56 -04:00
Johnny
e319768fd3 Merge pull request #1203 from itflow-org/fix-bulk-assign
Tickets - Fix bulk assign
2025-04-01 10:56:27 -04:00
Johnny
c30ffcf096 Merge pull request #1202 from itflow-org/user-activity
User activity
2025-04-01 10:55:17 -04:00
wrongecho
7286248fef Ticket assign
Remove the role check altogether, its the old way of doing the roles anyway
2025-04-01 09:12:24 +01:00
wrongecho
dc49f80cc3 Tickets - Fix bulk assign
Fix bulk assigning tickets to agents
2025-04-01 09:03:33 +01:00
wrongecho
1ae2da8054 User activity
Hide the See More button if the user can't actually access the logs due to not being an admin
2025-04-01 08:46:22 +01:00
johnnyq
090f4cb560 Fix adding location phone extension when addign a client 2025-03-31 19:33:07 -04:00
johnnyq
0914716b8e Allow user to redact client replied tickets 2025-03-31 18:42:56 -04:00
johnnyq
ab463c1773 Tidy Phone Country code add + placeholder 2025-03-31 17:30:33 -04:00
johnnyq
36af4d11fc Few more phone fixes 2025-03-31 16:52:47 -04:00
johnnyq
14d8dc6fa6 Fix php errors thrown when formatPhone is blank 2025-03-31 16:35:36 -04:00
johnnyq
2032b48ad3 DB Update Set Country codes to default to NULL and not 1 Nullify all current country codes 2025-03-31 12:06:36 -04:00
Johnny
2af795f548 Merge pull request #1201 from itflow-org/fix-users
Fix users
2025-03-31 11:22:47 -04:00
Johnny
7b4edb2948 Merge pull request #1200 from itflow-org/recurring-invoices
Recurring invoices
2025-03-31 11:22:30 -04:00
wrongecho
17a906fd03 Users bugfixes
- Fix syntax error when adding user, thanks @fleetlognorge
- Fix old reference to scheduled_tickets
2025-03-31 08:35:42 +01:00
wrongecho
af46a1fd96 Fix syntax error when adding user, thanks @fleetlognorge 2025-03-31 08:32:14 +01:00
wrongecho
393c0b8c11 Recurring invoices
- Fix the delete link
- Cron should only flag recurring invoices that with a next-run in the past if the recurring invoice is active
2025-03-31 08:28:22 +01:00
johnnyq
e92f2f714d Fix Ticket Assign to 2025-03-30 20:48:12 -04:00
johnnyq
42606067c0 If no country code is entered display the number only no spaces hyphens or perenthesis 2025-03-30 12:22:43 -04:00
johnnyq
98bb65509d Fix setting country code in company details 2025-03-30 11:45:27 -04:00
johnnyq
a2599e5d43 Fix network location edit 2025-03-30 02:14:06 -04:00
Johnny
0390b1bc2a Merge pull request #1198 from itflow-org/develop
Develop to Master
2025-03-29 18:23:37 -04:00
johnnyq
531f3ec741 Update app version and changelog 2025-03-29 18:18:52 -04:00
johnnyq
127afdca0d DB.sql revert 2 2025-03-29 18:16:25 -04:00
johnnyq
c4df5bf988 DB.sql revert 2025-03-29 18:12:32 -04:00
Johnny
30234e044d Merge pull request #1197 from itflow-org/develop
Merge Develop into Master
2025-03-29 17:47:08 -04:00
johnnyq
1e98ee8916 Update app version and Changelog hotfix 2025-03-29 17:43:00 -04:00
johnnyq
d5665c2577 Update db.sql to match the mediumtext of the updates 2025-03-29 17:35:53 -04:00
johnnyq
762ec51a19 Fix issue with missing phone numbers 2025-03-29 16:41:40 -04:00
johnnyq
309ad724ec Fix client export to only show licnesed software by the selected client only 2025-03-29 16:22:48 -04:00
ssteeltm
a5f7b7fa9c fix: missing kanban ticket settings 2025-03-28 12:00:48 -03:00
763 changed files with 9878 additions and 9260 deletions

View File

@@ -2,6 +2,98 @@
This file documents all notable changes made to ITFlow. This file documents all notable changes made to ITFlow.
## [25.05]
### Added / Changed
- Expanded file upload allow-list to include .bat and .stk file types.
- Added full backup/restore functionality. Backup downloads a zip that includes the SQL dump and uploads folder, setup now has option to restore from zip backup.
- Migrated Asset and Contact Links to modals to resolve variable overlap issue.
- Added Pagination to Notification Modal.
- Removed 500 Records Per Page option.
- Removed unused old DB checks in the top nav.
- Clients can now use the portal to setup Stripe automatic payments themselves for recurring invoices
- Automatic payments are now disabled for all recurring invoices if the saved payment method is removed
- Added Card Details and Payment added to Client Stripe.
- UI / UX updates to guest pay Make use of cards.
- Don't show Checkbox columns when ticket is closed, compact ticket list now matches round pills for status and priority.
- Ticket UI/UX update allow the ticket toolbar to be a little more mobile-friendly
- UI / UX Updates to Expenses - Combine Category and Description into 1 column.
- Country information is now displayed in Invoices, Quotes, Recurring Invoices, Clients, Locations, and the client top header.
- Added country-based search filters in Locations and Clients sections.
- Changed the settings name from Integrations to Identity Providers to make room for future iDPs (e.g. Google).
- Bump FullCalendar from 6.1.15 to 6.1.17.
- Bump DataTables from 2.2.2 to 2.3.1.
- Bump TCPDF from 6.8.2 to 6.9.4.
- Bump tinyMCE from 7.7.1 to 7.9.0.
- Bump phpMailer from 6.9.2 to 6.10.0.
- Bump stripe-php from 16.4.0 to 17.2.1.
### Fixed
- "None" option for SMTP encryption now functions correctly.
- Debug table row counts now reflect actual counts instead of relying on SHOW TABLE STATUS.
- Archived Categories now display properly.
- Stripe saved payment methods are now limited to credit/debit cards only.
## [25.03.6]
### Fixed
- Set default to date to 2035-12-31 as 9999-12-31 and 2999-12-31 broke certain browsers.
- Update Client PDF Export, add header added company logo.
- Present Larger clearer Warning about updates on update page.
- Allow to search by project reference.
## [25.03.5]
### Fixed
- Fixed the user listing issue when copying a trip.
- Corrected the display of recurring invoice amounts on the dashboard.
- Fixed the linking of entities with assets and contacts.
- Resolved the issue with displaying the correct mobile country code in the contact listing.
- Set the default date to `9999-12-31` to ensure future items (like invoices) are displayed by default.
- Fixed the display issue where file folders were not showing properly during document creation.
- Migrated from Dragula to SortableJS for a more modern, mobile-friendly solution.
- Added Handlebars icons for drag-and-drop items.
- Changed behavior to open Contact and Asset Details pages directly instead of using a modal.
## [25.03.4]
### Fixed
- Ability to remove additional assets from the ticket details screen.
- Fix the ability to remove assets from edit ticket not working when only 1 asset exists.
- Fix Database Backup corruption.
- Client Portal - show ticket number instead of ticket id in ticket listing.
- Add Purchase Reference to copy asset.
- Add Link to asset details from the global search.
- Fix Bulk assign ticket only showing contacts instead of ITFlow users.
## [25.03.3]
### Fixed
- Fix adding ITFlow user.
- Do not alert on inactive recurring invoices.
- Fix ticket user assignment including bulk assignment.
- Fix adding a location phone extension.
- Do not default to +1 Country code, instead default to null.
- Do not format numbers unless a country code is entered.
- Fix editing network location.
- Fix ticket redaction on client replies.
- Remove more from user activity as it requires admin privledges.
- Fix MFA Enforcement page.
## [25.03.2]
### Fixed
- Revert DB.sql change
## [25.03.1]
### Fixed
- Phone number missing in various sections.
- Match Database.
- Client Export Only display licenses users and assets from the selected client only.
## [25.03] ## [25.03]
### Fixed ### Fixed

View File

@@ -93,6 +93,7 @@ If you want to improve ITFlow, feel free to fork the repo and create a pull requ
Were incredibly grateful to the organizations and individuals who support the project - a big thank you to: Were incredibly grateful to the organizations and individuals who support the project - a big thank you to:
- CompuMatter - CompuMatter
- F1 for HELP - F1 for HELP
- JetBrains (PhpStorm)
## License ## License
ITFlow is distributed "as is" under the GPL License, WITHOUT WARRANTY OF ANY KIND. See [`LICENSE`](https://github.com/itflow-org/itflow/blob/master/LICENSE) for details. ITFlow is distributed "as is" under the GPL License, WITHOUT WARRANTY OF ANY KIND. See [`LICENSE`](https://github.com/itflow-org/itflow/blob/master/LICENSE) for details.

View File

@@ -12,10 +12,8 @@
We operate a rolling release model. Any bug fixes will be released into latest version of ITFlow, so you must stay up-to-date. We operate a rolling release model. Any bug fixes will be released into latest version of ITFlow, so you must stay up-to-date.
| Version | Supported | | Version | Supported |
| ------- | ------------------ | |---------| ------------------ |
| Beta | :x: | | 25.05 | :white_check_mark: |
| 24.12 | :white_check_mark: |
| 25.1 | :white_check_mark: (When released) |
## Reporting a Vulnerability via GitHub Security Advisories ## Reporting a Vulnerability via GitHub Security Advisories

View File

@@ -48,14 +48,16 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<h3 class="card-title"><i class="fas fa-fw fa-history mr-2"></i>App Logs</h3> <h3 class="card-title"><i class="fas fa-fw fa-history mr-2"></i>App Logs</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<form class="mb-4" autocomplete="off"> <form autocomplete="off">
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-4">
<div class="input-group"> <div class="form-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search app logs"> <div class="input-group">
<div class="input-group-append"> <input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search app logs">
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button> <div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button> <button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -75,7 +75,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<form class="mb-4" autocomplete="off"> <form class="mb-4" autocomplete="off">
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-4">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search audit logs"> <input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search audit logs">
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button> <button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
@@ -85,7 +85,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<div class="form-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="client" onchange="this.form.submit()"> <select class="form-control select2" name="client" onchange="this.form.submit()">
<option value="">- All Clients -</option> <option value="">- All Clients -</option>
@@ -105,7 +105,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<div class="form-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="user" onchange="this.form.submit()"> <select class="form-control select2" name="user" onchange="this.form.submit()">
<option value="">- All Users -</option> <option value="">- All Users -</option>
@@ -125,7 +125,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<div class="form-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="type" onchange="this.form.submit()"> <select class="form-control select2" name="type" onchange="this.form.submit()">
<option value="">- All Types -</option> <option value="">- All Types -</option>
@@ -144,7 +144,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<div class="form-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="action" onchange="this.form.submit()"> <select class="form-control select2" name="action" onchange="this.form.submit()">
<option value="">- All Actions -</option> <option value="">- All Actions -</option>
@@ -198,7 +198,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<hr> <hr>
<div class="table-responsive-sm"> <div class="table-responsive-sm">
<table class="table table-sm table-striped table-borderless table-hover"> <table class="table table-sm table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>"> <thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?> text-nowrap">
<tr> <tr>
<th> <th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_created_at&order=<?php echo $disp; ?>"> <a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_created_at&order=<?php echo $disp; ?>">

View File

@@ -8,7 +8,7 @@ require_once "includes/inc_all_admin.php";
</div> </div>
<div class="card-body" style="text-align: center;"> <div class="card-body" style="text-align: center;">
<div class="alert alert-secondary">If you are unable to back up the entire VM, you'll need to back up the files & database individually. There is no built-in restore. See the <a href="https://docs.itflow.org/backups" target="_blank">docs here</a>.</div> <div class="alert alert-secondary">If you are unable to back up the entire VM, you'll need to back up the files & database individually. There is no built-in restore. See the <a href="https://docs.itflow.org/backups" target="_blank">docs here</a>.</div>
<a class="btn btn-primary btn-lg p-3" href="post.php?download_database&csrf_token=<?php echo $_SESSION['csrf_token'] ?>"><i class="fas fa-fw fa-4x fa-download"></i><br><br>Download database</a> <a class="btn btn-primary btn-lg p-3" href="post.php?download_backup&csrf_token=<?php echo $_SESSION['csrf_token'] ?>"><i class="fas fa-fw fa-4x fa-download"></i><br><br>Download Backup</a>
</div> </div>
</div> </div>
@@ -20,12 +20,12 @@ require_once "includes/inc_all_admin.php";
<div class="card-body"> <div class="card-body">
<form action="post.php" method="POST"> <form action="post.php" method="POST">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>"> <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="row d-flex justify-content-center"> <div class="d-flex justify-content-center">
<div class="input-group col-4"> <div class="input-group col-sm-4">
<div class="input-group-prepend"> <input type="password" class="form-control" placeholder="Enter your account password" name="password" autocomplete="new-password" required>
<input type="password" class="form-control" placeholder="Enter your account password" name="password" autocomplete="new-password" required> <div class="input-group-append">
<button class="btn btn-primary" type="submit" name="backup_master_key"><i class="fas fa-key"></i></button>
</div> </div>
<button class="btn btn-primary" type="submit" name="backup_master_key"><i class="fas fa-fw fa-key mr-2"></i>Get Master Key</button>
</div> </div>
</div> </div>
</form> </form>

View File

@@ -27,10 +27,6 @@ $sql = mysqli_query(
); );
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
if (isset($_GET['archived'])) {
$category = "Archived";
}
?> ?>
<div class="card card-dark"> <div class="card card-dark">
@@ -98,7 +94,7 @@ if (isset($_GET['archived'])) {
} else { } else {
echo 'btn-default'; echo 'btn-default';
} ?>">Ticket</a> } ?>">Ticket</a>
<a href="?archived=1" <a href="?<?php echo $url_query_strings_sort ?>&archived=1"
class="btn <?php if (isset($_GET['archived'])) { class="btn <?php if (isset($_GET['archived'])) {
echo 'btn-primary'; echo 'btn-primary';
} else { } else {
@@ -150,7 +146,7 @@ if (isset($_GET['archived'])) {
</button> </button>
<div class="dropdown-menu"> <div class="dropdown-menu">
<?php <?php
if ($category == "Archived") { if ($archived) {
?> ?>
<a class="dropdown-item text-success confirm-link" <a class="dropdown-item text-success confirm-link"
href="post.php?unarchive_category=<?php echo $category_id; ?>"> href="post.php?unarchive_category=<?php echo $category_id; ?>">

View File

@@ -296,7 +296,13 @@ if ($tablesResult) {
while ($table = $tablesResult->fetch_assoc()) { while ($table = $tablesResult->fetch_assoc()) {
$tableName = $table['Name']; $tableName = $table['Name'];
$tableRows = $table['Rows'];
// Accurate row count
$countResult = $mysqli->query("SELECT COUNT(*) AS cnt FROM `$tableName`");
$countRow = $countResult->fetch_assoc();
$tableRows = $countRow['cnt'];
$countResult->free();
$dataLength = $table['Data_length']; $dataLength = $table['Data_length'];
$indexLength = $table['Index_length']; $indexLength = $table['Index_length'];
$tableSize = ($dataLength + $indexLength) / (1024 * 1024); // Size in MB $tableSize = ($dataLength + $indexLength) / (1024 * 1024); // Size in MB
@@ -336,11 +342,6 @@ if ($tablesResult) {
'name' => 'Total database size (MB)', 'name' => 'Total database size (MB)',
'value' => round($totalSize, 2) . ' MB', 'value' => round($totalSize, 2) . ' MB',
]; ];
} else {
$databaseStats[] = [
'name' => 'Database connection error',
'value' => $mysqli->error,
];
} }
// Section: Database Structure Comparison // Section: Database Structure Comparison
@@ -518,24 +519,26 @@ $mysqli->close();
</ul> </ul>
<hr> <hr>
<table class="table table-bordered mb-3"> <div class="table-responsive">
<tr> <table class="table table-bordered mb-3">
<th>ITFlow release version</th> <tr>
<th><?php echo APP_VERSION; ?></th> <th>ITFlow release version</th>
</tr> <th><?php echo APP_VERSION; ?></th>
<tr> </tr>
<td>Current DB Version</td> <tr>
<td><?php echo CURRENT_DATABASE_VERSION; ?></td> <td>Current DB Version</td>
</tr> <td><?php echo CURRENT_DATABASE_VERSION; ?></td>
<tr> </tr>
<td>Current Code Commit</td> <tr>
<td><?php echo $commitHash; ?></td> <td>Current Code Commit</td>
</tr> <td><?php echo $commitHash; ?></td>
<tr> </tr>
<td>Current Branch</td> <tr>
<td><?php echo $gitBranch; ?></td> <td>Current Branch</td>
</tr> <td><?php echo $gitBranch; ?></td>
</table> </tr>
</table>
</div>
<!-- System Information Table --> <!-- System Information Table -->
<h3>System Information</h3> <h3>System Information</h3>
@@ -552,200 +555,209 @@ $mysqli->close();
<!-- PHP Extensions and Configuration Table --> <!-- PHP Extensions and Configuration Table -->
<h3 class="mt-3">PHP Extensions and Configuration</h3> <h3 class="mt-3">PHP Extensions and Configuration</h3>
<table class="table table-sm table-bordered"> <div class="table-responsive">
<!-- PHP Extensions Section --> <table class="table table-sm table-bordered">
<thead> <!-- PHP Extensions Section -->
<tr class="table-secondary"> <thead>
<th colspan="3">PHP Extensions</th> <tr class="table-secondary">
</tr> <th colspan="3">PHP Extensions</th>
</thead>
<tbody>
<?php foreach ($phpExtensions as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr> </tr>
<?php endforeach; ?> </thead>
</tbody> <tbody>
<!-- PHP Configuration Section --> <?php foreach ($phpExtensions as $check): ?>
<thead> <tr>
<tr class="table-secondary"> <td><?= htmlspecialchars($check['name']); ?></td>
<th colspan="3">PHP Configuration</th> <td class="text-center">
</tr> <?php if ($check['passed']): ?>
</thead> <i class="fas fa-check" style="color:green"></i>
<tbody> <?php else: ?>
<?php foreach ($phpConfig as $check): ?> <i class="fas fa-times" style="color:red"></i>
<tr> <?php endif; ?>
<td><?= htmlspecialchars($check['name']); ?></td> </td>
<td class="text-center"> <td><?= htmlspecialchars($check['value']); ?></td>
<?php if ($check['passed']): ?> </tr>
<i class="fas fa-check" style="color:green"></i> <?php endforeach; ?>
<?php else: ?> </tbody>
<i class="fas fa-times" style="color:red"></i> <!-- PHP Configuration Section -->
<?php endif; ?> <thead>
</td> <tr class="table-secondary">
<td><?= htmlspecialchars($check['value']); ?></td> <th colspan="3">PHP Configuration</th>
</tr> </tr>
<?php endforeach; ?> </thead>
</tbody> <tbody>
<thead> <?php foreach ($phpConfig as $check): ?>
<tr class="table-secondary"> <tr>
<th colspan="3">Shell Commands</th> <td><?= htmlspecialchars($check['name']); ?></td>
</tr> <td class="text-center">
</thead> <?php if ($check['passed']): ?>
<tbody> <i class="fas fa-check" style="color:green"></i>
<?php foreach ($shellCommands as $check): ?> <?php else: ?>
<tr> <i class="fas fa-times" style="color:red"></i>
<td><?= htmlspecialchars($check['name']); ?></td> <?php endif; ?>
<td class="text-center"> </td>
<?php if ($check['passed']): ?> <td><?= htmlspecialchars($check['value']); ?></td>
<i class="fas fa-check" style="color:green"></i> </tr>
<?php else: ?> <?php endforeach; ?>
<i class="fas fa-times" style="color:red"></i> </tbody>
<?php endif; ?> <thead>
</td> <tr class="table-secondary">
<td><?= htmlspecialchars($check['value']); ?></td> <th colspan="3">Shell Commands</th>
</tr> </tr>
<?php endforeach; ?> </thead>
</tbody> <tbody>
<thead> <?php foreach ($shellCommands as $check): ?>
<tr class="table-secondary"> <tr>
<th colspan="3">SSL Checks</th> <td><?= htmlspecialchars($check['name']); ?></td>
</tr> <td class="text-center">
</thead> <?php if ($check['passed']): ?>
<tbody> <i class="fas fa-check" style="color:green"></i>
<?php foreach ($sslChecks as $check): ?> <?php else: ?>
<tr> <i class="fas fa-times" style="color:red"></i>
<td><?= htmlspecialchars($check['name']); ?></td> <?php endif; ?>
<td class="text-center"> </td>
<?php if ($check['passed']): ?> <td><?= htmlspecialchars($check['value']); ?></td>
<i class="fas fa-check" style="color:green"></i> </tr>
<?php else: ?> <?php endforeach; ?>
<i class="fas fa-times" style="color:red"></i> </tbody>
<?php endif; ?> <thead>
</td> <tr class="table-secondary">
<td><?= htmlspecialchars($check['value']); ?></td> <th colspan="3">SSL Checks</th>
</tr> </tr>
<?php endforeach; ?> </thead>
</tbody> <tbody>
<thead> <?php foreach ($sslChecks as $check): ?>
<tr class="table-secondary"> <tr>
<th colspan="3">Domain Checks</th> <td><?= htmlspecialchars($check['name']); ?></td>
</tr> <td class="text-center">
</thead> <?php if ($check['passed']): ?>
<tbody> <i class="fas fa-check" style="color:green"></i>
<?php foreach ($domainChecks as $check): ?> <?php else: ?>
<tr> <i class="fas fa-times" style="color:red"></i>
<td><?= htmlspecialchars($check['name']); ?></td> <?php endif; ?>
<td class="text-center"> </td>
<?php if ($check['passed']): ?> <td><?= htmlspecialchars($check['value']); ?></td>
<i class="fas fa-check" style="color:green"></i> </tr>
<?php else: ?> <?php endforeach; ?>
<i class="fas fa-times" style="color:red"></i> </tbody>
<?php endif; ?> <thead>
</td> <tr class="table-secondary">
<td><?= htmlspecialchars($check['value']); ?></td> <th colspan="3">Domain Checks</th>
</tr> </tr>
<?php endforeach; ?> </thead>
</tbody> <tbody>
<?php foreach ($domainChecks as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<!-- File Permissions Table --> <!-- File Permissions Table -->
<thead> <thead>
<tr class="table-secondary"> <tr class="table-secondary">
<th colspan="3">File Permissions</th> <th colspan="3">File Permissions</th>
</tr>
</thead>
<tbody>
<?php foreach ($filePermissions as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr> </tr>
<?php endforeach; ?> </thead>
</tbody> <tbody>
</table> <?php foreach ($filePermissions as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Database Structure Comparison Table --> <!-- Database Structure Comparison Table -->
<h3 class="mt-3">Database Structure Comparison</h3> <h3 class="mt-3">Database Structure Comparison</h3>
<table class="table table-sm table-bordered"> <div class="table-responsive">
<tbody> <table class="table table-sm table-bordered">
<?php if (!empty($dbComparison)): ?> <tbody>
<?php foreach ($dbComparison as $issue): ?> <?php if (!empty($dbComparison)): ?>
<?php foreach ($dbComparison as $issue): ?>
<tr>
<td><?= htmlspecialchars($issue['name']); ?></td>
<td colspan="2"><?= htmlspecialchars($issue['status']); ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr> <tr>
<td><?= htmlspecialchars($issue['name']); ?></td> <td colspan="3">No discrepancies found between the database and db.sql file.</td>
<td colspan="2"><?= htmlspecialchars($issue['status']); ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endif; ?>
<?php else: ?> </tbody>
<tr> </table>
<td colspan="3">No discrepancies found between the database and db.sql file.</td> </div>
</tr>
<?php endif; ?>
</tbody>
</table>
<!-- Uploads Directory Stats Table --> <!-- Uploads Directory Stats Table -->
<h3 class="mt-3">Uploads Directory Stats</h3> <h3 class="mt-3">Uploads Directory Stats</h3>
<table class="table table-sm table-bordered"> <div class="table-responsive">
<tbody> <table class="table table-sm table-bordered">
<?php foreach ($uploadsStats as $stat): ?> <tbody>
<tr> <?php foreach ($uploadsStats as $stat): ?>
<td><?= htmlspecialchars($stat['name']); ?></td> <tr>
<td colspan="2"><?= htmlspecialchars($stat['value']); ?></td> <td><?= htmlspecialchars($stat['name']); ?></td>
</tr> <td colspan="2"><?= htmlspecialchars($stat['value']); ?></td>
<?php endforeach; ?> </tr>
</tbody> <?php endforeach; ?>
</table> </tbody>
</table>
</div>
<!-- Database Stats Table --> <!-- Database Stats Table -->
<h3 class="mt-3">Database Stats</h3> <h3 class="mt-3">Database Stats</h3>
<table class="table table-sm table-bordered"> <div class="table-responsive">
<tbody> <table class="table table-sm table-bordered">
<?php foreach ($databaseStats as $stat): ?> <tbody>
<tr> <?php foreach ($databaseStats as $stat): ?>
<td><?= htmlspecialchars($stat['name']); ?></td> <tr>
<td colspan="2"><?= htmlspecialchars($stat['value']); ?></td> <td><?= htmlspecialchars($stat['name']); ?></td>
</tr> <td colspan="2"><?= htmlspecialchars($stat['value']); ?></td>
<?php endforeach; ?> </tr>
</tbody> <?php endforeach; ?>
</table> </tbody>
</table>
</div>
<!-- Table Stats Table --> <!-- Table Stats Table -->
<h3 class="mt-3">Table Stats</h3> <h3 class="mt-3">Table Stats</h3>
<table class="table table-sm table-bordered"> <div class="table-responsive">
<thead> <table class="table table-sm table-bordered">
<tr> <thead>
<th>Table Name</th>
<th>Fields / Rows</th>
<th>Size (MB)</th>
</tr>
</thead>
<tbody>
<?php foreach ($tableDetails as $table): ?>
<tr> <tr>
<td><?= htmlspecialchars($table['name']); ?></td> <th>Table Name</th>
<td><?= htmlspecialchars("Fields: {$table['fields']}, Rows: {$table['rows']}"); ?></td> <th>Fields / Rows</th>
<td><?= htmlspecialchars($table['size'] . ' MB'); ?></td> <th>Size (MB)</th>
</tr> </tr>
<?php endforeach; ?> </thead>
</tbody> <tbody>
</table> <?php foreach ($tableDetails as $table): ?>
<tr>
<td><?= htmlspecialchars($table['name']); ?></td>
<td><?= htmlspecialchars("Fields: {$table['fields']}, Rows: {$table['rows']}"); ?></td>
<td><?= htmlspecialchars($table['size'] . ' MB'); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div> </div>

View File

@@ -4,13 +4,29 @@ require_once "includes/inc_all_admin.php";
<div class="card card-dark"> <div class="card card-dark">
<div class="card-header py-3"> <div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-plug mr-2"></i>Integration Settings</h3> <h3 class="card-title"><i class="fas fa-fw fa-fingerprint mr-2"></i>Identity Providers</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<form action="post.php" method="post" autocomplete="off"> <form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>"> <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<h4>Client Portal SSO via Microsoft Entra</h4> <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>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
</div>
<select class="form-control select2" readonly>
<option <?php if (empty($config_azure_client_id)) { echo "selected"; } ?>>Disabled</option>
<option <?php if ($config_azure_client_id) { echo "selected"; } ?>>Microsoft Entra</option>
<option>Google (WIP)</option>
<option>Custom SSO (WIP)</option>
</select>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label>MS Entra OAuth App (Client) ID</label> <label>MS Entra OAuth App (Client) ID</label>
<div class="input-group"> <div class="input-group">
@@ -33,11 +49,10 @@ require_once "includes/inc_all_admin.php";
<hr> <hr>
<button type="submit" name="edit_integrations_settings" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button> <button type="submit" name="edit_identity_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
</form> </form>
</div> </div>
</div> </div>
<?php require_once "includes/footer.php"; <?php require_once "includes/footer.php";

View File

@@ -50,7 +50,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<hr> <hr>
<div class="table-responsive-sm"> <div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover"> <table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>"> <thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?> text-nowrap">
<tr> <tr>
<th> <th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=role_name&order=<?php echo $disp; ?>"> <a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=role_name&order=<?php echo $disp; ?>">

View File

@@ -113,13 +113,13 @@ $company_initials = nullable_htmlentities(initials($company_name));
<label>Phone</label> <label>Phone</label>
<div class="form-row"> <div class="form-row">
<div class="col-9"> <div class="col-md-9">
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="phone_country_code" value="+<?php echo $company_phone_country_code; ?>" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="phone_country_code" value="<?php echo $company_phone_country_code; ?>" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="phone" value="<?php echo $company_phone; ?>" placeholder="Phone Number" maxlength="200"> <input type="tel" class="form-control" name="phone" value="<?php echo $company_phone; ?>" placeholder="Phone Number" maxlength="200">
</div> </div>
</div> </div>

View File

@@ -319,7 +319,7 @@ require_once "includes/inc_all_admin.php";
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>"> <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="input-group-append"> <div class="input-group-append">
<button type="submit" name="test_email_imap" class="btn btn-success"><i class="fas fa-fw fa-inbox mr-2"></i>Test</button> <button type="submit" name="test_email_imap" class="btn btn-success" disabled><i class="fas fa-fw fa-inbox mr-2"></i>Test (WIP)</button>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -18,158 +18,159 @@ require_once "includes/inc_all_admin.php";
<label class="custom-control-label" for="enableCronSwitch">Enable Cron (recommended) <small>(several cron scripts must also be added to cron with correct schedules, <a href="https://docs.itflow.org/cron">docs</a>)</small></label> <label class="custom-control-label" for="enableCronSwitch">Enable Cron (recommended) <small>(several cron scripts must also be added to cron with correct schedules, <a href="https://docs.itflow.org/cron">docs</a>)</small></label>
</div> </div>
</div> </div>
<div class="table-responsive">
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th>Notification</th>
<th>App Notify</th>
<th>Tech Email Notify</th>
<th>Client Email Notify</th>
<th>Create Ticket</th>
</tr>
</thead>
<tbody>
<tr>
<th colspan=5>Expirations</th>
</tr>
<tr>
<th>
<div><i class="fas fa-fw fa-globe mr-2"></i>Domain Expiration Notice</div>
<small class="text-muted">
(This setting triggers a notification when a domain is approaching its expiration date, specifically at 1, 7 and 45 days prior to expiry.)
</small>
</th>
<td>
<div class="custom-control custom-checkbox text-center">
<input type="checkbox" class="custom-control-input" name="config_enable_alert_domain_expire" id="customCheck1" <?php if ($config_enable_alert_domain_expire == 1) { echo "checked"; } ?> value="1">
<label class="custom-control-label" for="customCheck1"></label>
</div>
</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<th>
<div><i class="fas fa-fw fa-lock mr-2"></i>Certificate Expiration Notice</div>
<small class="text-muted">
(This setting triggers a notification when a certificate is approaching its expiration date, specifically at 1, 7 and 45 days prior to expiry.)
</small>
</th>
<td>
</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<th>
<div><i class="fas fa-fw fa-desktop mr-2"></i>Asset Warranty Expiration Notice</div>
<small class="text-muted">
(This setting triggers a notification when an asset is approaching its expiration date, specifically at 1, 7 and 45 days prior to expiry.)
</small>
</th>
<td>
</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<th colspan=5>Billing</th>
</tr>
<tr>
<th>
<div><i class="fas fa-fw fa-file-invoice mr-2"></i>Invoice Reminders</div>
<small class="text-muted">
(This will automatically dispatch a reminder email for the invoice to the primary contact's email every 30 days following the invoice's due date.)
</small>
</th>
<td>
<table class="table table-bordered"> </td>
<thead class="thead-dark"> <td></td>
<tr> <td>
<th>Notification</th> <div class="custom-control custom-checkbox text-center">
<th>App Notify</th> <input type="checkbox" class="custom-control-input" name="config_send_invoice_reminders" <?php if ($config_send_invoice_reminders == 1) { echo "checked"; } ?> value="1" id="sendInvoiceRemindersSwitch">
<th>Tech Email Notify</th> <label class="custom-control-label" for="sendInvoiceRemindersSwitch"></label>
<th>Client Email Notify</th> </div>
<th>Create Ticket</th> </td>
</tr> <td></td>
</thead> </tr>
<tbody> <tr>
<tr> <th>
<th colspan=5>Expirations</th> <div><i class="fas fa-fw fa-redo-alt mr-2"></i>Send Recurring Invoice</div>
</tr> <small class="text-muted">
<tr> (This will notify all primary and billing contacts of a client that a new invoice was generated from recurring invoices)
<th> </small>
<div><i class="fas fa-fw fa-globe mr-2"></i>Domain Expiration Notice</div> </th>
<small class="text-muted"> <td>
(This setting triggers a notification when a domain is approaching its expiration date, specifically at 1, 7 and 45 days prior to expiry.)
</small>
</th>
<td>
<div class="custom-control custom-checkbox text-center">
<input type="checkbox" class="custom-control-input" name="config_enable_alert_domain_expire" id="customCheck1" <?php if ($config_enable_alert_domain_expire == 1) { echo "checked"; } ?> value="1">
<label class="custom-control-label" for="customCheck1"></label>
</div>
</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<th>
<div><i class="fas fa-fw fa-lock mr-2"></i>Certificate Expiration Notice</div>
<small class="text-muted">
(This setting triggers a notification when a certificate is approaching its expiration date, specifically at 1, 7 and 45 days prior to expiry.)
</small>
</th>
<td>
</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<th>
<div><i class="fas fa-fw fa-desktop mr-2"></i>Asset Warranty Expiration Notice</div>
<small class="text-muted">
(This setting triggers a notification when an asset is approaching its expiration date, specifically at 1, 7 and 45 days prior to expiry.)
</small>
</th>
<td>
</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<th colspan=5>Billing</th>
</tr>
<tr>
<th>
<div><i class="fas fa-fw fa-file-invoice mr-2"></i>Invoice Reminders</div>
<small class="text-muted">
(This will automatically dispatch a reminder email for the invoice to the primary contact's email every 30 days following the invoice's due date.)
</small>
</th>
<td>
</td> </td>
<td></td> <td></td>
<td> <td>
<div class="custom-control custom-checkbox text-center"> <div class="custom-control custom-checkbox text-center">
<input type="checkbox" class="custom-control-input" name="config_send_invoice_reminders" <?php if ($config_send_invoice_reminders == 1) { echo "checked"; } ?> value="1" id="sendInvoiceRemindersSwitch"> <input type="checkbox" class="custom-control-input" name="config_recurring_auto_send_invoice" <?php if ($config_recurring_auto_send_invoice == 1) { echo "checked"; } ?> value="1" id="sendRecurringSwitch">
<label class="custom-control-label" for="sendInvoiceRemindersSwitch"></label> <label class="custom-control-label" for="sendRecurringSwitch"></label>
</div> </div>
</td> </td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<th> <th colspan=5>Operational</th>
<div><i class="fas fa-fw fa-redo-alt mr-2"></i>Send Recurring Invoice</div> </tr>
<small class="text-muted"> <tr>
(This will notify all primary and billing contacts of a client that a new invoice was generated from recurring invoices) <th>
</small> <div><i class="fas fa-fw fa-bell mr-2"></i>Send clients general notification emails</div>
</th> <small class="text-secondary">(Should clients receive automatic emails when tickets are raised/closed?)</small>
<td> </th>
<td></td>
</td> <td></td>
<td></td> <td>
<td> <div class="custom-control custom-checkbox text-center">
<div class="custom-control custom-checkbox text-center"> <input type="checkbox" class="custom-control-input" name="config_ticket_client_general_notifications" <?php if($config_ticket_client_general_notifications == 1){ echo "checked"; } ?> value="1" id="ticketNotificationSwitch">
<input type="checkbox" class="custom-control-input" name="config_recurring_auto_send_invoice" <?php if ($config_recurring_auto_send_invoice == 1) { echo "checked"; } ?> value="1" id="sendRecurringSwitch"> <label class="custom-control-label" for="ticketNotificationSwitch"></label>
<label class="custom-control-label" for="sendRecurringSwitch"></label> </div>
</div> </td>
</td> <td></td>
<td></td> </tr>
</tr> <tr>
<tr> <th>
<th colspan=5>Operational</th> <div><i class="fas fa-fw fa-link mr-2"></i>Shared Item View</div>
</tr> <small class="text-secondary">(Notify when Shared items are viewed)</small>
<tr> </th>
<th> <td></td>
<div><i class="fas fa-fw fa-bell mr-2"></i>Send clients general notification emails</div> <td></td>
<small class="text-secondary">(Should clients receive automatic emails when tickets are raised/closed?)</small> <td>
</th> </td>
<td></td> <td></td>
<td></td> </tr>
<td> <tr>
<div class="custom-control custom-checkbox text-center"> <th>
<input type="checkbox" class="custom-control-input" name="config_ticket_client_general_notifications" <?php if($config_ticket_client_general_notifications == 1){ echo "checked"; } ?> value="1" id="ticketNotificationSwitch"> <div><i class="fas fa-fw fa-clock mr-2"></i>Cron Execution</div>
<label class="custom-control-label" for="ticketNotificationSwitch"></label> <small class="text-secondary">(Notify when the nightly cron job ran)</small>
</div> </th>
</td> <td></td>
<td></td> <td></td>
</tr> <td>
<tr> </td>
<th> <td></td>
<div><i class="fas fa-fw fa-link mr-2"></i>Shared Item View</div> </tr>
<small class="text-secondary">(Notify when Shared items are viewed)</small> <tr>
</th> <th>
<td></td> <div><i class="fas fa-fw fa-download mr-2"></i>ITFlow Updates</div>
<td></td> <small class="text-secondary">(Notify when ITFlow has an update)</small>
<td> </th>
</td> <td></td>
<td></td> <td></td>
</tr> <td>
<tr> </td>
<th> <td></td>
<div><i class="fas fa-fw fa-clock mr-2"></i>Cron Execution</div> </tr>
<small class="text-secondary">(Notify when the nightly cron job ran)</small> </tbody>
</th> </table>
<td></td> </div>
<td></td>
<td>
</td>
<td></td>
</tr>
<tr>
<th>
<div><i class="fas fa-fw fa-download mr-2"></i>ITFlow Updates</div>
<small class="text-secondary">(Notify when ITFlow has an update)</small>
</th>
<td></td>
<td></td>
<td>
</td>
<td></td>
</tr>
</tbody>
</table>
<hr> <hr>

View File

@@ -11,56 +11,63 @@ $stripe_clients_sql = mysqli_query($mysqli, "SELECT * FROM client_stripe LEFT JO
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="table-responsive">
<table class="table border border-dark"> <table class="table border border-dark">
<thead class="thead-dark"> <thead class="thead-dark text-nowrap">
<tr>
<th>Client</th>
<th>Stripe Customer ID</th>
<th>Stripe Payment ID</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($stripe_clients_sql)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$stripe_id = nullable_htmlentities($row['stripe_id']);
$stripe_pm = nullable_htmlentities($row['stripe_pm']);
?>
<tr> <tr>
<td><?php echo "$client_name ($client_id)" ?></td> <th>Client</th>
<td><?php echo $stripe_id; ?></td> <th>Stripe Customer ID</th>
<td><?php echo $stripe_pm ?></td> <th>Stripe Payment ID</th>
<td> <th>Payment Details</th>
<div class="dropdown dropleft text-center"> <th>Created</th>
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown"> <th class="text-center">Action</th>
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<?php if (!empty($stripe_pm)) { ?>
<a class="dropdown-item text-danger confirm-link" href="post.php?stripe_remove_pm&client_id=<?php echo $client_id ?>&pm=<?php echo $stripe_pm ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-credit-card mr-2"></i>Delete payment method
</a>
<?php } else { ?>
<a data-toggle="tooltip" data-placement="left" title="May result in duplicate customer records in Stripe" class="dropdown-item text-danger confirm-link" href="post.php?stripe_reset_customer&client_id=<?php echo $client_id ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Reset Stripe
</a>
<?php } ?>
</div>
</div>
</td>
</tr> </tr>
</thead>
<tbody>
<?php } ?> <?php
while ($row = mysqli_fetch_array($stripe_clients_sql)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
$stripe_id = nullable_htmlentities($row['stripe_id']);
$stripe_pm = nullable_htmlentities($row['stripe_pm']);
$stripe_pm_details = nullable_htmlentities($row['stripe_pm_details']);
$stripe_pm_created_at = nullable_htmlentities($row['stripe_pm_created_at']);
</tbody> ?>
</table>
<tr>
<td><?php echo "$client_name ($client_id)"; ?></td>
<td><?php echo $stripe_id; ?></td>
<td><?php echo $stripe_pm; ?></td>
<td><?php echo $stripe_pm_details; ?></td>
<td><?php echo $stripe_pm_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">
<?php if (!empty($stripe_pm)) { ?>
<a class="dropdown-item text-danger confirm-link" href="post.php?stripe_remove_pm&client_id=<?php echo $client_id ?>&pm=<?php echo $stripe_pm ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-credit-card mr-2"></i>Delete payment method
</a>
<?php } else { ?>
<a data-toggle="tooltip" data-placement="left" title="May result in duplicate customer records in Stripe" class="dropdown-item text-danger confirm-link" href="post.php?stripe_reset_customer&client_id=<?php echo $client_id ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Reset Stripe
</a>
<?php } ?>
</div>
</div>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div> </div>

View File

@@ -73,6 +73,31 @@ require_once "includes/inc_all_admin.php";
</div> </div>
</div> </div>
<div class="form-group">
<label>Tickets Default View</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>
<select class="form-control" name="config_ticket_default_view">
<option value=0 <?php if ($config_ticket_default_view == 0) { echo "selected"; } ?>>List</option>
<option value=1 <?php if ($config_ticket_default_view == 1) { echo "selected"; } ?>>Compact</option>
<option value=2 <?php if ($config_ticket_default_view == 2) { echo "selected"; } ?>>Kanban</option>
</select>
</div>
</div>
<div class="form-group">
<label>Kanban Settings</label>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="config_ticket_ordering" <?php if ($config_ticket_ordering == 1) { echo "checked"; } ?> value="1" id="ticketOrderingSwitch">
<label class="custom-control-label" for="ticketOrderingSwitch">Allow ticket ordering within its column<small class="text-secondary">(uncheked will result in ordering it by priority and id)</small></label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="config_ticket_moving_columns" <?php if ($config_ticket_moving_columns == 1) { echo "checked"; } ?> value="1" id="ticketMovingColumnsSwitch">
<label class="custom-control-label" for="ticketMovingColumnsSwitch">Allow moving columns</label>
</div>
</div>
<hr> <hr>
<button type="submit" name="edit_ticket_settings" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button> <button type="submit" name="edit_ticket_settings" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>

View File

@@ -1,7 +1,7 @@
<?php <?php
// Default Column Sortby Filter // Default Column Sortby Filter
$sort = "ticket_status_name"; $sort = "ticket_status_order";
$order = "ASC"; $order = "ASC";
require_once "includes/inc_all_admin.php"; require_once "includes/inc_all_admin.php";
@@ -79,7 +79,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
if ($ticket_status_active) { if ($ticket_status_active) {
$ticket_status_display = "<div class='text-success text-bold'>Active</div>"; $ticket_status_display = "<div class='text-success text-bold'>Active</div>";
} else { } else {
$ticket_status_display = "<div class='text-secondary'>Disabled</div>"; $ticket_status_display = "<div class='text-secondary'>Inactive</div>";
} }
?> ?>
@@ -97,7 +97,6 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<span class='badge badge-pill text-light p-2' style="background-color: <?php echo $ticket_status_color; ?>"><?php echo $ticket_status_name; ?></span> <span class='badge badge-pill text-light p-2' style="background-color: <?php echo $ticket_status_color; ?>"><?php echo $ticket_status_name; ?></span>
<td><?php echo $ticket_status_display; ?></td> <td><?php echo $ticket_status_display; ?></td>
<td> <td>
<?php if ( $ticket_status_id > 5 ) { ?>
<div class="dropdown dropleft text-center"> <div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown"> <button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i> <i class="fas fa-ellipsis-h"></i>
@@ -106,13 +105,14 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<a class="dropdown-item" href="#" data-toggle="ajax-modal" data-ajax-url="ajax/ajax_custom_ticket_status_edit.php" data-ajax-id="<?php echo $ticket_status_id; ?>"> <a class="dropdown-item" href="#" data-toggle="ajax-modal" data-ajax-url="ajax/ajax_custom_ticket_status_edit.php" data-ajax-id="<?php echo $ticket_status_id; ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit <i class="fas fa-fw fa-edit mr-2"></i>Edit
</a> </a>
<div class="dropdown-divider"></div> <?php if (!$ticket_status_active) { ?>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_ticket_status=<?php echo $ticket_status_id; ?>"> <div class="dropdown-divider"></div>
<i class="fas fa-fw fa-trash mr-2"></i>Delete <a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_ticket_status=<?php echo $ticket_status_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token']; ?>">
</a> <i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
<?php } ?>
</div> </div>
</div> </div>
<?php } ?>
</td> </td>
</tr> </tr>

View File

@@ -20,7 +20,7 @@ $sql_ticket_templates = mysqli_query($mysqli, "SELECT * FROM ticket_templates WH
$row = mysqli_fetch_array($sql_ticket_templates); $row = mysqli_fetch_array($sql_ticket_templates);
$ticket_template_name = nullable_htmlentities($row['ticket_template_name']); $ticket_template_name = nullable_htmlentities($row['ticket_template_name']);
$ticket_template_description = nullable_htmlentities($row['ticket_template_description']); //$ticket_template_description = nullable_htmlentities($row['ticket_template_description']);
$ticket_template_subject = nullable_htmlentities($row['ticket_template_subject']); $ticket_template_subject = nullable_htmlentities($row['ticket_template_subject']);
$ticket_template_details = $purifier->purify($row['ticket_template_details']); $ticket_template_details = $purifier->purify($row['ticket_template_details']);
$ticket_template_created_at = nullable_htmlentities($row['ticket_template_created_at']); $ticket_template_created_at = nullable_htmlentities($row['ticket_template_created_at']);
@@ -30,156 +30,135 @@ $ticket_template_updated_at = nullable_htmlentities($row['ticket_template_update
$sql_task_templates = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE task_template_ticket_template_id = $ticket_template_id ORDER BY task_template_order ASC, task_template_id ASC"); $sql_task_templates = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE task_template_ticket_template_id = $ticket_template_id ORDER BY task_template_order ASC, task_template_id ASC");
?> ?>
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
<ol class="breadcrumb d-print-none"> <ol class="breadcrumb d-print-none">
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="clients.php">Home</a> <a href="clients.php">Home</a>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="admin_user.php">Admin</a> <a href="admin_user.php">Admin</a>
</li> </li>
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="admin_ticket_template.php">Ticket Templates</a> <a href="admin_ticket_template.php">Ticket Templates</a>
</li> </li>
<li class="breadcrumb-item active"><i class="fas fa-life-ring mr-2"></i><?php echo $ticket_template_name; ?></li> <li class="breadcrumb-item active"><i class="fas fa-life-ring mr-2"></i><?php echo $ticket_template_name; ?></li>
</ol> </ol>
<div class="row"> <div class="row">
<div class="col-8"> <div class="col-9">
<div class="card card-dark"> <div class="card card-dark">
<div class="card-header"> <div class="card-header">
<h3 class="card-title mt-2"> <h3 class="card-title mt-2">
<div class="media"> <div class="media">
<i class="fa fa-fw fa-2x fa-life-ring mr-3"></i> <i class="fa fa-fw fa-2x fa-life-ring mr-3"></i>
<div class="media-body"> <div class="media-body">
<h3 class="mb-0"><?php echo $ticket_template_name; ?></h3> <h3 class="mb-0"><?php echo $ticket_template_name; ?></h3>
<div><small class="text-secondary"><?php echo $ticket_template_description; ?></small></div> <div><small class="text-secondary"><?php //echo $ticket_template_description; ?></small></div>
</div>
</div> </div>
</h3>
<div class="card-tools">
<button type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#editTicketTemplateModal">
<i class="fas fa-edit"></i>
</button>
</div> </div>
</div> </h3>
<h5><?php echo $ticket_template_subject; ?></h5> <div class="card-tools">
<div class="card-body prettyContent"> <button type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#editTicketTemplateModal">
<?php echo $ticket_template_details; ?> <i class="fas fa-edit"></i>
</button>
</div> </div>
</div> </div>
<h5><?php echo $ticket_template_subject; ?></h5>
</div> <div class="card-body prettyContent">
<?php echo $ticket_template_details; ?>
<div class="col-4">
<div class="card card-dark">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-fw fa-tasks mr-2"></i>Tasks</h5>
</div>
<div class="card-body">
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="ticket_template_id" value="<?php echo $ticket_template_id; ?>">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tasks"></i></span>
</div>
<input type="text" class="form-control" name="task_name" placeholder="Create a task" required>
<div class="input-group-append">
<button type="submit" name="add_ticket_template_task" class="btn btn-primary"><i class="fas fa-fw fa-check"></i></button>
</div>
</div>
</div>
</form>
<table class="table table-striped table-sm">
<?php
while($row = mysqli_fetch_array($sql_task_templates)){
$task_id = intval($row['task_template_id']);
$task_name = nullable_htmlentities($row['task_template_name']);
$task_completion_estimate = intval($row['task_template_completion_estimate']);
$task_description = nullable_htmlentities($row['task_template_description']);
?>
<tr data-task-id="<?php echo $task_id; ?>">
<td><i class="far fa-fw fa-square text-secondary"></i></td>
<td>
<a href="#" class="grab-cursor">
<span class="text-secondary"><?php echo $task_completion_estimate; ?>m</span>
<span class="text-dark"> - <?php echo $task_name; ?></span>
</a>
</td>
<td class="text-right">
<div class="float-right">
<div class="dropdown dropleft text-center">
<button class="btn btn-link text-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"
data-toggle = "ajax-modal"
data-ajax-url = "ajax/ajax_ticket_template_task_edit.php"
data-ajax-id = "<?php echo $task_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_task_template=<?php echo $task_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash-alt mr-2"></i>Delete
</a>
</div>
</div>
</div>
</td>
</tr>
<?php
}
?>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>
<script src="js/pretty_content.js"></script> <div class="col-3">
<script src="plugins/dragula/dragula.min.js"></script>
<script>
$(document).ready(function() {
var container = $('.table tbody')[0];
dragula([container]) <div class="card card-dark">
.on('drop', function (el, target, source, sibling) { <div class="card-header">
// Handle the drop event to update the order in the database <h5 class="card-title"><i class="fa fa-fw fa-tasks mr-2"></i>Tasks</h5>
var rows = $(container).children(); </div>
var positions = rows.map(function(index, row) { <div class="card-body">
return { <form action="post.php" method="post" autocomplete="off">
id: $(row).data('taskId'), <input type="hidden" name="ticket_template_id" value="<?php echo $ticket_template_id; ?>">
order: index <div class="form-group">
}; <div class="input-group input-group-sm">
}).get(); <input type="text" class="form-control" name="task_name" placeholder="Create a task" required maxlength="200">
<div class="input-group-append">
// Send the new order to the server <button type="submit" name="add_ticket_template_task" class="btn btn-primary"><i class="fas fa-fw fa-check"></i></button>
$.ajax({ </div>
url: 'ajax.php', </div>
method: 'POST', </div>
data: { </form>
update_task_templates_order: true, // Adjust the parameter name if needed <table class="table table-sm" id="tasks">
ticket_template_id: <?php echo $ticket_template_id; ?>, <?php
positions: positions while($row = mysqli_fetch_array($sql_task_templates)){
}, $task_id = intval($row['task_template_id']);
success: function(data) { $task_name = nullable_htmlentities($row['task_template_name']);
// Handle success $task_completion_estimate = intval($row['task_template_completion_estimate']);
}, //$task_description = nullable_htmlentities($row['task_template_description']);
error: function(error) { ?>
console.error('Error updating order:', error); <tr data-task-id="<?php echo $task_id; ?>">
<td>
<a href="#" class="drag-handle"><i class="fas fa-bars text-muted mr-2"></i></a>
<span class="text-dark"><?php echo $task_name; ?></span>
</td>
<td class="text-right">
<div class="float-right">
<div class="dropdown dropleft text-center">
<button class="btn btn-light text-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"
data-toggle = "ajax-modal"
data-ajax-url = "ajax/ajax_ticket_template_task_edit.php"
data-ajax-id = "<?php echo $task_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_task_template=<?php echo $task_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash-alt mr-2"></i>Delete
</a>
</div>
</div>
</div>
</td>
</tr>
<?php
} }
}); ?>
}); </table>
}); </div>
</script> </div>
</div>
</div>
<script src="js/pretty_content.js"></script>
<script src="plugins/SortableJS/Sortable.min.js"></script>
<script>
new Sortable(document.querySelector('table#tasks tbody'), {
handle: '.drag-handle',
animation: 150,
onEnd: function (evt) {
const rows = document.querySelectorAll('table#tasks tbody tr');
const positions = Array.from(rows).map((row, index) => ({
id: row.dataset.taskId,
order: index
}));
$.post('ajax.php', {
update_task_templates_order: true,
ticket_template_id: <?php echo $ticket_template_id; ?>,
positions: positions
});
}
});
</script>
<?php <?php

View File

@@ -32,8 +32,11 @@ $git_log = shell_exec("git log $repo_branch..origin/$repo_branch --pretty=format
<?php } ?> <?php } ?>
<?php if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { ?> <?php if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { ?>
<div class="alert alert-warning"> <div class="alert alert-danger">
<strong>Ensure you have a current <a href="https://docs.itflow.org/backups">app & database backup</a> before updating!</strong> <h1 class="font-weight-bold text-center">⚠️ DANGER ⚠️</h1>
<h2 class="font-weight-bold text-center">Do NOT run updates without first taking a backup</h2>
<p>VM Snapshots are highly recommended over other methods - see the <a href="https://docs.itflow.org/backups" class="alert-link" target="_blank">docs</a>. Review the <a href="https://github.com/itflow-org/itflow/blob/master/CHANGELOG.md" class="alert-link" target="_blank">changelog</a> for breaking changes that may require manual remediation.</p>
<p class="text-center font-weight-bold">Ignore this warning at your own risk.</p>
</div> </div>
<br> <br>
<a class="btn btn-dark btn-lg my-4" href="post.php?update_db"><i class="fas fa-fw fa-4x fa-download mb-1"></i><h5>Update Database</h5></a> <a class="btn btn-dark btn-lg my-4" href="post.php?update_db"><i class="fas fa-fw fa-4x fa-download mb-1"></i><h5>Update Database</h5></a>
@@ -46,9 +49,17 @@ $git_log = shell_exec("git log $repo_branch..origin/$repo_branch --pretty=format
<?php } else { <?php } else {
if (!empty($git_log)) { ?> if (!empty($git_log)) { ?>
<div class="alert alert-danger">
<h1 class="font-weight-bold text-center">⚠️ DANGER ⚠️</h1>
<h2 class="font-weight-bold text-center">Do NOT run updates without first taking a backup</h2>
<p>VM Snapshots are highly recommended over other methods - see the <a href="https://docs.itflow.org/backups" class="alert-link" target="_blank">docs</a>. Review the <a href="https://github.com/itflow-org/itflow/blob/master/CHANGELOG.md" class="alert-link" target="_blank">changelog</a> for breaking changes that may require manual remediation.</p>
<p class="text-center font-weight-bold">Ignore this warning at your own risk.</p>
</div>
<a class="btn btn-primary btn-lg my-4" href="post.php?update"><i class="fas fa-fw fa-4x fa-download mb-1"></i><h5>Update App</h5></a> <a class="btn btn-primary btn-lg my-4 confirm-link" href="post.php?no"><i class="fas fa-fw fa-4x fa-download mb-1"></i><h5>TEST</h5></a>
<a class="btn btn-danger btn-lg" href="post.php?update&force_update=1"><i class="fas fa-fw fa-4x fa-hammer mb-1"></i><h5>FORCE Update App</h5></a>
<a class="btn btn-primary btn-lg my-4 confirm-link" href="post.php?update"><i class="fas fa-fw fa-4x fa-download mb-1"></i><h5>Update App</h5></a>
<a class="btn btn-danger btn-lg confirm-link" href="post.php?update&force_update=1"><i class="fas fa-fw fa-4x fa-hammer mb-1"></i><h5>FORCE Update App</h5></a>
<?php } else { ?> <?php } else { ?>
<p><strong>Application Release Version:<br><strong class="text-dark"><?php echo APP_VERSION; ?></strong></p> <p><strong>Application Release Version:<br><strong class="text-dark"><?php echo APP_VERSION; ?></strong></p>
@@ -56,6 +67,17 @@ $git_log = shell_exec("git log $repo_branch..origin/$repo_branch --pretty=format
<p class="text-secondary">Code Commit:<br><strong class="text-dark"><?php echo $current_version; ?></strong></p> <p class="text-secondary">Code Commit:<br><strong class="text-dark"><?php echo $current_version; ?></strong></p>
<p class="text-muted">You are up to date!<br>Everything is going to be alright</p> <p class="text-muted">You are up to date!<br>Everything is going to be alright</p>
<i class="far fa-3x text-dark fa-smile-wink"></i><br> <i class="far fa-3x text-dark fa-smile-wink"></i><br>
<?php if (rand(1,10) == 1) { ?>
<br>
<div class="alert alert-info alert-dismissible fade show" role="alert">
You're up to date, but when was the last time you checked your ITFlow backup works?
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<?php } ?>
<?php } <?php }
} }

View File

@@ -294,6 +294,7 @@ if (isset($_GET['get_active_clients'])) {
$mysqli, $mysqli,
"SELECT client_id, client_name FROM clients "SELECT client_id, client_name FROM clients
WHERE client_archived_at IS NULL WHERE client_archived_at IS NULL
$access_permission_query
ORDER BY client_accessed_at DESC" ORDER BY client_accessed_at DESC"
); );
@@ -315,7 +316,9 @@ if (isset($_GET['get_client_contacts'])) {
$contact_sql = mysqli_query( $contact_sql = mysqli_query(
$mysqli, $mysqli,
"SELECT contact_id, contact_name, contact_primary, contact_important, contact_technical FROM contacts "SELECT contact_id, contact_name, contact_primary, contact_important, contact_technical FROM contacts
LEFT JOIN clients on contact_client_id = client_id
WHERE contacts.contact_archived_at IS NULL AND contact_client_id = $client_id WHERE contacts.contact_archived_at IS NULL AND contact_client_id = $client_id
$access_permission_query
ORDER BY contact_primary DESC, contact_technical DESC, contact_important DESC, contact_name" ORDER BY contact_primary DESC, contact_technical DESC, contact_important DESC, contact_name"
); );
@@ -586,13 +589,13 @@ if (isset($_POST['update_recurring_invoice_items_order'])) {
enforceUserPermission('module_sales', 2); enforceUserPermission('module_sales', 2);
$positions = $_POST['positions']; $positions = $_POST['positions'];
$recurring_id = intval($_POST['recurring_id']); $recurring_invoice_id = intval($_POST['recurring_invoice_id']);
foreach ($positions as $position) { foreach ($positions as $position) {
$id = intval($position['id']); $id = intval($position['id']);
$order = intval($position['order']); $order = intval($position['order']);
mysqli_query($mysqli, "UPDATE invoice_items SET item_order = $order WHERE item_recurring_id = $recurring_id AND item_id = $id"); mysqli_query($mysqli, "UPDATE invoice_items SET item_order = $order WHERE item_recurring_invoice_id = $recurring_invoice_id AND item_id = $id");
} }
// return a response // return a response

View File

@@ -364,6 +364,16 @@ ob_start();
</div> </div>
<?php if ($asset_type !== 'Virtual Machine') { ?> <?php if ($asset_type !== 'Virtual Machine') { ?>
<div class="form-group">
<label>Purchase Reference</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" name="purchase_reference" placeholder="eg. Invoice, PO Number" >
</div>
</div>
<div class="form-group"> <div class="form-group">
<label>Purchase Date</label> <label>Purchase Date</label>
<div class="input-group"> <div class="input-group">

View File

@@ -0,0 +1,69 @@
<?php
require_once '../includes/ajax_header.php';
$asset_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM assets
WHERE asset_id = $asset_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$asset_name = nullable_htmlentities($row['asset_name']);
$client_id = intval($row['asset_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-key mr-2"></i>Link Credential to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<select class="form-control select2" name="credential_id">
<option value="">- Select a Credential -</option>
<?php
$sql_credentials_select = mysqli_query($mysqli, "
SELECT credentials.credential_id, credentials.credential_name
FROM credentials
LEFT JOIN assets ON credentials.credential_asset_id = assets.asset_id
AND credentials.credential_asset_id = $asset_id
WHERE credentials.credential_client_id = $client_id
AND credentials.credential_asset_id = 0
AND credentials.credential_archived_at IS NULL
ORDER BY credentials.credential_name ASC
");
while ($row = mysqli_fetch_array($sql_credentials_select)) {
$credential_id = intval($row['credential_id']);
$credential_name = nullable_htmlentities($row['credential_name']);
?>
<option value="<?php echo $credential_id ?>"><?php echo $credential_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_asset_to_credential" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";
?>

View File

@@ -0,0 +1,70 @@
<?php
require_once '../includes/ajax_header.php';
$asset_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM assets
WHERE asset_id = $asset_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$asset_name = nullable_htmlentities($row['asset_name']);
$client_id = intval($row['asset_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-folder mr-2"></i>Link Document to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-folder"></i></span>
</div>
<select class="form-control select2" name="document_id">
<option value="">- Select a Document -</option>
<?php
$sql_documents_select = mysqli_query($mysqli, "
SELECT documents.document_id, documents.document_name
FROM documents
LEFT JOIN asset_documents
ON documents.document_id = asset_documents.document_id
AND asset_documents.asset_id = $asset_id
WHERE documents.document_client_id = $client_id
AND documents.document_archived_at IS NULL
AND asset_documents.asset_id IS NULL
ORDER BY documents.document_name ASC
");
while ($row = mysqli_fetch_array($sql_documents_select)) {
$document_id = intval($row['document_id']);
$document_name = nullable_htmlentities($row['document_name']);
?>
<option value="<?php echo $document_id ?>"><?php echo $document_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_asset_to_document" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";
?>

View File

@@ -0,0 +1,73 @@
<?php
require_once '../includes/ajax_header.php';
$asset_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM assets
WHERE asset_id = $asset_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$asset_name = nullable_htmlentities($row['asset_name']);
$client_id = intval($row['asset_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-paperclip mr-2"></i>Link File to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paperclip"></i></span>
</div>
<select class="form-control select2" name="file_id">
<option value="">- Select a File -</option>
<?php
$sql_files_select = mysqli_query($mysqli, "
SELECT files.file_id, files.file_name, folders.folder_name
FROM files
LEFT JOIN asset_files
ON files.file_id = asset_files.file_id
AND asset_files.asset_id = $asset_id
LEFT JOIN folders
ON folders.folder_id = files.file_folder_id
WHERE files.file_client_id = $client_id
AND asset_files.asset_id IS NULL
ORDER BY folders.folder_name ASC, files.file_name ASC
");
while ($row = mysqli_fetch_array($sql_files_select)) {
$file_id = intval($row['file_id']);
$file_name = nullable_htmlentities($row['file_name']);
$folder_name = nullable_htmlentities($row['folder_name']);
?>
<option value="<?php echo $file_id ?>"><?php echo "$folder_name/$file_name"; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_asset_to_file" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";
?>

View File

@@ -0,0 +1,68 @@
<?php
require_once '../includes/ajax_header.php';
$asset_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM assets
WHERE asset_id = $asset_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$asset_name = nullable_htmlentities($row['asset_name']);
$client_id = intval($row['asset_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-stream mr-2"></i>Link Service to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stream"></i></span>
</div>
<select class="form-control select2" name="service_id">
<option value="">- Select a Service -</option>
<?php
$sql_services_select = mysqli_query($mysqli, "
SELECT services.service_id, services.service_name
FROM services
LEFT JOIN service_assets
ON services.service_id = service_assets.service_id
AND service_assets.asset_id = $asset_id
WHERE services.service_client_id = $client_id
AND service_assets.asset_id IS NULL
ORDER BY services.service_name ASC
");
while ($row = mysqli_fetch_array($sql_services_select)) {
$service_id = intval($row['service_id']);
$service_name = nullable_htmlentities($row['service_name']);
?>
<option value="<?php echo $service_id ?>"><?php echo $service_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_service_to_asset" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";

View File

@@ -0,0 +1,73 @@
<?php
require_once '../includes/ajax_header.php';
$asset_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM assets
WHERE asset_id = $asset_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$asset_name = nullable_htmlentities($row['asset_name']);
$client_id = intval($row['asset_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>License Software to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<select class="form-control select2" name="software_id">
<option value="">- Select a Device Software License -</option>
<?php
$sql_software_select = mysqli_query($mysqli, "
SELECT software.software_id, software.software_name
FROM software
LEFT JOIN software_assets
ON software.software_id = software_assets.software_id
AND software_assets.asset_id = $asset_id
WHERE software.software_client_id = $client_id
AND software.software_archived_at IS NULL
AND software.software_license_type = 'Device'
AND software_assets.asset_id IS NULL
ORDER BY software.software_name ASC
");
while ($row = mysqli_fetch_array($sql_software_select)) {
$software_id = intval($row['software_id']);
$software_name = nullable_htmlentities($row['software_name']);
?>
<option value="<?php echo $software_id ?>"><?php echo $software_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_software_to_asset" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";

View File

@@ -22,7 +22,7 @@ $contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_
$contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code)); $contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code));
$contact_extension = nullable_htmlentities($row['contact_extension']); $contact_extension = nullable_htmlentities($row['contact_extension']);
$contact_mobile_country_code = nullable_htmlentities($row['contact_mobile_country_code']); $contact_mobile_country_code = nullable_htmlentities($row['contact_mobile_country_code']);
$contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_mobile_country_code)); $contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_mobile'], $contact_mobile_country_code));
$contact_email = nullable_htmlentities($row['contact_email']); $contact_email = nullable_htmlentities($row['contact_email']);
$contact_photo = nullable_htmlentities($row['contact_photo']); $contact_photo = nullable_htmlentities($row['contact_photo']);
$contact_pin = nullable_htmlentities($row['contact_pin']); $contact_pin = nullable_htmlentities($row['contact_pin']);

View File

@@ -19,7 +19,7 @@ $contact_extension = nullable_htmlentities($row['contact_extension']);
$contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_code']); $contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_code']);
$contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code)); $contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code));
$contact_mobile_country_code = nullable_htmlentities($row['contact_mobile_country_code']); $contact_mobile_country_code = nullable_htmlentities($row['contact_mobile_country_code']);
$contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_mobile_country_code)); $contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_mobile'], $contact_mobile_country_code));
$contact_email = nullable_htmlentities($row['contact_email']); $contact_email = nullable_htmlentities($row['contact_email']);
$contact_pin = nullable_htmlentities($row['contact_pin']); $contact_pin = nullable_htmlentities($row['contact_pin']);
$contact_photo = nullable_htmlentities($row['contact_photo']); $contact_photo = nullable_htmlentities($row['contact_photo']);
@@ -121,7 +121,7 @@ ob_start();
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="phone_country_code" value="<?php echo "+$contact_phone_country_code"; ?>" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="phone_country_code" value="<?php echo "$contact_phone_country_code"; ?>" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="phone" value="<?php echo $contact_phone; ?>" placeholder="Phone Number" maxlength="200"> <input type="tel" class="form-control" name="phone" value="<?php echo $contact_phone; ?>" placeholder="Phone Number" maxlength="200">
</div> </div>
</div> </div>
@@ -141,7 +141,7 @@ ob_start();
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-mobile-alt"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-mobile-alt"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="mobile_country_code" value="<?php echo "+$contact_mobile_country_code"; ?>" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="mobile_country_code" value="<?php echo "$contact_mobile_country_code"; ?>" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="mobile" value="<?php echo $contact_mobile; ?>" placeholder="Phone Number"> <input type="tel" class="form-control" name="mobile" value="<?php echo $contact_mobile; ?>" placeholder="Phone Number">
</div> </div>
</div> </div>

View File

@@ -0,0 +1,67 @@
<?php
require_once '../includes/ajax_header.php';
$contact_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM contacts
WHERE contact_id = $contact_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$contact_name = nullable_htmlentities($row['contact_name']);
$client_id = intval($row['contact_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-desktop mr-2"></i>Link Asset to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
</div>
<select class="form-control select2" name="asset_id">
<option value="">- Select an Asset -</option>
<?php
$sql_assets_select = mysqli_query($mysqli, "
SELECT asset_id, asset_name
FROM assets
WHERE asset_client_id = $client_id
AND asset_contact_id = 0
AND asset_archived_at IS NULL
ORDER BY asset_name ASC
");
while ($row = mysqli_fetch_array($sql_assets_select)) {
$asset_id = intval($row['asset_id']);
$asset_name = nullable_htmlentities($row['asset_name']);
?>
<option value="<?php echo $asset_id ?>"><?php echo $asset_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_contact_to_asset" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";
?>

View File

@@ -0,0 +1,67 @@
<?php
require_once '../includes/ajax_header.php';
$contact_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM contacts
WHERE contact_id = $contact_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$contact_name = nullable_htmlentities($row['contact_name']);
$client_id = intval($row['contact_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-key mr-2"></i>Link Credential to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<select class="form-control select2" name="credential_id">
<option value="">- Select a Credential -</option>
<?php
$sql_credentials_select = mysqli_query($mysqli, "
SELECT credential_id, credential_name
FROM credentials
WHERE credential_client_id = $client_id
AND credential_contact_id = 0
AND credential_archived_at IS NULL
ORDER BY credential_name ASC
");
while ($row = mysqli_fetch_array($sql_credentials_select)) {
$credential_id = intval($row['credential_id']);
$credential_name = nullable_htmlentities($row['credential_name']);
?>
<option value="<?php echo $credential_id ?>"><?php echo $credential_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_contact_to_credential" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";
?>

View File

@@ -0,0 +1,70 @@
<?php
require_once '../includes/ajax_header.php';
$contact_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM contacts
WHERE contact_id = $contact_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$contact_name = nullable_htmlentities($row['contact_name']);
$client_id = intval($row['contact_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-folder mr-2"></i>Link Document to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-folder"></i></span>
</div>
<select class="form-control select2" name="document_id">
<option value="">- Select a Document -</option>
<?php
$sql_documents_select = mysqli_query($mysqli, "
SELECT documents.document_id, documents.document_name
FROM documents
LEFT JOIN contact_documents
ON documents.document_id = contact_documents.document_id
AND contact_documents.contact_id = $contact_id
WHERE documents.document_client_id = $client_id
AND documents.document_archived_at IS NULL
AND contact_documents.contact_id IS NULL
ORDER BY documents.document_name ASC
");
while ($row = mysqli_fetch_array($sql_documents_select)) {
$document_id = intval($row['document_id']);
$document_name = nullable_htmlentities($row['document_name']);
?>
<option value="<?php echo $document_id ?>"><?php echo $document_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_contact_to_document" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";
?>

View File

@@ -0,0 +1,72 @@
<?php
require_once '../includes/ajax_header.php';
$contact_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM contacts
WHERE contact_id = $contact_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$contact_name = nullable_htmlentities($row['contact_name']);
$client_id = intval($row['contact_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-paperclip mr-2"></i>Link File to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paperclip"></i></span>
</div>
<select class="form-control select2" name="file_id">
<option value="">- Select a File -</option>
<?php
$sql_files_select = mysqli_query($mysqli, "
SELECT files.file_id, files.file_name, folders.folder_name
FROM files
LEFT JOIN contact_files
ON files.file_id = contact_files.file_id
AND contact_files.contact_id = $contact_id
LEFT JOIN folders
ON folders.folder_id = files.file_folder_id
WHERE files.file_client_id = $client_id
AND contact_files.contact_id IS NULL
ORDER BY folders.folder_name ASC, files.file_name ASC
");
while ($row = mysqli_fetch_array($sql_files_select)) {
$file_id = intval($row['file_id']);
$file_name = nullable_htmlentities($row['file_name']);
$folder_name = nullable_htmlentities($row['folder_name']);
?>
<option value="<?php echo $file_id ?>"><?php echo "$folder_name/$file_name"; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_contact_to_file" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";

View File

@@ -0,0 +1,68 @@
<?php
require_once '../includes/ajax_header.php';
$contact_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM contacts
WHERE contact_id = $contact_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$contact_name = nullable_htmlentities($row['contact_name']);
$client_id = intval($row['contact_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-stream mr-2"></i>Link Service to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stream"></i></span>
</div>
<select class="form-control select2" name="service_id">
<option value="">- Select a Service -</option>
<?php
$sql_services_select = mysqli_query($mysqli, "
SELECT services.service_id, services.service_name
FROM services
LEFT JOIN service_contacts
ON services.service_id = service_contacts.service_id
AND service_contacts.contact_id = $contact_id
WHERE services.service_client_id = $client_id
AND service_contacts.contact_id IS NULL
ORDER BY services.service_name ASC
");
while ($row = mysqli_fetch_array($sql_services_select)) {
$service_id = intval($row['service_id']);
$service_name = nullable_htmlentities($row['service_name']);
?>
<option value="<?php echo $service_id ?>"><?php echo $service_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_service_to_contact" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";

View File

@@ -0,0 +1,71 @@
<?php
require_once '../includes/ajax_header.php';
$contact_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM contacts
WHERE contact_id = $contact_id
LIMIT 1
");
$row = mysqli_fetch_array($sql);
$contact_name = nullable_htmlentities($row['contact_name']);
$client_id = intval($row['contact_client_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>License Software to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<select class="form-control select2" name="software_id">
<option value="">- Select a User Software License -</option>
<?php
$sql_software_select = mysqli_query($mysqli, "
SELECT software.software_id, software.software_name
FROM software
LEFT JOIN software_contacts
ON software.software_id = software_contacts.software_id
AND software_contacts.contact_id = $contact_id
WHERE software.software_client_id = $client_id
AND software.software_archived_at IS NULL
AND software.software_license_type = 'User'
AND software_contacts.contact_id IS NULL
ORDER BY software.software_name ASC
");
while ($row = mysqli_fetch_array($sql_software_select)) {
$software_id = intval($row['software_id']);
$software_name = nullable_htmlentities($row['software_name']);
?>
<option value="<?php echo $software_id ?>"><?php echo $software_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_software_to_contact" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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";
?>

View File

@@ -8,6 +8,7 @@ $sql = mysqli_query($mysqli, "SELECT * FROM ticket_statuses WHERE ticket_status_
$row = mysqli_fetch_array($sql); $row = mysqli_fetch_array($sql);
$ticket_status_name = nullable_htmlentities($row['ticket_status_name']); $ticket_status_name = nullable_htmlentities($row['ticket_status_name']);
$ticket_status_color = nullable_htmlentities($row['ticket_status_color']); $ticket_status_color = nullable_htmlentities($row['ticket_status_color']);
$ticket_status_order = intval($row['ticket_status_order']);
$ticket_status_active = intval($row['ticket_status_active']); $ticket_status_active = intval($row['ticket_status_active']);
// Generate the HTML form content using output buffering. // Generate the HTML form content using output buffering.
@@ -30,7 +31,7 @@ ob_start();
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
</div> </div>
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $ticket_status_name; ?>" required> <input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $ticket_status_name; ?>" required <?php if ($ticket_status_id <= 5) { echo "readonly"; } ?>>
</div> </div>
</div> </div>
@@ -44,6 +45,16 @@ ob_start();
</div> </div>
</div> </div>
<div class="form-group">
<label>Order</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
</div>
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order" value="<?php echo $ticket_status_order; ?>">
</div>
</div>
<div class="form-group"> <div class="form-group">
<label>Status <strong class="text-danger">*</strong></label> <label>Status <strong class="text-danger">*</strong></label>
<div class="input-group"> <div class="input-group">
@@ -52,7 +63,7 @@ ob_start();
</div> </div>
<select class="form-control select2" name="status" required> <select class="form-control select2" name="status" required>
<option <?php if ($ticket_status_active == 1) { echo "selected"; } ?> value="1">Active</option> <option <?php if ($ticket_status_active == 1) { echo "selected"; } ?> value="1">Active</option>
<option <?php if ($ticket_status_active == 0) { echo "selected"; } ?> value="0">Disabled</option> <option <?php if ($ticket_status_active == 0) { echo "selected"; } ?> value="0" <?php if ($ticket_status_id <= 5) { echo "disabled"; } ?>>Inactive</option>
</select> </select>
</div> </div>
</div> </div>

View File

@@ -209,7 +209,7 @@ ob_start();
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="phone_country_code" value="<?php echo "+$location_phone_country_code"; ?>" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="phone_country_code" value="<?php echo $location_phone_country_code; ?>" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="phone" value="<?php echo $location_phone; ?>" placeholder="Phone Number" maxlength="200"> <input type="tel" class="form-control" name="phone" value="<?php echo $location_phone; ?>" placeholder="Phone Number" maxlength="200">
</div> </div>
</div> </div>
@@ -229,7 +229,7 @@ ob_start();
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fax"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-fax"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="fax_country_code" value="<?php echo "+$location_fax_country_code"; ?>" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="fax_country_code" value="<?php echo $location_fax_country_code; ?>" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="fax" value="<?php echo $location_fax; ?>" placeholder="Phone Number" maxlength="200"> <input type="tel" class="form-control" name="fax" value="<?php echo $location_fax; ?>" placeholder="Phone Number" maxlength="200">
</div> </div>
</div> </div>

View File

@@ -90,7 +90,7 @@ ob_start();
$location_id = intval($row['location_id']); $location_id = intval($row['location_id']);
$location_name = nullable_htmlentities($row['location_name']); $location_name = nullable_htmlentities($row['location_name']);
?> ?>
<option value="<?php echo $location_id; ?>" <?php if ($location_id = $network_location_id) { echo "selected"; } ?>> <option value="<?php echo $location_id; ?>" <?php if ($location_id == $network_location_id) { echo "selected"; } ?>>
<?php echo $location_name; ?> <?php echo $location_name; ?>
</option> </option>
<?php <?php

View File

@@ -25,46 +25,56 @@ ob_start();
<div class="modal-body bg-white"> <div class="modal-body bg-white">
<?php if ($num_notifications) { ?> <?php if ($num_notifications) { ?>
<table class="table table-sm table-hover table-borderless">
<?php while ($row = mysqli_fetch_array($sql)) {
$notification_id = intval($row["notification_id"]); <?php while ($row = mysqli_fetch_array($sql)) {
$notification_type = nullable_htmlentities($row["notification_type"]);
$notification_details = nullable_htmlentities($row["notification"]); $notification_id = intval($row["notification_id"]);
$notification_action = nullable_htmlentities( $notification_type = nullable_htmlentities($row["notification_type"]);
$row["notification_action"] $notification_details = nullable_htmlentities($row["notification"]);
); $notification_action = nullable_htmlentities(
$notification_timestamp_formated = date( $row["notification_action"]
"M d g:ia", );
strtotime($row["notification_timestamp"]) $notification_timestamp_formated = date(
); "M d g:ia",
$notification_client_id = intval($row["notification_client_id"]); strtotime($row["notification_timestamp"])
if (empty($notification_action)) { );
$notification_action = "#"; $notification_client_id = intval($row["notification_client_id"]);
if (empty($notification_action)) {
$notification_action = "#";
}
?>
<tr class="notification-item">
<th>
<a class="text-dark" href="<?php echo $notification_action; ?>">
<i class="fas fa-bullhorn mr-2"></i><?php echo $notification_type; ?>
<small class="text-muted float-right">
<?php echo $notification_timestamp_formated; ?>
</small>
<br>
<small class="text-secondary text-wrap"><?php echo $notification_details; ?></small>
</a>
</th>
</tr>
<?php
} }
?> ?>
</table>
<a class="text-dark dropdown-item px-1" href="<?php echo $notification_action; ?>"> <div class="text-center mt-2">
<div> <button id="prev-btn" class="btn btn-sm btn-outline-secondary mr-2"><i class="fas fa-caret-left"></i></button>
<span class="text-bold"> <button id="next-btn" class="btn btn-sm btn-outline-secondary"><i class="fas fa-caret-right"></i></button>
<i class="fas fa-bullhorn mr-2"></i><?php echo $notification_type; ?>
</span>
<small class="text-muted float-right">
<?php echo $notification_timestamp_formated; ?>
</small>
</div> </div>
<small class="text-secondary text-wrap"><?php echo $notification_details; ?></small> <?php } else { ?>
</a> <div class="text-center text-secondary pt-3">
<?php
}} else { ?>
<div class="text-center text-secondary py-5">
<i class='far fa-6x fa-bell-slash'></i> <i class='far fa-6x fa-bell-slash'></i>
<h3 class="mt-3">No Notifications</h3> <h3 class="mt-3">No Notifications</h3>
</div> </div>
<?php } ?> <?php } ?>
</div> </div>
<div class="modal-footer bg-white justify-content-end"> <div class="modal-footer bg-white">
<?php if ($num_notifications) { ?> <?php if ($num_notifications) { ?>
<a href="post.php?dismiss_all_notifications&csrf_token=<?php echo $_SESSION[ <a href="post.php?dismiss_all_notifications&csrf_token=<?php echo $_SESSION[
@@ -85,4 +95,41 @@ ob_start();
</button> </button>
</div> </div>
<script>
$(document).ready(function () {
var perPage = 8;
var $items = $(".notification-item");
var totalItems = $items.length;
var totalPages = Math.ceil(totalItems / perPage);
var currentPage = 0;
function showPage(page) {
$items.hide().slice(page * perPage, (page + 1) * perPage).show();
$("#prev-btn").prop("disabled", page === 0);
$("#next-btn").prop("disabled", page >= totalPages - 1);
$("#page-indicator").text(`Page ${page + 1} of ${totalPages} (${totalItems} total)`);
}
$("#prev-btn").on("click", function () {
if (currentPage > 0) {
currentPage--;
showPage(currentPage);
}
});
$("#next-btn").on("click", function () {
if (currentPage < totalPages - 1) {
currentPage++;
showPage(currentPage);
}
});
if (totalItems <= perPage) {
$("#prev-btn, #next-btn, #page-indicator").hide();
}
showPage(currentPage);
});
</script>
<?php require_once "../includes/ajax_footer.php"; <?php require_once "../includes/ajax_footer.php";

View File

@@ -85,6 +85,25 @@ ob_start();
</select> </select>
</div> </div>
</div> </div>
<div class="form-group">
<label>Client</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-users"></i></span>
</div>
<select class="form-control select2" name="client_id">
<option value="0">- No Client -</option>
<?php
$sql = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL $access_permission_query ORDER BY client_name ASC");
while ($row = mysqli_fetch_array($sql)) {
$select_client_id = intval($row['client_id']);
$select_client_name = nullable_htmlentities($row['client_name']);
?>
<option value="<?php echo $select_client_id; ?>" <?php if ($client_id == $select_client_id) { echo "selected"; } ?>><?php echo $select_client_name; ?></option>
<?php } ?>
</select>
</div>
</div>
</div> </div>
<div class="modal-footer bg-white"> <div class="modal-footer bg-white">
<button type="submit" name="edit_project" class="btn btn-primary text-bold"> <button type="submit" name="edit_project" class="btn btn-primary text-bold">

View File

@@ -11,9 +11,6 @@ $sql = mysqli_query($mysqli, "SELECT * FROM ticket_replies
); );
$row = mysqli_fetch_array($sql); $row = mysqli_fetch_array($sql);
$ticket_reply_type = nullable_htmlentities($row['ticket_reply_type']);
$ticket_reply_time_worked = date_create($row['ticket_reply_time_worked']);
$ticket_reply_time_worked_formatted = date_format($ticket_reply_time_worked, 'H:i:s');
$ticket_reply = nullable_htmlentities($row['ticket_reply']); $ticket_reply = nullable_htmlentities($row['ticket_reply']);
$client_id = intval($row['ticket_client_id']); $client_id = intval($row['ticket_client_id']);
@@ -31,10 +28,6 @@ ob_start();
<form action="post.php" method="post" autocomplete="off"> <form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="ticket_reply_id" value="<?php echo $ticket_reply_id; ?>"> <input type="hidden" name="ticket_reply_id" value="<?php echo $ticket_reply_id; ?>">
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>"> <input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
<input type="hidden" name="ticket_reply_type" value="<?php echo $ticket_reply_type; ?>">
<?php if (!empty($ticket_reply_time_worked)) { ?>
<input type="hidden" name="time" value="<?php echo $ticket_reply_time_worked_formatted; ?>">
<?php } ?>
<div class="modal-body bg-white"> <div class="modal-body bg-white">
@@ -44,7 +37,7 @@ ob_start();
</div> </div>
<div class="modal-footer bg-white"> <div class="modal-footer bg-white">
<button type="submit" name="edit_ticket_reply" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button> <button type="submit" name="redact_ticket_reply" 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> <button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div> </div>
</form> </form>

View File

@@ -10,7 +10,7 @@ $row = mysqli_fetch_array($sql);
$task_template_name = nullable_htmlentities($row['task_template_name']); $task_template_name = nullable_htmlentities($row['task_template_name']);
$task_template_order = intval($row['task_template_order']); $task_template_order = intval($row['task_template_order']);
$task_template_completion_estimate = intval($row['task_template_completion_estimate']); $task_template_completion_estimate = intval($row['task_template_completion_estimate']);
$task_template_description = nullable_htmlentities($row['task_template_description']); //$task_template_description = nullable_htmlentities($row['task_template_description']);
// Generate the HTML form content using output buffering. // Generate the HTML form content using output buffering.
ob_start(); ob_start();

View File

@@ -115,7 +115,7 @@ ob_start();
$sql_users = mysqli_query($mysqli, "SELECT users.user_id, user_name FROM users $sql_users = mysqli_query($mysqli, "SELECT users.user_id, user_name FROM users
LEFT JOIN user_settings on users.user_id = user_settings.user_id LEFT JOIN user_settings on users.user_id = user_settings.user_id
WHERE user_role > 1 AND user_archived_at IS NULL ORDER BY user_name ASC" WHERE user_role_id > 1 AND user_archived_at IS NULL ORDER BY user_name ASC"
); );
while ($row = mysqli_fetch_array($sql_users)) { while ($row = mysqli_fetch_array($sql_users)) {
$user_id_select = intval($row['user_id']); $user_id_select = intval($row['user_id']);

View File

@@ -127,7 +127,7 @@ ob_start();
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="phone_country_code" value="<?php echo "+$vendor_phone_country_code"; ?>" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="phone_country_code" value="<?php echo $vendor_phone_country_code; ?>" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="phone" value="<?php echo $vendor_phone; ?>" placeholder="Phone Number" maxlength="200"> <input type="tel" class="form-control" name="phone" value="<?php echo $vendor_phone; ?>" placeholder="Phone Number" maxlength="200">
</div> </div>
</div> </div>

View File

@@ -39,7 +39,7 @@ if (!empty($subject)) {
// Insert ticket // Insert ticket
$url_key = randomString(156); $url_key = randomString(156);
$insert_sql = mysqli_query($mysqli,"INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = 1, ticket_billable = $billable, ticket_vendor_ticket_number = '$vendor_ticket_number', ticket_vendor_id = $vendor_id, ticket_created_by = 0, ticket_assigned_to = $assigned_to, ticket_contact_id = $contact, ticket_url_key = '$url_key', ticket_client_id = $client_id"); $insert_sql = mysqli_query($mysqli,"INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_source = 'API', ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = 1, ticket_billable = $billable, ticket_vendor_ticket_number = '$vendor_ticket_number', ticket_vendor_id = $vendor_id, ticket_created_by = 0, ticket_assigned_to = $assigned_to, ticket_contact_id = $contact, ticket_url_key = '$url_key', ticket_client_id = $client_id");
// Check insert & get insert ID // Check insert & get insert ID
if ($insert_sql) { if ($insert_sql) {

View File

@@ -94,7 +94,7 @@ if (isset($_GET['asset_id'])) {
$ticket_count = mysqli_num_rows($sql_related_tickets); $ticket_count = mysqli_num_rows($sql_related_tickets);
// Related Recurring Tickets Query // Related Recurring Tickets Query
$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM recurring_tickets $sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT recurring_tickets.* FROM recurring_tickets
LEFT JOIN recurring_ticket_assets ON recurring_tickets.recurring_ticket_id = recurring_ticket_assets.recurring_ticket_id LEFT JOIN recurring_ticket_assets ON recurring_tickets.recurring_ticket_id = recurring_ticket_assets.recurring_ticket_id
WHERE recurring_ticket_asset_id = $asset_id OR recurring_ticket_assets.asset_id = $asset_id WHERE recurring_ticket_asset_id = $asset_id OR recurring_ticket_assets.asset_id = $asset_id
GROUP BY recurring_tickets.recurring_ticket_id GROUP BY recurring_tickets.recurring_ticket_id
@@ -244,7 +244,7 @@ if (isset($_GET['asset_id'])) {
data-ajax-id="<?php echo $asset_id; ?>"> data-ajax-id="<?php echo $asset_id; ?>">
<i class="fas fa-fw fa-edit"></i> <i class="fas fa-fw fa-edit"></i>
</button> </button>
<h3 class="text-bold"><i class="fa fa-fw text-secondary fa-<?php echo $device_icon; ?> mr-3"></i><?php echo $asset_name; ?></h3> <h4 class="text-bold"><i class="fa fa-fw text-secondary fa-<?php echo $device_icon; ?> mr-3"></i><?php echo $asset_name; ?></h4>
<?php if ($asset_photo) { ?> <?php if ($asset_photo) { ?>
<img class="img-fluid img-circle p-3" alt="asset_photo" src="<?php echo "uploads/clients/$client_id/$asset_photo"; ?>"> <img class="img-fluid img-circle p-3" alt="asset_photo" src="<?php echo "uploads/clients/$client_id/$asset_photo"; ?>">
<?php } ?> <?php } ?>
@@ -378,34 +378,47 @@ if (isset($_GET['asset_id'])) {
<div class="dropdown dropleft"> <div class="dropdown dropleft">
<button type="button" class="btn btn-outline-primary" data-toggle="dropdown"><i class="fas fa-link mr-2"></i>Link</button> <button type="button" class="btn btn-outline-primary" data-toggle="dropdown"><i class="fas fa-link mr-2"></i>Link</button>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkSoftwareModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_asset_link_software.php"
data-ajax-id="<?php echo $asset_id; ?>">
<i class="fa fa-fw fa-cube mr-2"></i>License <i class="fa fa-fw fa-cube mr-2"></i>License
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkCredentialModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_asset_link_credential.php"
data-ajax-id="<?php echo $asset_id; ?>">
<i class="fa fa-fw fa-key mr-2"></i>Credential <i class="fa fa-fw fa-key mr-2"></i>Credential
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkServiceModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_asset_link_service.php"
data-ajax-id="<?php echo $asset_id; ?>">
<i class="fa fa-fw fa-stream mr-2"></i>Service <i class="fa fa-fw fa-stream mr-2"></i>Service
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkDocumentModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_asset_link_document.php"
data-ajax-id="<?php echo $asset_id; ?>">
<i class="fa fa-fw fa-folder mr-2"></i>Document <i class="fa fa-fw fa-folder mr-2"></i>Document
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkFileModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_asset_link_file.php"
data-ajax-id="<?php echo $asset_id; ?>">
<i class="fa fa-fw fa-paperclip mr-2"></i>File <i class="fa fa-fw fa-paperclip mr-2"></i>File
</a> </a>
</div> </div>
</div> </div>
</div> </div>
<div class="card card-dark"> <div class="card card-dark">
<div class="card-header py-2"> <div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fa fa-fw fa-ethernet mr-2"></i><?php echo $asset_name; ?> Network Interfaces</h3> <h3 class="card-title mt-2"><i class="fa fa-fw fa-ethernet mr-2"></i>Interfaces</h3>
<div class="card-tools"> <div class="card-tools">
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addAssetInterfaceModal"> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addAssetInterfaceModal">
@@ -1193,10 +1206,4 @@ require_once "modals/credential_add_modal.php";
require_once "modals/client_document_add_modal.php"; require_once "modals/client_document_add_modal.php";
require_once "modals/client_file_upload_modal.php"; require_once "modals/client_file_upload_modal.php";
require_once "modals/asset_link_software_modal.php";
require_once "modals/asset_link_credential_modal.php";
require_once "modals/asset_link_service_modal.php";
require_once "modals/asset_link_document_modal.php";
require_once "modals/asset_link_file_modal.php";
require_once "includes/footer.php"; require_once "includes/footer.php";

View File

@@ -205,7 +205,7 @@ if (mysqli_num_rows($os_sql) > 0) {
</div> </div>
<?php if ($client_url) { ?> <?php if ($client_url) { ?>
<div class="col-md-2"> <div class="col-md-2">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="location" onchange="this.form.submit()"> <select class="form-control select2" name="location" onchange="this.form.submit()">
<option value="">- All Locations -</option> <option value="">- All Locations -</option>
@@ -233,7 +233,7 @@ if (mysqli_num_rows($os_sql) > 0) {
</div> </div>
<?php } else { ?> <?php } else { ?>
<div class="col-md-2"> <div class="col-md-2">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="client" onchange="this.form.submit()"> <select class="form-control select2" name="client" onchange="this.form.submit()">
<option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option> <option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option>
@@ -338,7 +338,7 @@ if (mysqli_num_rows($os_sql) > 0) {
<div class="table-responsive"> <div class="table-responsive">
<table class="table border table-hover"> <table class="table border table-hover">
<thead class="thead-light <?php if (!$num_rows[0]) { echo "d-none"; } ?>"> <thead class="thead-light <?php if (!$num_rows[0]) { echo "d-none"; } ?> text-nowrap">
<tr> <tr>
<td class="bg-light pr-0"> <td class="bg-light pr-0">
<div class="form-check"> <div class="form-check">
@@ -519,7 +519,7 @@ if (mysqli_num_rows($os_sql) > 0) {
if ($contact_name) { if ($contact_name) {
$contact_name_display = "<a href='#' $contact_name_display = "<a href='#'
data-toggle='ajax-modal' data-toggle='ajax-modal'
data-modal-size='lg' data-modal-size='xl'
data-ajax-url='ajax/ajax_contact_details.php' data-ajax-url='ajax/ajax_contact_details.php'
data-ajax-id='$asset_contact_id'> data-ajax-id='$asset_contact_id'>
$contact_name $contact_archive_display $contact_name $contact_archive_display
@@ -550,11 +550,7 @@ if (mysqli_num_rows($os_sql) > 0) {
</div> </div>
</td> </td>
<td> <td>
<a class="text-dark" href="#" <a class="text-dark" href="asset_details.php?client_id=<?php echo $client_id; ?>&asset_id=<?php echo $asset_id; ?>">
data-toggle="ajax-modal"
data-modal-size="lg"
data-ajax-url="ajax/ajax_asset_details.php?<?php echo $client_url; ?>"
data-ajax-id="<?php echo $asset_id; ?>">
<div class="media"> <div class="media">
<i class="fa fa-fw fa-2x fa-<?php echo $device_icon; ?> mr-3 mt-1"></i> <i class="fa fa-fw fa-2x fa-<?php echo $device_icon; ?> mr-3 mt-1"></i>
<div class="media-body"> <div class="media-body">

View File

@@ -56,6 +56,7 @@ header("X-Frame-Options: DENY"); // Legacy
</a> </a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown1"> <div class="dropdown-menu" aria-labelledby="navbarDropdown1">
<a class="dropdown-item" href="invoices.php">Invoices</a> <a class="dropdown-item" href="invoices.php">Invoices</a>
<a class="dropdown-item" href="recurring_invoices.php">Recurring Invoices</a>
<a class="dropdown-item" href="quotes.php">Quotes</a> <a class="dropdown-item" href="quotes.php">Quotes</a>
<a class="dropdown-item" href="autopay.php">Auto Payment</a> <a class="dropdown-item" href="autopay.php">Auto Payment</a>
</div> </div>

View File

@@ -38,7 +38,7 @@ if (isset($_POST['add_ticket'])) {
$new_config_ticket_next_number = $config_ticket_next_number + 1; $new_config_ticket_next_number = $config_ticket_next_number + 1;
mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1");
mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_category = $category, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = 1, ticket_billable = $config_ticket_default_billable, ticket_created_by = 0, ticket_contact_id = $session_contact_id, ticket_url_key = '$url_key', ticket_client_id = $session_client_id"); mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_source = 'Portal', ticket_category = $category, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = 1, ticket_billable = $config_ticket_default_billable, ticket_created_by = $session_user_id, ticket_contact_id = $session_contact_id, ticket_url_key = '$url_key', ticket_client_id = $session_client_id");
$ticket_id = mysqli_insert_id($mysqli); $ticket_id = mysqli_insert_id($mysqli);
// Notify agent DL of the new ticket, if populated with a valid email // Notify agent DL of the new ticket, if populated with a valid email
@@ -597,10 +597,22 @@ if (isset($_GET['stripe_save_card'])) {
// Get some card/payment method details for the email/logging // Get some card/payment method details for the email/logging
$payment_method_details = $stripe->paymentMethods->retrieve($payment_method); $payment_method_details = $stripe->paymentMethods->retrieve($payment_method);
$card_info = sanitizeInput($payment_method_details->card->display_brand) . " " . sanitizeInput($payment_method_details->card->last4); $card_type = sanitizeInput($payment_method_details->card->brand);
$last4 = sanitizeInput($payment_method_details->card->last4);
$expiry_month = sanitizeInput($payment_method_details->card->exp_month);
$expiry_year = sanitizeInput($payment_method_details->card->exp_year);
// Format the payment details string (Visa - 4324 | Exp 12/25)
$stripe_pm_details = "$card_type - $last4 | Exp $expiry_month/$expiry_year";
// Save the formatted payment details into stripe_pm_details
$update_query = "
UPDATE client_stripe
SET stripe_pm_details = '$stripe_pm_details'
WHERE client_id = $session_client_id LIMIT 1";
mysqli_query($mysqli, $update_query);
// Send email confirmation // Send email confirmation
// Company Details & Settings // Company Details & Settings
$sql_settings = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1"); $sql_settings = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
$row = mysqli_fetch_array($sql_settings); $row = mysqli_fetch_array($sql_settings);
@@ -617,7 +629,7 @@ if (isset($_GET['stripe_save_card'])) {
if (!empty($config_smtp_host)) { if (!empty($config_smtp_host)) {
$subject = "Payment method saved"; $subject = "Payment method saved";
$body = "Hello $session_contact_name,<br><br>Were writing to confirm that your payment details have been securely stored with Stripe, our trusted payment processor.<br><br>By agreeing to save your payment information, you have authorized us to automatically bill your card ($card_info) for any future invoices. The payment details youve provided are securely stored with Stripe and will be used solely for invoices. We do not have access to your full card details.<br><br>You may update or remove your payment information at any time using the portal.<br><br>Thank you for your business!<br><br>--<br>$company_name - Billing Department<br>$config_invoice_from_email<br>$company_phone"; $body = "Hello $session_contact_name,<br><br>Were writing to confirm that your payment details have been securely stored with Stripe, our trusted payment processor.<br><br>By agreeing to save your payment information, you have authorized us to automatically bill your card ($stripe_pm_details) for any future invoices. The payment details youve provided are securely stored with Stripe and will be used solely for invoices. We do not have access to your full card details.<br><br>You may update or remove your payment information at any time using the portal.<br><br>Thank you for your business!<br><br>--<br>$company_name - Billing Department<br>$config_invoice_from_email<br>$company_phone";
$data = [ $data = [
[ [
@@ -635,12 +647,11 @@ if (isset($_GET['stripe_save_card'])) {
} }
// Logging // Logging
logAction("Stripe", "Update", "$session_contact_name saved payment method ($card_info) for future automatic payments (PM: $payment_method)", $session_client_id, $session_client_id); logAction("Stripe", "Update", "$session_contact_name saved payment method ($stripe_pm_details) for future automatic payments (PM: $payment_method)", $session_client_id, $session_client_id);
// Redirect // Redirect
$_SESSION['alert_message'] = "Payment method saved - thank you"; $_SESSION['alert_message'] = "Payment method saved - thank you";
header('Location: autopay.php'); header('Location: autopay.php');
} }
if (isset($_GET['stripe_remove_pm'])) { if (isset($_GET['stripe_remove_pm'])) {
@@ -677,7 +688,15 @@ if (isset($_GET['stripe_remove_pm'])) {
} }
// Remove payment method from ITFlow // Remove payment method from ITFlow
mysqli_query($mysqli, "UPDATE client_stripe SET stripe_pm = NULL WHERE client_id = $session_client_id LIMIT 1"); mysqli_query($mysqli, "UPDATE client_stripe SET stripe_pm = NULL, stripe_pm_details = NULL WHERE client_id = $session_client_id LIMIT 1");
// Remove Auto Pay on recurring invoices that are stripe
$sql_recurring_invoices = mysqli_query($mysqli, "SELECT recurring_invoice_id FROM recurring_invoices WHERE recurring_invoice_client_id = $session_client_id");
while ($row = mysqli_fetch_array($sql_recurring_invoices)) {
$recurring_invoice_id = intval($row['recurring_invoice_id']);
mysqli_query($mysqli, "DELETE FROM recurring_payments WHERE recurring_payment_method = 'Stripe' AND recurring_payment_recurring_invoice_id = $recurring_invoice_id");
}
// Logging & Redirect // Logging & Redirect
logAction("Stripe", "Update", "$session_contact_name deleted saved Stripe payment method (PM: $payment_method)", $session_client_id, $session_client_id); logAction("Stripe", "Update", "$session_contact_name deleted saved Stripe payment method (PM: $payment_method)", $session_client_id, $session_client_id);
@@ -685,3 +704,49 @@ if (isset($_GET['stripe_remove_pm'])) {
$_SESSION['alert_message'] = "Payment method removed"; $_SESSION['alert_message'] = "Payment method removed";
header('Location: autopay.php'); header('Location: autopay.php');
} }
if (isset($_POST['add_recurring_payment'])) {
$recurring_invoice_id = intval($_POST['recurring_invoice_id']);
// Get Recurring Info for logging and alerting
$sql = mysqli_query($mysqli, "SELECT * FROM recurring_invoices WHERE recurring_invoice_id = $recurring_invoice_id");
$row = mysqli_fetch_array($sql);
$recurring_invoice_prefix = sanitizeInput($row['recurring_invoice_prefix']);
$recurring_invoice_number = intval($row['recurring_invoice_number']);
$recurring_invoice_amount = floatval($row['recurring_invoice_amount']);
$recurring_invoice_currency_code = sanitizeInput($row['recurring_invoice_currency_code']);
mysqli_query($mysqli,"INSERT INTO recurring_payments SET recurring_payment_currency_code = '$recurring_invoice_currency_code', recurring_payment_account_id = $config_stripe_account, recurring_payment_method = 'Stripe', recurring_payment_recurring_invoice_id = $recurring_invoice_id");
// Get Payment ID for reference
$recurring_payment_id = mysqli_insert_id($mysqli);
// Logging
logAction("Recurring Invoice", "Auto Payment", "$session_name created Auto Pay for Recurring Invoice $recurring_invoice_prefix$recurring_invoice_number in the amount of " . numfmt_format_currency($currency_format, $recurring_invoice_amount, $recurring_invoice_currency_code), $session_client_id, $recurring_invoice_id);
$_SESSION['alert_message'] = "Automatic Payment enabled for Recurring Invoice $recurring_invoice_prefix$recurring_invoice_number";
header("Location: " . $_SERVER["HTTP_REFERER"]);
}
if (isset($_POST['delete_recurring_payment'])) {
$recurring_invoice_id = intval($_POST['recurring_invoice_id']);
// Get the invoice total and details
$sql = mysqli_query($mysqli,"SELECT * FROM recurring_invoices WHERE recurring_invoice_id = $recurring_invoice_id");
$row = mysqli_fetch_array($sql);
$recurring_invoice_prefix = sanitizeInput($row['recurring_invoice_prefix']);
$recurring_invoice_number = intval($row['recurring_invoice_number']);
mysqli_query($mysqli,"DELETE FROM recurring_payments WHERE recurring_payment_recurring_invoice_id = $recurring_invoice_id");
// Logging
logAction("Recurring Invoice", "Auto Payment", "$session_name removed auto Pay from Recurring Invoice $recurring_invoice_prefix$recurring_invoice_number", $session_client_id, $recurring_invoice_id);
$_SESSION['alert_message'] = "Automatic Payment disabled for Recurring Invoice $recurring_invoice_prefix$recurring_invoice_number";
header("Location: " . $_SERVER["HTTP_REFERER"]);
}

View File

@@ -0,0 +1,120 @@
<?php
/*
* Client Portal
* Invoices for PTC
*/
header("Content-Security-Policy: default-src 'self'");
require_once "includes/inc_all.php";
if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) {
header("Location: post.php?logout");
exit();
}
// Get client's StripeID from database
$stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT * FROM client_stripe WHERE client_id = $session_client_id LIMIT 1"));
if ($stripe_client_details) {
$stripe_pm = sanitizeInput($stripe_client_details['stripe_pm']);
}
$recurring_invoices_sql = mysqli_query($mysqli, "SELECT * FROM recurring_invoices
LEFT JOIN recurring_payments ON recurring_payment_recurring_invoice_id = recurring_invoice_id
WHERE recurring_invoice_client_id = $session_client_id
AND recurring_invoice_status = 1
ORDER BY recurring_invoice_next_date DESC"
);
?>
<h3>Recurring Invoices</h3>
<div class="row">
<div class="col-md-10">
<table class="table tabled-bordered border border-dark">
<thead class="thead-dark">
<tr>
<th>Scope</th>
<th>Amount</th>
<th>Next Bill Date</th>
<th>Frequency</th>
<?php if ($config_stripe_enable) { ?>
<th>Auto Pay</th>
<?php } ?>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($recurring_invoices_sql)) {
$recurring_invoice_id = intval($row['recurring_invoice_id']);
$recurring_invoice_prefix = nullable_htmlentities($row['recurring_invoice_prefix']);
$recurring_invoice_number = intval($row['recurring_invoice_number']);
$recurring_invoice_scope = nullable_htmlentities($row['recurring_invoice_scope']);
$recurring_invoice_status = nullable_htmlentities($row['recurring_invoice_status']);
$recurring_invoice_next_date = nullable_htmlentities($row['recurring_invoice_next_date']);
$recurring_invoice_frequency = nullable_htmlentities($row['recurring_invoice_frequency']);
$recurring_invoice_amount = floatval($row['recurring_invoice_amount']);
$recurring_payment_id = intval($row['recurring_payment_id']);
$recurring_payment_recurring_invoice_id = intval($row['recurring_payment_recurring_invoice_id']);
if ($config_stripe_enable) {
if ($recurring_payment_recurring_invoice_id) {
$auto_pay_display = "
Yes
<a href='post.php?delete_recurring_payment=$recurring_payment_id' title='Remove'>
<i class='fas fa-fw fa-times-circle'></i>
</a>
";
} else {
$auto_pay_display = "
<a href='#' data-toggle='modal' data-target='#addRecurringPaymentModal$recurring_invoice_id'>
Create
</a>
";
//require "recurring_payment_add_modal.php";
}
}
if (empty($recurring_invoice_scope)) {
$recurring_invoice_scope_display = "-";
} else {
$recurring_invoice_scope_display = $recurring_invoice_scope;
}
?>
<tr>
<td><?php echo $recurring_invoice_scope_display; ?></td>
<td><?php echo numfmt_format_currency($currency_format, $recurring_invoice_amount, $session_company_currency); ?></td>
<td><?php echo $recurring_invoice_next_date; ?></td>
<td><?php echo ucwords($recurring_invoice_frequency); ?>ly</td>
<?php if ($config_stripe_enable) { ?>
<td>
<?php if ($stripe_pm) { ?>
<form class="form" action="post.php" method="post">
<input type="hidden" name="recurring_invoice_id" value="<?php echo $recurring_invoice_id; ?>">
<?php if ($recurring_payment_recurring_invoice_id) { ?>
<button type="submit" name="delete_recurring_payment" class="btn btn-outline-dark"><i class="fas fa-times mr-2"></i>Disable</button>
<?php } else { ?>
<button type="submit" name="add_recurring_payment" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Enable</button>
<?php } ?>
</form>
<?php } else { ?>
<a href="autopay.php">Add Card Details First</a>
<?php } ?>
</td>
<?php } ?>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
<?php
require_once "includes/footer.php";

View File

@@ -62,7 +62,7 @@ $all_tickets = mysqli_query($mysqli, "SELECT ticket_id, ticket_prefix, ticket_nu
$ticket_contact_name = nullable_htmlentities($row['contact_name']); $ticket_contact_name = nullable_htmlentities($row['contact_name']);
echo "<tr>"; echo "<tr>";
echo "<td> <a href='ticket.php?id=$ticket_id'> $ticket_prefix$ticket_id</a></td>"; echo "<td> <a href='ticket.php?id=$ticket_id'> $ticket_prefix$ticket_number</a></td>";
echo "<td> <a href='ticket.php?id=$ticket_id'> $ticket_subject</a></td>"; echo "<td> <a href='ticket.php?id=$ticket_id'> $ticket_subject</a></td>";
echo "<td>$ticket_contact_name</td>"; echo "<td>$ticket_contact_name</td>";
echo "<td>$ticket_status</td>"; echo "<td>$ticket_status</td>";

View File

@@ -9,9 +9,6 @@ require_once "includes/inc_all_client.php";
// Perms // Perms
enforceUserPermission('module_support'); enforceUserPermission('module_support');
// Rebuild URL
$url_query_strings_sort = http_build_query($get_copy);
$sql = mysqli_query( $sql = mysqli_query(
$mysqli, $mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM racks "SELECT SQL_CALC_FOUND_ROWS * FROM racks
@@ -323,7 +320,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
} else { } else {
// No device in this row // No device in this row
?> ?>
<td class="text-center">No device</td> <td class="text-center text-secondary">--</td>
<td></td> <td></td>
<?php <?php
} }

View File

@@ -62,7 +62,7 @@ $sql = mysqli_query(
WHERE (client_name LIKE '%$q%' OR client_abbreviation LIKE '%$q%' OR client_type LIKE '%$q%' OR client_referral LIKE '%$q%' WHERE (client_name LIKE '%$q%' OR client_abbreviation LIKE '%$q%' OR client_type LIKE '%$q%' OR client_referral LIKE '%$q%'
OR contact_email LIKE '%$q%' OR contact_name LIKE '%$q%' OR contact_phone LIKE '%$phone_query%' OR contact_email LIKE '%$q%' OR contact_name LIKE '%$q%' OR contact_phone LIKE '%$phone_query%'
OR contact_mobile LIKE '%$phone_query%' OR location_address LIKE '%$q%' OR contact_mobile LIKE '%$phone_query%' OR location_address LIKE '%$q%'
OR location_city LIKE '%$q%' OR location_state LIKE '%$q%' OR location_zip LIKE '%$q%' OR location_city LIKE '%$q%' OR location_state LIKE '%$q%' OR location_zip LIKE '%$q%' OR location_country LIKE '%$q%'
OR tag_name LIKE '%$q%' OR client_tax_id_number LIKE '%$q%') OR tag_name LIKE '%$q%' OR client_tax_id_number LIKE '%$q%')
AND client_$archive_query AND client_$archive_query
AND DATE(client_created_at) BETWEEN '$dtf' AND '$dtt' AND DATE(client_created_at) BETWEEN '$dtf' AND '$dtt'
@@ -111,7 +111,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<input type="hidden" name="archived" value="<?php echo $archived; ?>"> <input type="hidden" name="archived" value="<?php echo $archived; ?>">
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<div class="input-group"> <div class="input-group mb-3 mb-sm-0">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search <?php if($leads_filter == 0){ echo "clients"; } else { echo "leads"; } ?>" autofocus> <input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search <?php if($leads_filter == 0){ echo "clients"; } else { echo "leads"; } ?>" autofocus>
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button> <button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
@@ -126,7 +126,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<a href="?leads=1" class="btn btn-<?php if ($leads_filter == 1){ echo "primary"; } else { echo "default"; } ?>"><i class="fa fa-fw fa-bullhorn mr-2"></i>Leads</a> <a href="?leads=1" class="btn btn-<?php if ($leads_filter == 1){ echo "primary"; } else { echo "default"; } ?>"><i class="fa fa-fw fa-bullhorn mr-2"></i>Leads</a>
</div> </div>
<div class="btn-group mr-2"> <div class="btn-group">
<a href="?<?php echo $url_query_strings_sort ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>" <a href="?<?php echo $url_query_strings_sort ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
class="btn btn-<?php if ($archived == 1) { echo "primary"; } else { echo "default"; } ?>"> class="btn btn-<?php if ($archived == 1) { echo "primary"; } else { echo "default"; } ?>">
<i class="fa fa-fw fa-archive mr-2"></i>Archived <i class="fa fa-fw fa-archive mr-2"></i>Archived
@@ -248,7 +248,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<hr> <hr>
<div class="table-responsive-sm"> <div class="table-responsive-sm">
<table class="table table-striped table-hover table-borderless"> <table class="table table-striped table-hover table-borderless">
<thead class="<?php if ($num_rows[0] == 0) { echo "d-none"; } ?>"> <thead class="<?php if ($num_rows[0] == 0) { echo "d-none"; } ?> text-nowrap">
<tr> <tr>
<th> <th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>"> <a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>">
@@ -286,7 +286,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
if (empty($location_address) && empty($location_city) && empty($location_state) && empty($location_zip)) { if (empty($location_address) && empty($location_city) && empty($location_state) && empty($location_zip)) {
$location_address_display = "-"; $location_address_display = "-";
} else { } else {
$location_address_display = "$location_address<br>$location_city $location_state $location_zip"; $location_address_display = "<i class='fa fa-fw fa-map-marker-alt text-secondary mr-2'></i>$location_address<br><i class='fa fa-fw mr-2'></i>$location_city $location_state $location_zip<br><i class='fa fa-fw mr-2'></i><small>$location_country</small>";
} }
$contact_id = intval($row['contact_id']); $contact_id = intval($row['contact_id']);
$contact_name = nullable_htmlentities($row['contact_name']); $contact_name = nullable_htmlentities($row['contact_name']);

View File

@@ -32,7 +32,7 @@ if (isset($_GET['contact_id'])) {
$contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code)); $contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code));
$contact_extension = nullable_htmlentities($row['contact_extension']); $contact_extension = nullable_htmlentities($row['contact_extension']);
$contact_mobile_country_code = nullable_htmlentities($row['contact_mobile_country_code']); $contact_mobile_country_code = nullable_htmlentities($row['contact_mobile_country_code']);
$contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_mobile_country_code)); $contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_mobile'], $contact_mobile_country_code));
$contact_email = nullable_htmlentities($row['contact_email']); $contact_email = nullable_htmlentities($row['contact_email']);
$contact_photo = nullable_htmlentities($row['contact_photo']); $contact_photo = nullable_htmlentities($row['contact_photo']);
$contact_pin = nullable_htmlentities($row['contact_pin']); $contact_pin = nullable_htmlentities($row['contact_pin']);
@@ -294,31 +294,47 @@ if (isset($_GET['contact_id'])) {
<div class="dropdown dropleft"> <div class="dropdown dropleft">
<button type="button" class="btn btn-outline-primary" data-toggle="dropdown"><i class="fas fa-link mr-2"></i>Link</button> <button type="button" class="btn btn-outline-primary" data-toggle="dropdown"><i class="fas fa-link mr-2"></i>Link</button>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkAssetModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_contact_link_asset.php"
data-ajax-id="<?php echo $contact_id; ?>">
<i class="fa fa-fw fa-desktop mr-2"></i>Asset <i class="fa fa-fw fa-desktop mr-2"></i>Asset
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkSoftwareModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_contact_link_software.php"
data-ajax-id="<?php echo $contact_id; ?>">
<i class="fa fa-fw fa-cube mr-2"></i>License <i class="fa fa-fw fa-cube mr-2"></i>License
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkCredentialModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_contact_link_credential.php"
data-ajax-id="<?php echo $contact_id; ?>">
<i class="fa fa-fw fa-key mr-2"></i>Credential <i class="fa fa-fw fa-key mr-2"></i>Credential
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkServiceModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_contact_link_service.php"
data-ajax-id="<?php echo $contact_id; ?>">
<i class="fa fa-fw fa-stream mr-2"></i>Service <i class="fa fa-fw fa-stream mr-2"></i>Service
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkDocumentModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_contact_link_document.php"
data-ajax-id="<?php echo $contact_id; ?>">
<i class="fa fa-fw fa-folder mr-2"></i>Document <i class="fa fa-fw fa-folder mr-2"></i>Document
</a> </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#linkFileModal"> <a class="dropdown-item text-dark" href="#"
data-toggle="ajax-modal"
data-ajax-url="ajax/ajax_contact_link_file.php"
data-ajax-id="<?php echo $contact_id; ?>">
<i class="fa fa-fw fa-paperclip mr-2"></i>File <i class="fa fa-fw fa-paperclip mr-2"></i>File
</a> </a>
</div> </div>
</div> </div>
</div> </div>
@@ -1192,11 +1208,4 @@ require_once "modals/credential_add_modal.php";
require_once "modals/client_document_add_modal.php"; require_once "modals/client_document_add_modal.php";
require_once "modals/client_file_upload_modal.php"; require_once "modals/client_file_upload_modal.php";
require_once "modals/contact_link_asset_modal.php";
require_once "modals/contact_link_software_modal.php";
require_once "modals/contact_link_credential_modal.php";
require_once "modals/contact_link_service_modal.php";
require_once "modals/contact_link_document_modal.php";
require_once "modals/contact_link_file_modal.php";
require_once "includes/footer.php"; require_once "includes/footer.php";

View File

@@ -110,7 +110,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select onchange="this.form.submit()" class="form-control select2" name="tags[]" data-placeholder="- Select Tags -" multiple> <select onchange="this.form.submit()" class="form-control select2" name="tags[]" data-placeholder="- Select Tags -" multiple>
<?php <?php
@@ -137,7 +137,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php if ($client_url) { ?> <?php if ($client_url) { ?>
<div class="col-md-2"> <div class="col-md-2">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="location" onchange="this.form.submit()"> <select class="form-control select2" name="location" onchange="this.form.submit()">
<option value="">- All Locations -</option> <option value="">- All Locations -</option>
@@ -165,7 +165,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
<?php } else { ?> <?php } else { ?>
<div class="col-md-2"> <div class="col-md-2">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="client" onchange="this.form.submit()"> <select class="form-control select2" name="client" onchange="this.form.submit()">
<option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option> <option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option>
@@ -319,7 +319,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
} else { } else {
$contact_phone_display = "<div><i class='fas fa-fw fa-phone mr-2'></i><a href='tel:$contact_phone'>$contact_phone$contact_extension_display</a></div>"; $contact_phone_display = "<div><i class='fas fa-fw fa-phone mr-2'></i><a href='tel:$contact_phone'>$contact_phone$contact_extension_display</a></div>";
} }
$contact_mobile_country_code = nullable_htmlentities($row['contact_phone_country_code']); $contact_mobile_country_code = nullable_htmlentities($row['contact_mobile_country_code']);
$contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_mobile'], $contact_mobile_country_code)); $contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_mobile'], $contact_mobile_country_code));
if (empty($contact_mobile)) { if (empty($contact_mobile)) {
$contact_mobile_display = ""; $contact_mobile_display = "";
@@ -374,7 +374,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$sql_related_assets = mysqli_query($mysqli, "SELECT * FROM assets WHERE asset_contact_id = $contact_id ORDER BY asset_id DESC"); $sql_related_assets = mysqli_query($mysqli, "SELECT * FROM assets WHERE asset_contact_id = $contact_id ORDER BY asset_id DESC");
$asset_count = mysqli_num_rows($sql_related_assets); $asset_count = mysqli_num_rows($sql_related_assets);
if ($asset_count) { if ($asset_count) {
$asset_count_display = "<span class='mr-2 badge badge-pill badge-dark p-2' title='$asset_count Assets'><i class='fas fa-fw fa-desktop mr-2'></i>$asset_count</span>"; $asset_count_display = "<span class='mr-2 mb-1 badge badge-pill badge-dark p-2' title='$asset_count Assets'><i class='fas fa-fw fa-desktop mr-2'></i>$asset_count</span>";
} else { } else {
$asset_count_display = ''; $asset_count_display = '';
} }
@@ -383,7 +383,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$sql_related_credentials = mysqli_query($mysqli, "SELECT * FROM credentials WHERE credential_contact_id = $contact_id ORDER BY credential_id DESC"); $sql_related_credentials = mysqli_query($mysqli, "SELECT * FROM credentials WHERE credential_contact_id = $contact_id ORDER BY credential_id DESC");
$credential_count = mysqli_num_rows($sql_related_credentials); $credential_count = mysqli_num_rows($sql_related_credentials);
if ($credential_count) { if ($credential_count) {
$credential_count_display = "<span class='mr-2 badge badge-pill badge-secondary p-2' title='$credential_count Credentials'><i class='fas fa-fw fa-key mr-2'></i>$credential_count</span>"; $credential_count_display = "<span class='mr-2 mb-1 badge badge-pill badge-secondary p-2' title='$credential_count Credentials'><i class='fas fa-fw fa-key mr-2'></i>$credential_count</span>";
} else { } else {
$credential_count_display = ''; $credential_count_display = '';
} }
@@ -392,7 +392,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$sql_related_software = mysqli_query($mysqli, "SELECT * FROM software, software_contacts WHERE software.software_id = software_contacts.software_id AND software_contacts.contact_id = $contact_id"); $sql_related_software = mysqli_query($mysqli, "SELECT * FROM software, software_contacts WHERE software.software_id = software_contacts.software_id AND software_contacts.contact_id = $contact_id");
$software_count = mysqli_num_rows($sql_related_software); $software_count = mysqli_num_rows($sql_related_software);
if ($software_count) { if ($software_count) {
$software_count_display = "<span class='mr-2 badge badge-pill badge-secondary p-2' title='$software_count Licenses'><i class='fas fa-fw fa-cube mr-2'></i>$software_count</span>"; $software_count_display = "<span class='mr-2 mb-1 badge badge-pill badge-secondary p-2' title='$software_count Licenses'><i class='fas fa-fw fa-cube mr-2'></i>$software_count</span>";
} else { } else {
$software_count_display = ''; $software_count_display = '';
} }
@@ -401,7 +401,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_contact_id = $contact_id"); $sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_contact_id = $contact_id");
$ticket_count = mysqli_num_rows($sql_related_tickets); $ticket_count = mysqli_num_rows($sql_related_tickets);
if ($ticket_count) { if ($ticket_count) {
$ticket_count_display = "<span class='mr-2 badge badge-pill badge-secondary p-2' title='$ticket_count Tickets'><i class='fas fa-fw fa-life-ring mr-2'></i>$ticket_count</span>"; $ticket_count_display = "<span class='mr-2 mb-1 badge badge-pill badge-secondary p-2' title='$ticket_count Tickets'><i class='fas fa-fw fa-life-ring mr-2'></i>$ticket_count</span>";
} else { } else {
$ticket_count_display = ''; $ticket_count_display = '';
} }
@@ -410,7 +410,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$sql_related_documents = mysqli_query($mysqli, "SELECT * FROM documents, contact_documents WHERE documents.document_id = contact_documents.document_id AND contact_documents.contact_id = $contact_id"); $sql_related_documents = mysqli_query($mysqli, "SELECT * FROM documents, contact_documents WHERE documents.document_id = contact_documents.document_id AND contact_documents.contact_id = $contact_id");
$document_count = mysqli_num_rows($sql_related_documents); $document_count = mysqli_num_rows($sql_related_documents);
if ($document_count) { if ($document_count) {
$document_count_display = "<span class='mr-2 badge badge-pill badge-secondary p-2' title='$document_count Documents'><i class='fas fa-fw fa-file-alt mr-2'></i>$document_count</span>"; $document_count_display = "<span class='mr-2 mb-1 badge badge-pill badge-secondary p-2' title='$document_count Documents'><i class='fas fa-fw fa-file-alt mr-2'></i>$document_count</span>";
} else { } else {
$document_count_display = ''; $document_count_display = '';
} }
@@ -445,11 +445,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
</td> </td>
<td> <td>
<a class="text-dark" href="#" <a class="text-dark" href="contact_details.php?client_id=<?php echo $client_id; ?>&contact_id=<?php echo $contact_id; ?>">
data-toggle="ajax-modal"
data-modal-size="lg"
data-ajax-url="ajax/ajax_contact_details.php?<?php echo $client_url; ?>"
data-ajax-id="<?php echo $contact_id; ?>">
<div class="media"> <div class="media">
<?php if ($contact_photo) { ?> <?php if ($contact_photo) { ?>
<span class="fa-stack fa-2x mr-3 text-center"> <span class="fa-stack fa-2x mr-3 text-center">

View File

@@ -124,7 +124,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select onchange="this.form.submit()" class="form-control select2" name="tags[]" data-placeholder="- Select Tags -" multiple> <select onchange="this.form.submit()" class="form-control select2" name="tags[]" data-placeholder="- Select Tags -" multiple>
<?php <?php
@@ -151,7 +151,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php if ($client_url) { ?> <?php if ($client_url) { ?>
<div class="col-md-2"> <div class="col-md-2">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="location" onchange="this.form.submit()"> <select class="form-control select2" name="location" onchange="this.form.submit()">
<option value="">- All Asset Locations -</option> <option value="">- All Asset Locations -</option>
@@ -171,7 +171,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
<?php } else { ?> <?php } else { ?>
<div class="col-md-2"> <div class="col-md-2">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="client" onchange="this.form.submit()"> <select class="form-control select2" name="client" onchange="this.form.submit()">
<option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option> <option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option>
@@ -242,7 +242,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="table-responsive-sm"> <div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover"> <table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>"> <thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?> text-nowrap">
<tr> <tr>
<td class="pr-0"> <td class="pr-0">
<div class="form-check"> <div class="form-check">
@@ -335,7 +335,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$credential_tags_display = implode('', $credential_tag_name_display_array); $credential_tags_display = implode('', $credential_tag_name_display_array);
if ($credential_contact_id) { if ($credential_contact_id) {
$credential_contact_display = "<a href='#' class='mr-2 badge badge-pill badge-dark p-2' title='$contact_name' $credential_contact_display = "<a href='#' class='mr-2 mb-1 badge badge-pill badge-dark p-2' title='$contact_name'
data-toggle='ajax-modal' data-toggle='ajax-modal'
data-modal-size='lg' data-modal-size='lg'
data-ajax-url='ajax/ajax_contact_details.php' data-ajax-url='ajax/ajax_contact_details.php'
@@ -346,7 +346,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
} }
if ($credential_asset_id) { if ($credential_asset_id) {
$credential_asset_display = "<a href='#' class='mr-2 badge badge-pill badge-secondary p-2' title='$asset_name' data-toggle='ajax-modal' $credential_asset_display = "<a href='#' class='mr-2 mb-1 badge badge-pill badge-secondary p-2' title='$asset_name' data-toggle='ajax-modal'
data-modal-size='lg' data-modal-size='lg'
data-ajax-url='ajax/ajax_asset_details.php' data-ajax-url='ajax/ajax_asset_details.php'
data-ajax-id='$credential_asset_id'> data-ajax-id='$credential_asset_id'>
@@ -412,11 +412,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
</a> </a>
</td> </td>
<td><?php echo $credential_username_display; ?></td> <td class="text-nowrap"><?php echo $credential_username_display; ?></td>
<td> <td class="text-nowrap">
<button class="btn p-0" type="button" data-toggle="popover" data-trigger="focus" data-placement="top" data-content="<?php echo $credential_password; ?>"><i class="fas fa-2x fa-ellipsis-h text-secondary"></i><i class="fas fa-2x fa-ellipsis-h text-secondary"></i></button><button class="btn btn-sm clipboardjs" type="button" data-clipboard-text="<?php echo $credential_password; ?>"><i class="far fa-copy text-secondary"></i></button> <button class="btn p-0" type="button" data-toggle="popover" data-trigger="focus" data-placement="top" data-content="<?php echo $credential_password; ?>"><i class="fas fa-2x fa-ellipsis-h text-secondary"></i><i class="fas fa-2x fa-ellipsis-h text-secondary"></i></button><button class="btn btn-sm clipboardjs" type="button" data-clipboard-text="<?php echo $credential_password; ?>"><i class="far fa-copy text-secondary"></i></button>
</td> </td>
<td><?php echo $otp_display; ?></td> <td class="text-nowrap"><?php echo $otp_display; ?></td>
<td><?php echo $credential_uri_display; ?></td> <td><?php echo $credential_uri_display; ?></td>
<td> <td>
<?php echo "$credential_contact_display$credential_asset_display"; ?> <?php echo "$credential_contact_display$credential_asset_display"; ?>

View File

@@ -20,10 +20,10 @@
} }
} }
.grab-cursor { .drag-handle {
cursor: grab; cursor: grab !important;
} }
.grab-cursor:active { .drag-handle:active {
cursor: grabbing; cursor: grabbing !important;
} }

View File

@@ -0,0 +1,15 @@
/*!
* AdminLTE 3.2.0 Specific Dropdown Fix
* Targets .fix-quote-dropdown only
* Prevents alignment bugs in split button dropdowns going too far left
* (ChatGPT)
*/
.fix-quote-dropdown .dropdown-menu {
left: auto !important;
right: 0 !important;
top: calc(100% + 0.25rem) !important;
transform: none !important;
min-width: max-content;
z-index: 1050;
}

View File

@@ -1,41 +1,83 @@
/* General Popover Styling */
.popover { .popover {
max-width: 600px; max-width: 600px;
} }
/* Kanban Board Container */
#kanban-board { #kanban-board {
display: flex; display: flex;
box-sizing: border-box;
overflow-x: auto; overflow-x: auto;
box-sizing: border-box;
min-width: 400px; min-width: 400px;
height: calc(100vh - 210px); height: calc(100vh - 210px);
} }
/* Kanban Column */
.kanban-column { .kanban-column {
flex: 1; /* Allows columns to grow equally */ flex: 1;
margin: 0 10px; /* Space between columns */
min-width: 300px; min-width: 300px;
max-width: 300px; max-width: 300px;
margin: 0 10px;
background: #f4f4f4; background: #f4f4f4;
padding: 10px;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 4px;
padding: 10px;
min-height: calc(100vh - 230px); min-height: calc(100vh - 230px);
max-height: calc(100vh - 230px); max-height: calc(100vh - 230px);
box-sizing: border-box; box-sizing: border-box;
display: flex;
flex-direction: column;
} }
.kanban-column div { /* Column Inner Scrollable Task Area */
max-height: calc(100vh - 280px); /* Set your desired max height */ .kanban-status {
overflow-y: auto; /* Adds a scrollbar when content exceeds max height */ flex: 1;
overflow-y: auto;
min-height: 60px;
position: relative;
padding: 5px;
background-color: #f9f9f9;
border-radius: 4px;
} }
/* Individual Task Cards */
.task { .task {
background: #fff; background: #fff;
margin: 5px 0; margin: 5px 0;
padding: 10px; padding: 10px;
border: 1px solid #ddd; border: 1px solid #ddd;
user-select: none; /* Prevent text selection */ border-radius: 4px;
cursor: grab;
user-select: none;
} }
.drag-handle-class { /* Grabbing Cursor State */
touch-action: none; .task:active {
float: right; cursor: grabbing;
}
/* Drag Handle (shown on mobile or with class targeting) */
.drag-handle-class {
float: right;
touch-action: none;
cursor: grab;
}
/* Placeholder shown in empty columns */
.empty-placeholder {
border: 2px dashed #ccc;
background-color: #fcfcfc;
color: #999;
font-style: italic;
padding: 12px;
margin: 10px 0;
text-align: center;
border-radius: 4px;
pointer-events: none;
}
/* Sortable drop zone feedback (optional visual cue) */
.kanban-status.sortable-over {
background-color: #eaf6ff;
transition: background-color 0.2s ease;
} }

View File

@@ -41,7 +41,7 @@ $sql_years_select = mysqli_query($mysqli, "
<input type="hidden" name="enable_technical" value="0"> <input type="hidden" name="enable_technical" value="0">
<label for="year" class="mr-sm-2">Select Year:</label> <label for="year" class="mr-sm-2">Select Year:</label>
<select id="year" onchange="this.form.submit()" class="form-control mr-sm-3 col-sm-2" name="year"> <select id="year" onchange="this.form.submit()" class="form-control mr-sm-3 col-sm-2 mb-3 mb-sm-0" name="year">
<?php while ($row = mysqli_fetch_array($sql_years_select)) { <?php while ($row = mysqli_fetch_array($sql_years_select)) {
$year_select = $row['all_years']; $year_select = $row['all_years'];
if (empty($year_select)) { if (empty($year_select)) {
@@ -55,7 +55,7 @@ $sql_years_select = mysqli_query($mysqli, "
</select> </select>
<?php if ($session_user_role == 1 || ($session_user_role == 3 && $config_module_enable_accounting == 1)) { ?> <?php if ($session_user_role == 1 || ($session_user_role == 3 && $config_module_enable_accounting == 1)) { ?>
<div class="custom-control custom-switch mr-sm-3"> <div class="custom-control custom-switch mr-3">
<input type="checkbox" onchange="this.form.submit()" class="custom-control-input" id="customSwitch1" name="enable_financial" value="1" <?php if ($user_config_dashboard_financial_enable == 1) { echo "checked"; } ?>> <input type="checkbox" onchange="this.form.submit()" class="custom-control-input" id="customSwitch1" name="enable_financial" value="1" <?php if ($user_config_dashboard_financial_enable == 1) { echo "checked"; } ?>>
<label class="custom-control-label" for="customSwitch1">Financial</label> <label class="custom-control-label" for="customSwitch1">Financial</label>
</div> </div>
@@ -151,7 +151,7 @@ if ($user_config_dashboard_financial_enable == 1) {
$row = mysqli_fetch_array($sql_unbilled_tickets); $row = mysqli_fetch_array($sql_unbilled_tickets);
$unbilled_tickets = intval($row['unbilled_tickets']); $unbilled_tickets = intval($row['unbilled_tickets']);
} else { } else {
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(recurring_id) AS recurring_invoices_added FROM recurring WHERE YEAR(recurring_created_at) = $year")); $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(recurring_invoice_id) AS recurring_invoices_added FROM recurring_invoices WHERE YEAR(recurring_invoice_created_at) = $year"));
$recurring_invoices_added = intval($row['recurring_invoices_added']); $recurring_invoices_added = intval($row['recurring_invoices_added']);
} }

View File

@@ -3424,10 +3424,52 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.1.2'"); mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.1.2'");
} }
// if (CURRENT_DATABASE_VERSION == '2.1.2') { if (CURRENT_DATABASE_VERSION == '2.1.2') {
// // Insert queries here required to update to DB version 2.1.3
// 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");
// Update country_code to NULL for `locations` table
mysqli_query($mysqli, "ALTER TABLE `locations` MODIFY `location_phone_country_code` VARCHAR(10) DEFAULT NULL");
mysqli_query($mysqli, "ALTER TABLE `locations` MODIFY `location_fax_country_code` VARCHAR(10) DEFAULT NULL");
// Update country_code to NULL for `vendors` table
mysqli_query($mysqli, "ALTER TABLE `vendors` MODIFY `vendor_phone_country_code` VARCHAR(10) DEFAULT NULL");
// Update country_code to NULL for `companies` table
mysqli_query($mysqli, "ALTER TABLE `companies` MODIFY `company_phone_country_code` VARCHAR(10) DEFAULT NULL");
// Set country_code to NULL for `contacts` table
mysqli_query($mysqli, "UPDATE `contacts` SET `contact_phone_country_code` = NULL");
mysqli_query($mysqli, "UPDATE `contacts` SET `contact_mobile_country_code` = NULL");
// Set country_code to NULL for `locations` table
mysqli_query($mysqli, "UPDATE `locations` SET `location_phone_country_code` = NULL");
mysqli_query($mysqli, "UPDATE `locations` SET `location_fax_country_code` = NULL");
// Set country_code to NULL for `vendors` table
mysqli_query($mysqli, "UPDATE `vendors` SET `vendor_phone_country_code` = NULL");
// Set country_code to NULL for `companies` table
mysqli_query($mysqli, "UPDATE `companies` SET `company_phone_country_code` = NULL");
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.1.3'");
}
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'");
}
// if (CURRENT_DATABASE_VERSION == '2.1.4') {
// // Insert queries here required to update to DB version 2.1.5
// // Then, update the database to the next sequential version // // Then, update the database to the next sequential version
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.1.3'"); // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.1.5'");
// } // }
} else { } else {

16
db.sql
View File

@@ -482,6 +482,8 @@ CREATE TABLE `client_stripe` (
`client_id` int(11) NOT NULL, `client_id` int(11) NOT NULL,
`stripe_id` varchar(255) NOT NULL, `stripe_id` varchar(255) NOT NULL,
`stripe_pm` varchar(255) DEFAULT NULL, `stripe_pm` varchar(255) DEFAULT NULL,
`stripe_pm_details` varchar(200) DEFAULT NULL,
`stripe_pm_created_at` datetime NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`client_id`) PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET character_set_client = @saved_cs_client */;
@@ -546,7 +548,7 @@ CREATE TABLE `companies` (
`company_state` varchar(200) DEFAULT NULL, `company_state` varchar(200) DEFAULT NULL,
`company_zip` varchar(200) DEFAULT NULL, `company_zip` varchar(200) DEFAULT NULL,
`company_country` varchar(200) DEFAULT NULL, `company_country` varchar(200) DEFAULT NULL,
`company_phone_country_code` varchar(10) DEFAULT '1', `company_phone_country_code` varchar(10) DEFAULT NULL,
`company_phone` varchar(200) DEFAULT NULL, `company_phone` varchar(200) DEFAULT NULL,
`company_email` varchar(200) DEFAULT NULL, `company_email` varchar(200) DEFAULT NULL,
`company_website` varchar(200) DEFAULT NULL, `company_website` varchar(200) DEFAULT NULL,
@@ -680,10 +682,10 @@ CREATE TABLE `contacts` (
`contact_name` varchar(200) NOT NULL, `contact_name` varchar(200) NOT NULL,
`contact_title` varchar(200) DEFAULT NULL, `contact_title` varchar(200) DEFAULT NULL,
`contact_email` varchar(200) DEFAULT NULL, `contact_email` varchar(200) DEFAULT NULL,
`contact_phone_country_code` varchar(10) DEFAULT '1', `contact_phone_country_code` varchar(10) DEFAULT NULL,
`contact_phone` varchar(200) DEFAULT NULL, `contact_phone` varchar(200) DEFAULT NULL,
`contact_extension` varchar(200) DEFAULT NULL, `contact_extension` varchar(200) DEFAULT NULL,
`contact_mobile_country_code` varchar(10) DEFAULT '1', `contact_mobile_country_code` varchar(10) DEFAULT NULL,
`contact_mobile` varchar(200) DEFAULT NULL, `contact_mobile` varchar(200) DEFAULT NULL,
`contact_photo` varchar(200) DEFAULT NULL, `contact_photo` varchar(200) DEFAULT NULL,
`contact_pin` varchar(255) DEFAULT NULL, `contact_pin` varchar(255) DEFAULT NULL,
@@ -1115,10 +1117,10 @@ CREATE TABLE `locations` (
`location_city` varchar(200) DEFAULT NULL, `location_city` varchar(200) DEFAULT NULL,
`location_state` varchar(200) DEFAULT NULL, `location_state` varchar(200) DEFAULT NULL,
`location_zip` varchar(200) DEFAULT NULL, `location_zip` varchar(200) DEFAULT NULL,
`location_phone_country_code` varchar(10) DEFAULT '1', `location_phone_country_code` varchar(10) DEFAULT NULL,
`location_phone` varchar(200) DEFAULT NULL, `location_phone` varchar(200) DEFAULT NULL,
`location_phone_extension` varchar(10) DEFAULT NULL, `location_phone_extension` varchar(10) DEFAULT NULL,
`location_fax_country_code` varchar(10) DEFAULT '1', `location_fax_country_code` varchar(10) DEFAULT NULL,
`location_fax` varchar(200) DEFAULT NULL, `location_fax` varchar(200) DEFAULT NULL,
`location_hours` varchar(200) DEFAULT NULL, `location_hours` varchar(200) DEFAULT NULL,
`location_photo` varchar(200) DEFAULT NULL, `location_photo` varchar(200) DEFAULT NULL,
@@ -2468,7 +2470,7 @@ CREATE TABLE `vendors` (
`vendor_name` varchar(200) NOT NULL, `vendor_name` varchar(200) NOT NULL,
`vendor_description` varchar(200) DEFAULT NULL, `vendor_description` varchar(200) DEFAULT NULL,
`vendor_contact_name` varchar(200) DEFAULT NULL, `vendor_contact_name` varchar(200) DEFAULT NULL,
`vendor_phone_country_code` varchar(10) DEFAULT '1', `vendor_phone_country_code` varchar(10) DEFAULT NULL,
`vendor_phone` varchar(200) DEFAULT NULL, `vendor_phone` varchar(200) DEFAULT NULL,
`vendor_extension` varchar(200) DEFAULT NULL, `vendor_extension` varchar(200) DEFAULT NULL,
`vendor_email` varchar(200) DEFAULT NULL, `vendor_email` varchar(200) DEFAULT NULL,
@@ -2498,4 +2500,4 @@ CREATE TABLE `vendors` (
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2025-03-26 11:13:46 -- Dump completed on 2025-05-24 13:23:21

View File

@@ -92,7 +92,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-md-2"></div> <div class="col-md-2"></div>
<?php } else { ?> <?php } else { ?>
<div class="col-md-2"> <div class="col-md-2">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="client" onchange="this.form.submit()"> <select class="form-control select2" name="client" onchange="this.form.submit()">
<option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option> <option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option>
@@ -162,7 +162,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>"> <input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
<?php } ?> <?php } ?>
<table class="table table-striped table-borderless table-hover"> <table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>"> <thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?> text-nowrap">
<tr> <tr>
<td class="pr-0"> <td class="pr-0">
<div class="form-check"> <div class="form-check">

View File

@@ -228,19 +228,18 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
Date <?php if ($sort == 'expense_date') { echo $order_icon; } ?> Date <?php if ($sort == 'expense_date') { echo $order_icon; } ?>
</a> </a>
</th> </th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=vendor_name&order=<?php echo $disp; ?>">
Vendor <?php if ($sort == 'vendor_name') { echo $order_icon; } ?>
</a>
</th>
<th> <th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=category_name&order=<?php echo $disp; ?>"> <a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=category_name&order=<?php echo $disp; ?>">
Category <?php if ($sort == 'category_name') { echo $order_icon; } ?> Category <?php if ($sort == 'category_name') { echo $order_icon; } ?>
</a> </a>
/
<a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=expense_description&order=<?php echo $disp; ?>">
Description <?php if ($sort == 'expense_description') { echo $order_icon; } ?>
</a>
</th> </th>
<th> <th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=expense_description&order=<?php echo $disp; ?>"> <a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=vendor_name&order=<?php echo $disp; ?>">
Description <?php if ($sort == 'expense_description') { echo $order_icon; } ?> Vendor <?php if ($sort == 'vendor_name') { echo $order_icon; } ?>
</a> </a>
</th> </th>
<th class="text-right"> <th class="text-right">
@@ -313,9 +312,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php echo $expense_date; ?> <?php echo $expense_date; ?>
</a> </a>
</td> </td>
<td>
<?php echo $category_name; ?>
<div class="text-secondary"><small><?php echo truncate($expense_description, 60); ?></small></div>
</td>
<td><?php echo $vendor_name; ?></td> <td><?php echo $vendor_name; ?></td>
<td><?php echo $category_name; ?></td>
<td><?php echo truncate($expense_description, 50); ?></td>
<td class="text-bold text-right"><?php echo numfmt_format_currency($currency_format, $expense_amount, $expense_currency_code); ?></td> <td class="text-bold text-right"><?php echo numfmt_format_currency($currency_format, $expense_amount, $expense_currency_code); ?></td>
<td><?php echo $account_name; ?></td> <td><?php echo $account_name; ?></td>
<td><?php echo $client_name_display; ?></td> <td><?php echo $client_name_display; ?></td>

View File

@@ -194,140 +194,161 @@ function truncate($text, $chars) {
} }
function formatPhoneNumber($phoneNumber, $country_code = '', $show_country_code = false) { function formatPhoneNumber($phoneNumber, $country_code = '', $show_country_code = false) {
// Remove all non-digit characters // Remove all non-digit characters
$digits = preg_replace('/\D/', '', $phoneNumber); $digits = preg_replace('/\D/', '', $phoneNumber);
$formatted = ''; $formatted = '';
// If no digits at all, fallback early
if (strlen($digits) === 0) {
return $phoneNumber;
}
// Helper function to safely check the first digit
$startsWith = function($str, $char) {
return isset($str[0]) && $str[0] === $char;
};
switch ($country_code) { switch ($country_code) {
case '1': // USA/Canada — (123) 456-7890 case '1': // USA/Canada
if (strlen($digits) === 10) { if (strlen($digits) === 10) {
$formatted = '(' . substr($digits, 0, 3) . ') ' . substr($digits, 3, 3) . '-' . substr($digits, 6); $formatted = '(' . substr($digits, 0, 3) . ') ' . substr($digits, 3, 3) . '-' . substr($digits, 6);
} }
break; break;
case '44': // UK — 07123 456 789 case '44': // UK
if ($digits[0] === '0') $digits = substr($digits, 1); if ($startsWith($digits, '0')) {
$digits = substr($digits, 1);
}
if (strlen($digits) === 10) { if (strlen($digits) === 10) {
$formatted = '0' . substr($digits, 0, 4) . ' ' . substr($digits, 4, 3) . ' ' . substr($digits, 7); $formatted = '0' . substr($digits, 0, 4) . ' ' . substr($digits, 4, 3) . ' ' . substr($digits, 7);
} }
break; break;
case '61': // Australia — 0412 345 678 case '61': // Australia
if ($digits[0] === '0') $digits = substr($digits, 1); if ($startsWith($digits, '0')) {
$digits = substr($digits, 1);
}
if (strlen($digits) === 9) { if (strlen($digits) === 9) {
$formatted = '0' . substr($digits, 0, 4) . ' ' . substr($digits, 4, 3) . ' ' . substr($digits, 7); $formatted = '0' . substr($digits, 0, 4) . ' ' . substr($digits, 4, 3) . ' ' . substr($digits, 7);
} }
break; break;
case '91': // India — 91234 56789 case '91': // India
if (strlen($digits) === 10) { if (strlen($digits) === 10) {
$formatted = substr($digits, 0, 5) . ' ' . substr($digits, 5); $formatted = substr($digits, 0, 5) . ' ' . substr($digits, 5);
} }
break; break;
case '81': // Japan — 03-1234-5678 case '81': // Japan
if ($digits[0] === '0') $digits = substr($digits, 1); if ($startsWith($digits, '0')) {
$digits = substr($digits, 1);
}
if (strlen($digits) >= 9 && strlen($digits) <= 10) { if (strlen($digits) >= 9 && strlen($digits) <= 10) {
$formatted = '0' . substr($digits, 0, 2) . '-' . substr($digits, 2, 4) . '-' . substr($digits, 6); $formatted = '0' . substr($digits, 0, 2) . '-' . substr($digits, 2, 4) . '-' . substr($digits, 6);
} }
break; break;
case '49': // Germany — 030 12345678 case '49': // Germany
if ($digits[0] === '0') $digits = substr($digits, 1); if ($startsWith($digits, '0')) {
$digits = substr($digits, 1);
}
if (strlen($digits) >= 10) { if (strlen($digits) >= 10) {
$formatted = '0' . substr($digits, 0, 3) . ' ' . substr($digits, 3); $formatted = '0' . substr($digits, 0, 3) . ' ' . substr($digits, 3);
} }
break; break;
case '33': // France — 01 23 45 67 89 case '33': // France
if ($digits[0] === '0') $digits = substr($digits, 1); if ($startsWith($digits, '0')) {
$digits = substr($digits, 1);
}
if (strlen($digits) === 9) { if (strlen($digits) === 9) {
$formatted = '0' . implode(' ', str_split($digits, 2)); $formatted = '0' . implode(' ', str_split($digits, 2));
} }
break; break;
case '34': // Spain — 612 345 678 case '34': // Spain
if (strlen($digits) === 9) { if (strlen($digits) === 9) {
$formatted = substr($digits, 0, 3) . ' ' . substr($digits, 3, 3) . ' ' . substr($digits, 6); $formatted = substr($digits, 0, 3) . ' ' . substr($digits, 3, 3) . ' ' . substr($digits, 6);
} }
break; break;
case '39': // Italy — 312 345 6789 case '39': // Italy
if ($digits[0] === '0') $digits = substr($digits, 1); if ($startsWith($digits, '0')) {
$digits = substr($digits, 1);
}
$formatted = '0' . implode(' ', str_split($digits, 3)); $formatted = '0' . implode(' ', str_split($digits, 3));
break; break;
case '55': // Brazil — (11) 91234-5678 case '55': // Brazil
if (strlen($digits) === 11) { if (strlen($digits) === 11) {
$formatted = '(' . substr($digits, 0, 2) . ') ' . substr($digits, 2, 5) . '-' . substr($digits, 7); $formatted = '(' . substr($digits, 0, 2) . ') ' . substr($digits, 2, 5) . '-' . substr($digits, 7);
} }
break; break;
case '7': // Russia — 8 (912) 345-67-89 case '7': // Russia
if ($digits[0] === '8') $digits = substr($digits, 1); if ($startsWith($digits, '8')) {
$digits = substr($digits, 1);
}
if (strlen($digits) === 10) { if (strlen($digits) === 10) {
$formatted = '8 (' . substr($digits, 0, 3) . ') ' . substr($digits, 3, 3) . '-' . substr($digits, 6, 2) . '-' . substr($digits, 8); $formatted = '8 (' . substr($digits, 0, 3) . ') ' . substr($digits, 3, 3) . '-' . substr($digits, 6, 2) . '-' . substr($digits, 8);
} }
break; break;
case '86': // China — 138 0013 8000 case '86': // China
if (strlen($digits) === 11) { if (strlen($digits) === 11) {
$formatted = substr($digits, 0, 3) . ' ' . substr($digits, 3, 4) . ' ' . substr($digits, 7); $formatted = substr($digits, 0, 3) . ' ' . substr($digits, 3, 4) . ' ' . substr($digits, 7);
} }
break; break;
case '82': // South Korea — 010-1234-5678 case '82': // South Korea
if (strlen($digits) === 11) { if (strlen($digits) === 11) {
$formatted = substr($digits, 0, 3) . '-' . substr($digits, 3, 4) . '-' . substr($digits, 7); $formatted = substr($digits, 0, 3) . '-' . substr($digits, 3, 4) . '-' . substr($digits, 7);
} }
break; break;
case '62': // Indonesia — 0812 3456 7890 case '62': // Indonesia
if ($digits[0] !== '0') $digits = '0' . $digits; if (!$startsWith($digits, '0')) {
$digits = '0' . $digits;
}
if (strlen($digits) === 12) { if (strlen($digits) === 12) {
$formatted = substr($digits, 0, 4) . ' ' . substr($digits, 4, 4) . ' ' . substr($digits, 8); $formatted = substr($digits, 0, 4) . ' ' . substr($digits, 4, 4) . ' ' . substr($digits, 8);
} }
break; break;
case '63': // Philippines — 0912 345 6789 case '63': // Philippines
if (strlen($digits) === 11) { if (strlen($digits) === 11) {
$formatted = substr($digits, 0, 4) . ' ' . substr($digits, 4, 3) . ' ' . substr($digits, 7); $formatted = substr($digits, 0, 4) . ' ' . substr($digits, 4, 3) . ' ' . substr($digits, 7);
} }
break; break;
case '234': // Nigeria — 0801 234 5678 case '234': // Nigeria
if ($digits[0] !== '0') $digits = '0' . $digits; if (!$startsWith($digits, '0')) {
$digits = '0' . $digits;
}
if (strlen($digits) === 11) { if (strlen($digits) === 11) {
$formatted = substr($digits, 0, 4) . ' ' . substr($digits, 4, 3) . ' ' . substr($digits, 7); $formatted = substr($digits, 0, 4) . ' ' . substr($digits, 4, 3) . ' ' . substr($digits, 7);
} }
break; break;
case '27': // South Africa — 082 123 4567 case '27': // South Africa
if (strlen($digits) >= 9 && strlen($digits) <= 10) { if (strlen($digits) >= 9 && strlen($digits) <= 10) {
$formatted = substr($digits, 0, 3) . ' ' . substr($digits, 3, 3) . ' ' . substr($digits, 6); $formatted = substr($digits, 0, 3) . ' ' . substr($digits, 3, 3) . ' ' . substr($digits, 6);
} }
break; break;
case '971': // UAE — 050 123 4567 case '971': // UAE
if (strlen($digits) === 9) { if (strlen($digits) === 9) {
$formatted = substr($digits, 0, 3) . ' ' . substr($digits, 3, 3) . ' ' . substr($digits, 6); $formatted = substr($digits, 0, 3) . ' ' . substr($digits, 3, 3) . ' ' . substr($digits, 6);
} }
break; break;
default: default:
// If no match, do nothing here and use fallback below // fallback — do nothing, use raw digits later
break; break;
} }
// Fallback if formatting failed
if (!$formatted && strlen($digits) >= 7) {
$formatted = substr($digits, 0, 3) . ' ' . substr($digits, 3, 3) . ' ' . substr($digits, 6);
}
// Still no formatting? Use raw digits
if (!$formatted) { if (!$formatted) {
$formatted = $digits ?: $phoneNumber; // Use original input if digits are empty $formatted = $digits ?: $phoneNumber;
} }
return $show_country_code && $country_code ? "+$country_code $formatted" : $formatted; return $show_country_code && $country_code ? "+$country_code $formatted" : $formatted;
@@ -684,7 +705,17 @@ function sendSingleEmail($config_smtp_host, $config_smtp_username, $config_smtp_
$mail->SMTPAuth = $smtp_auth; // Enable SMTP authentication $mail->SMTPAuth = $smtp_auth; // Enable SMTP authentication
$mail->Username = $config_smtp_username; // SMTP username $mail->Username = $config_smtp_username; // SMTP username
$mail->Password = $config_smtp_password; // SMTP password $mail->Password = $config_smtp_password; // SMTP password
$mail->SMTPSecure = $config_smtp_encryption; // Enable TLS encryption, `ssl` also accepted if ($config_smtp_encryption == 'None') {
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
));
$mail->SMTPSecure = false;
$mail->SMTPAutoTLS = false;
} else {
$mail->SMTPSecure = $config_smtp_encryption; // Enable TLS encryption, `ssl` also accepted
}
$mail->Port = $config_smtp_port; // TCP port to connect to $mail->Port = $config_smtp_port; // TCP port to connect to
//Recipients //Recipients
@@ -1593,10 +1624,14 @@ function getFieldById($table, $id, $field, $escape_method = 'sql') {
} }
// Recursive function to display folder options - Used in folders files and documents // Recursive function to display folder options - Used in folders files and documents
function display_folder_options($parent_folder_id, $client_id, $indent = 0) { function display_folder_options($parent_folder_id, $client_id, $folder_location = 0, $indent = 0) {
global $mysqli; global $mysqli;
$sql_folders = mysqli_query($mysqli, "SELECT * FROM folders WHERE parent_folder = $parent_folder_id AND folder_location = 1 AND folder_client_id = $client_id ORDER BY folder_name ASC"); $folder_location = intval($folder_location);
// 0 = Document Folders
// 1 = File Folders
$sql_folders = mysqli_query($mysqli, "SELECT * FROM folders WHERE parent_folder = $parent_folder_id AND folder_location = $folder_location AND folder_client_id = $client_id ORDER BY folder_name ASC");
while ($row = mysqli_fetch_array($sql_folders)) { while ($row = mysqli_fetch_array($sql_folders)) {
$folder_id = intval($row['folder_id']); $folder_id = intval($row['folder_id']);
$folder_name = nullable_htmlentities($row['folder_name']); $folder_name = nullable_htmlentities($row['folder_name']);
@@ -1606,13 +1641,14 @@ function display_folder_options($parent_folder_id, $client_id, $indent = 0) {
// Check if this folder is selected // Check if this folder is selected
$selected = ''; $selected = '';
if ((isset($_GET['folder_id']) && $_GET['folder_id'] == $folder_id) || (isset($_POST['folder']) && $_POST['folder'] == $folder_id)) { if ((isset($_GET['folder_id']) && intval($_GET['folder_id']) === $folder_id) ||
(isset($_POST['folder']) && intval($_POST['folder']) === $folder_id)) {
$selected = 'selected'; $selected = 'selected';
} }
echo "<option value=\"$folder_id\" $selected>$indentation$folder_name</option>"; echo "<option value=\"$folder_id\" $selected>$indentation$folder_name</option>";
// Recursively display subfolders // Recursively display subfolders
display_folder_options($folder_id, $client_id, $indent + 1); display_folder_options($folder_id, $client_id, $folder_location, $indent + 1);
} }
} }

View File

@@ -721,7 +721,6 @@ if (isset($_GET['query'])) {
} else { } else {
$asset_serial_display = $asset_serial; $asset_serial_display = $asset_serial;
} }
$asset_mac = nullable_htmlentities($row['asset_mac']);
$asset_uri = nullable_htmlentities($row['asset_uri']); $asset_uri = nullable_htmlentities($row['asset_uri']);
$asset_status = nullable_htmlentities($row['asset_status']); $asset_status = nullable_htmlentities($row['asset_status']);
$asset_created_at = nullable_htmlentities($row['asset_created_at']); $asset_created_at = nullable_htmlentities($row['asset_created_at']);
@@ -746,9 +745,9 @@ if (isset($_GET['query'])) {
?> ?>
<tr> <tr>
<td> <td>
<i class="fa fa-fw text-secondary fa-<?php echo $device_icon; ?> mr-2"></i><?php echo $asset_name; ?> <i class="fa fa-fw text-secondary fa-<?php echo $device_icon; ?> mr-2"></i><a href="asset_details.php?client_id=<?php echo $client_id; ?>&asset_id=<?php echo $asset_id; ?>"><?php echo $asset_name; ?></a>
<?php if(!empty($asset_uri)){ ?> <?php if(!empty($asset_uri)){ ?>
<a href="<?php echo $asset_uri; ?>" target="_blank"><i class="fas fa-fw fa-external-link-alt ml-2"></i></a> <a href="<?php echo $asset_uri; ?>" target="_blank"><i class="fas fa-fw fa-external-link-alt ml-2"></i></a>
<?php } ?> <?php } ?>
</td> </td>
<td><?php echo $asset_type; ?></td> <td><?php echo $asset_type; ?></td>

View File

@@ -97,9 +97,7 @@ if (isset($_GET['stripe_create_pi'])) {
'itflow_invoice_number' => $invoice_prefix . $invoice_number, 'itflow_invoice_number' => $invoice_prefix . $invoice_number,
'itflow_invoice_id' => $invoice_id, 'itflow_invoice_id' => $invoice_id,
], ],
'automatic_payment_methods' => [ 'payment_method_types' => ['card'],
'enabled' => true,
],
]); ]);
$output = [ $output = [

View File

@@ -97,71 +97,85 @@ if (isset($_GET['invoice_id'], $_GET['url_key']) && !isset($_GET['payment_intent
<!-- Show invoice details --> <!-- Show invoice details -->
<div class="col-sm"> <div class="col-sm">
<h3>Payment for Invoice: <?php echo $invoice_prefix . $invoice_number ?></h3>
<br>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Product</th>
<th class="text-center">Qty</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<?php
$item_total = 0;
while ($row = mysqli_fetch_array($sql_invoice_items)) {
$item_name = nullable_htmlentities($row['item_name']);
$item_quantity = floatval($row['item_quantity']);
$item_total = floatval($row['item_total']);
?>
<div class="card">
<div class="card-header">
<h3 class="card-title">Payment for Invoice: <strong><?php echo "$invoice_prefix$invoice_number"; ?></strong></h3>
</div>
<div class="table-responsive">
<table class="table">
<thead>
<tr> <tr>
<td><?php echo $item_name; ?></td> <th>Product</th>
<td class="text-center"><?php echo $item_quantity; ?></td> <th class="text-center">Qty</th>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_total, $invoice_currency_code); ?></td> <th class="text-right">Total</th>
</tr> </tr>
</thead>
<tbody>
<?php
<?php } ?> $item_total = 0;
while ($row = mysqli_fetch_array($sql_invoice_items)) {
$item_name = nullable_htmlentities($row['item_name']);
$item_quantity = floatval($row['item_quantity']);
$item_total = floatval($row['item_total']);
?>
<tr>
<td><?php echo $item_name; ?></td>
<td class="text-center"><?php echo $item_quantity; ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_total, $invoice_currency_code); ?></td>
</tr>
</tbody> <?php } ?>
</table> <?php if ($invoice_discount > 0) { ?>
<tr class="text-right">
<td colspan="2">Discount</td>
<td>
<?php echo numfmt_format_currency($currency_format, $invoice_discount, $invoice_currency_code); ?>
</td>
</tr>
<?php } ?>
<?php if (intval($amount_paid) > 0) { ?>
<tr class="text-right">
<td colspan="2">Paid</td>
<td>
<?php echo numfmt_format_currency($currency_format, $amount_paid, $invoice_currency_code); ?>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div> </div>
<br>
<i><?php if ($invoice_discount > 0){ echo "Discount: " . numfmt_format_currency($currency_format, $invoice_discount, $invoice_currency_code); } ?>
</i>
<br>
<i><?php if (intval($amount_paid) > 0) { ?> Already paid: <?php echo numfmt_format_currency($currency_format, $amount_paid, $invoice_currency_code); } ?></i>
</div> </div>
<!-- End invoice details--> <!-- End invoice details-->
<!-- Show Stripe payment form --> <!-- Show Stripe payment form -->
<div class="col-sm offset-sm-1"> <div class="col-sm offset-sm-1">
<h1>Payment Total:</h1> <div class="card">
<form id="payment-form"> <div class="card-header">
<h1><?php echo numfmt_format_currency($currency_format, $balance_to_pay, $invoice_currency_code); ?></h1> <h3 class="card-title">Payment Total: <strong><?php echo numfmt_format_currency($currency_format, $balance_to_pay, $invoice_currency_code); ?></strong></h3>
<input type="hidden" id="stripe_publishable_key" value="<?php echo $config_stripe_publishable ?>">
<input type="hidden" id="invoice_id" value="<?php echo $invoice_id ?>">
<input type="hidden" id="url_key" value="<?php echo $invoice_url_key ?>">
<br>
<div id="link-authentication-element">
<!--Stripe.js injects the Link Authentication Element-->
</div> </div>
<div id="payment-element"> <div class="card-body">
<!--Stripe.js injects the Payment Element--> <form id="payment-form">
<input type="hidden" id="stripe_publishable_key" value="<?php echo $config_stripe_publishable ?>">
<input type="hidden" id="invoice_id" value="<?php echo $invoice_id ?>">
<input type="hidden" id="url_key" value="<?php echo $invoice_url_key ?>">
<div id="payment-element">
<!--Stripe.js injects the Payment Element-->
</div>
<br>
<button type="submit" id="submit" class="btn btn-primary btn-lg btn-block text-bold" hidden="hidden">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text"><i class="fas fa-check mr-2"></i>Pay Invoice</span>
</button>
<div id="payment-message" class="hidden"></div>
</form>
</div> </div>
<br> </div>
<button type="submit" id="submit" class="btn btn-primary btn-lg btn-block text-bold" hidden="hidden">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text"><i class="fas fa-check mr-2"></i>Pay Invoice</span>
</button>
<div id="payment-message" class="hidden"></div>
</form>
</div> </div>
<!-- End Stripe payment form --> <!-- End Stripe payment form -->

View File

@@ -50,6 +50,7 @@ $location_address = nullable_htmlentities($row['location_address']);
$location_city = nullable_htmlentities($row['location_city']); $location_city = nullable_htmlentities($row['location_city']);
$location_state = nullable_htmlentities($row['location_state']); $location_state = nullable_htmlentities($row['location_state']);
$location_zip = nullable_htmlentities($row['location_zip']); $location_zip = nullable_htmlentities($row['location_zip']);
$location_country = nullable_htmlentities($row['location_country']);
$contact_email = nullable_htmlentities($row['contact_email']); $contact_email = nullable_htmlentities($row['contact_email']);
$contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_code']); $contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_code']);
$contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code)); $contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code));
@@ -71,6 +72,7 @@ $company_address = nullable_htmlentities($row['company_address']);
$company_city = nullable_htmlentities($row['company_city']); $company_city = nullable_htmlentities($row['company_city']);
$company_state = nullable_htmlentities($row['company_state']); $company_state = nullable_htmlentities($row['company_state']);
$company_zip = nullable_htmlentities($row['company_zip']); $company_zip = nullable_htmlentities($row['company_zip']);
$company_country = nullable_htmlentities($row['company_country']);
$company_phone_country_code = nullable_htmlentities($row['company_phone_country_code']); $company_phone_country_code = nullable_htmlentities($row['company_phone_country_code']);
$company_phone = nullable_htmlentities(formatPhoneNumber($row['company_phone'], $company_phone_country_code)); $company_phone = nullable_htmlentities(formatPhoneNumber($row['company_phone'], $company_phone_country_code));
$company_email = nullable_htmlentities($row['company_email']); $company_email = nullable_htmlentities($row['company_email']);
@@ -198,6 +200,7 @@ if ($balance > 0) {
<li><h4><strong><?php echo $company_name; ?></strong></h4></li> <li><h4><strong><?php echo $company_name; ?></strong></h4></li>
<li><?php echo $company_address; ?></li> <li><?php echo $company_address; ?></li>
<li><?php echo "$company_city $company_state $company_zip"; ?></li> <li><?php echo "$company_city $company_state $company_zip"; ?></li>
<li><small><?php echo $company_country; ?></small></li>
<li><?php echo $company_phone; ?></li> <li><?php echo $company_phone; ?></li>
<li><?php echo $company_email; ?></li> <li><?php echo $company_email; ?></li>
</ul> </ul>
@@ -209,6 +212,7 @@ if ($balance > 0) {
<li><h4><strong><?php echo $client_name; ?></strong></h4></li> <li><h4><strong><?php echo $client_name; ?></strong></h4></li>
<li><?php echo $location_address; ?></li> <li><?php echo $location_address; ?></li>
<li><?php echo "$location_city $location_state $location_zip"; ?></li> <li><?php echo "$location_city $location_state $location_zip"; ?></li>
<li><small><?php echo $location_country; ?></small></li>
<li><?php echo "$contact_phone $contact_extension"; ?></li> <li><?php echo "$contact_phone $contact_extension"; ?></li>
<li><?php echo $contact_mobile; ?></li> <li><?php echo $contact_mobile; ?></li>
<li><?php echo $contact_email; ?></li> <li><?php echo $contact_email; ?></li>
@@ -407,11 +411,11 @@ if ($balance > 0) {
{ {
columns: [ columns: [
{ {
text: <?php echo json_encode(html_entity_decode("$company_address \n $company_city $company_state $company_zip \n $company_phone \n $company_website")) ?>, text: <?php echo json_encode(html_entity_decode("$company_address \n $company_city $company_state $company_zip \n $company_country \n $company_phone \n $company_website")) ?>,
style: 'invoiceBillingAddress' style: 'invoiceBillingAddress'
}, },
{ {
text: <?php echo json_encode(html_entity_decode("$location_address \n $location_city $location_state $location_zip \n $contact_email \n $contact_phone")) ?>, text: <?php echo json_encode(html_entity_decode("$location_address \n $location_city $location_state $location_zip \n $location_country \n $contact_email \n $contact_phone")) ?>,
style: 'invoiceBillingAddressClient' style: 'invoiceBillingAddressClient'
}, },
] ]

View File

@@ -51,6 +51,7 @@ $location_address = nullable_htmlentities($row['location_address']);
$location_city = nullable_htmlentities($row['location_city']); $location_city = nullable_htmlentities($row['location_city']);
$location_state = nullable_htmlentities($row['location_state']); $location_state = nullable_htmlentities($row['location_state']);
$location_zip = nullable_htmlentities($row['location_zip']); $location_zip = nullable_htmlentities($row['location_zip']);
$location_country = nullable_htmlentities($row['location_country']);
$contact_email = nullable_htmlentities($row['contact_email']); $contact_email = nullable_htmlentities($row['contact_email']);
$contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_code']); $contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_code']);
$contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code)); $contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code));
@@ -71,6 +72,7 @@ $company_address = nullable_htmlentities($row['company_address']);
$company_city = nullable_htmlentities($row['company_city']); $company_city = nullable_htmlentities($row['company_city']);
$company_state = nullable_htmlentities($row['company_state']); $company_state = nullable_htmlentities($row['company_state']);
$company_zip = nullable_htmlentities($row['company_zip']); $company_zip = nullable_htmlentities($row['company_zip']);
$company_country = nullable_htmlentities($row['company_country']);
$company_phone_country_code = nullable_htmlentities($row['company_phone_country_code']); $company_phone_country_code = nullable_htmlentities($row['company_phone_country_code']);
$company_phone = nullable_htmlentities(formatPhoneNumber($row['company_phone'], $company_phone_country_code)); $company_phone = nullable_htmlentities(formatPhoneNumber($row['company_phone'], $company_phone_country_code));
$company_email = nullable_htmlentities($row['company_email']); $company_email = nullable_htmlentities($row['company_email']);
@@ -136,6 +138,7 @@ if ($quote_status == "Draft" || $quote_status == "Sent" || $quote_status == "Vie
<li><h4><strong><?php echo $company_name; ?></strong></h4></li> <li><h4><strong><?php echo $company_name; ?></strong></h4></li>
<li><?php echo $company_address; ?></li> <li><?php echo $company_address; ?></li>
<li><?php echo "$company_city $company_state $company_zip"; ?></li> <li><?php echo "$company_city $company_state $company_zip"; ?></li>
<li><small><?php echo $company_country; ?></small></li>
<li><?php echo $company_phone; ?></li> <li><?php echo $company_phone; ?></li>
<li><?php echo $company_email; ?></li> <li><?php echo $company_email; ?></li>
</ul> </ul>
@@ -148,6 +151,7 @@ if ($quote_status == "Draft" || $quote_status == "Sent" || $quote_status == "Vie
<li><h4><strong><?php echo $client_name; ?></strong></h4></li> <li><h4><strong><?php echo $client_name; ?></strong></h4></li>
<li><?php echo $location_address; ?></li> <li><?php echo $location_address; ?></li>
<li><?php echo "$location_city $location_state $location_zip"; ?></li> <li><?php echo "$location_city $location_state $location_zip"; ?></li>
<li><small><?php echo $location_country; ?></small></li>
<li><?php echo "$contact_phone $contact_extension"; ?></li> <li><?php echo "$contact_phone $contact_extension"; ?></li>
<li><?php echo $contact_mobile; ?></li> <li><?php echo $contact_mobile; ?></li>
<li><?php echo $contact_email; ?></li> <li><?php echo $contact_email; ?></li>
@@ -345,11 +349,11 @@ if ($quote_status == "Draft" || $quote_status == "Sent" || $quote_status == "Vie
{ {
columns: [ columns: [
{ {
text: <?php echo json_encode(html_entity_decode("$company_address \n $company_city $company_state $company_zip \n $company_phone \n $company_website")) ?>, text: <?php echo json_encode(html_entity_decode("$company_address \n $company_city $company_state $company_zip \n $company_country \n $company_phone \n $company_website")) ?>,
style: 'invoiceBillingAddress' style: 'invoiceBillingAddress'
}, },
{ {
text: <?php echo json_encode(html_entity_decode("$location_address \n $location_city $location_state $location_zip \n $contact_email \n $contact_phone")) ?>, text: <?php echo json_encode(html_entity_decode("$location_address \n $location_city $location_state $location_zip \n $location_country \n $contact_email \n $contact_phone")) ?>,
style: 'invoiceBillingAddressClient' style: 'invoiceBillingAddressClient'
}, },
] ]

View File

@@ -145,7 +145,7 @@
</li> </li>
<!-- SETTINGS Section --> <!-- 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_settings_integration.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']), ['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' : ''); ?>">
<a href="#" class="nav-link"> <a href="#" class="nav-link">
<p> <p>
SETTINGS SETTINGS
@@ -246,9 +246,9 @@
<!-- Currently the only integration is the client portal SSO --> <!-- Currently the only integration is the client portal SSO -->
<?php if ($config_client_portal_enable) { ?> <?php if ($config_client_portal_enable) { ?>
<li class="nav-item"> <li class="nav-item">
<a href="admin_settings_integration.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_settings_integration.php' ? 'active' : ''); ?>"> <a href="admin_identity_provider.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_identity_provider.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-plug"></i> <i class="nav-icon fas fa-fingerprint"></i>
<p>Integrations</p> <p>Identity Provider</p>
</a> </a>
</li> </li>
<?php } ?> <?php } ?>

View File

@@ -5,4 +5,4 @@
* Update this file each time we merge develop into master. Format is YY.MM (add a .v if there is more than one release a month. * Update this file each time we merge develop into master. Format is YY.MM (add a .v if there is more than one release a month.
*/ */
DEFINE("APP_VERSION", "25.03"); DEFINE("APP_VERSION", "25.05");

View File

@@ -153,18 +153,20 @@
</a> </a>
</li> </li>
<li class="nav-item"> <?php if (lookupUserPermission("module_credential") >= 1) { ?>
<a href="credentials.php?client_id=<?php echo $client_id; ?>" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "credentials.php") { echo "active"; } ?>"> <li class="nav-item">
<i class="nav-icon fas fa-key"></i> <a href="credentials.php?client_id=<?php echo $client_id; ?>" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "credentials.php") { echo "active"; } ?>">
<p> <i class="nav-icon fas fa-key"></i>
Credentials <p>
<?php Credentials
if ($num_credentials > 0) { ?> <?php
<span class="right badge text-light"><?php echo $num_credentials; ?></span> if ($num_credentials > 0) { ?>
<?php } ?> <span class="right badge text-light"><?php echo $num_credentials; ?></span>
</p> <?php } ?>
</a> </p>
</li> </a>
</li>
<?php } ?>
<li class="nav-item"> <li class="nav-item">
<a href="networks.php?client_id=<?php echo $client_id; ?>" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "networks.php") { echo "active"; } ?>"> <a href="networks.php?client_id=<?php echo $client_id; ?>" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "networks.php") { echo "active"; } ?>">

View File

@@ -5,4 +5,4 @@
* It is used in conjunction with database_updates.php * It is used in conjunction with database_updates.php
*/ */
DEFINE("LATEST_DATABASE_VERSION", "2.1.2"); DEFINE("LATEST_DATABASE_VERSION", "2.1.4");

View File

@@ -26,7 +26,6 @@ if ($total_found_rows > 5) {
<option <?php if ($user_config_records_per_page == 20) { echo "selected"; } ?> >20</option> <option <?php if ($user_config_records_per_page == 20) { echo "selected"; } ?> >20</option>
<option <?php if ($user_config_records_per_page == 50) { echo "selected"; } ?> >50</option> <option <?php if ($user_config_records_per_page == 50) { echo "selected"; } ?> >50</option>
<option <?php if ($user_config_records_per_page == 100) { echo "selected"; } ?> >100</option> <option <?php if ($user_config_records_per_page == 100) { echo "selected"; } ?> >100</option>
<option <?php if ($user_config_records_per_page == 500) { echo "selected"; } ?> >500</option>
</select> </select>
</form> </form>
</div> </div>

View File

@@ -100,7 +100,7 @@ if ($_GET['canned_date'] == "custom" && !empty($_GET['dtf'])) {
$dtt = date('Y-m-d', strtotime("last day of december last year")); $dtt = date('Y-m-d', strtotime("last day of december last year"));
} else { } else {
$dtf = "NULL"; $dtf = "NULL";
$dtt = date('Y-m-d'); $dtt = '2035-12-31';
} }
// Archived // Archived

View File

@@ -121,6 +121,8 @@ $config_log_retention = intval($row['config_log_retention']);
// Locale // Locale
$config_currency_format = "US_en"; $config_currency_format = "US_en";
$config_timezone = $row['config_timezone']; $config_timezone = $row['config_timezone'];
$config_date_format = "Y-m-d";
$config_time_format = "H:i";
// Theme // Theme
$config_theme = $row['config_theme']; $config_theme = $row['config_theme'];

View File

@@ -59,7 +59,12 @@
<div> <div>
<a href="//maps.<?php echo $session_map_source; ?>.com/?q=<?php echo "$location_address $location_zip"; ?>" target="_blank"> <a href="//maps.<?php echo $session_map_source; ?>.com/?q=<?php echo "$location_address $location_zip"; ?>" target="_blank">
<i class="fa fa-fw fa-map-marker-alt text-secondary ml-1 mr-2"></i><?php echo $location_address; ?> <i class="fa fa-fw fa-map-marker-alt text-secondary ml-1 mr-2"></i><?php echo $location_address; ?>
<div><i class="fa fa-fw ml-1 mr-2"></i><?php echo "$location_city $location_state $location_zip"; ?></div> <div>
<i class="fa fa-fw ml-1 mr-2"></i><?php echo "$location_city $location_state $location_zip"; ?>
</div>
<div>
<i class="fa fa-fw ml-1 mr-2"></i><small><?php echo $location_country; ?></small>
</div>
</a> </a>
</div> </div>
<?php } <?php }

View File

@@ -28,37 +28,33 @@
<!-- Right navbar links --> <!-- Right navbar links -->
<ul class="navbar-nav ml-auto"> <ul class="navbar-nav ml-auto">
<?php if(CURRENT_DATABASE_VERSION > '1.4.5' ) { // Check DB Version REMOVE on Decemeber 1st 2024 -Johnny ?> <!--Custom Nav Link -->
<?php
$sql_custom_links = mysqli_query($mysqli, "SELECT * FROM custom_links WHERE custom_link_location = 2 AND custom_link_archived_at IS NULL
ORDER BY custom_link_order ASC, custom_link_name ASC"
);
<!--Custom Nav Link --> while ($row = mysqli_fetch_array($sql_custom_links)) {
<?php $custom_link_name = nullable_htmlentities($row['custom_link_name']);
$sql_custom_links = mysqli_query($mysqli, "SELECT * FROM custom_links WHERE custom_link_location = 2 AND custom_link_archived_at IS NULL $custom_link_uri = nullable_htmlentities($row['custom_link_uri']);
ORDER BY custom_link_order ASC, custom_link_name ASC" $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 = "";
}
while ($row = mysqli_fetch_array($sql_custom_links)) { ?>
$custom_link_name = nullable_htmlentities($row['custom_link_name']);
$custom_link_uri = nullable_htmlentities($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" title="<?php echo $custom_link_name; ?>">
<a href="<?php echo $custom_link_uri; ?>" <?php echo $target; ?> class="nav-link">
<i class="fas fa-<?php echo $custom_link_icon; ?> nav-icon"></i>
</a>
</li>
<li class="nav-item" title="<?php echo $custom_link_name; ?>"> <?php } ?>
<a href="<?php echo $custom_link_uri; ?>" <?php echo $target; ?> class="nav-link"> <!-- End Custom Nav Links -->
<i class="fas fa-<?php echo $custom_link_icon; ?> nav-icon"></i>
</a>
</li>
<?php } ?>
<!-- End Custom Nav Links -->
<?php } // End DB Check ?>
<!-- New Notifications Dropdown --> <!-- New Notifications Dropdown -->
<?php <?php

View File

@@ -16,6 +16,10 @@ if (file_exists("config.php")) {
<hr> <hr>
<?php <?php
if (isset($config_start_page)) { ?>
<meta http-equiv="refresh" content="0;url=<?php echo $config_start_page; ?>">
<?php }
require_once "includes/footer.php"; require_once "includes/footer.php";

View File

@@ -53,6 +53,7 @@ if (isset($_GET['invoice_id'])) {
$location_city = nullable_htmlentities($row['location_city']); $location_city = nullable_htmlentities($row['location_city']);
$location_state = nullable_htmlentities($row['location_state']); $location_state = nullable_htmlentities($row['location_state']);
$location_zip = nullable_htmlentities($row['location_zip']); $location_zip = nullable_htmlentities($row['location_zip']);
$location_country = nullable_htmlentities($row['location_country']);
$contact_email = nullable_htmlentities($row['contact_email']); $contact_email = nullable_htmlentities($row['contact_email']);
$contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_code']); $contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_code']);
$contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code)); $contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code));
@@ -165,7 +166,6 @@ if (isset($_GET['invoice_id'])) {
?> ?>
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
<ol class="breadcrumb d-print-none"> <ol class="breadcrumb d-print-none">
<?php if (isset($_GET['client_id'])) { ?> <?php if (isset($_GET['client_id'])) { ?>
@@ -193,25 +193,26 @@ if (isset($_GET['invoice_id'])) {
<div class="card-header d-print-none"> <div class="card-header d-print-none">
<div class="row"> <div class="row">
<div class="col-8"> <div class="col-8">
<?php if (lookupUserPermission("module_sales") >= 2) { ?>
<?php if ($invoice_status == 'Draft') { ?> <?php if ($invoice_status == 'Draft') { ?>
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown"> <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-paper-plane mr-2"></i>Send <i class="fas fa-fw fa-paper-plane mr-2"></i>Send
</button> </button>
<div class="dropdown-menu"> <div class="dropdown-menu">
<?php if (!empty($config_smtp_host) && !empty($contact_email)) { ?> <?php if (!empty($config_smtp_host) && !empty($contact_email)) { ?>
<a class="dropdown-item" href="post.php?email_invoice=<?php echo $invoice_id; ?>"> <a class="dropdown-item" href="post.php?email_invoice=<?php echo $invoice_id; ?>">
<i class="fas fa-fw fa-paper-plane mr-2"></i>Send Email <i class="fas fa-fw fa-paper-plane mr-2"></i>Send Email
</a>
<div class="dropdown-divider"></div>
<?php } ?>
<a class="dropdown-item" href="post.php?mark_invoice_sent=<?php echo $invoice_id; ?>">
<i class="fas fa-fw fa-check mr-2"></i>Mark Sent
</a> </a>
</div> <div class="dropdown-divider"></div>
<?php } ?> <?php } ?>
<a class="dropdown-item" href="post.php?mark_invoice_sent=<?php echo $invoice_id; ?>">
<i class="fas fa-fw fa-check mr-2"></i>Mark Sent
</a>
</div>
<?php if ($invoice_status !== 'Paid' && $invoice_status !== 'Cancelled' && $invoice_status !== 'Draft' && $invoice_amount != 0) { ?> <?php if ($invoice_status !== 'Paid' && $invoice_status !== 'Cancelled' && $invoice_status !== 'Draft' && $invoice_amount != 0) { ?>
<a class="btn btn-success" href="#" data-toggle="modal" data-target="#addPaymentModal"> <a class="btn btn-success" href="#" data-toggle="modal" data-target="#addPaymentModal">
@@ -229,7 +230,7 @@ if (isset($_GET['invoice_id'])) {
Mark Non-Billable Mark Non-Billable
</a> </a>
<?php } ?> <?php } ?>
<?php } // End lookup Perm ?>
</div> </div>
<div class="col-4"> <div class="col-4">
@@ -283,6 +284,7 @@ if (isset($_GET['invoice_id'])) {
</div> </div>
</div> </div>
<?php } ?>
</div> </div>
@@ -308,6 +310,7 @@ if (isset($_GET['invoice_id'])) {
<li><h4><strong><?php echo $company_name; ?></strong></h4></li> <li><h4><strong><?php echo $company_name; ?></strong></h4></li>
<li><?php echo $company_address; ?></li> <li><?php echo $company_address; ?></li>
<li><?php echo "$company_city $company_state $company_zip"; ?></li> <li><?php echo "$company_city $company_state $company_zip"; ?></li>
<li><small><?php echo $company_country; ?></small></li>
<li><?php echo $company_phone; ?></li> <li><?php echo $company_phone; ?></li>
<li><?php echo $company_email; ?></li> <li><?php echo $company_email; ?></li>
<li><?php echo $company_website; ?></li> <li><?php echo $company_website; ?></li>
@@ -318,6 +321,7 @@ if (isset($_GET['invoice_id'])) {
<li><h4><strong><?php echo $client_name; ?></strong></h4></li> <li><h4><strong><?php echo $client_name; ?></strong></h4></li>
<li><?php echo $location_address; ?></li> <li><?php echo $location_address; ?></li>
<li><?php echo "$location_city $location_state $location_zip"; ?></li> <li><?php echo "$location_city $location_state $location_zip"; ?></li>
<li><small><?php echo $location_country; ?></small></li>
<li><?php echo "$contact_phone $contact_extension"; ?></li> <li><?php echo "$contact_phone $contact_extension"; ?></li>
<li><?php echo $contact_mobile; ?></li> <li><?php echo $contact_mobile; ?></li>
<li><?php echo $contact_email; ?></li> <li><?php echo $contact_email; ?></li>
@@ -381,26 +385,33 @@ if (isset($_GET['invoice_id'])) {
<tr data-item-id="<?php echo $item_id; ?>"> <tr data-item-id="<?php echo $item_id; ?>">
<td class="d-print-none"> <td class="d-print-none">
<?php if ($invoice_status !== "Paid" && $invoice_status !== "Cancelled") { ?> <?php if ($invoice_status !== "Paid" && $invoice_status !== "Cancelled") { ?>
<div class="dropdown">
<button class="btn btn-sm btn-light" type="button" data-toggle="dropdown"> <div class="btn-group">
<i class="fas fa-ellipsis-v"></i> <button type="button" class="btn btn-sm btn-link drag-handle">
<i class="fas fa-bars text-muted"></i>
</button> </button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" <div class="dropdown">
data-toggle="ajax-modal" <button class="btn btn-sm btn-light" type="button" data-toggle="dropdown">
data-ajax-url="ajax/ajax_item_edit.php" <i class="fas fa-ellipsis-v"></i>
data-ajax-id="<?php echo $item_id; ?>" </button>
> <div class="dropdown-menu">
<i class="fa fa-fw fa-edit mr-2"></i>Edit <a class="dropdown-item" href="#"
</a> data-toggle="ajax-modal"
<div class="dropdown-divider"></div> data-ajax-url="ajax/ajax_item_edit.php"
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_invoice_item=<?php echo $item_id; ?>"><i class="fa fa-fw fa-trash mr-2"></i>Delete</a> data-ajax-id="<?php echo $item_id; ?>"
>
<i class="fa 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_invoice_item=<?php echo $item_id; ?>"><i class="fa fa-fw fa-trash mr-2"></i>Delete</a>
</div>
</div> </div>
</div> </div>
<?php } ?> <?php } ?>
</td> </td>
<td class="grab-cursor"><?php echo $item_name; ?></td> <td><?php echo $item_name; ?></td>
<td><?php echo nl2br($item_description); ?></td> <td><?php echo nl2br($item_description); ?></td>
<td class="text-center"><?php echo number_format($item_quantity, 2); ?></td> <td class="text-center"><?php echo number_format($item_quantity, 2); ?></td>
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $invoice_currency_code); ?></td> <td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $invoice_currency_code); ?></td>
@@ -410,7 +421,7 @@ if (isset($_GET['invoice_id'])) {
<?php <?php
} }
?> ?>
<tr class="d-print-none" <?php if ($invoice_status == "Paid" || $invoice_status == "Cancelled") { echo "hidden"; } ?>> <tr class="d-print-none" <?php if ($invoice_status == "Paid" || $invoice_status == "Cancelled" || lookupUserPermission("module_sales") <= 1) { echo "hidden"; } ?>>
<form action="post.php" method="post" autocomplete="off"> <form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="invoice_id" value="<?php echo $invoice_id; ?>"> <input type="hidden" name="invoice_id" value="<?php echo $invoice_id; ?>">
<input type="hidden" name="item_order" value="<?php echo mysqli_num_rows($sql_invoice_items) + 1; ?>"> <input type="hidden" name="item_order" value="<?php echo mysqli_num_rows($sql_invoice_items) + 1; ?>">
@@ -772,11 +783,11 @@ require_once "includes/footer.php";
{ {
columns: [ columns: [
{ {
text: <?php echo json_encode(html_entity_decode("$company_address \n $company_city $company_state $company_zip \n $company_phone \n $company_website")) ?>, text: <?php echo json_encode(html_entity_decode("$company_address \n $company_city $company_state $company_zip \n $company_country \n $company_phone \n $company_website")) ?>,
style: 'invoiceBillingAddress' style: 'invoiceBillingAddress'
}, },
{ {
text: <?php echo json_encode(html_entity_decode("$location_address \n $location_city $location_state $location_zip \n $contact_email \n $contact_phone")) ?>, text: <?php echo json_encode(html_entity_decode("$location_address \n $location_city $location_state $location_zip \n $location_country \n $contact_email \n $contact_phone")) ?>,
style: 'invoiceBillingAddressClient' style: 'invoiceBillingAddressClient'
}, },
] ]
@@ -1178,38 +1189,23 @@ require_once "includes/footer.php";
} }
</script> </script>
<script src="plugins/dragula/dragula.min.js"></script> <script src="plugins/SortableJS/Sortable.min.js"></script>
<script> <script>
$(document).ready(function() { new Sortable(document.querySelector('table#items tbody'), {
var container = $('table#items tbody')[0]; handle: '.drag-handle',
animation: 150,
onEnd: function (evt) {
const rows = document.querySelectorAll('table#items tbody tr');
const positions = Array.from(rows).map((row, index) => ({
id: row.dataset.itemId,
order: index
}));
dragula([container]) $.post('ajax.php', {
.on('drop', function (el, target, source, sibling) { update_invoice_items_order: true,
// Handle the drop event to update the order in the database invoice_id: <?php echo $invoice_id; ?>,
var rows = $(container).children(); positions: positions
var positions = rows.map(function(index, row) {
return {
id: $(row).data('itemId'),
order: index
};
}).get();
// Send the new order to the server
$.ajax({
url: 'ajax.php',
method: 'POST',
data: {
update_invoice_items_order: true,
invoice_id: <?php echo $invoice_id; ?>,
positions: positions
},
success: function(data) {
// Handle success
},
error: function(error) {
console.error('Error updating order:', error);
}
});
}); });
}
}); });
</script> </script>

View File

@@ -172,7 +172,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php } ?> <?php } ?>
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-4">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) {echo stripslashes(nullable_htmlentities($q));} ?>" placeholder="Search Invoices"> <input type="search" class="form-control" name="q" value="<?php if (isset($q)) {echo stripslashes(nullable_htmlentities($q));} ?>" placeholder="Search Invoices">
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button> <button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
@@ -228,7 +228,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<hr> <hr>
<div class="table-responsive-sm"> <div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover"> <table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>"> <thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?> text-nowrap">
<tr> <tr>
<th> <th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=invoice_number&order=<?php echo $disp ?>"> <a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=invoice_number&order=<?php echo $disp ?>">

View File

@@ -23,9 +23,6 @@ async function initialize() {
elements = stripe.elements({ clientSecret }); elements = stripe.elements({ clientSecret });
const linkAuthenticationElement = elements.create("linkAuthentication");
linkAuthenticationElement.mount("#link-authentication-element");
const paymentElementOptions = { const paymentElementOptions = {
layout: "tabs", layout: "tabs",
}; };

View File

@@ -1,146 +1,126 @@
$(document).ready(function() { $(document).ready(function () {
console.log('CONFIG_TICKET_MOVING_COLUMNS: ' + CONFIG_TICKET_MOVING_COLUMNS); console.log('CONFIG_TICKET_MOVING_COLUMNS:', CONFIG_TICKET_MOVING_COLUMNS);
console.log('CONFIG_TICKET_ORDERING: ' + CONFIG_TICKET_ORDERING); console.log('CONFIG_TICKET_ORDERING:', CONFIG_TICKET_ORDERING);
// Function to detect touch devices // -------------------------------
function isTouchDevice() { // Drag: Kanban Columns (Statuses)
return 'ontouchstart' in window || navigator.maxTouchPoints; // -------------------------------
} new Sortable(document.querySelector('#kanban-board'), {
animation: 150,
// Initialize Dragula for the Kanban board handle: '.panel-title',
let boardDrake = dragula([ draggable: '.kanban-column',
document.querySelector('#kanban-board') onEnd: function () {
], { const columnPositions = Array.from(document.querySelectorAll('#kanban-board .kanban-column')).map((col, index) => ({
moves: function(el, container, handle) { status_id: $(col).data('status-id'),
return handle.classList.contains('panel-title');
},
accepts: function(el, target, source, sibling) {
return CONFIG_TICKET_MOVING_COLUMNS === 1;
}
});
// Log the event of moving the column panel-title
boardDrake.on('drag', function(el) {
//console.log('Dragging column:', el.querySelector('.panel-title').innerText);
});
boardDrake.on('drop', function(el, target, source, sibling) {
//console.log('Dropped column:', el.querySelector('.panel-title').innerText);
// Get all columns and their positions
let columns = document.querySelectorAll('#kanban-board .kanban-column');
let columnPositions = [];
columns.forEach(function(column, index) {
let statusId = $(column).data('status-id'); // Assuming you have a data attribute for status ID
columnPositions.push({
status_id: statusId,
status_kanban: index status_kanban: index
}); }));
});
// Send AJAX request to update all column positions if (CONFIG_TICKET_MOVING_COLUMNS === 1) {
$.ajax({ $.post('ajax.php', {
url: 'ajax.php', update_kanban_status_position: true,
type: 'POST', positions: columnPositions
data: { }).done(() => {
update_kanban_status_position: true, console.log('Ticket status kanban orders updated.');
positions: columnPositions }).fail((xhr) => {
}, console.error('Error updating status order:', xhr.responseText);
success: function(response) { });
console.log('Ticket status kanban orders updated successfully.');
// Optionally, you can refresh the page or update the UI here
},
error: function(xhr, status, error) {
console.error('Error updating ticket status kanban orders:', error);
}
});
});
// Initialize Dragula for the Kanban Cards
let drake = dragula([
...document.querySelectorAll('#status')
], {
moves: function(el, container, handle) {
if (isTouchDevice()) {
return handle.classList.contains('drag-handle-class');
} else {
return true; // Allow dragging on the entire task element for desktop
} }
} }
}); });
// -------------------------------
// Drag: Tasks within Columns
// -------------------------------
document.querySelectorAll('.kanban-status').forEach(statusCol => {
new Sortable(statusCol, {
group: 'tickets',
animation: 150,
handle: isTouchDevice() ? '.drag-handle-class' : undefined,
onStart: () => hidePlaceholders(),
onEnd: function (evt) {
const target = evt.to;
const movedEl = evt.item;
// Disallow reordering in same column if config says so
if (CONFIG_TICKET_ORDERING === 0 && evt.from === evt.to) {
evt.from.insertBefore(movedEl, evt.from.children[evt.oldIndex]);
showPlaceholders();
return;
}
const columnId = $(target).data('status-id');
const positions = Array.from(target.querySelectorAll('.task')).map((card, index) => {
const ticketId = $(card).data('ticket-id');
const oldStatus = ticketId === $(movedEl).data('ticket-id')
? $(movedEl).data('ticket-status-id')
: false;
$(card).data('ticket-status-id', columnId); // update DOM
return {
ticket_id: ticketId,
ticket_order: index,
ticket_oldStatus: oldStatus,
ticket_status: columnId
};
});
$.post('ajax.php', {
update_kanban_ticket: true,
positions: positions
}).done(() => {
console.log('Updated kanban ticket positions.');
}).fail((xhr) => {
console.error('Error updating ticket positions:', xhr.responseText);
});
// Refresh placeholders after update
showPlaceholders();
}
});
});
// -------------------------------
// 📱 Touch Support: Show drag handle on mobile
// -------------------------------
if (isTouchDevice()) { if (isTouchDevice()) {
const moveList = document.querySelectorAll('.task'); $('.drag-handle-class').css('display', 'inline');
moveList.forEach(task => { }
task.querySelector('.drag-handle-class').style.display = 'inline';
// -------------------------------
// Placeholder Management
// -------------------------------
function showPlaceholders() {
document.querySelectorAll('.kanban-status').forEach(status => {
const placeholderClass = 'empty-placeholder';
// Remove existing placeholder
const existing = status.querySelector(`.${placeholderClass}`);
if (existing) existing.remove();
// Only show if there are no tasks
if (status.querySelectorAll('.task').length === 0) {
const placeholder = document.createElement('div');
placeholder.className = `${placeholderClass} text-muted text-center p-2`;
placeholder.innerText = 'Drop ticket here';
placeholder.style.pointerEvents = 'none';
status.appendChild(placeholder);
}
}); });
} }
drake.on('drag', function(el) { function hidePlaceholders() {
el.style.cursor = 'grabbing'; document.querySelectorAll('.empty-placeholder').forEach(el => el.remove());
}); }
drake.on('dragend', function(el) { // Run once on load
el.style.cursor = 'grab'; showPlaceholders();
});
// Add event listener for the drop event
drake.on('drop', function (el, target, source, sibling) {
// Log the target ID to the console
//console.log('Dropped into:', target.getAttribute('data-column-name'));
if (CONFIG_TICKET_ORDERING === 0 && source == target) { // -------------------------------
drake.cancel(true); // Move the card back to its original position // Utility: Detect touch device
return; // -------------------------------
} function isTouchDevice() {
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
// Get all cards in the target column and their positions }
let cards = $(target).children('.task');
let positions = [];
//id of current status / column
let columnId = $(target).data('status-id');
let movedTicketId = $(el).data('ticket-id');
let movedTicketStatusId = $(el).data('ticket-status-id');
cards.each(function(index, card) {
let ticketId = $(card).data('ticket-id');
let statusId = $(card).data('ticket-status-id');
let oldStatus = false;
if (ticketId == movedTicketId) {
oldStatus = movedTicketStatusId;
}
//update the status id of the card if needed
if (statusId != columnId) {
$(card).data('ticket-status-id', columnId);
statusId = columnId;
}
positions.push({
ticket_id: ticketId,
ticket_order: index,
ticket_oldStatus: oldStatus,
ticket_status: statusId ?? null// Get the new status ID from the target column
});
});
//console.log(positions);
// Send AJAX request to update all ticket kanban orders and statuses
$.ajax({
url: 'ajax.php',
type: 'POST',
data: {
update_kanban_ticket: true,
positions: positions
},
success: function(response) {
//console.log('Ticket kanban orders and statuses updated successfully.');
},
error: function(xhr, status, error) {
console.error('Error updating ticket kanban orders and statuses:', error);
}
});
});
}); });

View File

@@ -47,7 +47,7 @@ $sql = mysqli_query(
LEFT JOIN tags ON tags.tag_id = location_tags.tag_id LEFT JOIN tags ON tags.tag_id = location_tags.tag_id
WHERE location_$archive_query WHERE location_$archive_query
$tag_query $tag_query
AND (location_name LIKE '%$q%' OR location_description LIKE '%$q%' OR location_address LIKE '%$q%' OR location_phone LIKE '%$phone_query%' OR tag_name LIKE '%$q%' OR client_name LIKE '%$q%') AND (location_name LIKE '%$q%' OR location_description LIKE '%$q%' OR location_address LIKE '%$q%' OR location_city LIKE '%$q%' OR location_state LIKE '%$q%' OR location_zip LIKE '%$q%' OR location_country LIKE '%$q%' OR location_phone LIKE '%$phone_query%' OR tag_name LIKE '%$q%' OR client_name LIKE '%$q%')
$access_permission_query $access_permission_query
$client_query $client_query
GROUP BY location_id GROUP BY location_id
@@ -99,7 +99,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select onchange="this.form.submit()" class="form-control select2" name="tags[]" data-placeholder="- Select Tags -" multiple> <select onchange="this.form.submit()" class="form-control select2" name="tags[]" data-placeholder="- Select Tags -" multiple>
<?php <?php
$sql_tags_filter = mysqli_query($mysqli, " $sql_tags_filter = mysqli_query($mysqli, "
@@ -126,7 +126,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-md-2"></div> <div class="col-md-2"></div>
<?php } else { ?> <?php } else { ?>
<div class="col-md-2"> <div class="col-md-2">
<div class="input-group"> <div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="client" onchange="this.form.submit()"> <select class="form-control select2" name="client" onchange="this.form.submit()">
<option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option> <option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option>
@@ -333,7 +333,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div> </div>
</a> </a>
</td> </td>
<td><a href="//maps.<?php echo $session_map_source; ?>.com?q=<?php echo "$location_address $location_zip"; ?>" target="_blank"><?php echo $location_address; ?><br><?php echo "$location_city $location_state $location_zip"; ?></a></td> <td><a href="//maps.<?php echo $session_map_source; ?>.com?q=<?php echo "$location_address $location_zip"; ?>" target="_blank"><?php echo $location_address; ?><br><?php echo "$location_city $location_state $location_zip<br><small>$location_country</small>"; ?></a></td>
<td> <td>
<?php echo $location_phone_display; ?> <?php echo $location_phone_display; ?>
<?php echo $location_fax_display; ?> <?php echo $location_fax_display; ?>

View File

@@ -1,7 +1,7 @@
<?php <?php
require_once "config.php"; require_once "config.php";
require_once "functions.php"; require_once "functions.php";
require_once "check_login.php"; require_once "includes/check_login.php";
require_once 'plugins/totp/totp.php'; //TOTP MFA Lib require_once 'plugins/totp/totp.php'; //TOTP MFA Lib
// Get Company Logo // Get Company Logo

View File

@@ -75,20 +75,23 @@
<div class="tab-pane fade" id="pills-support"> <div class="tab-pane fade" id="pills-support">
<label>Support Phone</label> <label>Support Phone / <span class="text-secondary">Extension</span></label>
<div class="form-row"> <div class="form-row">
<div class="col-8"> <div class="col-9">
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div> </div>
<input type="text" class="form-control" name="phone" placeholder="Phone Number" maxlength="200"> <input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="phone" placeholder="Phone Number" maxlength="200">
</div> </div>
</div> </div>
</div> </div>
<div class="col-4"> <div class="col-3">
<input type="text" class="form-control" name="extension" placeholder="Prompts" maxlength="200"> <div class="form-group">
<input type="text" class="form-control" name="extension" placeholder="ext." maxlength="200">
</div>
</div> </div>
</div> </div>

View File

@@ -1,51 +0,0 @@
<div class="modal" id="linkCredentialModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-key mr-2"></i>Link Credential to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<select class="form-control select2" name="credential_id">
<option value="">- Select a Credential -</option>
<?php
$sql_credentials_select = mysqli_query($mysqli, "SELECT credential_id, credential_name FROM credentials
WHERE credential_client_id = $client_id
AND credential_asset_id != $contact_id
AND credential_asset_id = 0
AND credential_archived_at IS NULL
ORDER BY credential_name ASC"
);
while ($row = mysqli_fetch_array($sql_credentials_select)) {
$credential_id = intval($row['credential_id']);
$credential_name = nullable_htmlentities($row['credential_name']);
?>
<option value="<?php echo $credential_id ?>"><?php echo $credential_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_asset_to_credential" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -1,57 +0,0 @@
<div class="modal" id="linkDocumentModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-folder mr-2"></i>Link Document to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-folder"></i></span>
</div>
<select class="form-control select2" name="document_id">
<option value="">- Select a Document -</option>
<?php
// Check if there are any associated documents
if ($linked_documents) {
$excluded_document_ids = implode(",", $linked_documents);
$exclude_condition = "AND document_id NOT IN ($excluded_document_ids)";
} else {
$exclude_condition = ""; // No condition if there are no displayed documents
}
$sql_documents_select = mysqli_query($mysqli, "SELECT * FROM documents
WHERE document_client_id = $client_id
AND document_archived_at IS NULL
$exclude_condition
ORDER BY document_name ASC"
);
while ($row = mysqli_fetch_array($sql_documents_select)) {
$document_id = intval($row['document_id']);
$document_name = nullable_htmlentities($row['document_name']);
?>
<option value="<?php echo $document_id ?>"><?php echo $document_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_asset_to_document" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -1,59 +0,0 @@
<div class="modal" id="linkFileModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-paperclip mr-2"></i>Link File to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paperclip"></i></span>
</div>
<select class="form-control select2" name="file_id">
<option value="">- Select a File -</option>
<?php
// Check if there are any associated files
if (!empty($linked_files)) {
$excluded_file_ids = implode(",", $linked_files);
$exclude_condition = "AND file_id NOT IN ($excluded_file_ids)";
} else {
$exclude_condition = ""; // No condition if there are no displayed vendors
}
$sql_files_select = mysqli_query($mysqli, "SELECT * FROM files
LEFT JOIN folders ON folder_id = file_folder_id
WHERE file_client_id = $client_id
$exclude_condition
ORDER BY folder_name ASC, file_name ASC"
);
while ($row = mysqli_fetch_array($sql_files_select)) {
$file_id = intval($row['file_id']);
$file_name = nullable_htmlentities($row['file_name']);
$folder_name = nullable_htmlentities($row['folder_name']);
?>
<option value="<?php echo $file_id ?>"><?php echo "$folder_name/$file_name"; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_asset_to_file" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -1,56 +0,0 @@
<div class="modal" id="linkServiceModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-stream mr-2"></i>Link Service to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stream"></i></span>
</div>
<select class="form-control select2" name="service_id">
<option value="">- Select a Service -</option>
<?php
// Check if there are any associated services
if (!empty($linked_services)) {
$excluded_service_ids = implode(",", $linked_services);
$exclude_condition = "AND service_id NOT IN ($excluded_service_ids)";
} else {
$exclude_condition = ""; // No condition if there are no displayed services
}
$sql_services_select = mysqli_query($mysqli, "SELECT * FROM services
WHERE service_client_id = $client_id
$exclude_condition
ORDER BY service_name ASC"
);
while ($row = mysqli_fetch_array($sql_services_select)) {
$service_id = intval($row['service_id']);
$service_name = nullable_htmlentities($row['service_name']);
?>
<option value="<?php echo $service_id ?>"><?php echo $service_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_service_to_asset" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -1,58 +0,0 @@
<div class="modal" id="linkSoftwareModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>License Software to <strong><?php echo $asset_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="asset_id" value="<?php echo $asset_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<select class="form-control select2" name="software_id">
<option value="">- Select a Device Software License -</option>
<?php
// Check if there are any associated sofctware
if (!empty($linked_software)) {
$excluded_software_ids = implode(",", $linked_software);
$exclude_condition = "AND software_id NOT IN ($excluded_software_ids)";
} else {
$exclude_condition = ""; // No condition if there are no displayed software
}
$sql_software_select = mysqli_query($mysqli, "SELECT * FROM software
WHERE software_client_id = $client_id
AND software_archived_at IS NULL
AND software_license_type = 'Device'
$exclude_condition
ORDER BY software_name ASC"
);
while ($row = mysqli_fetch_array($sql_software_select)) {
$software_id = intval($row['software_id']);
$software_name = nullable_htmlentities($row['software_name']);
?>
<option value="<?php echo $software_id ?>"><?php echo $software_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_software_to_asset" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -208,7 +208,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="location_phone_country_code" value="+1" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="location_phone_country_code" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="location_phone" placeholder="Phone Number" maxlength="200"> <input type="tel" class="form-control" name="location_phone" placeholder="Phone Number" maxlength="200">
</div> </div>
</div> </div>
@@ -228,7 +228,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fax"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-fax"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="location_fax_country_code" value="+1" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="location_fax_country_code" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="location_fax" placeholder="Fax Number"> <input type="tel" class="form-control" name="location_fax" placeholder="Fax Number">
</div> </div>
</div> </div>
@@ -267,7 +267,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="contact_phone_country_code" value="+1" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="contact_phone_country_code" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="contact_phone" placeholder="Phone Number" maxlength="200"> <input type="tel" class="form-control" name="contact_phone" placeholder="Phone Number" maxlength="200">
</div> </div>
</div> </div>
@@ -287,7 +287,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-mobile-alt"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-mobile-alt"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="contact_mobile_country_code" value="+1" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="contact_mobile_country_code" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="contact_mobile" placeholder="Mobile Phone Number"> <input type="tel" class="form-control" name="contact_mobile" placeholder="Mobile Phone Number">
</div> </div>
</div> </div>

View File

@@ -37,14 +37,14 @@
<option value="0">/</option> <option value="0">/</option>
<?php <?php
// Start displaying folder options from the root (parent_folder = 0) // Start displaying folder options from the root (parent_folder = 0)
display_folder_options(0, $client_id); display_folder_options(0, $client_id, 1);
?> ?>
</select> </select>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<input type="file" class="form-control-file" name="file[]" multiple id="fileInput" accept=".jpg, .jpeg, .gif, .png, .webp, .pdf, .txt, .md, .doc, .docx, .odt, .csv, .xls, .xlsx, .ods, .pptx, .odp, .zip, .tar, .gz, .xml, .msg, .json, .wav, .mp3, .ogg, .mov, .mp4, .av1, .ovpn, .cfg, .ps1, .vsdx, .drawio, .pfx, .unf, .key"> <input type="file" class="form-control-file" name="file[]" multiple id="fileInput" accept=".jpg, .jpeg, .gif, .png, .webp, .pdf, .txt, .md, .doc, .docx, .odt, .csv, .xls, .xlsx, .ods, .pptx, .odp, .zip, .tar, .gz, .xml, .msg, .json, .wav, .mp3, .ogg, .mov, .mp4, .av1, .ovpn, .cfg, .ps1, .vsdx, .drawio, .pfx, .unf, .key, .stk, .bat">
</div> </div>
<small class="text-secondary">Up to 20 files can be uploaded at once by holding down CTRL and selecting files</small> <small class="text-secondary">Up to 20 files can be uploaded at once by holding down CTRL and selecting files</small>

View File

@@ -101,7 +101,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="phone_country_code" value="+1" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="phone" placeholder="Phone Number" maxlength="200"> <input type="tel" class="form-control" name="phone" placeholder="Phone Number" maxlength="200">
</div> </div>
</div> </div>
@@ -121,7 +121,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-mobile-alt"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-mobile-alt"></i></span>
</div> </div>
<input type="tel" class="form-control col-2" name="mobile_country_code" value="+1" placeholder="Code" maxlength="4"> <input type="tel" class="form-control col-2" name="mobile_country_code" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="mobile" placeholder="Mobile Phone Number"> <input type="tel" class="form-control" name="mobile" placeholder="Mobile Phone Number">
</div> </div>
</div> </div>

View File

@@ -1,51 +0,0 @@
<div class="modal" id="linkAssetModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-desktop mr-2"></i>Link Asset to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
</div>
<select class="form-control select2" name="asset_id">
<option value="">- Select an Asset -</option>
<?php
$sql_assets_select = mysqli_query($mysqli, "SELECT asset_id, asset_name FROM assets
WHERE asset_client_id = $client_id
AND asset_contact_id != $contact_id
AND asset_contact_id = 0
AND asset_archived_at IS NULL
ORDER BY asset_name ASC"
);
while ($row = mysqli_fetch_array($sql_assets_select)) {
$asset_id = intval($row['asset_id']);
$asset_name = nullable_htmlentities($row['asset_name']);
?>
<option value="<?php echo $asset_id ?>"><?php echo $asset_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_contact_to_asset" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -1,51 +0,0 @@
<div class="modal" id="linkCredentialModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-key mr-2"></i>Link Credential to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
</div>
<select class="form-control select2" name="credential_id">
<option value="">- Select a Credential -</option>
<?php
$sql_credentials_select = mysqli_query($mysqli, "SELECT credential_id, credential_name FROM credentials
WHERE credential_client_id = $client_id
AND credential_contact_id != $contact_id
AND credential_contact_id = 0
AND credential_archived_at IS NULL
ORDER BY credential_name ASC"
);
while ($row = mysqli_fetch_array($sql_credentials_select)) {
$credential_id = intval($row['credential_id']);
$credential_name = nullable_htmlentities($row['credential_name']);
?>
<option value="<?php echo $credential_id ?>"><?php echo $credential_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_contact_to_credential" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -1,57 +0,0 @@
<div class="modal" id="linkDocumentModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-folder mr-2"></i>Link Document to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-folder"></i></span>
</div>
<select class="form-control select2" name="document_id">
<option value="">- Select a Document -</option>
<?php
// Check if there are any associated documents
if ($linked_documents) {
$excluded_document_ids = implode(",", $linked_documents);
$exclude_condition = "AND document_id NOT IN ($excluded_document_ids)";
} else {
$exclude_condition = ""; // No condition if there are no displayed documents
}
$sql_documents_select = mysqli_query($mysqli, "SELECT * FROM documents
WHERE document_client_id = $client_id
AND document_archived_at IS NULL
$exclude_condition
ORDER BY document_name ASC"
);
while ($row = mysqli_fetch_array($sql_documents_select)) {
$document_id = intval($row['document_id']);
$document_name = nullable_htmlentities($row['document_name']);
?>
<option value="<?php echo $document_id ?>"><?php echo $document_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_contact_to_document" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -1,59 +0,0 @@
<div class="modal" id="linkFileModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-paperclip mr-2"></i>Link File to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paperclip"></i></span>
</div>
<select class="form-control select2" name="file_id">
<option value="">- Select a File -</option>
<?php
// Check if there are any associated files
if (!empty($linked_files)) {
$excluded_file_ids = implode(",", $linked_files);
$exclude_condition = "AND file_id NOT IN ($excluded_file_ids)";
} else {
$exclude_condition = ""; // No condition if there are no displayed vendors
}
$sql_files_select = mysqli_query($mysqli, "SELECT * FROM files
LEFT JOIN folders ON folder_id = file_folder_id
WHERE file_client_id = $client_id
$exclude_condition
ORDER BY folder_name ASC, file_name ASC"
);
while ($row = mysqli_fetch_array($sql_files_select)) {
$file_id = intval($row['file_id']);
$file_name = nullable_htmlentities($row['file_name']);
$folder_name = nullable_htmlentities($row['folder_name']);
?>
<option value="<?php echo $file_id ?>"><?php echo "$folder_name/$file_name"; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_contact_to_file" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -1,56 +0,0 @@
<div class="modal" id="linkServiceModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-stream mr-2"></i>Link Service to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stream"></i></span>
</div>
<select class="form-control select2" name="service_id">
<option value="">- Select a Service -</option>
<?php
// Check if there are any associated services
if (!empty($linked_services)) {
$excluded_service_ids = implode(",", $linked_services);
$exclude_condition = "AND service_id NOT IN ($excluded_service_ids)";
} else {
$exclude_condition = ""; // No condition if there are no displayed services
}
$sql_services_select = mysqli_query($mysqli, "SELECT * FROM services
WHERE service_client_id = $client_id
$exclude_condition
ORDER BY service_name ASC"
);
while ($row = mysqli_fetch_array($sql_services_select)) {
$service_id = intval($row['service_id']);
$service_name = nullable_htmlentities($row['service_name']);
?>
<option value="<?php echo $service_id ?>"><?php echo $service_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_service_to_contact" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -1,58 +0,0 @@
<div class="modal" id="linkSoftwareModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>License Software to <strong><?php echo $contact_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="contact_id" value="<?php echo $contact_id; ?>">
<div class="modal-body bg-white">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<select class="form-control select2" name="software_id">
<option value="">- Select a User Software License -</option>
<?php
// Check if there are any associated sofctware
if (!empty($linked_software)) {
$excluded_software_ids = implode(",", $linked_software);
$exclude_condition = "AND software_id NOT IN ($excluded_software_ids)";
} else {
$exclude_condition = ""; // No condition if there are no displayed software
}
$sql_software_select = mysqli_query($mysqli, "SELECT * FROM software
WHERE software_client_id = $client_id
AND software_archived_at IS NULL
AND software_license_type = 'User'
$exclude_condition
ORDER BY software_name ASC"
);
while ($row = mysqli_fetch_array($sql_software_select)) {
$software_id = intval($row['software_id']);
$software_name = nullable_htmlentities($row['software_name']);
?>
<option value="<?php echo $software_id ?>"><?php echo $software_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
</div>
<div class="modal-footer bg-white">
<button type="submit" name="link_software_to_contact" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</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

@@ -27,7 +27,7 @@
</div> </div>
<div class="modal-footer bg-white"> <div class="modal-footer bg-white">
<button type="submit" name="add_invoice_recurring" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create Invoice</button> <button type="submit" name="add_invoice_recurring" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create Recurring Invoice</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button> <button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div> </div>
</form> </form>

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