diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 21cf847c..84329956 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: ["https://donate.itflow.org"] +custom: ["https://services.itflow.org"] diff --git a/.htaccess b/.htaccess new file mode 100644 index 00000000..f38dbabc --- /dev/null +++ b/.htaccess @@ -0,0 +1,2 @@ +# Prevent access to .git, .github, and config.php +RedirectMatch 404 ^/(\.git|\.github|config\.php) \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..385e5534 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,116 @@ +# Changelog + +This file documents all notable changes made to ITFlow. + +## [25.02] +### Fixed +- Migrated several reports to the new permissions/roles system +- Resolved issue with empty task box showing for closed/resolved tickets +- Corrected ticket priority sorting +- Cloned asset interfaces when transferring assets between clients + +### Added / Changed +- Restored max number of records per page option back to 500 since we dont have repeating modals. +- Bulk Categorize Tickets feature +- Renamed "Interface port" to "Interface Description." "Interface Name" should now refer to port name and/or number +- Changed "Transfer Asset to Client" from a single action to a bulk action +- Updated Filter Footer UI to show "Showing x to x of x records" instead of just the total records +- Added Client Overview section to view client assets, contacts, licenses, credentials, etc. +- Introduced Quick Peek for asset details, contact information, and document viewing throughout the ITFlow App, all made possible by AJAX +- Enabled Simple Drag-and-Drop Ordering for Invoices, Recurring Invoices, Quotes, Ticket Tasks, and Ticket Template Tasks +- Added new Ticket View options: Kanban and Simple View +- Migrated all repeating modals to the new AJAX modal function for faster loading times and quicker development +- Allowed clients to upload PDF documents to accepted quotes +- Client Portal now shows ticket category +- Custom links can now be added to the Client Portal navbar +- Lots of little tweaks to UI, performance, bugs, etc. + +### Breaking Changes +- Cron scripts have officially been moved to the /scripts folder and are no longer in the root directory; they must be updated to function properly + +## [25.01.3] +### Fixed +- Fixed ticket assignment modal showing client contacts. + +## [25.01.2] +### Fixed +- Fixed app version. + +## [25.01.1] + +### Added / Changed +- Redesigned the Multi-Factor Authentication (MFA) Setup and Enforcement Flow UI/UX for a more intuitive user experience. +- Added a "Member" column in the user roles listing for improved visibility. +- General UI/UX improvements, along with minor performance optimizations and cleanups. + +### Fixed +- Fixed an issue where Stripe was not appearing as a recurring payment option. +- Corrected inaccurate Quarter 2 Expense results in the Profit & Loss Report. +- Resolved TOTP code not displaying correctly on hover in the Contact or Asset Details sections. +- Archived contacts no longer appear in the Bulk Mail section. +- Fixed an issue where the Ticket Assign Modal was showing both ITFlow and client users. +- Fixed issue with login key redirecting to legacy client portal page. + +## [25.01] + +### Added / Changed +- Added support for saving cards in Stripe for automatic invoice payments. +- Page titles now display detailed information (e.g., page name, client selection, company name, ticket and invoice info) for easier multi-tab navigation. +- Reintroduced the new admin role-check for admin pages. +- Admin roles can now be archived. +- Debug mode now shows the current Git branch. +- The auto-acknowledgment email for email-parsed tickets now includes a guest link. +- Recurring tickets no longer require a contact. +- Stripe online payment setup now prompts you to set the income/expense account. +- New cron/CLI scripts have been moved to the `/scripts` subfolder — remember to update your cron configurations! +- Moved modal includes to `/modals` to tidy up the root directory. +- Moved most include files to `/includes` to improve directory structure. +- Moved guest pages to `/guest` for better organization. +- Renamed the include file `pagination.php` to `filter_footer.php`, as it is used in conjunction with `filter_header.php` for page filtering. +- Guest ticket feedback now shows the ticket prefix and number, not just the ID. +- Individual POST handler logic pages are no longer directly accessible. +- Added the ability to delete payments on the Payments and Client Payments pages. +- Implemented domain history tracking. +- Added Asset Interface Linking/Connections to show what interface is connected to which interface port of another asset. +- Added Force Recurring Ticket option in more locations, not just for recurring tickets. +- Implemented row spanning and centered devices that occupy multiple units in a rack. +- Added tooltips to main navigation badge counts to clarify what is being counted. +- Reduced max records per page from 500 to 100 to prevent performance issues. +- Updated several plugins: + - `stripe-php` from 10.5.0 to 16.4.0 + - `Inputmask` from 5.0.8 to 5.0.9 + - `DataTables` from 2.1.8 to 2.2.1 + - `pdfmake` from 0.2.8 to 0.2.18 + - `php-mime-mail-parser` to 9.0.1 + - `TinyMCE` from 7.5.1 to 7.6.1 +- Removed unused libraries from the vendor folder and moved Stripe to the plugins folder, eliminating the vendor folder. +- Merged the MFA TOTP functionality files `base32static.php` and `rfc6238.php` into a single file (`totp`) and moved it to the plugins folder. +- No longer need to pass the DB connection (`$mysqli`) to the `addToMailQueue` function. +- Disabled HTML Purifier caching. +- Replaced the `nullable_htmlentities` function with `htmlspecialchars`. +- Updated filter variable naming. +- Implemented other minor UI updates, performance optimizations, and directory cleanups. + +### Fixed +- Fixed an issue where the ticket edit modal didn't show multi-client or no-client projects. +- Fixed asset interface losing DHCP settings. +- Fixed a 500 error when creating or editing recurring expenses due to an incorrect variable name. +- Fixed tickets created via the portal/email not being marked as billable. +- Fixed issues with editing recurring expenses. +- Resolved a regression where the TinyMCE editor didn’t display when adding or editing ticket templates. +- Fixed a TinyMCE license issue. + +### Removed / Deprecated +- Deprecated the cron scripts in the root directory. Cron jobs should now use the ones in the `/scripts` subfolder, which no longer require a cron key and must be run via CLI. + +### BREAKING CHANGES +- The client portal has been moved from `/portal` to `/client`: + - Links in previous emails will be broken. + - The Azure Entra ID SSO Redirect URI needs to be updated to `/client`. + - You may need to update other links (e.g., website, support page). +- Guest links have been moved from `/` to `/guest`. Previous links will be broken. + +## [24.12] + +### Added / Changed +- Introduced versioned releases for the first time! \ No newline at end of file diff --git a/README.md b/README.md index 6c1791cc..4863620d 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,10 @@ [![Contributors][contributors-shield]][contributors-url] [![Stargazers][stars-shield]][stars-url] -[![Issues][issues-shield]][issues-url] [![Commits][commit-shield]][commit-url] [![GPL License][license-shield]][license-url] -
-

ITFlow

@@ -47,43 +42,26 @@ ### The Problem -- You're a busy MSP with 101 things to do. -- Information about your clients is unorganised and unstructured: scattered in random tickets or folders - when you do eventually find it, it's out of date. -- For some issues, you spend longer looking for the relevant documentation than actually working the ticket. +- You're a small but busy managed service provider with 101 things to do. Information about your clients is unorganised, unstructured and outdated. +- For some work, you seem to spend longer looking for the relevant documentation than actually working on the issue/project. - On top of the technical day to day, you also have to take care of the financial side of the business - consistent pricing, quotes/invoicing, and accounting. ### The Solution: ITFlow -- ITFlow consolidates common MSP needs (IT Documentation, ticketing and billing) into one system - -### In Beta -* This project is in beta with many ongoing changes. Updates may unintentionally introduce bugs/security issues. Writing functional, secure code is very difficult. -* Whilst we are confident the code is safe, nothing in life is 100% safe or risk-free. Use your best judgement before deciding to store highly confidential information in ITFlow. -* We are hoping to have a stable 1.0 release by early 2025. +- ITFlow consolidates common MSP needs (documentation, ticketing and billing) into one unified system. ## Getting Started -ITFlow is self-hosted. There is a full installation guide in the [docs](https://docs.itflow.org/installation). +### Self Hosting +- The best installation method is to use the [install script](https://docs.itflow.org/installation_script) on Ubuntu/Debian. A video walk through is available [here](https://www.youtube.com/watch?v=kKz9NOU_1XE). +``` + wget -O itflow_install.sh https://github.com/itflow-org/itflow-install-script/raw/main/itflow_install.sh + bash itflow_install.sh +``` +- Other manual installation methods are available in the [docs](https://docs.itflow.org/installation). - - -### Installation via Script (Recommended Method) - - **Requirements** - - Clean Install of Debian 12 or Ubuntu 22.04 - - A public IP Address - - Ports 80 (HTTP) and 443 (HTTPS) TCP accessible from the outside in - - A Fully Qualified Domain Name pointing to the public IP Address – example itflow.example.com - - **Process** - - Login as root - - Download & run install script - ``` - wget -O itflow_install.sh https://github.com/itflow-org/itflow-install-script/raw/main/itflow_install.sh - bash itflow_install.sh - ``` - - Follow Instructions & navigate to setup URL shown - - Leave us feedback in the [forum](https://forum.itflow.org/d/11-road-map) +### Managed Hosting +- If you'd prefer, we can [host ITFlow for you](https://services.itflow.org/hosting.php). ## Key Features @@ -95,14 +73,7 @@ ITFlow is self-hosted. There is a full installation guide in the [docs](https:// ## Roadmap / Future to-do -* Comprehensive API to allow custom third party integration -* CalDAV to integrate with 3rd party calendars -* CardDAV to integrate with 3rd party Address books -* Recent caller toast alerts to click and bring up the clients account right away -* FIDO2 WebAuthn Support for passwordless auth (TPM Fingerprint), (USB Hardware keys such as Yubikey) - -See the [forum](https://forum.itflow.org/t/added-to-roadmap) and the [open issues](https://github.com/itflow-org/itflow/issues) for a full list of proposed features & known issues. - +We track the implementation of confirmed features and bugs via [TaskFlow](https://tasks.dev.itflow.org/tasks.php). Use the [forum](https://forum.itflow.org) to request features or raise bug reports. ## Support & Contributions @@ -111,7 +82,7 @@ See the [forum](https://forum.itflow.org/t/added-to-roadmap) and the [open issue For help using ITFlow, bugs, feature requests, and general ideas / discussions please use the community [forum](https://forum.itflow.org). ### Contributing -If you want to improve ITFlow, feel free to fork the repo and create a pull request, but make sure to discuss significant changes or new features with fellow contributors on the forum first. This helps ensure that your contributions are aligned with project goals, and saves time for everyone. All contributions should follow our [code standards](https://docs.itflow.org/code_standards). +If you want to improve ITFlow, feel free to fork the repo and create a pull reques. Make sure to discuss significant changes or new features with fellow contributors on the forum first. This helps ensure that your contributions are aligned with project goals, and saves time for everyone. All contributions should follow our [code standards](https://docs.itflow.org/code_standards). See the [contributing guide](https://docs.itflow.org/contribute). #### Contributors @@ -122,13 +93,14 @@ If you want to improve ITFlow, feel free to fork the repo and create a pull requ We’re incredibly grateful to the organizations and individuals who support the project - a big thank you to: - CompuMatter - F1 for HELP -- JetBrains - ## 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. +## Security +* As of 2025, we now have a stable release of the project. +* Whilst we are confident in the safety of the code, no system is risk-free. Nearly all software has bugs. Use your best judgement before storing highly confidential information in ITFlow. +* If you have a security concern, privately report it [here](https://github.com/itflow-org/itflow/security/policy). diff --git a/SECURITY.md b/SECURITY.md index f374eeac..bd6ba7fd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,25 +1,21 @@ # Security Policy -## **Please do NOT report security concerns/vulnerabilities publicly (Github issues/forum)** +## **Please do NOT report security concerns/vulnerabilities publicly (Issues/forum)** ---- +**We take security seriously** -## In Beta - -ITFlow is currently in beta and is a work in progress. - -**We take security seriously.** Whilst we are confident the code is safe, nothing in life is 100% safe or risk-free. You should use your best judgment before entering confidential information into the app. - -We attempt to follow security best practices where possible, including [automated code scanning](https://sonarcloud.io/component_measures?id=itflow-org_itflow&metric=security_rating&view=list). - -[![Security](https://sonarcloud.io/api/project_badges/measure?project=itflow-org_itflow&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=itflow-org_itflow) +- Whilst we are confident in the safety of the code, no system is risk-free. Nearly all software has bugs. Use your best judgement before storing highly confidential information in ITFlow. +- We attempt to follow security best practices where possible, including [automated code scanning](https://sonarcloud.io/component_measures?id=itflow-org_itflow&metric=security_rating&view=list). +- [![Security](https://sonarcloud.io/api/project_badges/measure?project=itflow-org_itflow&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=itflow-org_itflow) ## Supported Versions +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 | | ------- | ------------------ | -| Beta | :white_check_mark: | -| 1.0 | Yet to be released | +| Beta | :x: | +| 24.12 | :white_check_mark: | +| 25.1 | :white_check_mark: (When released) | ## Reporting a Vulnerability via GitHub Security Advisories diff --git a/account_edit_modal.php b/account_edit_modal.php deleted file mode 100644 index 58df3b62..00000000 --- a/account_edit_modal.php +++ /dev/null @@ -1,37 +0,0 @@ - diff --git a/accounts.php b/accounts.php index f23a0f7e..51025e27 100644 --- a/accounts.php +++ b/accounts.php @@ -4,7 +4,7 @@ $sort = "account_name"; $order = "ASC"; -require_once "inc_all.php"; +require_once "includes/inc_all.php"; // Perms enforceUserPermission('module_financial'); @@ -85,7 +85,15 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); ?> - + + + + + @@ -94,7 +102,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - +
- @@ -174,7 +174,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- + - + @@ -184,10 +184,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
-
- + - + @@ -127,14 +127,14 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- + - + @@ -295,11 +295,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
-
@@ -34,5 +34,5 @@ require_once "inc_all_admin.php";
- - -
-
-

Bulk Mail

-
- -
-
-
-
- - -
- -
- -
Email Message
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
-
-
- -
- -
-
- -
- -
- -
Select Contacts
-
-
-
- - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
ClientNameTitleEmail
-
- -
-
- - - -
-
- -
- -
- -
- -
-
-
- - - - - -

@@ -134,10 +133,15 @@ if (isset($_GET['archived'])) { ?> - + + - + + -

" tabindex="-1"> - - diff --git a/admin_custom_link.php b/admin_custom_link.php index 4797e882..b5e1ebf6 100644 --- a/admin_custom_link.php +++ b/admin_custom_link.php @@ -4,7 +4,7 @@ $sort = "custom_link_name"; $order = "ASC"; -require_once "inc_all_admin.php"; +require_once "includes/inc_all_admin.php"; //Rebuild URL @@ -96,16 +96,21 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $custom_link_location = intval($row['custom_link_location']); if ($custom_link_location == 1) { $custom_link_location_display = "Main Side Nav"; - } else { + } elseif ($custom_link_location == 2) { $custom_link_location_display = "Top Nav"; + } elseif ($custom_link_location == 3) { + $custom_link_location_display = "Client Portal Nav"; } ?> - - - + + @@ -117,7 +122,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - " tabindex="-1"> - - diff --git a/admin_debug.php b/admin_debug.php index 894151c3..dcb11792 100644 --- a/admin_debug.php +++ b/admin_debug.php @@ -1,15 +1,16 @@ close(); - - + + - + + + + + + + + +
Current App VersionITFlow release version
Current DB Version
Current Code Commit
Current Branch
@@ -744,5 +753,5 @@ $mysqli->close(); - + - - + + + " tabindex="-1"> - - diff --git a/admin_legacy_debug.php b/admin_legacy_debug.php deleted file mode 100644 index 61591900..00000000 --- a/admin_legacy_debug.php +++ /dev/null @@ -1,335 +0,0 @@ - $count, - 'size' => $size - ]; -} - -// Function to compare two arrays recursively and return the differences -function arrayDiffRecursive($array1, $array2) { - $diff = array(); - - foreach ($array1 as $key => $value) { - if (is_array($value)) { - if (!isset($array2[$key]) || !is_array($array2[$key])) { - $diff[$key] = $value; - } else { - $recursiveDiff = arrayDiffRecursive($value, $array2[$key]); - if (!empty($recursiveDiff)) { - $diff[$key] = $recursiveDiff; - } - } - } else { - if (!isset($array2[$key]) || $array2[$key] !== $value) { - $diff[$key] = $value; - } - } - } - - return $diff; -} - -// Function to load the table structures from an SQL dump file URL -function loadTableStructuresFromSQLDumpURL($fileURL) { - $context = stream_context_create(array('http' => array('header' => 'Accept: application/octet-stream'))); - $fileContent = file_get_contents($fileURL, false, $context); - - if ($fileContent === false) { - return null; - } - - $structure = array(); - $queries = explode(";", $fileContent); - - foreach ($queries as $query) { - $query = trim($query); - - if (!empty($query)) { - if (preg_match("/^CREATE TABLE `(.*)` \((.*)\)$/s", $query, $matches)) { - $tableName = $matches[1]; - $tableStructure = $matches[2]; - $structure[$tableName] = array('structure' => $tableStructure); - } - } - } - - return $structure; -} - -// Function to fetch the database structure from the MySQL server -function fetchDatabaseStructureFromServer() { - - global $mysqli; - - $tables = array(); - - // Fetch table names - $result = $mysqli->query("SHOW TABLES"); - - if ($result->num_rows > 0) { - while ($row = $result->fetch_row()) { - $tableName = $row[0]; - $tables[$tableName] = array(); - } - } - - // Fetch table structures - foreach ($tables as $tableName => &$table) { - $result = $mysqli->query("SHOW CREATE TABLE `$tableName`"); - - if ($result->num_rows > 0) { - $row = $result->fetch_row(); - $table['structure'] = $row[1]; - } - } - - return $tables; -} - -//function to get current crontab and return it as an array -function get_crontab() { - $crontab = shell_exec('crontab -l'); - $crontab = explode(PHP_EOL, $crontab); - return $crontab; -} - -// URL to the SQL dump file -$fileURL = "https://raw.githubusercontent.com/itflow-org/itflow/master/db.sql"; - -// Load the desired table structures from the SQL dump file URL -$desiredStructure = loadTableStructuresFromSQLDumpURL($fileURL); - -if ($desiredStructure === null) { - die("Failed to load the desired table structures from the SQL dump file URL."); -} - -// Fetch the current database structure from the MySQL server -$currentStructure = fetchDatabaseStructureFromServer(); - -if ($currentStructure === null) { - die("Failed to fetch the current database structure from the server."); -} - -// Compare the structures and display the differences -$differences = arrayDiffRecursive($desiredStructure, $currentStructure); - -//DB Stats -// Query to fetch the number of tables -$tablesQuery = "SHOW TABLES"; -$tablesResult = $mysqli->query($tablesQuery); - -$numTables = $tablesResult->num_rows; -$numFields = 0; -$numRows = 0; - -// Loop through each table -while ($row = $tablesResult->fetch_row()) { - $tableName = $row[0]; - - // Query to fetch the number of fields - $fieldsQuery = "DESCRIBE `$tableName`"; - $fieldsResult = $mysqli->query($fieldsQuery); - - // Check if the query was successful - if ($fieldsResult) { - $numFields += $fieldsResult->num_rows; - - // Query to fetch the number of rows - $rowsQuery = "SELECT COUNT(*) FROM `$tableName`"; - $rowsResult = $mysqli->query($rowsQuery); - - // Check if the query was successful - if ($rowsResult) { - $numRows += $rowsResult->fetch_row()[0]; - } else { - echo "Error executing query: " . $mysqli->error; - } - } else { - echo "Error executing query: " . $mysqli->error; - } -} - -//Get loaded PHP modules -$loadedModules = get_loaded_extensions(); - -//Get Server Info / Service versions -$phpVersion = phpversion(); -$databaseInfo = mysqli_get_server_info($mysqli) . " / " . $mysqli->server_version; -$operatingSystem = php_uname(); -$webServer = $_SERVER['SERVER_SOFTWARE']; -$errorLog = ini_get('error_log') ?: "Debian/Ubuntu default is usually /var/log/apache2/error.log"; -$updates = fetchUpdates(); - -?> - -
-
-

Debug

-
-
- -

Debugging

- -
- -

Server Info

- - "; - echo "Database Version: " . $databaseInfo . "
"; - echo "Operating System: " . $operatingSystem . "
"; - echo "Web Server: " . $webServer . "
"; - echo "Apache/PHP Error Log: " . $errorLog - ?> - -
- -

File System

- "; - echo "Total size of files in $folderPath and its subdirectories: " . $totalSizeMB . " MB"; - ?> - -
-

ITFlow app

- current_version . "
"; - echo "Cron enabled: " . $config_enable_cron . "
"; - echo "App Timezone: " . $config_timezone; - ?> - -
- -

Database Structure Check

- -

Database stats

- - "; - echo "Total number of fields: " . $numFields . "
"; - echo "Total number of rows: " . $numRows . "
"; - echo "Current Database Version: " . CURRENT_DATABASE_VERSION . "
"; - ?> - -
- -

Table Stats

- - - - - - - - - - - - - - - - -
Table NameNumber of FieldsNumber of Rows
- -
- -

PHP Modules Installed

- - "; - } - ?> - -
- -

PHP Info

- (.*).*$%ms', '$1', $phpinfo); - - //Remove everything after the body tag - $phpinfo = preg_replace('%^(.*).*$%ms', '$1', $phpinfo); - - //Remove the body tag itself - $phpinfo = preg_replace('%^(.*)$%ms', '$1', $phpinfo); - - //Output the result - echo $phpinfo; - ?> - -
-
-
- - - - - ID - - Queued @@ -137,7 +132,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); Attempts - Action + Action @@ -175,15 +170,19 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - $email_from_name"?> $email_recipient_name"?> - - + + @@ -209,11 +208,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - + - diff --git a/admin_role.php b/admin_role.php index 9d592db3..f79def74 100644 --- a/admin_role.php +++ b/admin_role.php @@ -4,7 +4,7 @@ $sort = "user_role_is_admin"; $order = "DESC"; -require_once "inc_all_admin.php"; +require_once "includes/inc_all_admin.php"; //Rebuild URL @@ -21,7 +21,7 @@ $sql = mysqli_query( $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); ?> -
Roles are still in development. Permissions may not be fully enforced.
+
Roles are still in development. Permissions may not be fully enforced.
@@ -54,22 +54,15 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - Name - - - - - Description + Role + Members Admin - - User count - Action @@ -87,16 +80,32 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $sql_role_user_count = mysqli_query($mysqli, "SELECT COUNT(users.user_id) FROM users LEFT JOIN user_settings on users.user_id = user_settings.user_id WHERE user_role = $role_id AND user_archived_at IS NULL"); $role_user_count = mysqli_fetch_row($sql_role_user_count)[0]; + $sql_users = mysqli_query($mysqli, "SELECT * FROM users LEFT JOIN user_settings on users.user_id = user_settings.user_id WHERE user_role = $role_id AND user_archived_at IS NULL"); + // Initialize an empty array to hold user names + $user_names = []; + + // Fetch each row and store the user_name in the array + while($row = mysqli_fetch_assoc($sql_users)) { + $user_names[] = nullable_htmlentities($row['user_name']); + } + + // Convert the array of user names to a comma-separated string + $user_names_string = implode(",", $user_names) ; + + if (empty($user_names_string)) { + $user_names_string = "-"; + } + ?> - -
+
+ +
- + - -
" tabindex="-1"> - - diff --git a/admin_settings_ai.php b/admin_settings_ai.php index 91eba539..894859b8 100644 --- a/admin_settings_ai.php +++ b/admin_settings_ai.php @@ -1,6 +1,6 @@
@@ -73,5 +73,5 @@ require_once "inc_all_admin.php";
-
+

Company Details

-
- -
-
- -
- -
-
- -
-
- "> -
-
- -
- -
- -
- -
-
- -
- -
-
- -
- -
-
- -
- -
-
- -
- -
-
- -
- -
-
- -
- -
-
- -
- -
-
- -
- -
-
- -
- -
-
- -
- -
-
- +
+ + +
- -
-
+ +
+
+ +
+
+ +
+ +
+
-
- -
-
- +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
-
- -
- -
-
- -
- -
-
- -
- - -
-
@@ -120,5 +120,5 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
@@ -249,4 +249,4 @@ require_once "inc_all_admin.php";
@@ -39,5 +39,5 @@ require_once "inc_all_admin.php";
-
@@ -102,5 +102,5 @@ require_once "inc_all_admin.php";
@@ -327,5 +327,5 @@ require_once "inc_all_admin.php"; -
@@ -78,5 +78,5 @@ require_once "inc_all_admin.php";
@@ -19,19 +19,6 @@ require_once "inc_all_admin.php";
-
- -
-
- -
- - -
-
- @@ -193,4 +180,4 @@ require_once "inc_all_admin.php"; @@ -12,9 +12,6 @@ require_once "inc_all_admin.php"; -
Currently, we only integrate with Stripe. Please see this forum post.
-
-
value="1" id="enableStripeSwitch"> @@ -25,7 +22,7 @@ require_once "inc_all_admin.php";
">
- +
@@ -35,7 +32,7 @@ require_once "inc_all_admin.php";
- +
@@ -45,7 +42,7 @@ require_once "inc_all_admin.php";
- +
@@ -140,6 +137,8 @@ require_once "inc_all_admin.php";
+
Currently, we only integrate with Stripe. Please see this forum post.
+

@@ -151,5 +150,5 @@ require_once "inc_all_admin.php";
+ +
+
+

Online Payment - Client info

+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
ClientStripe Customer IDStripe Payment IDAction
+ +
+ + + + + +
@@ -41,4 +41,4 @@ require_once "inc_all_admin.php";
@@ -54,5 +54,5 @@ require_once "inc_all_admin.php";
@@ -44,7 +44,7 @@ require_once "inc_all_admin.php";
- +
@@ -62,5 +62,5 @@ require_once "inc_all_admin.php";
@@ -38,5 +38,5 @@ require_once "inc_all_admin.php";
@@ -63,5 +63,5 @@ require_once "inc_all_admin.php";
@@ -82,5 +82,5 @@ require_once "inc_all_admin.php";
- +
@@ -103,7 +103,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); -
" tabindex="-1"> - -
diff --git a/admin_tag.php b/admin_tag.php index 2ac68281..64ec7193 100644 --- a/admin_tag.php +++ b/admin_tag.php @@ -4,7 +4,7 @@ $sort = "tag_name"; $order = "ASC"; -require_once "inc_all_admin.php"; +require_once "includes/inc_all_admin.php"; //Rebuild URL @@ -87,7 +87,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); ?> - + @@ -98,7 +102,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); -
" tabindex="-1"> - - \ No newline at end of file diff --git a/admin_tax.php b/admin_tax.php index c6c891d2..bf8acbc5 100644 --- a/admin_tax.php +++ b/admin_tax.php @@ -4,7 +4,7 @@ $sort = "tax_name"; $order = "ASC"; -require_once "inc_all_admin.php"; +require_once "includes/inc_all_admin.php"; //Rebuild URL @@ -56,7 +56,15 @@ $num_rows = mysqli_num_rows($sql); ?> - + + + + + set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one $purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]); $purifier = new HTMLPurifier($purifier_config); @@ -29,6 +30,7 @@ $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"); ?> + @@ -317,110 +388,134 @@ if (isset($_GET['asset_id'])) {
-

Network Interfaces

+

Network Interfaces

- +
+ + + +
- +
"> - - - - - - - - + + + + + + + + + - + $network_name"; - } else { - $network_name_display = "-"; - } - $interface_notes = nullable_htmlentities($row['interface_notes']); - + // Prepare display text + $interface_mac_display = $interface_mac ?: '-'; + $interface_ip_display = $interface_ip ?: '-'; + $interface_type_display = $interface_type ?: '-'; + $network_name_display = $network_name + ? "$network_name" + : '-'; + // Connected interface details + $connected_asset_id = intval($row['connected_asset_id']); + $connected_asset_name = nullable_htmlentities($row['connected_asset_name']); + $connected_asset_type = nullable_htmlentities($row['connected_asset_type']); + $connected_asset_icon = getAssetIcon($connected_asset_type); + $connected_interface_name = nullable_htmlentities($row['connected_interface_name']); + + + // Show either "-" or "AssetName - Port" + if ($connected_asset_name) { + $connected_to_display = " + $connected_asset_name - $connected_interface_name + "; + } else { + $connected_to_display = "-"; + } ?> + - + - - - +
NameMACIPPortConnected ToAction
Name / PortTypeMACIPNetworkConnected ToAction
- - - + + + (Primary)"; } ?>
-
">
-

Passwords

+

Credentials

@@ -447,7 +542,7 @@ if (isset($_GET['asset_id'])) { if (empty($login_uri)) { $login_uri_display = "-"; } else { - $login_uri_display = "$login_uri"; + $login_uri_display = "$login_uri"; } $login_username = nullable_htmlentities(decryptLoginEntry($row['login_username'])); if (empty($login_username)) { @@ -461,7 +556,7 @@ if (isset($_GET['asset_id'])) { if (empty($login_otp_secret)) { $otp_display = "-"; } else { - $otp_display = " Hover.."; + $otp_display = " Hover.."; } $login_note = nullable_htmlentities($row['login_note']); $login_important = intval($row['login_important']); @@ -496,14 +591,18 @@ if (isset($_GET['asset_id'])) { - + - + @@ -513,7 +612,11 @@ if (isset($_GET['asset_id'])) {
+
"> +
+

Documents

+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Document TitleByCreatedUpdatedAction
+
+
+
+ + + + +
+
+
+
+
">

Files

@@ -728,7 +906,15 @@ if (isset($_GET['asset_id'])) { ?> - + + + + + @@ -742,10 +928,17 @@ if (isset($_GET['asset_id'])) {
+ +
@@ -105,6 +119,13 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); Expire + + + + Client + + + Action @@ -112,6 +133,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - )" data-target="#editCertificateModal"> +
@@ -166,14 +193,20 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- + +
+
- - +getMessage()); - $access_permission_query = ""; // Ensure safe default if query fails +$client_access_array = []; +while ($row = mysqli_fetch_assoc($user_client_access_result)) { + $client_access_array[] = $row['client_id']; } +$client_access_string = implode(',', $client_access_array); + +// Client access permission check +// Default allow, if a list of allowed clients is set & the user isn't an admin, restrict them +$access_permission_query = ""; +if ($client_access_string && !$session_is_admin) { + $access_permission_query = "AND clients.client_id IN ($client_access_string)"; +} // Include the settings vars require_once "get_settings.php"; - //Detects if using an Apple device and uses Apple Maps instead of google $iPod = stripos($_SERVER['HTTP_USER_AGENT'], "iPod"); $iPhone = stripos($_SERVER['HTTP_USER_AGENT'], "iPhone"); @@ -116,17 +109,5 @@ if ($iPod || $iPhone || $iPad) { $session_map_source = "google"; } - // Check if mobile device $session_mobile = isMobile(); - - -// Get Notification Count for the badge on the top nav -$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('notification_id') AS num FROM notifications WHERE (notification_user_id = $session_user_id OR notification_user_id = 0) AND notification_dismissed_at IS NULL")); -$num_notifications = $row['num']; - - -// FORCE MFA Setup -//if ($session_user_config_force_mfa == 1 && $session_token == NULL) { -// header("Location: force_mfa.php"); -//} diff --git a/client/autopay.php b/client/autopay.php new file mode 100644 index 00000000..e7953dde --- /dev/null +++ b/client/autopay.php @@ -0,0 +1,134 @@ + + +

AutoPay

+
+ +
+ + + + + Save card details
+ In order to set up automatic payments, you must create a customer record in Stripe.
+ First, you must authorize Stripe to store your card details for the purpose of automatic payment. +

+ +
+
+ +
+
+ + +
+
+ +
+ +
+
+
+ + + elseif (empty($stripe_pm)) { ?> + + Save card details
+ Please add the payment details you would like to save.
+ By adding payment details here, you grant consent for future automatic payments of invoices.

+ + + + +
+ +
+ + + + Manage saved payment methods + + customers->retrievePaymentMethod( + $stripe_id, + $stripe_pm, + [] + ); + + } catch (Exception $e) { + $error = $e->getMessage(); + error_log("Stripe payment error - encountered exception when fetching payment method info for $stripe_pm: $error"); + logApp("Stripe", "error", "Exception when fetching payment method info for $stripe_pm: $error"); + } + + $card_name = nullable_htmlentities($payment_method->billing_details->name); + $card_brand = nullable_htmlentities($payment_method->card->display_brand); + $card_last4 = nullable_htmlentities($payment_method->card->last4); + $card_expires = nullable_htmlentities($payment_method->card->exp_month) . "/" . nullable_htmlentities($payment_method->card->exp_year); + + ?> + +
+ +
+ Actions
+ - Remove saved payment method + + + + +
+ +
+ + +
-
+ @@ -93,4 +93,4 @@ if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) {
- + @@ -126,4 +126,4 @@ if ($row) {
-

Contacts

-
+
+

Contacts

+
+
@@ -75,4 +77,4 @@ $contacts_sql = mysqli_query($mysqli, "SELECT contact_id, contact_name, contact_
set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one $purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]); $purifier = new HTMLPurifier($purifier_config); @@ -41,7 +42,7 @@ if ($row) { $document_name = nullable_htmlentities($row['document_name']); $document_content = $purifier->purify($row['document_content']); } else { - header("Location: portal_post.php?logout"); + header("Location: post.php?logout"); exit(); } @@ -67,4 +68,4 @@ if ($row) {
- + diff --git a/portal/portal_header.php b/client/includes/header.php similarity index 58% rename from portal/portal_header.php rename to client/includes/header.php index 1e6f6c55..f06abcdc 100644 --- a/portal/portal_header.php +++ b/client/includes/header.php @@ -27,10 +27,8 @@ header("X-Frame-Options: DENY"); // Legacy - + - - @@ -52,28 +50,57 @@ header("X-Frame-Options: DENY"); // Legacy - - + - - - - - + + + + + + + + @@ -102,7 +129,6 @@ header("X-Frame-Options: DENY"); // Legacy " alt="..." height="50" width="50" class="img-circle img-responsive"> - diff --git a/portal/inc_portal.php b/client/includes/inc_all.php similarity index 72% rename from portal/inc_portal.php rename to client/includes/inc_all.php index acee6648..d0ea5d15 100644 --- a/portal/inc_portal.php +++ b/client/includes/inc_all.php @@ -5,13 +5,8 @@ */ require_once '../config.php'; - require_once '../get_settings.php'; - require_once '../functions.php'; - require_once 'check_login.php'; - -require_once 'portal_functions.php'; - -require_once "portal_header.php"; +require_once 'functions.php'; +require_once "header.php"; diff --git a/portal/index.php b/client/index.php similarity index 54% rename from portal/index.php rename to client/index.php index c76f1899..856a36bf 100644 --- a/portal/index.php +++ b/client/index.php @@ -4,14 +4,13 @@ * Landing / Home page for the client portal */ -header("Content-Security-Policy: default-src 'self' fonts.googleapis.com fonts.gstatic.com"); - -require_once "inc_portal.php"; +header("Content-Security-Policy: default-src 'self'"); +require_once "includes/inc_all.php"; ?> - + diff --git a/portal/invoices.php b/client/invoices.php similarity index 90% rename from portal/invoices.php rename to client/invoices.php index a6ed0bce..654a4bd3 100644 --- a/portal/invoices.php +++ b/client/invoices.php @@ -4,13 +4,13 @@ * Invoices for PTC */ -header("Content-Security-Policy: default-src 'self' fonts.googleapis.com fonts.gstatic.com"); +header("Content-Security-Policy: default-src 'self'"); -require_once "inc_portal.php"; +require_once "includes/inc_all.php"; if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) { - header("Location: portal_post.php?logout"); + header("Location: post.php?logout"); exit(); } @@ -76,7 +76,7 @@ $invoices_sql = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_clie ?> - "> + "> @@ -99,5 +99,5 @@ $invoices_sql = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_clie - + - - @@ -217,7 +215,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['login'])) { - + diff --git a/portal/login_microsoft.php b/client/login_microsoft.php similarity index 98% rename from portal/login_microsoft.php rename to client/login_microsoft.php index 54306c85..80e77a23 100644 --- a/portal/login_microsoft.php +++ b/client/login_microsoft.php @@ -29,7 +29,7 @@ $settings = mysqli_fetch_array($sql_settings); $client_id = $settings['config_azure_client_id']; $client_secret = $settings['config_azure_client_secret']; -$redirect_uri = "https://$config_base_url/portal/login_microsoft.php"; +$redirect_uri = "https://$config_base_url/client/login_microsoft.php"; # https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow $auth_code_url = "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize"; diff --git a/portal/login_reset.php b/client/login_reset.php similarity index 95% rename from portal/login_reset.php rename to client/login_reset.php index 428a7c29..43f17c7b 100644 --- a/portal/login_reset.php +++ b/client/login_reset.php @@ -4,7 +4,7 @@ * Password reset page */ -header("Content-Security-Policy: default-src 'self' fonts.googleapis.com fonts.gstatic.com"); +header("Content-Security-Policy: default-src 'self'"); require_once '../config.php'; require_once '../functions.php'; @@ -73,7 +73,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") { $client = intval($row['contact_client_id']); $token = randomString(156); - $url = "https://$config_base_url/portal/login_reset.php?email=$email&token=$token&client=$client"; + $url = "https://$config_base_url/client/login_reset.php?email=$email&token=$token&client=$client"; mysqli_query($mysqli, "UPDATE users SET user_password_reset_token = '$token' WHERE user_id = $user_id LIMIT 1"); mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Modify', log_description = 'Sent a portal password reset e-mail for $email.', log_ip = '$ip', log_user_agent = '$user_agent', log_client_id = $client"); @@ -91,7 +91,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") { 'body' => $body ] ]; - $mail = addToMailQueue($mysqli, $data); + $mail = addToMailQueue($data); // Error handling if ($mail !== true) { @@ -147,7 +147,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") { ] ]; - $mail = addToMailQueue($mysqli, $data); + $mail = addToMailQueue($data); // Error handling if ($mail !== true) { @@ -193,10 +193,8 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") { - + - - @@ -296,7 +294,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") { - + diff --git a/portal/portal_post.php b/client/post.php similarity index 60% rename from portal/portal_post.php rename to client/post.php index 37855e0d..4dff9ee9 100644 --- a/portal/portal_post.php +++ b/client/post.php @@ -4,13 +4,17 @@ * Process GET/POST requests */ -require_once "inc_portal.php"; - +require_once '../config.php'; +require_once '../get_settings.php'; +require_once '../functions.php'; +require_once 'check_login.php'; +require_once 'functions.php'; if (isset($_POST['add_ticket'])) { $subject = sanitizeInput($_POST['subject']); $details = mysqli_real_escape_string($mysqli, ($_POST['details'])); + $category = intval($_POST['category']); // Get settings from get_settings.php $config_ticket_prefix = sanitizeInput($config_ticket_prefix); @@ -34,7 +38,7 @@ if (isset($_POST['add_ticket'])) { $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, "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_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_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"); $ticket_id = mysqli_insert_id($mysqli); // Notify agent DL of the new ticket, if populated with a valid email @@ -57,7 +61,7 @@ if (isset($_POST['add_ticket'])) { 'body' => $email_body, ] ]; - addToMailQueue($mysqli, $data); + addToMailQueue($data); } // Custom action/notif handler @@ -122,7 +126,7 @@ if (isset($_POST['add_ticket_comment'])) { ] ]; - addToMailQueue($mysqli, $data); + addToMailQueue($data); } @@ -171,7 +175,7 @@ if (isset($_POST['add_ticket_comment'])) { } else { // The client does not have access to this ticket - header("Location: portal_post.php?logout"); + header("Location: post.php?logout"); exit(); } } @@ -200,7 +204,7 @@ if (isset($_POST['add_ticket_feedback'])) { header("Location: " . $_SERVER["HTTP_REFERER"]); } else { // The client does not have access to this ticket - header("Location: portal_post.php?logout"); + header("Location: post.php?logout"); exit(); } @@ -327,6 +331,12 @@ if (isset($_POST['edit_profile'])) { } if (isset($_POST['add_contact'])) { + + if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) { + header("Location: post.php?logout"); + exit(); + } + $contact_name = sanitizeInput($_POST['contact_name']); $contact_email = sanitizeInput($_POST['contact_email']); $contact_technical = intval($_POST['contact_technical']); @@ -368,6 +378,12 @@ if (isset($_POST['add_contact'])) { } if (isset($_POST['edit_contact'])) { + + if ($session_contact_primary == 0 && !$session_contact_is_technical_contact) { + header("Location: post.php?logout"); + exit(); + } + $contact_id = intval($_POST['contact_id']); $contact_name = sanitizeInput($_POST['contact_name']); $contact_email = sanitizeInput($_POST['contact_email']); @@ -413,3 +429,258 @@ if (isset($_POST['edit_contact'])) { customAction('contact_update', $contact_id); } + +if (isset($_POST['create_stripe_customer'])) { + + if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) { + header("Location: post.php?logout"); + exit(); + } + + // Get Stripe vars + $stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1")); + $config_stripe_enable = intval($stripe_vars['config_stripe_enable']); + $config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']); + + if (!$config_stripe_enable) { + header("Location: autopay.php"); + exit(); + } + + // Include stripe SDK + require_once '../plugins/stripe-php/init.php'; + + // Get client's StripeID from database (should be none) + $stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT stripe_id FROM client_stripe WHERE client_id = $session_client_id LIMIT 1")); + if (!$stripe_client_details) { + + try { + // Initiate Stripe + $stripe = new \Stripe\StripeClient($config_stripe_secret); + + // Create customer + $customer = $stripe->customers->create([ + 'name' => $session_client_name, + 'email' => $session_contact_email, + 'metadata' => [ + 'itflow_client_id' => $session_client_id, + 'consent' => $session_contact_name + ] + ]); + + } catch (Exception $e) { + $error = $e->getMessage(); + error_log("Stripe payment error - encountered exception when creating customer record for $session_client_name: $error"); + logApp("Stripe", "error", "Exception creating customer $session_client_name: $error"); + } + + // Get & Store customer ID + $stripe_id = sanitizeInput($customer->id); + + mysqli_query($mysqli, "INSERT INTO client_stripe SET client_id = $session_client_id, stripe_id = '$stripe_id'"); + + // Logging + logAction("Stripe", "Create", "$session_contact_name created Stripe customer for $session_client_name as $stripe_id and authorised future automatic payments", $session_client_id, $session_client_id); + + $_SESSION['alert_message'] = "Stripe customer created, thank you for your consent"; + + } else { + $_SESSION['alert_type'] = "danger"; + $_SESSION['alert_message'] = "Stripe customer already exists"; + } + + header('Location: autopay.php'); +} + +if (isset($_GET['create_stripe_checkout'])) { + + // This page is called by the autopay_setup_stripe.js, it returns a checkout session client secret + + if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) { + header("Location: post.php?logout"); + exit(); + } + + // Get Stripe vars + $stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1")); + $config_stripe_enable = intval($stripe_vars['config_stripe_enable']); + $config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']); + + if (!$config_stripe_enable) { + header("Location: autopay.php"); + exit(); + } + + // Client Currency + $client_currency_details = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT client_currency_code FROM clients WHERE client_id = $session_client_id LIMIT 1")); + $client_currency = $client_currency_details['client_currency_code']; + + // Define return URL that user is redirected to once payment method is verified by Stripe + $return_url = "https://$config_base_url/client/post.php?stripe_save_card&session_id={CHECKOUT_SESSION_ID}"; + + try { + // Initialize stripe + require_once '../plugins/stripe-php/init.php'; + $stripe = new \Stripe\StripeClient($config_stripe_secret); + + // Create checkout session (server side) + $checkout_session = $stripe->checkout->sessions->create([ + 'currency' => $client_currency, + 'mode' => 'setup', + 'ui_mode' => 'embedded', + 'return_url' => $return_url, + ]); + } catch (Exception $e) { + $error = $e->getMessage(); + error_log("Stripe payment error - encountered exception when creating checkout session: $error"); + logApp("Stripe", "error", "Exception creating checkout: $error"); + } + + // Return the client secret to the js script + echo json_encode(array('clientSecret' => $checkout_session->client_secret)); + + // No redirect & no point logging this +} + +if (isset($_GET['stripe_save_card'])) { + + if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) { + header("Location: post.php?logout"); + exit(); + } + + // Get Stripe vars + $stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1")); + $config_stripe_enable = intval($stripe_vars['config_stripe_enable']); + $config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']); + + if (!$config_stripe_enable) { + header("Location: autopay.php"); + exit(); + } + + // Get session ID from URL + $checkout_session_id = sanitizeInput($_GET['session_id']); + + // Get client's StripeID from database + $stripe_client_details = mysqli_fetch_array(mysqli_query($mysqli, "SELECT stripe_id FROM client_stripe WHERE client_id = $session_client_id LIMIT 1")); + $client_stripe_id = sanitizeInput($stripe_client_details['stripe_id']); + + try { + // Initialize stripe + require_once '../plugins/stripe-php/init.php'; + $stripe = new \Stripe\StripeClient($config_stripe_secret); + + // Retrieve checkout session + $checkout_session = $stripe->checkout->sessions->retrieve($checkout_session_id,[]); + + // Get setup intent + $setup_intent_id = $checkout_session->setup_intent; + + // Retrieve the setup intent details + $setup_intent = $stripe->setupIntents->retrieve($setup_intent_id, []); + + // Get the payment method token + $payment_method = sanitizeInput($setup_intent->payment_method); + + // Attach the payment method to the client in Stripe + $stripe->paymentMethods->attach($payment_method, ['customer' => $client_stripe_id]); + + } catch (Exception $e) { + $error = $e->getMessage(); + error_log("Stripe payment error - encountered exception when adding payment method info: $error"); + logApp("Stripe", "error", "Exception adding payment method: $error"); + } + + // Update ITFlow + mysqli_query($mysqli, "UPDATE client_stripe SET stripe_pm = '$payment_method' WHERE client_id = $session_client_id LIMIT 1"); + + // Get some card/payment method details for the email/logging + $payment_method_details = $stripe->paymentMethods->retrieve($payment_method); + $card_info = sanitizeInput($payment_method_details->card->display_brand) . " " . sanitizeInput($payment_method_details->card->last4); + + // Send email confirmation + + // Company Details & Settings + $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); + $company_name = sanitizeInput($row['company_name']); + $config_smtp_host = $row['config_smtp_host']; + $config_smtp_port = intval($row['config_smtp_port']); + $config_smtp_encryption = $row['config_smtp_encryption']; + $config_smtp_username = $row['config_smtp_username']; + $config_smtp_password = $row['config_smtp_password']; + $config_invoice_from_name = sanitizeInput($row['config_invoice_from_name']); + $config_invoice_from_email = sanitizeInput($row['config_invoice_from_email']); + $config_base_url = sanitizeInput($config_base_url); + + if (!empty($config_smtp_host)) { + $subject = "Payment method saved"; + $body = "Hello $session_contact_name,

We’re writing to confirm that your payment details have been securely stored with Stripe, our trusted payment processor.

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 you’ve provided are securely stored with Stripe and will be used solely for invoices. We do not have access to your full card details.

You may update or remove your payment information at any time using the portal.

Thank you for your business!

--
$company_name - Billing Department
$config_invoice_from_email
$company_phone"; + + $data = [ + [ + 'from' => $config_invoice_from_email, + 'from_name' => $config_invoice_from_name, + 'recipient' => $session_contact_email, + 'recipient_name' => $session_contact_name, + 'subject' => $subject, + 'body' => $body, + ] + ]; + + $mail = addToMailQueue($data); + + } + + // 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); + + // Redirect + $_SESSION['alert_message'] = "Payment method saved - thank you"; + header('Location: autopay.php'); + +} + +if (isset($_GET['stripe_remove_pm'])) { + + if ($session_contact_primary == 0 && !$session_contact_is_billing_contact) { + header("Location: post.php?logout"); + exit(); + } + + // Get Stripe vars + $stripe_vars = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_stripe_enable, config_stripe_publishable, config_stripe_secret FROM settings WHERE company_id = 1")); + $config_stripe_enable = intval($stripe_vars['config_stripe_enable']); + $config_stripe_secret = nullable_htmlentities($stripe_vars['config_stripe_secret']); + + if (!$config_stripe_enable) { + header("Location: autopay.php"); + exit(); + } + + $payment_method = sanitizeInput($_GET['pm']); + + try { + // Initialize stripe + require_once '../plugins/stripe-php/init.php'; + $stripe = new \Stripe\StripeClient($config_stripe_secret); + + // Detach PM + $stripe->paymentMethods->detach($payment_method, []); + + } catch (Exception $e) { + $error = $e->getMessage(); + error_log("Stripe payment error - encountered exception when removing payment method info for $payment_method: $error"); + logApp("Stripe", "error", "Exception removing payment method for $payment_method: $error"); + } + + // Remove payment method from ITFlow + mysqli_query($mysqli, "UPDATE client_stripe SET stripe_pm = NULL WHERE client_id = $session_client_id LIMIT 1"); + + // Logging & Redirect + logAction("Stripe", "Update", "$session_contact_name deleted saved Stripe payment method (PM: $payment_method)", $session_client_id, $session_client_id); + + $_SESSION['alert_message'] = "Payment method removed"; + header('Location: autopay.php'); +} \ No newline at end of file diff --git a/portal/profile.php b/client/profile.php similarity index 88% rename from portal/profile.php rename to client/profile.php index e77afeaa..6209fed5 100644 --- a/portal/profile.php +++ b/client/profile.php @@ -4,9 +4,9 @@ * User profile */ -header("Content-Security-Policy: default-src 'self' fonts.googleapis.com fonts.gstatic.com"); +header("Content-Security-Policy: default-src 'self'"); -require_once 'inc_portal.php'; +require_once 'includes/inc_all.php'; ?> @@ -30,7 +30,7 @@ require_once 'inc_portal.php';

Password

- +
@@ -46,5 +46,5 @@ require_once 'inc_portal.php'; - "> + "> @@ -88,4 +88,4 @@ $quotes_sql = mysqli_query($mysqli, "SELECT * FROM quotes WHERE quote_client_id
set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one $purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]); $purifier = new HTMLPurifier($purifier_config); @@ -18,25 +19,21 @@ $allowed_extensions = array('jpg', 'jpeg', 'gif', 'png', 'webp', 'pdf', 'txt', ' if (isset($_GET['id']) && intval($_GET['id'])) { $ticket_id = intval($_GET['id']); + $ticket_contact_snippet = "AND ticket_contact_id = $session_contact_id"; + // Bypass ticket contact being session_id for a primary / technical contact viewing all tickets if ($session_contact_primary == 1 || $session_contact_is_technical_contact) { - // For a primary / technical contact viewing all tickets - $ticket_sql = mysqli_query($mysqli, - "SELECT * FROM tickets - LEFT JOIN users on ticket_assigned_to = user_id - LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id - WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id" - ); - - } else { - // For a user viewing their own ticket - $ticket_sql = mysqli_query($mysqli, - "SELECT * FROM tickets - LEFT JOIN users on ticket_assigned_to = user_id - LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id - WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id AND ticket_contact_id = $session_contact_id" - ); + $ticket_contact_snippet = ''; } + $ticket_sql = mysqli_query($mysqli, + "SELECT * FROM tickets + LEFT JOIN users on ticket_assigned_to = user_id + LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id + LEFT JOIN categories ON ticket_category = category_id + WHERE ticket_id = $ticket_id AND ticket_client_id = $session_client_id + $ticket_contact_snippet" + ); + $ticket_row = mysqli_fetch_array($ticket_sql); if ($ticket_row) { @@ -51,7 +48,15 @@ if (isset($_GET['id']) && intval($_GET['id'])) { $ticket_resolved_at = nullable_htmlentities($ticket_row['ticket_resolved_at']); $ticket_closed_at = nullable_htmlentities($ticket_row['ticket_closed_at']); $ticket_feedback = nullable_htmlentities($ticket_row['ticket_feedback']); + $ticket_category = nullable_htmlentities($ticket_row['category_name']); + // Get Ticket Attachments (not associated with a specific reply) + $sql_ticket_attachments = mysqli_query( + $mysqli, + "SELECT * FROM ticket_attachments + WHERE ticket_attachment_reply_id IS NULL + AND ticket_attachment_ticket_id = $ticket_id" + ); // Get Tasks $sql_tasks = mysqli_query( $mysqli, "SELECT * FROM tasks WHERE task_ticket_id = $ticket_id ORDER BY task_order ASC, task_id ASC"); @@ -83,29 +88,43 @@ if (isset($_GET['id']) && intval($_GET['id'])) { Ticket - Resolve ticket + Resolve ticket
Subject:
-

- State: -
- Priority: -
+ State:
+ Priority:
+ + Category:
+ + Tasks:
- Assigned to: + + + Assigned to: + +

+
+ + $name | View"; + } + ?>
@@ -116,7 +135,7 @@ if (isset($_GET['id']) && intval($_GET['id'])) { - +
@@ -135,11 +154,11 @@ if (isset($_GET['id']) && intval($_GET['id'])) { @@ -149,7 +168,7 @@ if (isset($_GET['id']) && intval($_GET['id'])) {

Ticket closed. Please rate your ticket

- + + + +
+ +
- diff --git a/client_asset_copy_modal.php b/client_asset_copy_modal.php deleted file mode 100644 index 109b1e0b..00000000 --- a/client_asset_copy_modal.php +++ /dev/null @@ -1,393 +0,0 @@ - diff --git a/client_asset_edit_modal.php b/client_asset_edit_modal.php deleted file mode 100644 index 2083d5a9..00000000 --- a/client_asset_edit_modal.php +++ /dev/null @@ -1,429 +0,0 @@ - diff --git a/client_asset_interface_edit_modal.php b/client_asset_interface_edit_modal.php deleted file mode 100644 index 62aef022..00000000 --- a/client_asset_interface_edit_modal.php +++ /dev/null @@ -1,102 +0,0 @@ - diff --git a/client_asset_transfer_modal.php b/client_asset_transfer_modal.php deleted file mode 100644 index 76ecb451..00000000 --- a/client_asset_transfer_modal.php +++ /dev/null @@ -1,48 +0,0 @@ - diff --git a/client_assets.php b/client_assets.php deleted file mode 100644 index ad713306..00000000 --- a/client_assets.php +++ /dev/null @@ -1,648 +0,0 @@ - 0) { - while ($row = mysqli_fetch_array($os_sql)) { - $os_arr[] = $row; - } - $json_os = json_encode($os_arr); -} - -?> - -
-
- Workstations - - Servers - - Virtual - - Network - - Other -
-
- -
-
-

Assets

-
- = 2) { ?> -
- - - -
- -
-
-
-
- - - -
-
-
- s"> -
- -
-
-
-
-
- -
-
-
-
- -
-
-
-
- "> - Archived - - -
-
- -
-
-
-
- - -
- - "> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $asset_physical_location"; - } else { - $asset_physical_location_display = ""; - } - $asset_notes = nullable_htmlentities($row['asset_notes']); - $asset_created_at = nullable_htmlentities($row['asset_created_at']); - $asset_archived_at = nullable_htmlentities($row['asset_archived_at']); - $asset_vendor_id = intval($row['asset_vendor_id']); - $asset_location_id = intval($row['asset_location_id']); - $asset_contact_id = intval($row['asset_contact_id']); - $asset_network_id = intval($row['interface_network_id']); - - $device_icon = getAssetIcon($asset_type); - - $contact_name = nullable_htmlentities($row['contact_name']); - if (empty($contact_name)) { - $contact_name = "-"; - } - $contact_archived_at = nullable_htmlentities($row['contact_archived_at']); - if ($contact_archived_at) { - $contact_name_display = "
$contact_name
"; - } else { - $contact_name_display = $contact_name; - } - - $location_name = nullable_htmlentities($row['location_name']); - if (empty($location_name)) { - $location_name = "-"; - } - $location_archived_at = nullable_htmlentities($row['location_archived_at']); - if ($location_archived_at) { - $location_name_display = "
$location_name
"; - } else { - $location_name_display = $location_name; - } - - $sql_logins = mysqli_query($mysqli, "SELECT * FROM logins WHERE login_asset_id = $asset_id"); - $login_count = mysqli_num_rows($sql_logins); - - ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - Name - - - - Type - - - - Model - - - - Serial - - - - OS - - - - IP - - - - Purchase Date - - - - Install Date - - - - Warranty Expire - - - - Assigned To - - - - Location - - - - Status - - Action
-
- -
-
- -
- -
-
-
-
-
-
-
- -
- -
-
- - - -
- 0) { ?> - - - - - - - - - -
-
-
- - - - -
- -
-
- - - - - - - - - diff --git a/client_certificate_edit_modal.php b/client_certificate_edit_modal.php deleted file mode 100644 index 737ed6f9..00000000 --- a/client_certificate_edit_modal.php +++ /dev/null @@ -1,132 +0,0 @@ - diff --git a/client_contact_bulk_email_modal.php b/client_contact_bulk_email_modal.php deleted file mode 100644 index df04478b..00000000 --- a/client_contact_bulk_email_modal.php +++ /dev/null @@ -1,57 +0,0 @@ - \ No newline at end of file diff --git a/client_contact_edit_modal.php b/client_contact_edit_modal.php deleted file mode 100644 index ff78a295..00000000 --- a/client_contact_edit_modal.php +++ /dev/null @@ -1,288 +0,0 @@ - \ No newline at end of file diff --git a/client_document_details.php b/client_document_details.php index 89b96128..ce3d29ba 100644 --- a/client_document_details.php +++ b/client_document_details.php @@ -1,12 +1,13 @@ set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one $purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]); $purifier = new HTMLPurifier($purifier_config); @@ -37,6 +38,9 @@ $document_folder_id = intval($row['document_folder_id']); $document_parent = intval($row['document_parent']); $document_client_visible = intval($row['document_client_visible']); +// Override Tab Title // No Sanitizing needed as this var will opnly be used in the tab title +$page_title = $row['document_name']; + ?>
diff --git a/client_document_move_modal.php b/client_document_move_modal.php deleted file mode 100644 index 9586c6fe..00000000 --- a/client_document_move_modal.php +++ /dev/null @@ -1,95 +0,0 @@ - diff --git a/client_document_rename_modal.php b/client_document_rename_modal.php deleted file mode 100644 index fbfb1e76..00000000 --- a/client_document_rename_modal.php +++ /dev/null @@ -1,33 +0,0 @@ - diff --git a/client_document_view_modal.php b/client_document_view_modal.php deleted file mode 100644 index 758afffc..00000000 --- a/client_document_view_modal.php +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/client_documents.php b/client_documents.php index a017d4ab..c9220134 100644 --- a/client_documents.php +++ b/client_documents.php @@ -4,7 +4,7 @@ $sort = "document_name"; $order = "ASC"; -require_once "inc_all_client.php"; +require_once "includes/inc_all_client.php"; // Perms enforceUserPermission('module_support'); @@ -87,395 +87,410 @@ while ($folder_id > 0) { ?> +
+
+

+ Documents +

+
- -
-
-

- Documents -

-
- -
- - - +
+ + + -
- - -
-
- - -
-
-
- "> -
- -
+
+
+ + + +
+
+
+ "> +
+ +
+ +
+
+
+
+
-
-
- +
+ + +
+ + +
+ - - -
- - -
-
-

Folders

-
-
'; + + if ($subfolder_count > 0) { + // Display subfolders + echo ''; + } + + echo ''; + } + } + + // Start displaying folders from the root (parent_folder = 0) + display_folders(0, $client_id); + ?> + + +
+ +
+ +
+ + +
+
- -
diff --git a/client_edit_modal.php b/client_edit_modal.php deleted file mode 100644 index eedaa4ea..00000000 --- a/client_edit_modal.php +++ /dev/null @@ -1,222 +0,0 @@ - \ No newline at end of file diff --git a/client_events.php b/client_events.php deleted file mode 100644 index 61031c59..00000000 --- a/client_events.php +++ /dev/null @@ -1,282 +0,0 @@ - - - - - -
-
-
- - - - - - - - - - -" tabindex="-1"> - -
diff --git a/client_file_rename_modal.php b/client_file_rename_modal.php deleted file mode 100644 index 8f50485f..00000000 --- a/client_file_rename_modal.php +++ /dev/null @@ -1,42 +0,0 @@ - diff --git a/client_files.php b/client_files.php index 5cbe6d15..3898c829 100644 --- a/client_files.php +++ b/client_files.php @@ -4,7 +4,7 @@ $sort = "file_name"; $order = "ASC"; -require_once "inc_all_client.php"; +require_once "includes/inc_all_client.php"; // Folder @@ -195,7 +195,11 @@ while ($folder_id > 0) { '; echo '
'; - // Include the rename and create subfolder modals - require "folder_rename_modal.php"; - if ($subfolder_count > 0) { // Display subfolders echo ' - +
@@ -360,10 +361,18 @@ while ($folder_id > 0) { )"> Share - + Rename - + Move @@ -386,7 +395,7 @@ while ($folder_id > 0) {
@@ -559,10 +568,18 @@ while ($folder_id > 0) { )"> Share - + Rename - + Move @@ -583,11 +600,7 @@ while ($folder_id > 0) { @@ -595,12 +608,12 @@ while ($folder_id > 0) {
- + -
@@ -634,10 +647,7 @@ function prevFile() { - -
diff --git a/client_invoices.php b/client_invoices.php deleted file mode 100644 index 7685317e..00000000 --- a/client_invoices.php +++ /dev/null @@ -1,228 +0,0 @@ - - -
- -
-
- -
- -
-
- -
- -
-
-
- -
-
-
- 0) { ?> - - -
-
-
- -
-
-
-
- - "> - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Number - - - - Scope - - - - Amount - - - - Date - - - - Due - - - - Category - - - - Status - - Action
-
-
- - - - - -
-
- -
-
- -" tabindex="-1"> - -
diff --git a/client_login_edit_modal.php b/client_login_edit_modal.php deleted file mode 100644 index 947bd505..00000000 --- a/client_login_edit_modal.php +++ /dev/null @@ -1,269 +0,0 @@ - diff --git a/client_network_add_modal.php b/client_network_add_modal.php deleted file mode 100644 index 5f35b82f..00000000 --- a/client_network_add_modal.php +++ /dev/null @@ -1,172 +0,0 @@ - diff --git a/client_networks.php b/client_networks.php deleted file mode 100644 index 1257160b..00000000 --- a/client_networks.php +++ /dev/null @@ -1,247 +0,0 @@ - $sort, 'order' => $order))); - -$sql = mysqli_query( - $mysqli, - "SELECT SQL_CALC_FOUND_ROWS * FROM networks - LEFT JOIN locations ON location_id = network_location_id - WHERE network_client_id = $client_id - AND network_archived_at IS NULL - AND (network_name LIKE '%$q%' OR network_description LIKE '%$q%' OR network_vlan LIKE '%$q%' OR network LIKE '%$q%' OR network_gateway LIKE '%$q%' OR network_subnet LIKE '%$q%' OR network_primary_dns LIKE '%$q%' OR network_secondary_dns LIKE '%$q%' OR network_dhcp_range LIKE '%$q%' OR location_name LIKE '%$q%') - ORDER BY $sort $order LIMIT $record_from, $record_to" -); - -$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - -?> - -
-
-

Networks

-
-
- - 0) { ?> - - - -
- -
-
-
-
- -
- -
-
- -
- -
-
-
- -
-
- -
-
- -
-
-
-
- -
- - - - "> - - - - - - - - - - - - - - $network_secondary_dns"; - } else { - $network_dns_display = "-"; - } - $network_dhcp_range = nullable_htmlentities($row['network_dhcp_range']); - if (empty($network_dhcp_range)) { - $network_dhcp_range_display = "-"; - } else { - $network_dhcp_range_display = $network_dhcp_range; - } - $network_location_id = intval($row['network_location_id']); - $location_name = nullable_htmlentities($row['location_name']); - if (empty($location_name)) { - $location_name_display = "-"; - } else { - $location_name_display = $location_name; - } - - ?> - - - - - - - - - - - - - - - -
-
- -
-
- - Name - - - - vLAN - - - - IP / Network - - - - Gateway - - - - DNS - - - - DHCP Range - - - - Location - - Action
-
- - -
-
- )" data-target="#editNetworkModal"> -
- -
-
-
-
-
-
-
- -
-
- -
- -
-
- -
-
- - - - - - - NOW() ORDER BY item_created_at ASC LIMIT 5" ); @@ -241,7 +239,13 @@ $sql_asset_retired = mysqli_query( ?> - + + +
@@ -410,7 +414,7 @@ $sql_asset_retired = mysqli_query( ?>

- + --

@@ -430,7 +434,7 @@ $sql_asset_retired = mysqli_query( ?>

- + --

@@ -528,7 +532,7 @@ $sql_asset_retired = mysqli_query( ?>

- + --

@@ -548,7 +552,7 @@ $sql_asset_retired = mysqli_query( ?>

- + --

@@ -691,5 +695,5 @@ $sql_asset_retired = mysqli_query( - -
-
-

Payments

- 0) { ?> -
- -
- -
-
-
- -
- -
-
- -
- -
-
-
- -
-
-
-
- -
-
-
-
- - "> - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Payment Date - - - - Invoice Date - - - - Invoice - - - - Invoice Amount - - - - Payment Amount - - - - Method - - - - Reference - - - - Account - -
-
- -
-
- - - -
-
-

Quotes

-
-
- - 0) { ?> - - - -
-
-
-
-
- -
- -
-
- -
- -
-
-
- -
-
-
-
- -
-
-
-
- - "> - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Number - - - - Scope - - - - Amount - - - - Date - - - - Expire - - - - Category - - - - Status - - Action
- - - - - -
-
- -
-
- -" tabindex="-1"> - -
diff --git a/client_rack_unit_add_modal.php b/client_rack_unit_add_modal.php deleted file mode 100644 index c01225ad..00000000 --- a/client_rack_unit_add_modal.php +++ /dev/null @@ -1,78 +0,0 @@ - diff --git a/client_racks.php b/client_racks.php index f81407db..7e36c79a 100644 --- a/client_racks.php +++ b/client_racks.php @@ -4,7 +4,7 @@ $sort = "rack_name"; $order = "ASC"; -require_once "inc_all_client.php"; +require_once "includes/inc_all_client.php"; // Perms enforceUserPermission('module_support'); @@ -101,11 +101,19 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- - +
@@ -241,6 +352,5 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); +require_once "modals/client_rack_add_modal.php"; +require_once "includes/footer.php"; diff --git a/client_recurring_invoices.php b/client_recurring_invoices.php deleted file mode 100644 index 9395cb3d..00000000 --- a/client_recurring_invoices.php +++ /dev/null @@ -1,200 +0,0 @@ - - -
-
-

Recurring Invoices

-
-
- - 0) { ?> - - - -
-
-
-
-
- -
- -
-
- -
- -
-
-
- -
-
-
-
-
-
- -
-
-
-
- - "> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Number - - - - Scope - - - - Amount - - - - Frequency - - - - Last Sent - - - - Next Date - - - - Category - - - - Status - - Action
ly - - - - - -
-
- -
-
- - - -
-
-

Recurring Tickets

-
- -
-
- -
- -
- -
- -
-
- -
- -
-
-
- -
-
- -
-
- -
-
-
- -
- -
- - - - "> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - Subject - - - - Priority - - - - Frequency - - - - Next Run Date - - Action
-
- -
-
- -
- -
- -
- - - -
-
- - - - - -
diff --git a/client_service_edit_modal.php b/client_service_edit_modal.php deleted file mode 100644 index c2fa6bd6..00000000 --- a/client_service_edit_modal.php +++ /dev/null @@ -1,275 +0,0 @@ - diff --git a/client_service_view_modal.php b/client_service_view_modal.php deleted file mode 100644 index 4d8cb8e2..00000000 --- a/client_service_view_modal.php +++ /dev/null @@ -1,294 +0,0 @@ - diff --git a/client_trips.php b/client_trips.php deleted file mode 100644 index 6ff02c3f..00000000 --- a/client_trips.php +++ /dev/null @@ -1,186 +0,0 @@ - - -
-
-

Trips

-
-
- - 0) { ?> - - - -
- -
-
-
-
- -
- -
-
- -
- -
-
-
- -
-
-
-
- -
-
-
-
- - "> - - - - - - - - - - - - "; - } else { - $round_trip_display = ""; - } - $user_name = nullable_htmlentities($row['user_name']); - if (empty($user_name)) { - $user_name_display = "-"; - } else { - $user_name_display = $user_name; - } - - ?> - - - - - - - - - - - - - -
- - Date - - - - Driver - - - - Purpose - - - - From - - - - To - - - - Miles - - Action
- -
-
- -
-
- - - -
-
-

- Vendors -

-
-
- - - -
-
-
-
-
- - -
- -
-
- -
- -
-
-
- -
-
- "> - Archived - - -
-
- -
-
-
-
- - -
- - "> - - - - - - - - - - - Account #: $vendor_account_number"; - } else { - $vendor_account_number_display = ''; - } - $vendor_contact_name = nullable_htmlentities($row['vendor_contact_name']); - if ($vendor_contact_name) { - $vendor_contact_name_display = $vendor_contact_name; - } else { - $vendor_contact_name_display = "-"; - } - $vendor_phone = formatPhoneNumber($row['vendor_phone']); - $vendor_extension = nullable_htmlentities($row['vendor_extension']); - $vendor_email = nullable_htmlentities($row['vendor_email']); - $vendor_website = nullable_htmlentities($row['vendor_website']); - if ($vendor_website) { - $vendor_website_display = "$vendor_website "; - } else { - $vendor_website_display = "-"; - } - $vendor_hours = nullable_htmlentities($row['vendor_hours']); - $vendor_sla = nullable_htmlentities($row['vendor_sla']); - $vendor_code = nullable_htmlentities($row['vendor_code']); - $vendor_notes = nullable_htmlentities($row['vendor_notes']); - $vendor_created_at = nullable_htmlentities($row['vendor_created_at']); - $vendor_archived_at = nullable_htmlentities($row['vendor_archived_at']); - $vendor_template_id = intval($row['vendor_template_id']); - - - ?> - - - - - - - - - - - -
-
- -
-
- - Vendor - - - - Description - - - - Contact - - - - Website - - Action
-
- -
-
- -
- -
-
- -
-
-
-
- - - - -
- - -
- - -
- -
- -
-
-
- -
-
- - - -
-

Management

+

= 2) { ?>
- -
- +
+
-
- - -
- -
- -
- -
diff --git a/admin_ticket_template_edit_modal.php b/modals/admin_ticket_template_edit_modal.php similarity index 72% rename from admin_ticket_template_edit_modal.php rename to modals/admin_ticket_template_edit_modal.php index 1555057b..76678dc1 100644 --- a/admin_ticket_template_edit_modal.php +++ b/modals/admin_ticket_template_edit_modal.php @@ -18,7 +18,7 @@
- +
@@ -28,25 +28,14 @@
- +
-
- +
-
- - -
- -
- -
- -
diff --git a/admin_user_add_modal.php b/modals/admin_user_add_modal.php similarity index 97% rename from admin_user_add_modal.php rename to modals/admin_user_add_modal.php index 9d54bb0b..954b5b7d 100644 --- a/admin_user_add_modal.php +++ b/modals/admin_user_add_modal.php @@ -32,7 +32,7 @@
- +
@@ -42,7 +42,7 @@
- +
@@ -52,7 +52,7 @@
- +
diff --git a/admin_user_all_reset_password_modal.php b/modals/admin_user_all_reset_password_modal.php similarity index 96% rename from admin_user_all_reset_password_modal.php rename to modals/admin_user_all_reset_password_modal.php index 746bfaa1..a68dfdc5 100644 --- a/admin_user_all_reset_password_modal.php +++ b/modals/admin_user_all_reset_password_modal.php @@ -15,7 +15,7 @@
- +
diff --git a/admin_user_archive_modal.php b/modals/admin_user_archive_modal.php similarity index 100% rename from admin_user_archive_modal.php rename to modals/admin_user_archive_modal.php diff --git a/admin_user_export_modal.php b/modals/admin_user_export_modal.php similarity index 100% rename from admin_user_export_modal.php rename to modals/admin_user_export_modal.php diff --git a/admin_user_invite_modal.php b/modals/admin_user_invite_modal.php similarity index 89% rename from admin_user_invite_modal.php rename to modals/admin_user_invite_modal.php index 4feb00ba..21c93356 100644 --- a/admin_user_invite_modal.php +++ b/modals/admin_user_invite_modal.php @@ -17,7 +17,7 @@
- +
@@ -29,9 +29,7 @@ diff --git a/admin_vendor_template_add_modal.php b/modals/admin_vendor_template_add_modal.php similarity index 92% rename from admin_vendor_template_add_modal.php rename to modals/admin_vendor_template_add_modal.php index f7b014ad..4e5f5dcb 100644 --- a/admin_vendor_template_add_modal.php +++ b/modals/admin_vendor_template_add_modal.php @@ -37,7 +37,7 @@
- + @@ -47,7 +47,7 @@
- + @@ -57,7 +57,7 @@
- + @@ -67,7 +67,7 @@
- + @@ -83,12 +83,12 @@
- +
- +
@@ -98,7 +98,7 @@
- + @@ -108,7 +108,7 @@
- + @@ -118,7 +118,7 @@
- + @@ -128,7 +128,7 @@
- + @@ -138,7 +138,7 @@
- + diff --git a/client_asset_add_modal.php b/modals/asset_add_modal.php similarity index 85% rename from client_asset_add_modal.php rename to modals/asset_add_modal.php index 83032aa7..128ffb7d 100644 --- a/client_asset_add_modal.php +++ b/modals/asset_add_modal.php @@ -10,7 +10,6 @@
- @@ -68,7 +94,7 @@ + @@ -94,7 +120,7 @@
- + @@ -104,7 +130,7 @@
- + @@ -116,7 +142,7 @@
- + @@ -124,7 +150,7 @@
- +
@@ -132,7 +158,7 @@
+
@@ -180,7 +207,7 @@
- + @@ -190,7 +217,7 @@
- + @@ -200,7 +227,7 @@
- + @@ -210,7 +237,7 @@
- + @@ -224,10 +251,11 @@
- + +
@@ -235,7 +263,7 @@
- +
+
@@ -280,7 +309,7 @@
- + + +
+ +
+
+ +
+ +
+
+
- +
diff --git a/client_asset_bulk_add_ticket_modal.php b/modals/asset_bulk_add_ticket_modal.php similarity index 99% rename from client_asset_bulk_add_ticket_modal.php rename to modals/asset_bulk_add_ticket_modal.php index 1749e902..941b3f6b 100644 --- a/client_asset_bulk_add_ticket_modal.php +++ b/modals/asset_bulk_add_ticket_modal.php @@ -18,7 +18,7 @@
- +
diff --git a/client_asset_bulk_assign_contact_modal.php b/modals/asset_bulk_assign_contact_modal.php similarity index 100% rename from client_asset_bulk_assign_contact_modal.php rename to modals/asset_bulk_assign_contact_modal.php diff --git a/client_asset_bulk_assign_location_modal.php b/modals/asset_bulk_assign_location_modal.php similarity index 100% rename from client_asset_bulk_assign_location_modal.php rename to modals/asset_bulk_assign_location_modal.php diff --git a/client_asset_bulk_edit_status_modal.php b/modals/asset_bulk_edit_status_modal.php similarity index 100% rename from client_asset_bulk_edit_status_modal.php rename to modals/asset_bulk_edit_status_modal.php diff --git a/modals/asset_bulk_transfer_client_modal.php b/modals/asset_bulk_transfer_client_modal.php new file mode 100644 index 00000000..c469d395 --- /dev/null +++ b/modals/asset_bulk_transfer_client_modal.php @@ -0,0 +1,48 @@ + diff --git a/client_asset_export_modal.php b/modals/asset_export_modal.php similarity index 81% rename from client_asset_export_modal.php rename to modals/asset_export_modal.php index be26f8fd..8860ba99 100644 --- a/client_asset_export_modal.php +++ b/modals/asset_export_modal.php @@ -9,13 +9,15 @@ + +
diff --git a/client_asset_import_modal.php b/modals/asset_import_modal.php similarity index 77% rename from client_asset_import_modal.php rename to modals/asset_import_modal.php index cc10bd7b..512958e9 100644 --- a/client_asset_import_modal.php +++ b/modals/asset_import_modal.php @@ -9,7 +9,10 @@
+ + +
-
Download sample csv template
+
Download sample csv template
diff --git a/modals/asset_interface_add_modal.php b/modals/asset_interface_add_modal.php new file mode 100644 index 00000000..a5826367 --- /dev/null +++ b/modals/asset_interface_add_modal.php @@ -0,0 +1,224 @@ + diff --git a/modals/asset_interface_export_modal.php b/modals/asset_interface_export_modal.php new file mode 100644 index 00000000..f8b6f182 --- /dev/null +++ b/modals/asset_interface_export_modal.php @@ -0,0 +1,24 @@ + diff --git a/modals/asset_interface_import_modal.php b/modals/asset_interface_import_modal.php new file mode 100644 index 00000000..3df676e3 --- /dev/null +++ b/modals/asset_interface_import_modal.php @@ -0,0 +1,28 @@ + diff --git a/client_asset_interface_add_modal.php b/modals/asset_interface_multiple_add_modal.php similarity index 53% rename from client_asset_interface_add_modal.php rename to modals/asset_interface_multiple_add_modal.php index b2183a7c..18958b52 100644 --- a/client_asset_interface_add_modal.php +++ b/modals/asset_interface_multiple_add_modal.php @@ -1,8 +1,8 @@ - @@ -81,7 +107,7 @@
 https://
- +
@@ -94,7 +120,7 @@
- + diff --git a/client_certificate_export_modal.php b/modals/certificate_export_modal.php similarity index 79% rename from client_certificate_export_modal.php rename to modals/certificate_export_modal.php index 0c0b0077..632312cb 100644 --- a/client_certificate_export_modal.php +++ b/modals/certificate_export_modal.php @@ -8,12 +8,14 @@ + + diff --git a/client_add_modal.php b/modals/client_add_modal.php similarity index 77% rename from client_add_modal.php rename to modals/client_add_modal.php index 8e58f766..082b7428 100644 --- a/client_add_modal.php +++ b/modals/client_add_modal.php @@ -2,7 +2,7 @@ @@ -76,10 +76,10 @@
- +
+
@@ -108,7 +108,7 @@
- - + @@ -143,7 +143,7 @@
- + @@ -153,7 +153,7 @@
- + @@ -163,7 +163,7 @@
- + @@ -173,7 +173,7 @@
- + @@ -183,7 +183,7 @@
- + @@ -194,7 +194,7 @@ + @@ -222,7 +222,7 @@
- + @@ -234,12 +234,12 @@
- +
- +
@@ -250,7 +250,7 @@
- + @@ -260,7 +260,7 @@
- + @@ -268,58 +268,58 @@ -
+
-
- -
-
- +
+ +
+
+ +
+ ">
- ">
-
-
- -
-
- +
+ +
+
+ +
+
-
-
-
- -
-
- +
+ +
+
+ +
+
-
-
-
- -
-
- +
+ +
+
+ +
+
-
-
-
+
diff --git a/client_asset_documents_modal.php b/modals/client_asset_documents_modal.php similarity index 100% rename from client_asset_documents_modal.php rename to modals/client_asset_documents_modal.php diff --git a/client_delete_modal.php b/modals/client_delete_modal.php similarity index 100% rename from client_delete_modal.php rename to modals/client_delete_modal.php diff --git a/client_document_add_file_relation_modal.php b/modals/client_document_add_file_relation_modal.php similarity index 100% rename from client_document_add_file_relation_modal.php rename to modals/client_document_add_file_relation_modal.php diff --git a/client_document_add_from_template_modal.php b/modals/client_document_add_from_template_modal.php similarity index 100% rename from client_document_add_from_template_modal.php rename to modals/client_document_add_from_template_modal.php diff --git a/client_document_add_modal.php b/modals/client_document_add_modal.php similarity index 98% rename from client_document_add_modal.php rename to modals/client_document_add_modal.php index f493fb11..e4010942 100644 --- a/client_document_add_modal.php +++ b/modals/client_document_add_modal.php @@ -12,7 +12,7 @@ @@ -64,7 +64,7 @@
- +
Up to 20 files can be uploaded at once by holding down CTRL and selecting files diff --git a/client_file_view_modal.php b/modals/client_file_view_modal.php similarity index 100% rename from client_file_view_modal.php rename to modals/client_file_view_modal.php diff --git a/client_import_modal.php b/modals/client_import_modal.php similarity index 100% rename from client_import_modal.php rename to modals/client_import_modal.php diff --git a/client_rack_add_modal.php b/modals/client_rack_add_modal.php similarity index 97% rename from client_rack_add_modal.php rename to modals/client_rack_add_modal.php index 8f66ea33..54225372 100644 --- a/client_rack_add_modal.php +++ b/modals/client_rack_add_modal.php @@ -35,7 +35,7 @@
- +
@@ -70,7 +70,7 @@
- +
@@ -80,7 +80,7 @@
- +
@@ -100,7 +100,7 @@
- +
diff --git a/client_recurring_export_modal.php b/modals/client_recurring_export_modal.php similarity index 100% rename from client_recurring_export_modal.php rename to modals/client_recurring_export_modal.php diff --git a/client_contact_add_modal.php b/modals/contact_add_modal.php similarity index 84% rename from client_contact_add_modal.php rename to modals/contact_add_modal.php index 486447df..4e55037a 100644 --- a/client_contact_add_modal.php +++ b/modals/contact_add_modal.php @@ -8,7 +8,6 @@ -
- +
@@ -90,7 +116,7 @@
- + @@ -100,10 +126,11 @@
- + +
@@ -111,7 +138,7 @@
+
- +
diff --git a/client_contact_archive_modal.php b/modals/contact_archive_modal.php similarity index 100% rename from client_contact_archive_modal.php rename to modals/contact_archive_modal.php diff --git a/client_contact_bulk_assign_location_modal.php b/modals/contact_bulk_assign_location_modal.php similarity index 96% rename from client_contact_bulk_assign_location_modal.php rename to modals/contact_bulk_assign_location_modal.php index 404a545a..7b08ba3b 100644 --- a/client_contact_bulk_assign_location_modal.php +++ b/modals/contact_bulk_assign_location_modal.php @@ -17,7 +17,7 @@
+ diff --git a/client_contact_bulk_edit_phone_modal.php b/modals/contact_bulk_edit_phone_modal.php similarity index 95% rename from client_contact_bulk_edit_phone_modal.php rename to modals/contact_bulk_edit_phone_modal.php index cc27c317..8f1df37c 100644 --- a/client_contact_bulk_edit_phone_modal.php +++ b/modals/contact_bulk_edit_phone_modal.php @@ -16,7 +16,7 @@
- + diff --git a/client_contact_bulk_edit_role_modal.php b/modals/contact_bulk_edit_role_modal.php similarity index 100% rename from client_contact_bulk_edit_role_modal.php rename to modals/contact_bulk_edit_role_modal.php diff --git a/modals/contact_bulk_email_modal.php b/modals/contact_bulk_email_modal.php new file mode 100644 index 00000000..5d9e4e4c --- /dev/null +++ b/modals/contact_bulk_email_modal.php @@ -0,0 +1,61 @@ + \ No newline at end of file diff --git a/client_contact_create_note_modal.php b/modals/contact_create_note_modal.php similarity index 100% rename from client_contact_create_note_modal.php rename to modals/contact_create_note_modal.php diff --git a/client_contact_export_modal.php b/modals/contact_export_modal.php similarity index 80% rename from client_contact_export_modal.php rename to modals/contact_export_modal.php index 87efd742..aff19968 100644 --- a/client_contact_export_modal.php +++ b/modals/contact_export_modal.php @@ -8,12 +8,14 @@ + + diff --git a/client_contact_import_modal.php b/modals/contact_import_modal.php similarity index 81% rename from client_contact_import_modal.php rename to modals/contact_import_modal.php index bc567b60..d126d4f4 100644 --- a/client_contact_import_modal.php +++ b/modals/contact_import_modal.php @@ -8,7 +8,9 @@
+ +
-
Download: sample csv template
+
Download: sample csv template
diff --git a/client_contact_invite_modal.php b/modals/contact_invite_modal.php similarity index 98% rename from client_contact_invite_modal.php rename to modals/contact_invite_modal.php index baa87a27..0ea124c3 100644 --- a/client_contact_invite_modal.php +++ b/modals/contact_invite_modal.php @@ -19,7 +19,7 @@
- + @@ -81,4 +81,4 @@ - + \ No newline at end of file diff --git a/client_contact_link_asset_modal.php b/modals/contact_link_asset_modal.php similarity index 100% rename from client_contact_link_asset_modal.php rename to modals/contact_link_asset_modal.php diff --git a/client_contact_link_credential_modal.php b/modals/contact_link_credential_modal.php similarity index 100% rename from client_contact_link_credential_modal.php rename to modals/contact_link_credential_modal.php diff --git a/client_contact_link_document_modal.php b/modals/contact_link_document_modal.php similarity index 100% rename from client_contact_link_document_modal.php rename to modals/contact_link_document_modal.php diff --git a/client_contact_link_file_modal.php b/modals/contact_link_file_modal.php similarity index 100% rename from client_contact_link_file_modal.php rename to modals/contact_link_file_modal.php diff --git a/client_contact_link_service_modal.php b/modals/contact_link_service_modal.php similarity index 100% rename from client_contact_link_service_modal.php rename to modals/contact_link_service_modal.php diff --git a/client_contact_link_software_modal.php b/modals/contact_link_software_modal.php similarity index 100% rename from client_contact_link_software_modal.php rename to modals/contact_link_software_modal.php diff --git a/client_login_add_modal.php b/modals/credential_add_modal.php similarity index 86% rename from client_login_add_modal.php rename to modals/credential_add_modal.php index 8ce5edd0..e9a64b93 100644 --- a/client_login_add_modal.php +++ b/modals/credential_add_modal.php @@ -8,7 +8,6 @@
- @@ -110,12 +138,13 @@
- + +
@@ -222,6 +251,7 @@
+
diff --git a/client_login_bulk_assign_tags_modal.php b/modals/credential_bulk_assign_tags_modal.php similarity index 100% rename from client_login_bulk_assign_tags_modal.php rename to modals/credential_bulk_assign_tags_modal.php diff --git a/client_login_export_modal.php b/modals/credential_export_modal.php similarity index 79% rename from client_login_export_modal.php rename to modals/credential_export_modal.php index 13bb0a7e..4dbeb453 100644 --- a/client_login_export_modal.php +++ b/modals/credential_export_modal.php @@ -8,12 +8,14 @@
+ +
diff --git a/client_login_import_modal.php b/modals/credential_import_modal.php similarity index 82% rename from client_login_import_modal.php rename to modals/credential_import_modal.php index 566ccdda..cdbbe15c 100644 --- a/client_login_import_modal.php +++ b/modals/credential_import_modal.php @@ -8,7 +8,9 @@
+ +
-
Download sample csv template
+
Download sample csv template
diff --git a/custom_field_create_modal.php b/modals/custom_field_create_modal.php similarity index 95% rename from custom_field_create_modal.php rename to modals/custom_field_create_modal.php index a2b2ff54..d0b9916e 100644 --- a/custom_field_create_modal.php +++ b/modals/custom_field_create_modal.php @@ -14,7 +14,7 @@
- +
diff --git a/custom_field_edit_modal.php b/modals/custom_field_edit_modal.php similarity index 96% rename from custom_field_edit_modal.php rename to modals/custom_field_edit_modal.php index b86595a6..da542351 100644 --- a/custom_field_edit_modal.php +++ b/modals/custom_field_edit_modal.php @@ -13,7 +13,7 @@
- +
diff --git a/document_edit_visibility_modal.php b/modals/document_edit_visibility_modal.php similarity index 100% rename from document_edit_visibility_modal.php rename to modals/document_edit_visibility_modal.php diff --git a/client_domain_add_modal.php b/modals/domain_add_modal.php similarity index 82% rename from client_domain_add_modal.php rename to modals/domain_add_modal.php index abe56508..a420f9bd 100644 --- a/client_domain_add_modal.php +++ b/modals/domain_add_modal.php @@ -8,7 +8,6 @@
- +
@@ -130,6 +157,7 @@
+
diff --git a/client_domain_export_modal.php b/modals/domain_export_modal.php similarity index 79% rename from client_domain_export_modal.php rename to modals/domain_export_modal.php index cb743669..7b985881 100644 --- a/client_domain_export_modal.php +++ b/modals/domain_export_modal.php @@ -8,12 +8,14 @@
+ +
diff --git a/expense_add_modal.php b/modals/expense_add_modal.php similarity index 99% rename from expense_add_modal.php rename to modals/expense_add_modal.php index 5689f88d..8b5e3dbe 100644 --- a/expense_add_modal.php +++ b/modals/expense_add_modal.php @@ -112,7 +112,7 @@
- +
diff --git a/expense_bulk_edit_account_modal.php b/modals/expense_bulk_edit_account_modal.php similarity index 100% rename from expense_bulk_edit_account_modal.php rename to modals/expense_bulk_edit_account_modal.php diff --git a/expense_bulk_edit_category_modal.php b/modals/expense_bulk_edit_category_modal.php similarity index 100% rename from expense_bulk_edit_category_modal.php rename to modals/expense_bulk_edit_category_modal.php diff --git a/expense_bulk_edit_client_modal.php b/modals/expense_bulk_edit_client_modal.php similarity index 100% rename from expense_bulk_edit_client_modal.php rename to modals/expense_bulk_edit_client_modal.php diff --git a/expense_export_modal.php b/modals/expense_export_modal.php similarity index 86% rename from expense_export_modal.php rename to modals/expense_export_modal.php index acc4befc..d24b0212 100644 --- a/expense_export_modal.php +++ b/modals/expense_export_modal.php @@ -18,7 +18,7 @@ - + - + @@ -66,7 +66,7 @@ + diff --git a/invoice_add_modal.php b/modals/invoice_add_modal.php similarity index 98% rename from invoice_add_modal.php rename to modals/invoice_add_modal.php index ba8f6dec..392a8b71 100644 --- a/invoice_add_modal.php +++ b/modals/invoice_add_modal.php @@ -17,7 +17,7 @@
- + diff --git a/invoice_add_ticket_modal.php b/modals/invoice_add_ticket_modal.php similarity index 100% rename from invoice_add_ticket_modal.php rename to modals/invoice_add_ticket_modal.php diff --git a/invoice_export_modal.php b/modals/invoice_export_modal.php similarity index 92% rename from invoice_export_modal.php rename to modals/invoice_export_modal.php index 930dc762..efe941fb 100644 --- a/invoice_export_modal.php +++ b/modals/invoice_export_modal.php @@ -8,6 +8,9 @@
+ + + diff --git a/invoice_payment_add_modal.php b/modals/invoice_payment_add_modal.php similarity index 99% rename from invoice_payment_add_modal.php rename to modals/invoice_payment_add_modal.php index e86ca5b6..9b5d4d98 100644 --- a/invoice_payment_add_modal.php +++ b/modals/invoice_payment_add_modal.php @@ -118,7 +118,7 @@
- + diff --git a/invoice_recurring_add_modal.php b/modals/invoice_recurring_add_modal.php similarity index 100% rename from invoice_recurring_add_modal.php rename to modals/invoice_recurring_add_modal.php diff --git a/item_edit_modal.php b/modals/item_edit_modal.php similarity index 98% rename from item_edit_modal.php rename to modals/item_edit_modal.php index 136f974f..41c88a68 100644 --- a/item_edit_modal.php +++ b/modals/item_edit_modal.php @@ -25,7 +25,7 @@
- + diff --git a/client_location_add_modal.php b/modals/location_add_modal.php similarity index 82% rename from client_location_add_modal.php rename to modals/location_add_modal.php index 541a0527..b9fce811 100644 --- a/client_location_add_modal.php +++ b/modals/location_add_modal.php @@ -8,7 +8,6 @@ - @@ -104,7 +130,7 @@
- + @@ -126,7 +152,7 @@
- +
@@ -148,6 +174,7 @@
+
@@ -155,7 +182,7 @@
- +
@@ -165,7 +192,7 @@
- + @@ -175,7 +202,7 @@
- + diff --git a/client_location_bulk_assign_tags_modal.php b/modals/location_bulk_assign_tags_modal.php similarity index 100% rename from client_location_bulk_assign_tags_modal.php rename to modals/location_bulk_assign_tags_modal.php diff --git a/client_location_export_modal.php b/modals/location_export_modal.php similarity index 79% rename from client_location_export_modal.php rename to modals/location_export_modal.php index 42f2699c..b9bda966 100644 --- a/client_location_export_modal.php +++ b/modals/location_export_modal.php @@ -8,12 +8,14 @@ + +
diff --git a/client_location_import_modal.php b/modals/location_import_modal.php similarity index 81% rename from client_location_import_modal.php rename to modals/location_import_modal.php index 579d82b9..bbc14fa3 100644 --- a/client_location_import_modal.php +++ b/modals/location_import_modal.php @@ -8,7 +8,9 @@
+ +
-
Download: sample csv template
+
Download: sample csv template
diff --git a/client_network_edit_modal.php b/modals/network_add_modal.php similarity index 63% rename from client_network_edit_modal.php rename to modals/network_add_modal.php index 7a251adc..d23f8609 100644 --- a/client_network_edit_modal.php +++ b/modals/network_add_modal.php @@ -1,37 +1,62 @@ - - -
- + +
- +
@@ -139,26 +176,22 @@
- +
- - -
- + +
- +
- -

diff --git a/client_network_export_modal.php b/modals/network_export_modal.php similarity index 79% rename from client_network_export_modal.php rename to modals/network_export_modal.php index e2849ba9..47b32e61 100644 --- a/client_network_export_modal.php +++ b/modals/network_export_modal.php @@ -8,12 +8,14 @@
+ +
diff --git a/client_payment_export_modal.php b/modals/payment_export_modal.php similarity index 79% rename from client_payment_export_modal.php rename to modals/payment_export_modal.php index a76f2c93..bd618abd 100644 --- a/client_payment_export_modal.php +++ b/modals/payment_export_modal.php @@ -8,12 +8,14 @@
+ +
diff --git a/product_add_modal.php b/modals/product_add_modal.php similarity index 98% rename from product_add_modal.php rename to modals/product_add_modal.php index 20901ed1..8df44e19 100644 --- a/product_add_modal.php +++ b/modals/product_add_modal.php @@ -16,7 +16,7 @@
- + diff --git a/product_bulk_edit_category_modal.php b/modals/product_bulk_edit_category_modal.php similarity index 100% rename from product_bulk_edit_category_modal.php rename to modals/product_bulk_edit_category_modal.php diff --git a/product_export_modal.php b/modals/product_export_modal.php similarity index 100% rename from product_export_modal.php rename to modals/product_export_modal.php diff --git a/project_add_modal.php b/modals/project_add_modal.php similarity index 98% rename from project_add_modal.php rename to modals/project_add_modal.php index 8e7fbc49..463c2a7b 100644 --- a/project_add_modal.php +++ b/modals/project_add_modal.php @@ -18,7 +18,7 @@
- + diff --git a/project_ticket_add_modal.php b/modals/project_ticket_add_modal.php similarity index 100% rename from project_ticket_add_modal.php rename to modals/project_ticket_add_modal.php diff --git a/quote_add_modal.php b/modals/quote_add_modal.php similarity index 98% rename from quote_add_modal.php rename to modals/quote_add_modal.php index 95b7962e..cbacc62a 100644 --- a/quote_add_modal.php +++ b/modals/quote_add_modal.php @@ -17,7 +17,7 @@
- + diff --git a/client_quote_export_modal.php b/modals/quote_export_modal.php similarity index 79% rename from client_quote_export_modal.php rename to modals/quote_export_modal.php index e64c6283..9fa53607 100644 --- a/client_quote_export_modal.php +++ b/modals/quote_export_modal.php @@ -8,12 +8,14 @@
+ +
diff --git a/quote_note_modal.php b/modals/quote_note_modal.php similarity index 100% rename from quote_note_modal.php rename to modals/quote_note_modal.php diff --git a/quote_to_invoice_modal.php b/modals/quote_to_invoice_modal.php similarity index 100% rename from quote_to_invoice_modal.php rename to modals/quote_to_invoice_modal.php diff --git a/recurring_expense_create_modal.php b/modals/recurring_expense_create_modal.php similarity index 99% rename from recurring_expense_create_modal.php rename to modals/recurring_expense_create_modal.php index 04448ee6..a6e27710 100644 --- a/recurring_expense_create_modal.php +++ b/modals/recurring_expense_create_modal.php @@ -154,7 +154,7 @@
- + diff --git a/recurring_invoice_add_modal.php b/modals/recurring_invoice_add_modal.php similarity index 98% rename from recurring_invoice_add_modal.php rename to modals/recurring_invoice_add_modal.php index 92e9ebdc..54cad7ff 100644 --- a/recurring_invoice_add_modal.php +++ b/modals/recurring_invoice_add_modal.php @@ -17,7 +17,7 @@
- + diff --git a/recurring_invoice_note_modal.php b/modals/recurring_invoice_note_modal.php similarity index 100% rename from recurring_invoice_note_modal.php rename to modals/recurring_invoice_note_modal.php diff --git a/modals/recurring_payment_add_modal.php b/modals/recurring_payment_add_modal.php new file mode 100644 index 00000000..7366c390 --- /dev/null +++ b/modals/recurring_payment_add_modal.php @@ -0,0 +1,80 @@ + \ No newline at end of file diff --git a/recurring_ticket_add_modal.php b/modals/recurring_ticket_add_modal.php similarity index 99% rename from recurring_ticket_add_modal.php rename to modals/recurring_ticket_add_modal.php index f1eb7a27..e905d559 100644 --- a/recurring_ticket_add_modal.php +++ b/modals/recurring_ticket_add_modal.php @@ -40,7 +40,7 @@
- + @@ -130,7 +130,7 @@
- diff --git a/revenue_add_modal.php b/modals/revenue_add_modal.php similarity index 89% rename from revenue_add_modal.php rename to modals/revenue_add_modal.php index 477d2f3d..29a96a0a 100644 --- a/revenue_add_modal.php +++ b/modals/revenue_add_modal.php @@ -22,21 +22,6 @@ -
- -
-
- -
- -
-
-
@@ -157,7 +142,7 @@
- +
diff --git a/modals/service_add_modal.php b/modals/service_add_modal.php new file mode 100644 index 00000000..5d4e0f6b --- /dev/null +++ b/modals/service_add_modal.php @@ -0,0 +1,247 @@ + diff --git a/share_modal.php b/modals/share_modal.php similarity index 99% rename from share_modal.php rename to modals/share_modal.php index d9963ed7..852fa342 100644 --- a/share_modal.php +++ b/modals/share_modal.php @@ -68,7 +68,7 @@
- +

diff --git a/client_software_add_from_template_modal.php b/modals/software_add_from_template_modal.php similarity index 100% rename from client_software_add_from_template_modal.php rename to modals/software_add_from_template_modal.php diff --git a/client_software_add_modal.php b/modals/software_add_modal.php similarity index 85% rename from client_software_add_modal.php rename to modals/software_add_modal.php index 6a240715..8dc810c2 100644 --- a/client_software_add_modal.php +++ b/modals/software_add_modal.php @@ -8,7 +8,6 @@
- @@ -115,7 +143,7 @@
- + @@ -141,6 +169,8 @@ + +
+ +
diff --git a/client_software_export_modal.php b/modals/software_export_modal.php similarity index 100% rename from client_software_export_modal.php rename to modals/software_export_modal.php diff --git a/ticket_add_modal.php b/modals/ticket_add_modal.php similarity index 99% rename from ticket_add_modal.php rename to modals/ticket_add_modal.php index 1bcb80e6..7de8a529 100644 --- a/ticket_add_modal.php +++ b/modals/ticket_add_modal.php @@ -106,7 +106,7 @@
- +
diff --git a/ticket_add_watcher_modal.php b/modals/ticket_add_watcher_modal.php similarity index 100% rename from ticket_add_watcher_modal.php rename to modals/ticket_add_watcher_modal.php diff --git a/ticket_bulk_add_project_modal.php b/modals/ticket_bulk_add_project_modal.php similarity index 100% rename from ticket_bulk_add_project_modal.php rename to modals/ticket_bulk_add_project_modal.php diff --git a/ticket_bulk_assign_modal.php b/modals/ticket_bulk_assign_modal.php similarity index 100% rename from ticket_bulk_assign_modal.php rename to modals/ticket_bulk_assign_modal.php diff --git a/modals/ticket_bulk_edit_category_modal.php b/modals/ticket_bulk_edit_category_modal.php new file mode 100644 index 00000000..421e851d --- /dev/null +++ b/modals/ticket_bulk_edit_category_modal.php @@ -0,0 +1,42 @@ + diff --git a/ticket_bulk_edit_priority_modal.php b/modals/ticket_bulk_edit_priority_modal.php similarity index 100% rename from ticket_bulk_edit_priority_modal.php rename to modals/ticket_bulk_edit_priority_modal.php diff --git a/ticket_bulk_merge_modal.php b/modals/ticket_bulk_merge_modal.php similarity index 100% rename from ticket_bulk_merge_modal.php rename to modals/ticket_bulk_merge_modal.php diff --git a/ticket_bulk_reply_modal.php b/modals/ticket_bulk_reply_modal.php similarity index 100% rename from ticket_bulk_reply_modal.php rename to modals/ticket_bulk_reply_modal.php diff --git a/ticket_bulk_resolve_modal.php b/modals/ticket_bulk_resolve_modal.php similarity index 100% rename from ticket_bulk_resolve_modal.php rename to modals/ticket_bulk_resolve_modal.php diff --git a/ticket_change_client_modal.php b/modals/ticket_change_client_modal.php similarity index 100% rename from ticket_change_client_modal.php rename to modals/ticket_change_client_modal.php diff --git a/ticket_edit_asset_modal.php b/modals/ticket_edit_asset_modal.php similarity index 100% rename from ticket_edit_asset_modal.php rename to modals/ticket_edit_asset_modal.php diff --git a/ticket_edit_schedule_modal.php b/modals/ticket_edit_schedule_modal.php similarity index 100% rename from ticket_edit_schedule_modal.php rename to modals/ticket_edit_schedule_modal.php diff --git a/ticket_edit_vendor_modal.php b/modals/ticket_edit_vendor_modal.php similarity index 100% rename from ticket_edit_vendor_modal.php rename to modals/ticket_edit_vendor_modal.php diff --git a/client_ticket_export_modal.php b/modals/ticket_export_modal.php similarity index 79% rename from client_ticket_export_modal.php rename to modals/ticket_export_modal.php index 7253df50..1ad30ce4 100644 --- a/client_ticket_export_modal.php +++ b/modals/ticket_export_modal.php @@ -8,12 +8,14 @@ + +
diff --git a/ticket_invoice_add_modal.php b/modals/ticket_invoice_add_modal.php similarity index 100% rename from ticket_invoice_add_modal.php rename to modals/ticket_invoice_add_modal.php diff --git a/ticket_merge_modal.php b/modals/ticket_merge_modal.php similarity index 100% rename from ticket_merge_modal.php rename to modals/ticket_merge_modal.php diff --git a/top_nav_tickets_modal.php b/modals/top_nav_tickets_modal.php similarity index 100% rename from top_nav_tickets_modal.php rename to modals/top_nav_tickets_modal.php diff --git a/transfer_add_modal.php b/modals/transfer_add_modal.php similarity index 100% rename from transfer_add_modal.php rename to modals/transfer_add_modal.php diff --git a/trip_add_modal.php b/modals/trip_add_modal.php similarity index 98% rename from trip_add_modal.php rename to modals/trip_add_modal.php index f05d3047..88491f3f 100644 --- a/trip_add_modal.php +++ b/modals/trip_add_modal.php @@ -44,7 +44,7 @@
- + @@ -76,7 +76,7 @@
- +
diff --git a/trip_export_modal.php b/modals/trip_export_modal.php similarity index 92% rename from trip_export_modal.php rename to modals/trip_export_modal.php index cbdbe8b2..9f074c0d 100644 --- a/trip_export_modal.php +++ b/modals/trip_export_modal.php @@ -8,6 +8,9 @@
+ + +
@@ -47,7 +47,7 @@
- + @@ -57,7 +57,7 @@
- + @@ -67,7 +67,7 @@
- + @@ -83,12 +83,12 @@
- +
- +
@@ -98,7 +98,7 @@
- + @@ -108,7 +108,7 @@
- + @@ -118,7 +118,7 @@
- + @@ -128,7 +128,7 @@
- + @@ -138,7 +138,7 @@
- + diff --git a/vendor_contact_add_modal.php b/modals/vendor_contact_add_modal.php similarity index 90% rename from vendor_contact_add_modal.php rename to modals/vendor_contact_add_modal.php index ec11e89e..834a9a29 100644 --- a/vendor_contact_add_modal.php +++ b/modals/vendor_contact_add_modal.php @@ -17,7 +17,7 @@
- + @@ -27,7 +27,7 @@
- + @@ -37,7 +37,7 @@
- + @@ -49,12 +49,12 @@
- +
- +
@@ -64,7 +64,7 @@
- + @@ -74,7 +74,7 @@
- + diff --git a/vendor_contact_edit_modal.php b/modals/vendor_contact_edit_modal.php similarity index 86% rename from vendor_contact_edit_modal.php rename to modals/vendor_contact_edit_modal.php index f1e24cbc..4ed8b37f 100644 --- a/vendor_contact_edit_modal.php +++ b/modals/vendor_contact_edit_modal.php @@ -18,7 +18,7 @@
- + @@ -28,7 +28,7 @@
- + @@ -38,7 +38,7 @@
- + @@ -50,12 +50,12 @@
- +
- +
@@ -65,7 +65,7 @@
- + @@ -75,7 +75,7 @@
- + diff --git a/client_vendor_export_modal.php b/modals/vendor_export_modal.php similarity index 79% rename from client_vendor_export_modal.php rename to modals/vendor_export_modal.php index 7cb1cf69..ab904b19 100644 --- a/client_vendor_export_modal.php +++ b/modals/vendor_export_modal.php @@ -8,12 +8,14 @@
+ +
diff --git a/networks.php b/networks.php new file mode 100644 index 00000000..03c18a69 --- /dev/null +++ b/networks.php @@ -0,0 +1,273 @@ + $sort, 'order' => $order))); + +$sql = mysqli_query( + $mysqli, + "SELECT SQL_CALC_FOUND_ROWS * FROM networks + LEFT JOIN clients ON client_id = network_client_id + LEFT JOIN locations ON location_id = network_location_id + WHERE network_$archive_query + AND (network_name LIKE '%$q%' OR network_description LIKE '%$q%' OR network_vlan LIKE '%$q%' OR network LIKE '%$q%' OR network_gateway LIKE '%$q%' OR network_subnet LIKE '%$q%' OR network_primary_dns LIKE '%$q%' OR network_secondary_dns LIKE '%$q%' OR network_dhcp_range LIKE '%$q%' OR location_name LIKE '%$q%' OR client_name LIKE '%$q%') + $access_permission_query + $client_query + ORDER BY $sort $order LIMIT $record_from, $record_to" +); + +$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); + +?> + +
+
+

Networks

+
+
+ + 0) { ?> + + + +
+ +
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+ + + + "> + + + + + + + + + + + + + + + + + $network_secondary_dns"; + } else { + $network_dns_display = "-"; + } + $network_dhcp_range = nullable_htmlentities($row['network_dhcp_range']); + if (empty($network_dhcp_range)) { + $network_dhcp_range_display = "-"; + } else { + $network_dhcp_range_display = $network_dhcp_range; + } + $network_location_id = intval($row['network_location_id']); + $location_name = nullable_htmlentities($row['location_name']); + if (empty($location_name)) { + $location_name_display = "-"; + } else { + $location_name_display = $location_name; + } + + ?> + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + Name + + + + vLAN + + + + IP / Network + + + + Gateway + + + + DNS + + + + DHCP Range + + + + Location + + + + Client + + Action
+
+ +
+
+ +
+ +
+
+
+
+
+
+
+ +
+
+ +
+ +
+
+ +
+
+ + + + + + - +
-
-

Payments

+
+

Payments

+ 0) { ?> +
+ +
+
+ + +
@@ -69,7 +87,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- + - + @@ -158,14 +176,21 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); Invoice + Client + + + + Invoice Amount + + - Amount + Payment Amount @@ -183,6 +208,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); Account + @@ -193,7 +219,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $invoice_prefix = nullable_htmlentities($row['invoice_prefix']); $invoice_number = intval($row['invoice_number']); $invoice_status = nullable_htmlentities($row['invoice_status']); + $invoice_amount = floatval($row['invoice_amount']); + $invoice_currency_code = nullable_htmlentities($row['invoice_currency_code']); $invoice_date = nullable_htmlentities($row['invoice_date']); + $payment_id = intval($row['payment_id']); $payment_date = nullable_htmlentities($row['payment_date']); $payment_method = nullable_htmlentities($row['payment_method']); $payment_amount = floatval($row['payment_amount']); @@ -219,23 +248,46 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - - + + + + + + + + + + + + - +
- +
- +').appendTo($this); } oSettings.nTHead = thead[0]; - $('tr', thead).addClass(oClasses.thead.row); var tbody = $this.children('tbody'); if ( tbody.length === 0 ) { @@ -456,7 +437,6 @@ tfoot = $('').appendTo($this); } oSettings.nTFoot = tfoot[0]; - $('tr', tfoot).addClass(oClasses.tfoot.row); // Copy the data index array oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); @@ -539,7 +519,7 @@ * * @type string */ - builder: "bs4/dt-2.1.8", + builder: "bs4/dt-2.2.1", /** @@ -2173,6 +2153,10 @@ var width = _fnColumnsSumWidth(settings, [i], false, false); cols[i].colEl.css('width', width); + + if (settings.oScroll.sX) { + cols[i].colEl.css('min-width', width); + } } } @@ -3240,9 +3224,13 @@ // Add the number of cells needed to make up to the number of columns if (row.length === 1) { - var cells = $('td, th', row); + var cellCount = 0; + + $('td, th', row).each(function () { + cellCount += this.colSpan; + }); - for ( i=cells.length, ien=columns.length ; i') .html( columns[i][titleProp] || '' ) .appendTo( row ); @@ -3254,9 +3242,11 @@ if (side === 'header') { settings.aoHeader = detected; + $('tr', target).addClass(classes.thead.row); } else { settings.aoFooter = detected; + $('tr', target).addClass(classes.tfoot.row); } // Every cell needs to be passed through the renderer @@ -4519,7 +4509,7 @@ // So the array reference doesn't break set the results into the // existing array displayRows.length = 0; - displayRows.push.apply(displayRows, rows); + _fnArrayApply(displayRows, rows); } } @@ -5247,8 +5237,11 @@ // [].find, but it wasn't supported in Chrome until Sept 2015, and DT has 10 year // browser support var firstTr = null; + var start = _fnDataSource( settings ) !== 'ssp' + ? settings._iDisplayStart + : 0; - for (i=settings._iDisplayStart ; i col is set to and correct if needed @@ -5273,6 +5266,10 @@ if (colWidth !== colSizes[i].width) { colEl.style.width = colSizes[i].width + 'px'; + + if (scroll.sX) { + colEl.style.minWidth = colSizes[i].width + 'px'; + } } } } @@ -5365,6 +5362,14 @@ i, column, columnIdx; var styleWidth = table.style.width; + var containerWidth = _fnWrapperWidth(settings); + + // Don't re-run for the same width as the last time + if (containerWidth === settings.containerWidth) { + return false; + } + + settings.containerWidth = containerWidth; // If there is no width applied as a CSS style or as an attribute, we assume that // the width is intended to be 100%, which is usually is in CSS, but it is very @@ -5422,6 +5427,8 @@ // browser will collapse it. If this width is smaller than the // width the column requires, then it will have no effect if ( scrollX ) { + this.style.minWidth = width; + $( this ).append( $('
').css( { width: width, margin: 0, @@ -5490,15 +5497,15 @@ // If there is no width attribute or style, then allow the table to // collapse - if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { - tmpTable.width( tableContainer.clientWidth ); + if ( tmpTable.outerWidth() < tableContainer.clientWidth && tableWidthAttr ) { + tmpTable.outerWidth( tableContainer.clientWidth ); } } else if ( scrollY ) { - tmpTable.width( tableContainer.clientWidth ); + tmpTable.outerWidth( tableContainer.clientWidth ); } else if ( tableWidthAttr ) { - tmpTable.width( tableWidthAttr ); + tmpTable.outerWidth( tableWidthAttr ); } // Get the width of each column in the constructed table @@ -5531,20 +5538,55 @@ } if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) { - var bindResize = function () { - $(window).on('resize.DT-'+settings.sInstance, DataTable.util.throttle( function () { - if (! settings.bDestroying) { - _fnAdjustColumnSizing( settings ); - } - } ) ); - }; + var resize = DataTable.util.throttle( function () { + var newWidth = _fnWrapperWidth(settings); - bindResize(); + // Don't do it if destroying or the container width is 0 + if (! settings.bDestroying && newWidth !== 0) { + _fnAdjustColumnSizing( settings ); + } + } ); + + // For browsers that support it (~2020 onwards for wide support) we can watch for the + // container changing width. + if (window.ResizeObserver) { + // This is a tricky beast - if the element is visible when `.observe()` is called, + // then the callback is immediately run. Which we don't want. If the element isn't + // visible, then it isn't run, but we want it to run when it is then made visible. + // This flag allows the above to be satisfied. + var first = $(settings.nTableWrapper).is(':visible'); + + settings.resizeObserver = new ResizeObserver(function (e) { + if (first) { + first = false; + } + else { + resize(); + } + }); + + settings.resizeObserver.observe(settings.nTableWrapper); + } + else { + // For old browsers, the best we can do is listen for a window resize + $(window).on('resize.DT-'+settings.sInstance, resize); + } settings._reszEvt = true; } } + /** + * Get the width of the DataTables wrapper element + * + * @param {*} settings DataTables settings object + * @returns Width + */ + function _fnWrapperWidth(settings) { + return $(settings.nTableWrapper).is(':visible') + ? $(settings.nTableWrapper).width() + : 0; + } /** * Get the maximum strlen for each data column @@ -6153,15 +6195,26 @@ return; } + // Sort state saving uses [[idx, order]] structure. + var sorting = []; + _fnSortResolve(settings, sorting, settings.aaSorting ); + /* Store the interesting variables */ + var columns = settings.aoColumns; var state = { time: +new Date(), start: settings._iDisplayStart, length: settings._iDisplayLength, - order: $.extend( true, [], settings.aaSorting ), + order: sorting.map(function (sort) { + // If a column name is available, use it + return columns[sort[0]] && columns[sort[0]].sName + ? [ columns[sort[0]].sName, sort[1] ] + : sort.slice(); + } ), search: $.extend({}, settings.oPreviousSearch), columns: settings.aoColumns.map( function ( col, i ) { return { + name: col.sName, visible: col.bVisible, search: $.extend({}, settings.aoPreSearchCols[i]) }; @@ -6209,6 +6262,8 @@ function _fnImplementState ( settings, s, callback) { var i, ien; var columns = settings.aoColumns; + var currentNames = _pluck(settings.aoColumns, 'sName'); + settings._bLoadingState = true; // When StateRestore was introduced the state could now be implemented at any time @@ -6238,13 +6293,6 @@ return; } - // Number of columns have changed - all bets are off, no restore of settings - if ( s.columns && columns.length !== s.columns.length ) { - settings._bLoadingState = false; - callback(); - return; - } - // Store the saved state so it might be accessed at any time settings.oLoadedState = $.extend( true, {}, s ); @@ -6278,10 +6326,23 @@ if ( s.order !== undefined ) { settings.aaSorting = []; $.each( s.order, function ( i, col ) { - settings.aaSorting.push( col[0] >= columns.length ? - [ 0, col[1] ] : - col - ); + var set = [ col[0], col[1] ]; + + // A column name was stored and should be used for restore + if (typeof col[0] === 'string') { + var idx = currentNames.indexOf(col[0]); + + // Find the name from the current list of column names, or fallback to index 0 + set[0] = idx >= 0 + ? idx + : 0; + } + else if (set[0] >= columns.length) { + // If a column name, but it is out of bounds, set to 0 + set[0] = 0; + } + + settings.aaSorting.push(set); } ); } @@ -6292,31 +6353,65 @@ // Columns if ( s.columns ) { - for ( i=0, ien=s.columns.length ; i= 0) { + set.push(s.columns[idx]); + } + else { + // No matching column name in the state's columns, so this might be a new + // column and thus can't have a state already. + set.push({}); + } } else { - columns[i].bVisible = col.visible; + // If no name, but other columns did have a name, then there is no knowing + // where this one came from originally so it can't be restored. + set.push({}); + } + } + } + + // If the number of columns to restore is different from current, then all bets are off. + if (set.length === columns.length) { + for ( i=0, ien=set.length ; i 1 && // prevent infinite - $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10 - ) { - _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 })); + if (buttonEls.length) { + var outerHeight = $(buttonEls[0]).outerHeight(); + + if ( + opts.buttons > 1 && // prevent infinite + outerHeight > 0 && // will be 0 if hidden + $(host).height() >= (outerHeight * 2) - 10 + ) { + _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 })); + } } } @@ -13336,7 +13548,6 @@ switch ( button ) { case 'ellipsis': o.display = '…'; - o.disabled = true; break; case 'first': @@ -13523,7 +13734,7 @@ // Save text node content for macro updating var textNodes = []; - Array.from(div.find('label')[0].childNodes).forEach(function (el) { + Array.prototype.slice.call(div.find('label')[0].childNodes).forEach(function (el) { if (el.nodeType === Node.TEXT_NODE) { textNodes.push({ el: el, @@ -13541,7 +13752,6 @@ // Next, the select itself, along with the options var select = $(' - - - - -
-
-
diff --git a/products.php b/products.php index 8da75d9f..86e8729c 100644 --- a/products.php +++ b/products.php @@ -4,7 +4,7 @@ $sort = "product_name"; $order = "ASC"; -require_once "inc_all.php"; +require_once "includes/inc_all.php"; // Perms enforceUserPermission('module_sales'); @@ -12,11 +12,11 @@ enforceUserPermission('module_sales'); // Category Filter if (isset($_GET['category']) & !empty($_GET['category'])) { $category_query = 'AND (category_id = ' . intval($_GET['category']) . ')'; - $category = intval($_GET['category']); + $category_filter = intval($_GET['category']); } else { // Default - any $category_query = ''; - $category = ''; + $category_filter = ''; } //Rebuild URL @@ -70,7 +70,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- + + + + + @@ -218,7 +226,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - + -
@@ -268,7 +277,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
Nothing to see here'; - require_once "footer.php"; + require_once "includes/footer.php"; exit(); } @@ -59,6 +64,10 @@ if (isset($_GET['quote_id'])) { $client_net_terms = $config_default_net_terms; } + // Override Tab Title // No Sanitizing needed as this var will only be used in the tab title + $tab_title = $row['client_name']; + $page_title = "{$row['quote_prefix']}{$row['quote_number']}"; + $sql = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1"); $row = mysqli_fetch_array($sql); @@ -104,7 +113,14 @@ if (isset($_GET['quote_id'])) { $json_products = json_encode($products); } + // Quote File Attachments + $sql_quote_files = mysqli_query( + $mysqli, + "SELECT file_reference_name, file_name, file_created_at FROM quote_files LEFT JOIN files ON quote_files.file_id = files.file_id WHERE quote_id = $quote_id" + ); + ?> +