13 Commits

Author SHA1 Message Date
Johnny
ccaa45b0ae Merge pull request #1292 from itflow-org/develop
Develop to Master for26.08 Release
2026-08-04 15:29:17 -04:00
Johnny
698135d53d Merge pull request #1291 from itflow-org/develop
Develop to Master for Stable
2026-07-03 12:43:04 -04:00
Johnny
12e5d13282 Merge pull request #1289 from itflow-org/develop
Develop to Master for Stable Release
2026-07-01 11:19:50 -04:00
Johnny
60563e3392 Merge pull request #1284 from itflow-org/develop
Bump Version
2026-05-21 10:53:48 -04:00
Johnny
885a002611 Merge pull request #1281 from itflow-org/develop
Develop to Master
2026-05-04 17:00:10 -04:00
Johnny
5c89e50f30 Merge pull request #1278 from itflow-org/develop
Develop to master for release
2026-04-16 15:22:58 -04:00
Johnny
c650764eb5 Merge pull request #1271 from itflow-org/develop
Develop to Master
2026-03-14 13:03:05 -04:00
Johnny
20e2f22a27 Merge pull request #1268 from itflow-org/develop
Develop to Master for 26.02.1 Maint Release
2026-02-14 15:54:13 -05:00
Johnny
376285ef62 Merge pull request #1263 from itflow-org/develop
Develop to Master for 26.02 Release Take 2
2026-02-08 12:02:43 -05:00
Johnny
163aa3062e Merge pull request #1254 from itflow-org/develop
Develop to Master for 25.12.1 Maint Release
2025-12-14 14:03:56 -05:00
Johnny
03570ecd04 Merge pull request #1250 from itflow-org/develop
Develop to Master for 25.12 release
2025-12-06 14:36:09 -05:00
Johnny
c7ef3627ce Merge pull request #1247 from itflow-org/develop
Merge Develop into Master for v25.11.1 release
2025-11-17 12:22:24 -05:00
Johnny
d1dcc5fb7e Merge pull request #1246 from itflow-org/develop
Develop to Master for Release
2025-11-08 13:47:43 -05:00
672 changed files with 14450 additions and 13612 deletions

View File

@@ -2,52 +2,6 @@
This file documents all notable changes made to ITFlow.
## [26.08.2] Maint Release
### Upgrading to 26.08.2
Update the files from Settings > Update as normal. There is no database change in this release, so nothing else is required.
### Bug Fixes
- Calendar: fixed the agent calendar showing no events.
- Calendar: shared calendar feeds set to publish busy blocks only were publishing full event titles, locations and descriptions to anyone holding the subscription link.
- Cron: fixed Maintenance > Cron failing to load. Scheduled jobs themselves were unaffected and kept running.
- Exports: restored the missing columns on the ticket, quote, recurring invoice, software and user exports.
- API: restored the full record on the credentials list endpoint.
- Mail: switching an existing install from Standard SMTP/IMAP to Microsoft 365 or Google OAuth no longer leaves the old mail server behind, which stopped sending and ticket email fetching from working. The connection settings for OAuth providers are now fixed by the provider and cleared on save.
## [26.08.1] Maint Release
### Upgrading to 26.08.1
Update the files from Settings > Update as normal. This release moves the database to 2.6.7 and the web updater completes it for you — the command line step that 26.08 required is not needed again.
### Breaking Changes and Notes
- Client access: agents with restricted client access now see records that have no client assigned. Previously this varied by page — unassigned tickets and projects were visible, unassigned expenses and credentials were not. It is now consistent everywhere.
### Bug Fixes
- Setup: fixed the wizard closing itself after the first user, which left new 26.08 installs stuck in a redirect loop between `/setup` and `/login.php`.
- API: tightened client scoping on the expense read and record update endpoints.
- Income: revenue rows now respect restricted client access.
- Client PDF Export: fixed the export producing a CSV file, and each section is now gated on the module that owns it.
- AI: fixed model creation, per-use-case model selection, configurable temperature, and error reporting.
- Ticket: system-generated replies no longer record time worked that was never worked.
- Ticket: fixed an error when scheduling a ticket.
- Ticket: cancelling a schedule now cancels the calendar event on the recipient's calendar.
- Ticket: history no longer records a status change when the status did not change.
- Recurring Ticket: bulk priority changes no longer deny access to agents who are not administrators.
- Contact: deleting a contact now removes the linked portal user, and anonymizing now redacts the phone number.
- Calendar: fixed event deletion.
### New Features & Updates
- Performance: queries now select only the columns they use instead of `SELECT *`, cutting memory use and query time across the app and especially in the crons.
- Performance: removed client joins that were only there for scoping — side nav badge counts are significantly faster.
- Client scoping: added a `clientScopeSql()` helper so list queries scope on the owning column instead of a joined `clients.client_id`.
- Contributing: documented the column-selection and client-scoping conventions.
## [26.08]
### Upgrading to 26.08

View File

@@ -74,10 +74,6 @@ if (isset($_POST['edit_ticket_priority'])) {
### The `_model.php` pattern
Files named `agent/post/*_model.php` hold shared field collection/sanitization logic used by both the create and edit blocks of a module (e.g. `asset_model.php` is included by both `add_asset` and `edit_asset`). If create and edit share more than a couple of fields, use this pattern rather than duplicating. Model files carry the same `FROM_POST_HANDLER` guard and are excluded from the dispatcher's auto-load.
**`_model.php` is a reserved suffix.** The exclusion is a filename match, so a *handler* named `*_model.php` is silently never loaded — its form posts, nothing claims the request, and the user gets a blank page with no error anywhere. This is what happened to `admin/post/ai_model.php`, which is why the AI Models handler is now `admin/post/ai_models.php`. Name entity handlers around the suffix (`ai_models.php`, `users.php`, `api_keys.php`).
A POST that reaches the end of `admin/post.php` or `agent/post.php` without a handler claiming it is logged to App Logs as a `Request` warning, which is the fastest way to spot this class of mistake.
---
@@ -175,25 +171,7 @@ Everywhere else — anything under `agent/post/` — the call belongs in the blo
### 4. Client scoping is enforced, not assumed.
A user can be restricted to a subset of clients through `user_client_permissions`. Enforcing that has two halves, and a page usually needs both.
**One record — `enforceClientAccess()`.** After loading a record, call it (optionally with the record's client ID) so technicians restricted to specific clients cannot touch other clients' data by editing an ID in the URL. Look at how `resolve_ticket` does it.
**A list — `clientScopeSql()`.** Any query returning more than one row appends the fragment for that resource's own client column:
```php
$sql = mysqli_query($mysqli, "SELECT expense_id, expense_date, expense_amount, expense_description
FROM expenses
WHERE expense_archived_at IS NULL
" . clientScopeSql('expense_client_id') . "
ORDER BY expense_date DESC");
```
It returns `" AND ..."` or `""`, so it needs a `WHERE` to hang off — add `WHERE 1=1` if the query has no other condition. It is column-aware and takes an alias fine (`clientScopeSql('t.ticket_client_id')`). The API calls the same helper through the `apiClientScopeSql()` wrapper.
Scope on the resource's **own** column, not on a joined `clients.client_id`. Joining `clients` just to scope makes the filter depend on the join: with a `LEFT JOIN`, a row whose client column is `0` produces `NULL`, and `NULL IN (...)` is neither true nor false, so the row silently vanishes. If the query joins `clients` for `client_name`, keep the join for that — but still scope on the owning column.
**Records with no client (`0`) stay visible to restricted users.** `clientScopeSql()` emits `IN (0,...)` deliberately. Client restrictions partition *client* data, and a record belonging to no client is not any client's data to withhold. Do not hand-roll a variant that drops the `0` — the tree had accumulated several before this helper existed, disagreeing with each other, and reconciling them is what surfaced the inconsistency.
After loading a record, call `enforceClientAccess()` (optionally with the record's client ID) so technicians restricted to specific clients cannot touch other clients' data by editing an ID in the URL. Look at how `resolve_ticket` does it — including the "skip if the record has no client" case.
### 5. Escape on output.
@@ -228,29 +206,8 @@ Per [SECURITY.md](SECURITY.md) — never in a public issue.
---
## Conventions
**Only technician-entered time is time worked.** `ticket_replies.ticket_reply_time_worked` is billable labour and feeds ticket totals, the technician and client time reports, project totals, invoicing and the API. A reply the *system* writes — assignment, priority change, merge, close, invoice/quote created, schedule edited, task completed or reopened — is an audit trail, not work, and records `'00:00:00'`. Only a value the technician actually typed goes in that column. Task completion estimates are planning information and stay on the task; they are never converted into time worked. `agent/ticket.php` hides the clock badge on a reply whose time is exactly `00:00:00`, so a zero renders as no time rather than as "0m".
**Database naming.** Every column is prefixed with the singular name of the entity it belongs to: `tickets.ticket_id`, `tickets.ticket_subject`, `clients.client_name`. This makes JOIN results unambiguous, so a `SELECT *` across joins is never *wrong*. New tables must follow it.
**Select the columns you use, not `*`.** Unambiguous is not the same as cheap. `SELECT *` across three joined tables fetches every column of all three, including the `*_notes` and `*_details` TEXT columns, and throws away whatever the page never renders. A search result list that shows five fields was pulling sixty. List the columns instead:
```php
$sql = mysqli_query($mysqli, "SELECT ticket_id, ticket_prefix, ticket_number, ticket_subject, client_name
FROM tickets
LEFT JOIN clients ON ticket_client_id = client_id
WHERE ticket_archived_at IS NULL
" . clientScopeSql('ticket_client_id') . "");
```
Two things follow from that:
- A query whose result only feeds `mysqli_num_rows()` needs no columns at all — write `SELECT 1`. Do not select a primary key "just in case": if the query joins two tables that both carry that column name, an unqualified `SELECT ticket_template_id` is an ambiguous-column error.
- Keep the join even when no column of the joined table survives into the `SELECT`, if the join is doing work — supplying a `WHERE` term, an `ORDER BY`, or the client column you scope on. Dropping a join is a separate decision from trimming the column list.
The trade is real and worth stating: `SELECT *` picks up new columns for free, an explicit list does not. Add a column to a table and every query that needs it must be updated by hand, and the failure mode is a blank field or a PHP 8 undefined-key warning rather than an error. That is the price of not fetching data nobody reads, and the project has decided to pay it on anything that loops or touches a TEXT column.
The exception is `api/v1/*/read.php`. Those endpoints hand the whole row to `read_output.php`, which serialises it straight into the JSON response — there the row *is* the output contract, so `SELECT *` is correct and trimming it would silently drop fields from every consumer.
**Database naming.** Every column is prefixed with the singular name of the entity it belongs to: `tickets.ticket_id`, `tickets.ticket_subject`, `clients.client_name`. This makes JOIN results unambiguous and is why queries can `SELECT *` across joins safely. New tables must follow it.
The prefix is the entity name, which is usually but not always the singular of the table name. Where a table is named for its container rather than its row, the prefix follows the row: `calendar_events``event_*`, `asset_interfaces``interface_*`, `invoice_items` / `quote_items``item_*`, `rack_units``unit_*`, `user_roles``role_*`, `product_stock``stock_*`. Pick the prefix your columns will read best as and use it for every column in the table.
@@ -267,8 +224,6 @@ A single update run applies every pending migration in order, stopping at the fi
**After acting, log and notify.** State changes call `logAudit($type, $action, $description, $client_id, $entity_id)` for the audit trail. User-facing events may also call `appNotify()`. Fire `triggerCustomAction()` where a site might reasonably want a hook. Then call `flashAlert($message, $type)` and `redirect()` (defaults to the referer) rather than setting session keys or `header()` manually.
**Function names (post-rename).** Helpers were renamed for clarity in 2026; the old names **no longer exist** — code calling them fatals. If you're rebasing an old PR or following an old tutorial, translate: `sanitizeInput``escapeSql`, `nullable_htmlentities``escapeHtml`, `logAction``logAudit`, `flash_alert``flashAlert`, `customAction``triggerCustomAction`, `encryptLoginEntry`/`decryptLoginEntry``encryptCredentialEntry`/`decryptCredentialEntry`, `strtoAZaz09``toAlphanumeric`, `fetchUpdates``checkForUpdates`, `sanitize_url``escapeUrl`.
One removed **variable** deserves its own warning: the old `$access_permission_query` global is gone, replaced by `clientScopeSql()` (security rule 4). Unlike a removed function, it does not fatal — an undefined variable interpolates as an empty string, so a rebased query keeps running with **no client scoping at all**. Grep for it before rebasing anything that touches a list query.
**Helpers that fetch data return it raw.** If you add a `getXById()`-style helper, return the column value untouched and let callers escape it (security rule 1). Validating what the helper interpolates into its *own* query — table and column names, the id — is still the helper's job; that is query construction, not output escaping, and the two are not the same thing.

View File

@@ -27,7 +27,7 @@
·
<a href="https://forum.itflow.org/t/bug">Report Bug</a>
·
<a href="https://forum.itflow.org/t/features">Suggest Feature</a>
<a href="https://forum.itflow.org/t/features">Request Feature</a>
·
<a href="https://github.com/itflow-org/itflow/security/policy">Security</a>
</p>
@@ -44,16 +44,16 @@
### The Problem
- 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.
- MSP tools can be expensive, with recurring licensing costs adding up quickly.
- 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.
- 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 (documentation, ticketing and billing) into one unified, free platform that you control.
- ITFlow consolidates common MSP needs (documentation, ticketing and billing) into one unified system.
<!-- GETTING STARTED -->
## Getting Started
### Self Hosting
- The best installation method is to use the [install script](https://docs.itflow.org/installation_script) on Ubuntu/Debian. See the [video](https://www.youtube.com/watch?v=kKz9NOU_1XE) walk through.
- 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
@@ -61,25 +61,25 @@
- Other manual installation methods are available in the [docs](https://docs.itflow.org/installation).
### Managed Hosting
- If you'd prefer, we can [host ITFlow for you](https://services.itflow.org/hosting.php) (coming soon).
- If you'd prefer, we can [host ITFlow for you](https://services.itflow.org/hosting.php).
<!-- FEATURES -->
## Key Features
* Client documentation - assets, contacts, domains, docs/files, passwords, tickets, and more
* Billing - quotes, invoices and expenses, with Stripe integration for online payments
* Alerting - domain and SSL expiry/renewals
* Client Portal - self service quote, invoice, and ticket management for clients
* Client documentation - assets, contacts, domains, docs, files, passwords, and more
* Accounting / Billing - finance dashboard, quotes, invoices, accounting, expenses, etc
* Client Portal - self service quote/invoice/ticket management for clients
* Alerting - account balance, invoices, domain/SSL renewals
* Completely free & open-source alternative to ITGlue and Hudu
<!-- ROADMAP -->
## Roadmap / Future to-do
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 suggest new features or report bugs.
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.
<!-- CONTRIBUTING -->
## Support & Contributions
### Forum
For help using ITFlow, bugs, feature suggestions, and other general ideas or discussions, please use the community [forum](https://forum.itflow.org).
For help using ITFlow, bugs, feature requests, and general ideas / discussions please use the community [forum](https://forum.itflow.org).
### Contributing
We have temporarily paused PRs from the community.
@@ -90,16 +90,17 @@ We have temporarily paused PRs from the community.
</a>
### Supporters
Were incredibly grateful to the organizations and individuals who [sponsor](https://services.itflow.org) the project - a big thank you to:
Were incredibly grateful to the organizations and individuals who support the project - a big thank you to:
- CompuMatter
- F1 for HELP
- digiBandit
- JetBrains (PhpStorm)
## 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
* We've had a stable release of the project since Jan 2025.
* 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).

View File

@@ -1,12 +1,12 @@
# Security Policy
## **Please do NOT report security concerns/vulnerabilities publicly**
## **Please do NOT report security concerns/vulnerabilities publicly (Issues/forum)**
**ITFlow stores important information, so we take security seriously**
**We take security seriously**
- 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.
- 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.
@@ -19,8 +19,6 @@ We operate a rolling release model. Any bug fixes will be released into latest v
**Security contact: [GitHub Security Advisories](https://github.com/itflow-org/itflow/security/advisories/new)**
If you have discovered a security issue, please **[report it](https://github.com/itflow-org/itflow/security/advisories/new)** to us so we can fix it.
If you have discovered a security issue, please **[report it](https://github.com/itflow-org/itflow/security/advisories/new)** to us in as much detail as possible, so we can fix it.
You should expect to receive an initial acknowledgement within 7 days. If you don't receive any feedback, we may have missed the initial email from GitHub (we're human!). Please raise a forum discussion quoting ONLY the assigned GHSA ref.
Note: Please don't submit AI slop. AI can be useful for drafting and research, but you are responsible for reviewing and validating anything you submit. We reserve the right to restrict repository access if you repeatedly ignore this requirement.
You should expect to receive an initial acknowledgement within 72 hours. If you don't receive any feedback, we may have missed the initial email from GitHub (we're human!). Please raise a forum discussion quoting ONLY the assigned GHSA ref.

View File

@@ -6,8 +6,7 @@ $order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT ai_model_id, ai_model_name, ai_model_prompt, ai_model_use_case, ai_provider_id,
ai_provider_name FROM ai_models LEFT JOIN ai_providers ON ai_model_ai_provider_id = ai_provider_id ORDER BY $sort $order");
$sql = mysqli_query($mysqli, "SELECT * FROM ai_models LEFT JOIN ai_providers ON ai_model_ai_provider_id = ai_provider_id ORDER BY $sort $order");
$num_rows = mysqli_num_rows($sql);
@@ -25,9 +24,9 @@ $num_rows = mysqli_num_rows($sql);
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot me-2"></i>AI Models</h3>
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot mr-2"></i>AI Models</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/ai/ai_model_add.php"><i class="fas fa-plus me-2"></i>Add Model</button>
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/ai/ai_model_add.php"><i class="fas fa-plus mr-2"></i>Add Model</button>
</div>
</div>
<div class="card-body">
@@ -79,18 +78,18 @@ $num_rows = mysqli_num_rows($sql);
<td><?= $use_case ?></td>
<td><?= $prompt ?></td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/ai/ai_model_edit.php?id=<?= $model_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_ai_model=<?= $model_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>

View File

@@ -6,7 +6,7 @@ $order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT ai_provider_api_key, ai_provider_api_url, ai_provider_id, ai_provider_name FROM ai_providers ORDER BY $sort $order");
$sql = mysqli_query($mysqli, "SELECT * FROM ai_providers ORDER BY $sort $order");
$num_rows = mysqli_num_rows($sql);
@@ -14,9 +14,9 @@ $num_rows = mysqli_num_rows($sql);
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot me-2"></i>AI Providers</h3>
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot mr-2"></i>AI Providers</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/ai/ai_provider_add.php"><i class="fas fa-plus me-2"></i>Add Provider</button>
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/ai/ai_provider_add.php"><i class="fas fa-plus mr-2"></i>Add Provider</button>
</div>
</div>
<div class="card-body">
@@ -68,20 +68,20 @@ $num_rows = mysqli_num_rows($sql);
<td><?= $url ?></td>
<td><?= $key ?></td>
<td class="text-center">
<a class="badge bg-dark rounded-pill p-2" href="ai_models.php"><?= $ai_model_count ?></a>
<a class="badge badge-dark badge-pill p-2" href="ai_models.php"><?= $ai_model_count ?></a>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/ai/ai_provider_edit.php?id=<?= $provider_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_ai_provider=<?= $provider_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>

View File

@@ -8,7 +8,7 @@ require_once "includes/inc_all_admin.php";
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS api_key_created_at, api_key_expire, api_key_id, api_key_name, api_key_secret, user_name FROM api_keys
"SELECT SQL_CALC_FOUND_ROWS * FROM api_keys
LEFT JOIN users on api_key_user_id = user_id
WHERE (api_key_name LIKE '%$q%')
ORDER BY $sort $order LIMIT $record_from, $record_to"
@@ -20,9 +20,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-key me-2"></i>API Keys</h3>
<h3 class="card-title mt-2"><i class="fas fa-fw fa-key mr-2"></i>API Keys</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/api/api_key_add.php"><i class="fas fa-plus me-2"></i>New API Key</button>
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/api/api_key_add.php"><i class="fas fa-plus mr-2"></i>New API Key</button>
</div>
</div>
@@ -34,20 +34,22 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-md-4">
<div class="input-group mb-3 mb-md-0">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search keys">
<div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
<div class="col-md-8">
<div class="btn-group float-end">
<div class="dropdown ms-2" id="bulkActionButton" hidden>
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="fas fa-fw fa-layer-group me-2"></i>Bulk Action (<span id="selectedCount">0</span>)
<div class="btn-group float-right">
<div class="dropdown ml-2" id="bulkActionButton" hidden>
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
</button>
<div class="dropdown-menu">
<button class="dropdown-item text-danger text-bold"
type="submit" form="bulkActions" name="bulk_delete_api_keys">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</button>
</div>
</div>
@@ -67,7 +69,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
<tr>
<td class="pe-0">
<td class="pr-0">
<div class="form-check">
<input class="form-check-input" type="checkbox" onclick="checkAll(this)">
</div>
@@ -117,7 +119,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
?>
<tr>
<td class="pe-0">
<td class="pr-0">
<div class="form-check">
<input class="form-check-input bulk-select" type="checkbox" name="api_key_ids[]" value="<?= $api_key_id ?>">
</div>
@@ -128,22 +130,22 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<td><?= $api_key_created_at ?></td>
<td><?= $api_key_expire ?></td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/api/api_key_edit.php?id=<?= $api_key_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<?php if ($api_key_expire > date("Y-m-d H:i:s")) { ?>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?revoke_api_key=<?= $api_key_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-times me-2"></i>Revoke
<i class="fas fa-fw fa-times mr-2"></i>Revoke
</a>
<?php } ?>
<?php if ($api_key_expire < date("Y-m-d H:i:s")) { ?>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_api_key=<?= $api_key_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-times me-2"></i>Delete
<i class="fas fa-fw fa-times mr-2"></i>Delete
</a>
<?php } ?>
</div>

View File

@@ -28,7 +28,7 @@ if (isset($_GET['category']) & !empty($_GET['catergory'])) {
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS app_log_category, app_log_created_at, app_log_details, app_log_id, app_log_type FROM app_logs
"SELECT SQL_CALC_FOUND_ROWS * FROM app_logs
WHERE (app_log_type LIKE '%$q%' OR app_log_category LIKE '%$q%' OR app_log_details LIKE '%$q%')
AND DATE(app_log_created_at) BETWEEN '$dtf' AND '$dtt'
$log_type_query
@@ -42,24 +42,26 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-history me-2"></i>App Logs</h3>
<h3 class="card-title"><i class="fas fa-fw fa-history mr-2"></i>App Logs</h3>
</div>
<div class="card-body">
<form autocomplete="off">
<div class="row">
<div class="col-sm-4">
<div class="mb-3">
<div class="form-group">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search app logs">
<button class="btn btn-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#advancedFilter"><i class="fas fa-filter"></i></button>
<div class="input-group-append">
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
</div>
<div class="col-sm-2">
<div class="mb-3">
<select class="form-select select2" name="type" onchange="this.form.submit()">
<div class="form-group">
<select class="form-control select2" name="type" onchange="this.form.submit()">
<option value="">- All Types -</option>
<?php
@@ -77,8 +79,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div>
<div class="col-sm-2">
<div class="mb-3">
<select class="form-select select2" name="category" onchange="this.form.submit()">
<div class="form-group">
<select class="form-control select2" name="category" onchange="this.form.submit()">
<option value="">- All Categories -</option>
<?php
@@ -98,7 +100,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="collapse mt-3 <?php if (isset($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') { echo "show"; } ?>" id="advancedFilter">
<div class="row">
<div class="col-md-3">
<div class="mb-3">
<div class="form-group">
<label>Date range</label>
<input type="text" id="dateFilter" class="form-control" autocomplete="off">
<input type="hidden" name="canned_date" id="canned_date" value="<?= escapeHtml($_GET['canned_date']) ?? '' ?>">
@@ -149,7 +151,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
?>
<tr>
<td class="font-monospace"><?= $log_created_at ?></td>
<td class="text-monospace"><?= $log_created_at ?></td>
<td><?= $log_type ?></td>
<td><?= $log_category ?></td>
<td><?= $log_details ?></td>

View File

@@ -48,8 +48,7 @@ if (isset($_GET['action']) & !empty($_GET['action'])) {
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS client_id, client_name, log_action, log_created_at, log_description, log_entity_id, log_id,
log_ip, log_type, log_user_agent, user_id, user_name FROM logs
"SELECT SQL_CALC_FOUND_ROWS * FROM logs
LEFT JOIN users ON log_user_id = user_id
LEFT JOIN clients ON log_client_id = client_id
WHERE (log_type LIKE '%$q%' OR log_action LIKE '%$q%' OR log_description LIKE '%$q%' OR log_ip LIKE '%$q%' OR log_user_agent LIKE '%$q%' OR user_name LIKE '%$q%' OR client_name LIKE '%$q%')
@@ -67,7 +66,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-history me-2"></i>Audit Logs</h3>
<h3 class="card-title"><i class="fas fa-fw fa-history mr-2"></i>Audit Logs</h3>
</div>
<div class="card-body">
<form class="mb-4" autocomplete="off">
@@ -75,18 +74,20 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-sm-4">
<div class="input-group mb-3 mb-md-0">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search audit logs">
<button class="btn btn-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#advancedFilter"><i class="fas fa-filter"></i></button>
<div class="input-group-append">
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
<div class="col-sm-2">
<div class="input-group mb-3 mb-md-0">
<select class="form-select select2" name="client" onchange="this.form.submit()">
<select class="form-control select2" name="client" onchange="this.form.submit()">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients ORDER BY client_name ASC");
$sql_clients_filter = mysqli_query($mysqli, "SELECT * FROM clients ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$client_id = intval($row['client_id']);
$client_name = escapeHtml($row['client_name']);
@@ -102,11 +103,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-sm-2">
<div class="input-group mb-3 mb-md-0">
<select class="form-select select2" name="user" onchange="this.form.submit()">
<select class="form-control select2" name="user" onchange="this.form.submit()">
<option value="">- All Users -</option>
<?php
$sql_users_filter = mysqli_query($mysqli, "SELECT user_id, user_name FROM users ORDER BY user_name ASC");
$sql_users_filter = mysqli_query($mysqli, "SELECT * FROM users ORDER BY user_name ASC");
while ($row = mysqli_fetch_assoc($sql_users_filter)) {
$user_id = intval($row['user_id']);
$user_name = escapeHtml($row['user_name']);
@@ -122,7 +123,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-sm-2">
<div class="input-group mb-3 mb-md-0">
<select class="form-select select2" name="type" onchange="this.form.submit()">
<select class="form-control select2" name="type" onchange="this.form.submit()">
<option value="">- All Types -</option>
<?php
@@ -141,7 +142,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-sm-2">
<div class="input-group mb-3 mb-md-0">
<select class="form-select select2" name="action" onchange="this.form.submit()">
<select class="form-control select2" name="action" onchange="this.form.submit()">
<option value="">- All Actions -</option>
<?php
@@ -161,7 +162,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="collapse mt-3 <?php if (isset($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') { echo "show"; } ?>" id="advancedFilter">
<div class="row">
<div class="col-md-3">
<div class="mb-3">
<div class="form-group">
<label>Date range</label>
<input type="text" id="dateFilter" class="form-control" autocomplete="off">
<input type="hidden" name="canned_date" id="canned_date" value="<?= escapeHtml($_GET['canned_date']) ?? '' ?>">
@@ -253,7 +254,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
?>
<tr>
<td class="font-monospace"><?= $log_created_at ?></td>
<td class="text-monospace"><?= $log_created_at ?></td>
<td><?= $user_name_display ?></td>
<?php if(empty($client)) { ?>
<td><?= $client_name_display ?></td>
@@ -261,7 +262,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<td><?= $log_type ?></td>
<td><?= $log_action ?></td>
<td><?= $log_description ?></td>
<td class="font-monospace"><?= $log_ip ?></td>
<td class="text-monospace"><?= $log_ip ?></td>
<td><?= "$log_user_os<div class='text-secondary'>$log_user_browser</div>" ?></td>
</tr>

View File

@@ -19,8 +19,7 @@ $cron_is_running = $cron_last_dispatch_at !== null && (time() - strtotime($cron_
$backup_job = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT cron_job_enabled, cron_job_daily_at FROM cron_jobs WHERE cron_job_name = 'backup'"));
$backups = mysqli_query($mysqli, "SELECT backup_created_at, backup_error, backup_id, backup_size, backup_source, backup_status,
backup_type FROM backups ORDER BY backup_created_at DESC LIMIT 100");
$backups = mysqli_query($mysqli, "SELECT * FROM backups ORDER BY backup_created_at DESC LIMIT 100");
$pending_count = intval(mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(*) AS c FROM backups WHERE backup_status IN ('Pending','Running')"))['c']);
@@ -33,15 +32,15 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
unset($_SESSION['backup_master_key_reveal']);
?>
<div class="alert alert-warning">
<h5><i class="fas fa-fw fa-key me-2"></i>Master encryption key</h5>
<h5><i class="fas fa-fw fa-key mr-2"></i>Master encryption key</h5>
<p class="mb-2">Shown once. Refreshing this page will not show it again.</p>
<input type="text" class="form-control font-monospace" value="<?= escapeHtml($master_key_reveal) ?>" readonly onclick="this.select();">
<input type="text" class="form-control text-monospace" value="<?= escapeHtml($master_key_reveal) ?>" readonly onclick="this.select();">
</div>
<?php } ?>
<?php if ($backup_key === '') { ?>
<div class="alert alert-danger">
<h5><i class="fas fa-fw fa-exclamation-triangle me-2"></i>No backup encryption key</h5>
<h5><i class="fas fa-fw fa-exclamation-triangle mr-2"></i>No backup encryption key</h5>
ITFlow could not write a backup encryption key to <strong>config.php</strong>, so it cannot produce an encrypted backup.
Make config.php writable by the web server user and reload this page, or add a line like
<code>$config_backup_key = '&lt;32 random characters&gt;';</code> to it yourself.
@@ -50,26 +49,26 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<?php if (!$cron_is_running) { ?>
<div class="alert alert-danger">
<h5><i class="fas fa-fw fa-exclamation-triangle me-2"></i>Cron is not running</h5>
<h5><i class="fas fa-fw fa-exclamation-triangle mr-2"></i>Cron is not running</h5>
Backups are built by the cron dispatcher, not by your browser. Until cron is running, anything you
start here will sit in the queue. See <a href="cron.php">Maintenance &gt; Cron</a>.
</div>
<?php } elseif ($config_enable_cron == 0) { ?>
<div class="alert alert-warning">
<i class="fas fa-fw fa-exclamation-circle me-2"></i>Cron is switched off in
<i class="fas fa-fw fa-exclamation-circle mr-2"></i>Cron is switched off in
<a href="cron.php">Maintenance &gt; Cron</a>.
</div>
<?php } ?>
<div class="card card-dark mb-3">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-cloud-upload-alt me-2"></i>Create a Backup</h3>
<h3 class="card-title"><i class="fas fa-fw fa-cloud-upload-alt mr-2"></i>Create a Backup</h3>
</div>
<div class="card-body">
<?php if ($pending_count > 0) { ?>
<div class="alert alert-info">
<i class="fas fa-fw fa-spinner me-2"></i><strong><?= $pending_count ?></strong> backup<?= $pending_count == 1 ? ' is' : 's are' ?>
<i class="fas fa-fw fa-spinner mr-2"></i><strong><?= $pending_count ?></strong> backup<?= $pending_count == 1 ? ' is' : 's are' ?>
queued or building. You will get a notification when ready - this page does not refresh itself.
</div>
<?php } ?>
@@ -81,7 +80,7 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<h5>Full Backup</h5>
<p class="text-muted small">The database and everything in the uploads folder. This is the one to keep.</p>
<a class="btn btn-primary <?= $backup_key === '' ? 'disabled' : '' ?>" href="post.php?queue_backup=full&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-play me-2"></i>Start
<i class="fas fa-fw fa-play mr-2"></i>Start
</a>
</div>
</div>
@@ -91,7 +90,7 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<h5>Database Only</h5>
<p class="text-muted small">Just the SQL dump. Much smaller and much quicker, but no attachments or documents.</p>
<a class="btn btn-primary <?= $backup_key === '' ? 'disabled' : '' ?>" href="post.php?queue_backup=database&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-play me-2"></i>Start
<i class="fas fa-fw fa-play mr-2"></i>Start
</a>
</div>
</div>
@@ -100,8 +99,8 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<i class="fas fa-fw fa-3x fa-key text-dark mb-3"></i>
<h5>Master Key</h5>
<p class="text-muted small">The credential vault key. Only needed if every user password is lost - a normal restore recovers the vault on its own.</p>
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#masterKeyModal">
<i class="fas fa-fw fa-key me-2"></i>Export
<button type="button" class="btn btn-secondary" data-toggle="modal" data-target="#masterKeyModal">
<i class="fas fa-fw fa-key mr-2"></i>Export
</button>
</div>
</div>
@@ -112,7 +111,7 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<div class="card card-dark mb-3">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-archive me-2"></i>Backups</h3>
<h3 class="card-title"><i class="fas fa-fw fa-archive mr-2"></i>Backups</h3>
</div>
<div class="card-body p-0">
<div class="table-responsive">
@@ -124,7 +123,7 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<th>Size</th>
<th>Source</th>
<th>Status</th>
<th class="text-end">Action</th>
<th class="text-right">Action</th>
</tr>
</thead>
<tbody>
@@ -148,12 +147,12 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<td><?= $backup['backup_size'] > 0 ? escapeHtml(backupFormatBytes($backup['backup_size'])) : '-' ?></td>
<td><?= escapeHtml($backup['backup_source']) ?></td>
<td>
<span class="badge text-bg-<?= $badge ?>"><?= escapeHtml($status) ?></span>
<span class="badge badge-<?= $badge ?>"><?= escapeHtml($status) ?></span>
<?php if (!empty($backup['backup_error'])) { ?>
<br><small class="text-danger"><?= escapeHtml($backup['backup_error']) ?></small>
<?php } ?>
</td>
<td class="text-end">
<td class="text-right">
<?php if ($status === 'Complete') { ?>
<a class="btn btn-sm btn-primary" href="backup_download.php?backup_id=<?= $backup_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-download"></i>
@@ -173,11 +172,11 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<div class="card card-dark mb-3">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-lock me-2"></i>Encryption Key</h3>
<h3 class="card-title"><i class="fas fa-fw fa-lock mr-2"></i>Encryption Key</h3>
</div>
<div class="card-body">
<div class="alert alert-warning mb-3">
<i class="fas fa-fw fa-exclamation-triangle me-2"></i>
<i class="fas fa-fw fa-exclamation-triangle mr-2"></i>
<strong>Write this down and keep it somewhere other than this server.</strong>
Every backup is encrypted with it, and without it a backup cannot be restored - not by you,
not by anyone. It is stored in config.php and never in the database, which is what stops a
@@ -186,10 +185,12 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<?php if ($backup_key !== '') { ?>
<div class="input-group col-md-6 px-0">
<input type="text" class="form-control font-monospace" value="<?= escapeHtml($backup_key) ?>" readonly onclick="this.select();">
<input type="text" class="form-control text-monospace" value="<?= escapeHtml($backup_key) ?>" readonly onclick="this.select();">
<div class="input-group-append">
<button class="btn btn-secondary" type="button" onclick="navigator.clipboard.writeText('<?= escapeHtml($backup_key) ?>');">
<i class="fas fa-fw fa-copy"></i>
</button>
</div>
</div>
<?php } ?>
@@ -202,43 +203,43 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<div class="card card-dark mb-3">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-clock me-2"></i>Scheduled Backups &amp; Retention</h3>
<h3 class="card-title"><i class="fas fa-fw fa-clock mr-2"></i>Scheduled Backups &amp; Retention</h3>
</div>
<div class="card-body">
<form action="post.php" method="POST" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row g-2">
<div class="mb-3 col-md-4">
<div class="form-row">
<div class="form-group col-md-4">
<label>Scheduled backup type</label>
<select class="form-select" name="config_backup_cron_type">
<select class="form-control" name="config_backup_cron_type">
<option <?= $config_backup_cron_type === 'full' ? 'selected' : '' ?> value="full">Full Backup</option>
<option <?= $config_backup_cron_type === 'database' ? 'selected' : '' ?> value="database">Database Only</option>
</select>
</div>
<div class="mb-3 col-md-4">
<div class="form-group col-md-4">
<label>Keep backups for (days)</label>
<input type="number" class="form-control" name="config_backup_retention_days" min="0" value="<?= intval($config_backup_retention_days) ?>">
<small class="text-muted">0 disables age-based deletion.</small>
</div>
<div class="mb-3 col-md-4">
<div class="form-group col-md-4">
<label>Keep at most (per type)</label>
<input type="number" class="form-control" name="config_backup_retention_count" min="1" value="<?= intval($config_backup_retention_count) ?>">
<small class="text-muted">Counted separately for each type. The newest of each is never deleted.</small>
</div>
</div>
<button type="submit" name="edit_backup_settings" class="btn btn-primary"><i class="fas fa-fw fa-check me-2"></i>Save</button>
<button type="submit" name="edit_backup_settings" class="btn btn-primary"><i class="fas fa-fw fa-check mr-2"></i>Save</button>
</form>
<hr>
<p class="mb-0">
<?php if (!empty($backup_job) && intval($backup_job['cron_job_enabled']) === 1) { ?>
<i class="fas fa-fw fa-check text-success me-2"></i>Scheduled backups run daily at
<i class="fas fa-fw fa-check text-success mr-2"></i>Scheduled backups run daily at
<strong><?= escapeHtml(substr((string)$backup_job['cron_job_daily_at'], 0, 5)) ?></strong>.
<?php } else { ?>
<i class="fas fa-fw fa-times text-danger me-2"></i>Scheduled backups are switched off.
<i class="fas fa-fw fa-times text-danger mr-2"></i>Scheduled backups are switched off.
<?php } ?>
Turn them on or change the time in <a href="cron.php">Maintenance &gt; Cron</a>.
</p>
@@ -251,7 +252,7 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-undo me-2"></i>Restoring</h3>
<h3 class="card-title"><i class="fas fa-fw fa-undo mr-2"></i>Restoring</h3>
</div>
<div class="card-body">
<p>Restoring replaces the database and the uploads folder with what is in the archive. It cannot be done from here, on purpose - a running install is the wrong place to be dropping its own tables from a browser.</p>
@@ -265,8 +266,8 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-fw fa-key me-2"></i>Export Master Key</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-key mr-2"></i>Export Master Key</h5>
<button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
</div>
<form action="post.php" method="POST" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -274,13 +275,13 @@ if (!empty($_SESSION['backup_master_key_reveal'])) {
<div class="alert alert-warning">
This key decrypts every credential in this install. It is shown on screen and is not written anywhere.
</div>
<div class="mb-3">
<div class="form-group">
<label>Confirm your account password</label>
<input type="password" class="form-control" name="password" autocomplete="new-password" required>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="backup_master_key" class="btn btn-primary"><i class="fas fa-fw fa-key me-2"></i>Show Master Key</button>
<button type="submit" name="backup_master_key" class="btn btn-primary"><i class="fas fa-fw fa-key mr-2"></i>Show Master Key</button>
</div>
</form>
</div>

View File

@@ -23,7 +23,7 @@ if (!isset($_GET['backup_id'])) {
$backup_id = intval($_GET['backup_id']);
$sql = mysqli_query($mysqli, "SELECT backup_file_name FROM backups WHERE backup_id = $backup_id AND backup_status = 'Complete' LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM backups WHERE backup_id = $backup_id AND backup_status = 'Complete' LIMIT 1");
if (mysqli_num_rows($sql) !== 1) {
http_response_code(404);

View File

@@ -1,135 +0,0 @@
<?php
// Default Column Sortby Filter
$sort = "canned_response_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
/*
* A canned response either belongs to one ticket category or, with category id 0, offers
* itself on every ticket. The LEFT JOIN is what puts the category name on the row; a
* response whose category was deleted since falls back to the general case below.
*/
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS canned_response_id, canned_response_name, canned_response_body,
canned_response_category_id, category_name
FROM canned_responses
LEFT JOIN categories ON canned_response_category_id = category_id AND category_type = 'Ticket'
WHERE (canned_response_name LIKE '%$q%' OR canned_response_body LIKE '%$q%')
AND canned_response_archived_at IS NULL
ORDER BY $sort $order
LIMIT $record_from, $record_to"
);
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-comment-dots me-2"></i>Canned Responses</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/canned_response/canned_response_add.php" data-modal-size="lg"><i class="fas fa-plus me-2"></i>New Canned Response</button>
</div>
</div>
<div class="card-body">
<form autocomplete="off">
<div class="row">
<div class="col-md-4">
<div class="input-group mb-3 mb-md-0">
<input type="search" class="form-control" name="q" value="<?php if(isset($q)){ echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search Canned Responses">
<button class="btn btn-dark"><i class="fa fa-search"></i></button>
</div>
</div>
<div class="col-md-8">
</div>
</div>
</form>
<hr>
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if($num_rows[0] == 0) { echo "d-none"; } ?>">
<tr>
<th>
<a class="text-secondary" href="?<?= $url_query_strings_sort ?>&sort=canned_response_name&order=<?= $disp ?>">
Response <?php if ($sort == 'canned_response_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-secondary" href="?<?= $url_query_strings_sort ?>&sort=category_name&order=<?= $disp ?>">
Ticket Category <?php if ($sort == 'category_name') { echo $order_icon; } ?>
</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while($row = mysqli_fetch_assoc($sql)){
$canned_response_id = intval($row['canned_response_id']);
$canned_response_name = escapeHtml($row['canned_response_name']);
$canned_response_category_id = intval($row['canned_response_category_id']);
$canned_response_category_name = escapeHtml($row['category_name']);
// The body is HTML from TinyMCE. This is a preview, not a render - tags
// are stripped rather than escaped so the column reads as the sentence
// the agent will actually be inserting
$canned_response_preview = escapeHtml(mb_strimwidth(trim(html_entity_decode(strip_tags($row['canned_response_body']), ENT_QUOTES, 'UTF-8')), 0, 120, '...'));
?>
<tr>
<td>
<div class="d-flex">
<i class="fa fa-fw fa-2x fa-comment-dots me-3"></i>
<div class="flex-grow-1">
<div>
<a class="ajax-modal" href="#" data-modal-url="modals/canned_response/canned_response_edit.php?id=<?= $canned_response_id ?>" data-modal-size="lg">
<?= $canned_response_name ?>
</a>
</div>
<div><small class="text-secondary"><?= $canned_response_preview ?></small></div>
</div>
</div>
</td>
<td>
<?php if ($canned_response_category_id && $canned_response_category_name) { ?>
<span class="badge bg-secondary"><?= $canned_response_category_name ?></span>
<?php } else { ?>
<span class="badge bg-dark">All ticket categories</span>
<?php } ?>
</td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" data-bs-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/canned_response/canned_response_edit.php?id=<?= $canned_response_id ?>" data-modal-size="lg">
<i class="fas fa-fw fa-edit me-2"></i>Edit
</a>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_canned_response=<?= $canned_response_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
</a>
</div>
</div>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<?php
require_once "../includes/footer.php";

View File

@@ -15,7 +15,7 @@ if (isset($_GET['category'])) {
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS category_color, category_description, category_id, category_name FROM categories
"SELECT SQL_CALC_FOUND_ROWS * FROM categories
WHERE category_name LIKE '%$q%'
AND category_type = '$category'
AND category_$archive_query
@@ -67,7 +67,7 @@ $archive_toggle_url = '?' . http_build_query($archive_toggle_query);
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fa fa-fw fa-list-ul me-2"></i>
<h3 class="card-title mt-2"><i class="fa fa-fw fa-list-ul mr-2"></i>
<?= escapeHtml($category_label) ?> Categories
</h3>
<?php
@@ -75,7 +75,7 @@ $archive_toggle_url = '?' . http_build_query($archive_toggle_query);
?>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/category/category_add.php?category=<?= escapeHtml($category) ?>"><i
class="fas fa-plus me-2"></i>New <?= escapeHtml($category_label) ?> Category</button>
class="fas fa-plus mr-2"></i>New <?= escapeHtml($category_label) ?> Category</button>
</div>
<?php
}
@@ -85,15 +85,15 @@ $archive_toggle_url = '?' . http_build_query($archive_toggle_query);
<div class="row">
<!-- Category types -->
<div class="col-md-3 border-end mb-3">
<div class="col-md-3 border-right mb-3">
<ul class="nav nav-pills flex-column bg-light">
<?php foreach ($category_types as $category_type => $category_type_details) { ?>
<li class="nav-item">
<a class="nav-link<?php if ($category == $category_type) {
echo ' active';
} ?>" href="?category=<?= urlencode($category_type) ?>">
<i class="fa fa-fw <?= $category_type_details['icon'] ?> me-2"></i><?= escapeHtml($category_type_details['label']) ?>
<span class="badge rounded-pill bg-dark float-end mt-1"><?= $category_type_counts[$category_type] ?? 0 ?></span>
<i class="fa fa-fw <?= $category_type_details['icon'] ?> mr-2"></i><?= escapeHtml($category_type_details['label']) ?>
<span class="badge badge-pill badge-dark float-right mt-1"><?= $category_type_counts[$category_type] ?? 0 ?></span>
</a>
</li>
<?php } ?>
@@ -115,17 +115,19 @@ $archive_toggle_url = '?' . http_build_query($archive_toggle_query);
echo stripslashes(escapeHtml($q));
} ?>"
placeholder="Search <?= escapeHtml($category_label) ?> Categories ">
<div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
<div class="col-sm-6 mb-2">
<a href="<?= $archive_toggle_url ?>"
class="btn float-end <?php if ($archived) {
class="btn float-right <?php if ($archived) {
echo 'btn-primary';
} else {
echo 'btn-default';
} ?>"><i
class="fas fa-fw fa-archive me-2"></i>Archived</a>
class="fas fa-fw fa-archive mr-2"></i>Archived</a>
</div>
</div>
</form>
@@ -163,8 +165,8 @@ $archive_toggle_url = '?' . http_build_query($archive_toggle_query);
</td>
<td><i class="fa fa-3x fa-circle" style="color:<?= $category_color ?>;"></i></td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
@@ -173,22 +175,22 @@ $archive_toggle_url = '?' . http_build_query($archive_toggle_query);
?>
<a class="dropdown-item text-info confirm-link"
href="post.php?restore_category=<?= $category_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-redo me-2"></i>Restore
<i class="fas fa-fw fa-redo mr-2"></i>Restore
</a>
<a class="dropdown-item text-danger confirm-link"
href="post.php?delete_category=<?= $category_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
<?php
} else {
?>
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/category/category_edit.php?id=<?= $category_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<a class="dropdown-item text-danger confirm-link"
href="post.php?archive_category=<?= $category_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-archive me-2"></i>Archive
<i class="fas fa-fw fa-archive mr-2"></i>Archive
</a>
<?php
}

View File

@@ -20,10 +20,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file-contract me-2"></i>Contract Templates</h3>
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file-contract mr-2"></i>Contract Templates</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/contract_template/contract_template_add.php" data-modal-size="lg">
<i class="fas fa-plus me-2"></i>New Template
<i class="fas fa-plus mr-2"></i>New Template
</button>
</div>
</div>
@@ -32,7 +32,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<form autocomplete="off">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search templates">
<div class="input-group-append">
<button class="btn btn-secondary"><i class="fa fa-search"></i></button>
</div>
</div>
</form>
<hr>
@@ -61,15 +63,15 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$id = intval($row['contract_template_id']);
$name = escapeHtml($row['contract_template_name']);
$type = escapeHtml($row['contract_template_type']);
$freq = escapeHtml($row['contract_template_renewal_frequency']);
$sla_low_resp = escapeHtml($row['contract_template_sla_low_response_time']);
$sla_med_resp = escapeHtml($row['contract_template_sla_medium_response_time']);
$sla_high_resp = escapeHtml($row['contract_template_sla_high_response_time']);
$sla_low_res = escapeHtml($row['contract_template_sla_low_resolution_time']);
$sla_med_res = escapeHtml($row['contract_template_sla_medium_resolution_time']);
$sla_high_res = escapeHtml($row['contract_template_sla_high_resolution_time']);
$hourly_rate = escapeHtml($row['contract_template_rate_standard']);
$after_hours = escapeHtml($row['contract_template_rate_after_hours']);
$freq = escapeHtml($row['contract_template_update_frequency']);
$sla_low_resp = escapeHtml($row['sla_low_response_time']);
$sla_med_resp = escapeHtml($row['sla_medium_response_time']);
$sla_high_resp = escapeHtml($row['sla_high_response_time']);
$sla_low_res = escapeHtml($row['sla_low_resolution_time']);
$sla_med_res = escapeHtml($row['sla_medium_resolution_time']);
$sla_high_res = escapeHtml($row['sla_high_resolution_time']);
$hourly_rate = escapeHtml($row['contract_template_hourly_rate']);
$after_hours = escapeHtml($row['contract_template_after_hours_hourly_rate']);
$support_hours = escapeHtml($row['contract_template_support_hours']);
$net_terms = escapeHtml($row['contract_template_net_terms']);
$created = escapeHtml($row['contract_template_created_at']);
@@ -93,19 +95,19 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<td><?= $created ?></td>
<td><?= $updated ?></td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-size="xl"
data-modal-url="modals/contract_template/contract_template_edit.php?id=<?= $id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger text-bold" href="post.php?delete_contract_template=<?= $id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>

View File

@@ -22,9 +22,7 @@ foreach (cronJobRegistry() as $job) {
$cron_jobs[$job['name']]['row'] = null;
}
$sql = mysqli_query($mysqli, "SELECT cron_job_daily_at, cron_job_enabled, cron_job_id, cron_job_interval_minutes,
cron_job_last_duration, cron_job_last_error, cron_job_last_error_at, cron_job_last_run_at, cron_job_last_status,
cron_job_name, cron_job_run_now, cron_job_schedule FROM cron_jobs");
$sql = mysqli_query($mysqli, "SELECT * FROM cron_jobs");
while ($job_row = mysqli_fetch_assoc($sql)) {
if (isset($cron_jobs[$job_row['cron_job_name']])) {
$cron_jobs[$job_row['cron_job_name']]['row'] = $job_row;
@@ -35,13 +33,13 @@ while ($job_row = mysqli_fetch_assoc($sql)) {
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-clock me-2"></i>Cron</h3>
<h3 class="card-title"><i class="fas fa-fw fa-clock mr-2"></i>Cron</h3>
</div>
<div class="card-body">
<?php if (!$cron_is_running) { ?>
<div class="alert alert-danger">
<h5><i class="fas fa-fw fa-exclamation-triangle me-2"></i>Cron is not running</h5>
<h5><i class="fas fa-fw fa-exclamation-triangle mr-2"></i>Cron is not running</h5>
ITFlow last heard from cron <strong><?= escapeHtml(strtolower(cronJobTimeAgo($cron_last_dispatch_at))) ?></strong>.
Nothing below will run - no mail is being sent, no email is being turned into tickets, and invoices are not being generated.
Add this line to the crontab of the user that owns the ITFlow files:
@@ -49,26 +47,26 @@ while ($job_row = mysqli_fetch_assoc($sql)) {
</div>
<?php } else { ?>
<div class="alert alert-success">
<i class="fas fa-fw fa-check me-2"></i>Cron last checked in <strong><?= escapeHtml(strtolower(cronJobTimeAgo($cron_last_dispatch_at))) ?></strong>.
<span class="text-muted ms-2"><?= escapeHtml($cron_command) ?></span>
<i class="fas fa-fw fa-check mr-2"></i>Cron last checked in <strong><?= escapeHtml(strtolower(cronJobTimeAgo($cron_last_dispatch_at))) ?></strong>.
<span class="text-muted ml-2"><?= escapeHtml($cron_command) ?></span>
</div>
<?php } ?>
<?php if ($config_enable_cron == 0) { ?>
<div class="alert alert-warning">
<div class="float-end">
<div class="float-right">
<a class="btn btn-sm btn-warning" href="post.php?enable_cron=1&amp;csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-power-off me-2"></i>Turn cron on
<i class="fas fa-fw fa-power-off mr-2"></i>Turn cron on
</a>
</div>
<h5><i class="fas fa-fw fa-exclamation-circle me-2"></i>Cron is switched off</h5>
<h5><i class="fas fa-fw fa-exclamation-circle mr-2"></i>Cron is switched off</h5>
The dispatcher is running, but every job below stops itself immediately while this is off -
no mail is sent, no email becomes a ticket, and nothing is invoiced.
</div>
<?php } else { ?>
<p class="text-muted">
<small>
<i class="fas fa-fw fa-power-off me-1"></i>The master switch is <strong>on</strong>. Turning it off
<i class="fas fa-fw fa-power-off mr-1"></i>The master switch is <strong>on</strong>. Turning it off
stops every job at once without touching their schedules, which is what you want on a restored
backup or a staging clone - those come up with every job enabled and will otherwise email clients
and charge cards. Switching back on returns you to exactly this configuration.
@@ -112,24 +110,24 @@ while ($job_row = mysqli_fetch_assoc($sql)) {
$next_run = $job_row ? cronJobNextRun($job_row) : null;
if ($run_now) {
$status_badge = '<span class="badge bg-warning text-dark">Queued</span>';
$status_badge = '<span class="badge badge-warning">Queued</span>';
} elseif ($last_status === 'Running') {
$status_badge = '<span class="badge bg-info text-dark">Running</span>';
$status_badge = '<span class="badge badge-info">Running</span>';
} elseif ($last_status === 'Completed') {
$status_badge = '<span class="badge bg-success">Completed</span>';
$status_badge = '<span class="badge badge-success">Completed</span>';
} elseif ($last_status === 'Failed') {
$status_badge = '<span class="badge bg-danger">Failed</span>';
$status_badge = '<span class="badge badge-danger">Failed</span>';
} elseif ($last_status !== null) {
$status_badge = '<span class="badge bg-secondary">Stopped</span>';
$status_badge = '<span class="badge badge-secondary">Stopped</span>';
} else {
$status_badge = '<span class="badge bg-light text-dark">Never run</span>';
$status_badge = '<span class="badge badge-light">Never run</span>';
}
?>
<tr class="<?= $enabled ? '' : 'text-muted' ?>">
<td>
<strong><?= escapeHtml($job['label']) ?></strong>
<?php if (!$enabled) { ?><span class="badge bg-secondary ms-1">Disabled</span><?php } ?>
<?php if (!$enabled) { ?><span class="badge badge-secondary ml-1">Disabled</span><?php } ?>
<br><small class="text-secondary"><?= escapeHtml($job['description']) ?></small>
<br><small class="text-muted"><code>cron/<?= escapeHtml($job['script']) ?></code></small>
</td>
@@ -147,25 +145,25 @@ while ($job_row = mysqli_fetch_assoc($sql)) {
</td>
<td><?= $next_run === null ? '-' : escapeHtml(cronJobTimeAgo($next_run)) ?></td>
<td class="text-center">
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item <?= $cron_job_id === 0 ? 'disabled' : '' ?>" href="post.php?run_cron_job=<?= $cron_job_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-play me-2"></i>Run Now
<i class="fas fa-fw fa-play mr-2"></i>Run Now
</a>
<button class="dropdown-item ajax-modal <?= $cron_job_id === 0 ? 'disabled' : '' ?>" type="button" data-toggle="ajax-modal"
data-modal-url="modals/cron/cron_edit.php?id=<?= $cron_job_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit Schedule
<i class="fas fa-fw fa-edit mr-2"></i>Edit Schedule
</button>
<?php if ($enabled) { ?>
<a class="dropdown-item text-danger" href="post.php?disable_cron_job=<?= $cron_job_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-pause me-2"></i>Disable
<i class="fas fa-fw fa-pause mr-2"></i>Disable
</a>
<?php } else { ?>
<a class="dropdown-item text-success" href="post.php?enable_cron_job=<?= $cron_job_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-play-circle me-2"></i>Enable
<i class="fas fa-fw fa-play-circle mr-2"></i>Enable
</a>
<?php } ?>
</div>
@@ -176,13 +174,13 @@ while ($job_row = mysqli_fetch_assoc($sql)) {
<tr>
<td colspan="7" class="pt-0">
<div class="alert alert-danger mb-0 py-2">
<div class="float-end">
<div class="float-right">
<a class="text-danger" href="post.php?clear_cron_error=<?= $cron_job_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>" title="Dismiss">
<i class="fas fa-fw fa-times"></i>
</a>
</div>
<strong><i class="fas fa-fw fa-exclamation-triangle me-2"></i>Last error</strong>
<small class="text-muted ms-2"><?= escapeHtml(cronJobTimeAgo($last_error_at)) ?></small>
<strong><i class="fas fa-fw fa-exclamation-triangle mr-2"></i>Last error</strong>
<small class="text-muted ml-2"><?= escapeHtml(cronJobTimeAgo($last_error_at)) ?></small>
<div class="mt-1"><small><?= escapeHtml($last_error) ?></small></div>
</div>
</td>
@@ -195,7 +193,7 @@ while ($job_row = mysqli_fetch_assoc($sql)) {
<p class="text-muted mb-0">
<small>
<i class="fas fa-fw fa-info-circle me-1"></i>Run Now does not start the job in your browser - it asks the
<i class="fas fa-fw fa-info-circle mr-1"></i>Run Now does not start the job in your browser - it asks the
dispatcher to pick it up on its next pass, so a job starts within a minute and still runs on the command
line with the same locking as a scheduled run. Detailed per-job output is in
<a href="app_logs.php">App Logs</a>.

View File

@@ -8,8 +8,7 @@ require_once "includes/inc_all_admin.php";
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS custom_link_icon, custom_link_id, custom_link_location, custom_link_name,
custom_link_new_tab, custom_link_order, custom_link_uri FROM custom_links
"SELECT SQL_CALC_FOUND_ROWS * FROM custom_links
WHERE custom_link_name LIKE '%$q%'
ORDER BY $sort $order LIMIT $record_from, $record_to"
);
@@ -20,9 +19,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-external-link-alt me-2"></i>Custom Links</h3>
<h3 class="card-title mt-2"><i class="fas fa-fw fa-external-link-alt mr-2"></i>Custom Links</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/custom_link/custom_link_add.php"><i class="fas fa-plus me-2"></i>New Link</button>
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/custom_link/custom_link_add.php"><i class="fas fa-plus mr-2"></i>New Link</button>
</div>
</div>
@@ -32,7 +31,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<form autocomplete="off">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search Links">
<div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</form>
</div>
@@ -106,24 +107,24 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<td>
<a class="ajax-modal" href="#"
data-modal-url="modals/custom_link/custom_link_edit.php?id=<?= $custom_link_id ?>">
<i class="fa fa-fw fa-<?= $custom_link_icon ?> me-2"></i><?= $custom_link_name ?>
<i class="fa fa-fw fa-<?= $custom_link_icon ?> mr-2"></i><?= $custom_link_name ?>
</a>
</td>
<td><?= $custom_link_order_display ?></td>
<td><?= "$custom_link_uri $custom_link_new_tab_display" ?></td>
<td><?= $custom_link_location_display ?></td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/custom_link/custom_link_edit.php?id=<?= $custom_link_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_custom_link=<?= $custom_link_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>

View File

@@ -8,7 +8,7 @@
defined('FROM_DB_UPDATER') || die("Direct file access is not allowed");
// Migrate Payment Methods from Categories Table to new payment_methods table
$sql_categories = mysqli_query($mysqli, "SELECT category_name FROM categories WHERE category_type = 'Payment Method' AND category_name != 'Stripe' AND category_archived_at IS NULL");
$sql_categories = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Payment Method' AND category_name != 'Stripe' AND category_archived_at IS NULL");
while ($row = mysqli_fetch_assoc($sql_categories)) {
$category_name = escapeSql($row['category_name']);

View File

@@ -1,18 +0,0 @@
<?php
/*
* ITFlow - Database update to version 2.6.7 (from 2.6.6)
* Included by admin/database_updates.php - do not access directly
*/
defined('FROM_DB_UPDATER') || die("Direct file access is not allowed");
// The AI endpoints used to send a hardcoded temperature (0.5, or 0.3 for ticket
// summaries). Newer OpenAI models accept nothing but their own default and reject
// the request outright, which surfaced as "Failed to get a response from the AI API".
//
// Temperature is now per-model and optional: NULL means don't send the parameter
// at all, which is the setting that works on every provider. Existing rows get
// NULL so they stop sending it.
mysqli_query($mysqli, "ALTER TABLE `ai_models` ADD COLUMN IF NOT EXISTS `ai_model_temperature` decimal(3,2) DEFAULT NULL AFTER `ai_model_use_case`");

View File

@@ -1,20 +0,0 @@
<?php
/*
* ITFlow - Database update to version 2.6.8 (from 2.6.7)
* Included by admin/database_updates.php - do not access directly
*/
defined('FROM_DB_UPDATER') || die("Direct file access is not allowed");
// Maintenance > Update can now hand an update to cron rather than running it inside the
// request: the page stamps this column and cron/app_update.php takes it and runs
// scripts/update_cli.php in its own process. NULL means nothing is queued, which is why
// the job cannot update an install that did not ask for one.
mysqli_query($mysqli, "ALTER TABLE `settings` ADD COLUMN IF NOT EXISTS `config_update_queued_at` datetime DEFAULT NULL");
// Seeded here as well as by the dispatcher, which only creates rows on its next pass -
// without this, an update queued in the minutes after an upgrade would have no row to
// set cron_job_run_now on and would wait for that pass instead of starting.
mysqli_query($mysqli, "INSERT IGNORE INTO cron_jobs SET cron_job_name = 'app_update', cron_job_enabled = 0, cron_job_schedule = 'Daily', cron_job_daily_at = '05:00'");

View File

@@ -1,27 +0,0 @@
<?php
/*
* ITFlow - Database update to version 2.6.9 (from 2.6.8)
* Included by admin/database_updates.php - do not access directly
*/
defined('FROM_DB_UPDATER') || die("Direct file access is not allowed");
// Canned responses for ticket replies, managed from Admin > Canned Responses and
// picked from a dropdown on the ticket reply form.
//
// canned_response_category_id is a ticket category (categories.category_id where
// category_type = 'Ticket'), or 0 for one that offers itself on every ticket
// whatever its category.
mysqli_query($mysqli, "CREATE TABLE IF NOT EXISTS `canned_responses` (
`canned_response_id` int(11) NOT NULL AUTO_INCREMENT,
`canned_response_name` varchar(200) NOT NULL,
`canned_response_body` longtext DEFAULT NULL,
`canned_response_category_id` int(11) NOT NULL DEFAULT 0,
`canned_response_created_at` datetime NOT NULL DEFAULT current_timestamp(),
`canned_response_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),
`canned_response_archived_at` datetime DEFAULT NULL,
PRIMARY KEY (`canned_response_id`),
KEY `canned_response_category_id` (`canned_response_category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci");

View File

@@ -1,39 +0,0 @@
<?php
/*
* ITFlow - Database update to version 2.7.0 (from 2.6.9)
* Included by admin/database_updates.php - do not access directly
*/
defined('FROM_DB_UPDATER') || die("Direct file access is not allowed");
// The five built-in ticket statuses now have fixed SLA clock behaviour, matching
// getTicketStatusSlaLock() in functions/app.php: On Hold, Resolved and Closed
// pause the resolution clock, New and Open run it. Admin > Ticket Statuses no
// longer offers the choice on these five - only on custom statuses.
//
// On Hold shipped with the column defaulting to 0, so a stock install breached
// the resolution SLA on tickets parked waiting for a client or a vendor. Every
// install had to find the toggle themselves; this sets it for them.
//
// Resolved and Closed are set for consistency across the SLA surfaces, not to
// change behaviour - both already stopped the clocks via ticket_resolved_at and
// ticket_closed_at, which is what the cron and syncTicketSlaClock() actually read.
mysqli_query($mysqli, "UPDATE ticket_statuses SET ticket_status_pauses_sla = 1 WHERE ticket_status_id IN (3, 4, 5)");
mysqli_query($mysqli, "UPDATE ticket_statuses SET ticket_status_pauses_sla = 0 WHERE ticket_status_id IN (1, 2)");
/*
* Tickets already parked in On Hold are still holding an open sla_history
* interval, so their paused time would keep counting as consumed. Closing those
* intervals needs business-hours maths in the app timezone, and this file also
* runs from scripts/update_cli.php, which sets neither - so the first
* cron/ticket_sla.php run after the upgrade reconciles them instead, where the
* timezone is set. Nothing is lost by waiting a minute: the same cron no longer
* warns or breaches a paused ticket the moment the flag above lands.
*
* Breach verdicts already recorded against on-hold tickets are deliberately left
* alone. They were recorded by a clock that really was running under the old
* rules, and rewriting a stored miss is exactly what the reopen-integrity rules
* in functions/sla.php exist to prevent. Re-stamp an individual ticket (change
* its priority or its SLA) if you want it re-judged.
*/

View File

@@ -1,17 +0,0 @@
<?php
/*
* ITFlow - Database update to version 2.7.1 (from 2.7.0)
* Included by admin/database_updates.php - do not access directly
*/
defined('FROM_DB_UPDATER') || die("Direct file access is not allowed");
// Which client record, if any, is the MSP's own company. Most installs create one to
// hold their internal tickets, documentation, assets and credentials, but nothing in
// the app knew that record was special. Admin > Settings > Company Details sets it.
//
// 0 means no client is designated, which is every existing install - nothing changes
// until an administrator chooses one.
mysqli_query($mysqli, "ALTER TABLE `settings`
ADD COLUMN `config_internal_client_id` int(11) NOT NULL DEFAULT 0");

View File

@@ -114,12 +114,12 @@ $phpConfig[] = [
'value' => $post_max_size,
];
// PHP Memory Limit >= 512M
// PHP Memory Limit >= 128M
$memoryLimit = ini_get('memory_limit');
$memoryLimitBytes = toBytes($memoryLimit);
$memoryLimitPassed = $memoryLimitBytes >= (512 * 1024 * 1024);
$memoryLimitPassed = $memoryLimitBytes >= (128 * 1024 * 1024);
$phpConfig[] = [
'name' => 'PHP Memory Limit >= 512M',
'name' => 'PHP Memory Limit >= 128M',
'passed' => $memoryLimitPassed,
'value' => $memoryLimit,
];
@@ -508,7 +508,7 @@ $mysqli->close();
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-bug me-2"></i>Debug</h3>
<h3 class="card-title"><i class="fas fa-fw fa-bug mr-2"></i>Debug</h3>
</div>
<div class="card-body">

View File

@@ -15,8 +15,7 @@ if (isset($_GET['document_template_id'])) {
$document_template_id = intval($_GET['document_template_id']);
}
$sql_document = mysqli_query($mysqli, "SELECT document_template_content, document_template_created_at, document_template_description,
document_template_name, document_template_updated_at FROM document_templates WHERE document_template_id = $document_template_id LIMIT 1");
$sql_document = mysqli_query($mysqli, "SELECT * FROM document_templates WHERE document_template_id = $document_template_id LIMIT 1");
if (mysqli_num_rows($sql_document) == 0) {
echo "<center><h1 class='text-secondary mt-5'>Nothing to see here</h1><a class='btn btn-lg btn-secondary mt-3' href='javascript:history.back()'><i class='fa fa-fw fa-arrow-left'></i> Go Back</a></center>";
@@ -44,19 +43,19 @@ $document_template_updated_at = escapeHtml($row['document_template_updated_at'])
<li class="breadcrumb-item">
<a href="document_templates.php">Document Templates</a>
</li>
<li class="breadcrumb-item active"><i class="fas fa-file-alt me-2"></i><?= $document_template_name ?></li>
<li class="breadcrumb-item active"><i class="fas fa-file-alt mr-2"></i><?= $document_template_name ?></li>
</ol>
<div class="card card-dark">
<div class="card-header">
<h3 class="card-title mt-1"><i class="fa fa-fw fa-file-alt me-2"></i><?= $document_template_name ?></h3>
<h3 class="card-title mt-1"><i class="fa fa-fw fa-file-alt mr-2"></i><?= $document_template_name ?></h3>
<div class="card-tools">
<button type="button" class="btn btn-tool ajax-modal"
data-modal-size="xl"
data-modal-url="modals/document_template/document_template_edit.php?id=<?= $document_template_id ?>">
<i class="fas fa-edit me-2"></i>
<i class="fas fa-edit mr-2"></i>
</button>
</div>
</div>

View File

@@ -8,8 +8,7 @@
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS document_template_content, document_template_created_at, document_template_description,
document_template_id, document_template_name, document_template_updated_at, user_name FROM document_templates
"SELECT SQL_CALC_FOUND_ROWS * FROM document_templates
LEFT JOIN users ON document_template_created_by = user_id
WHERE user_name LIKE '%$q%' OR document_template_name LIKE '%$q%'
ORDER BY $sort $order LIMIT $record_from, $record_to"
@@ -21,10 +20,10 @@
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file-alt me-2"></i>Document Templates</h3>
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file-alt mr-2"></i>Document Templates</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/document_template/document_template_add.php" data-modal-size="xl">
<i class="fas fa-plus me-2"></i>New Template
<i class="fas fa-plus mr-2"></i>New Template
</button>
</div>
</div>
@@ -33,7 +32,9 @@
<form autocomplete="off">
<div class="input-group">
<input type="search" class="form-control " name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search templates">
<div class="input-group-append">
<button class="btn btn-secondary"><i class="fa fa-search"></i></button>
</div>
</div>
</form>
<hr>
@@ -81,9 +82,9 @@
<a class="text-dark ajax-modal" href="#"
data-modal-size="xl"
data-modal-url="modals/document_template/document_template_edit.php?id=<?= $document_template_id ?>">
<div class="d-flex">
<i class="fas fa-fw fa-2x fa-file-alt me-2"></i>
<div class="flex-grow-1">
<div class="media">
<i class="fas fa-fw fa-2x fa-file-alt mr-2"></i>
<div class="media-body">
<div><?= $document_template_name ?></div>
<div><small class="text-secondary"><?= $document_template_description ?></small></div>
</div>
@@ -96,23 +97,23 @@
</td>
<td><?= $document_template_updated_at ?></td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="document_template.php?document_template_id=<?= $document_template_id ?>">
<i class="fas fa-fw fa-eye me-2"></i>View
<i class="fas fa-fw fa-eye mr-2"></i>View
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item ajax-modal" href="#"
data-modal-size="xl"
data-modal-url="modals/document_template/document_template_edit.php?id=<?= $document_template_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger text-bold" href="post.php?delete_document_template=<?= $document_template_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>

View File

@@ -4,7 +4,7 @@ require_once "includes/inc_all_admin.php";
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-fingerprint me-2"></i>Identity Providers</h3>
<h3 class="card-title"><i class="fas fa-fw fa-fingerprint mr-2"></i>Identity Providers</h3>
</div>
<div class="card-body">
<form action="post.php" method="post" autocomplete="off">
@@ -12,11 +12,13 @@ require_once "includes/inc_all_admin.php";
<h4>Client Portal SSO via Microsoft Entra</h4>
<div class="mb-3">
<div class="form-group">
<label>Identity Provider <small class='text-secondary'>(Currently only works with Microsoft Entra ID/AAD)</small></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
<select class="form-select select2" readonly>
</div>
<select class="form-control select2" readonly>
<option <?php if (empty($config_azure_client_id)) { echo "selected"; } ?>>Disabled</option>
<option <?php if ($config_azure_client_id) { echo "selected"; } ?>>Microsoft Entra</option>
<option>Google (WIP)</option>
@@ -25,25 +27,29 @@ require_once "includes/inc_all_admin.php";
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>MS Entra OAuth App (Client) ID</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
</div>
<input type="text" class="form-control" name="azure_client_id" placeholder="e721e3b6-01d6-50e8-7f22-c84d951a52e7" maxlength="200" value="<?= escapeHtml($config_azure_client_id) ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>MS Entra OAuth Secret</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="password" class="form-control" name="azure_client_secret" placeholder="Auto-generated from App Registration" maxlength="200" value="<?= escapeHtml($config_azure_client_secret) ?>" autocomplete="new-password">
</div>
</div>
<hr>
<button type="submit" name="edit_identity_provider" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="submit" name="edit_identity_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
</form>
</div>

View File

@@ -1,17 +1,19 @@
<!-- Main Sidebar Container -->
<aside class="app-sidebar shadow d-print-none" data-bs-theme="dark">
<div class="sidebar-brand">
<a class="brand-link" href="/agent/<?= $config_start_page ?>">
<i class="fas fa-arrow-left me-2"></i>
<span class="brand-text h6 mb-0">Back | <strong>Administration</strong></span>
</a>
</div>
<aside class="main-sidebar sidebar-dark-<?= escapeHtml($config_theme) ?> d-print-none">
<a class="brand-link pb-1 mt-1" href="/agent/<?= $config_start_page ?>">
<p class="h6">
<i class="nav-icon fas fa-arrow-left ml-3 mr-2"></i>
<span class="brand-text">
Back | <strong>Administration</strong>
</span>
</p>
</a>
<!-- Sidebar -->
<div class="sidebar-wrapper">
<div class="sidebar">
<!-- Sidebar Menu -->
<nav>
<ul class="nav nav-pills sidebar-menu flex-column mt-2" data-lte-toggle="treeview" data-accordion="false">
<ul class="nav nav-pills nav-sidebar flex-column mt-2" data-widget="treeview" data-accordion="false">
<li class="nav-header">ACCESS</li>
<li class="nav-item">
<a href="/admin/users.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "users.php") {echo "active";} ?>">
@@ -124,12 +126,6 @@
<p>Ticket Templates</p>
</a>
</li>
<li class="nav-item">
<a href="/admin/canned_responses.php" class="nav-link <?= (basename($_SERVER['PHP_SELF']) == 'canned_responses.php' ? 'active' : '') ?>">
<i class="nav-icon fas fa-comment-dots"></i>
<p>Canned Responses</p>
</a>
</li>
<?php } ?>
<?php if ($config_module_enable_itdoc) { ?>
<!-- 2025-11-16 JQ - Hide Contracts not yet ready
@@ -215,7 +211,7 @@
</li>
<!-- SETTINGS Section -->
<li class="nav-item mt-2 <?= (in_array(basename($_SERVER['PHP_SELF']), ['settings_company.php', 'settings_localization.php', 'settings_theme.php', 'settings_security.php', 'settings_mail.php', 'settings_notification.php', 'settings_default.php', 'settings_invoice.php', 'settings_quote.php', 'settings_online_payment.php', 'settings_online_payment_clients.php', 'settings_project.php', 'settings_ticket.php', 'settings_ai.php', 'identity_providers.php', 'settings_telemetry.php', 'settings_module.php']) ? 'menu-open' : '') ?>">
<li class="nav-item has-treeview mt-2 <?= (in_array(basename($_SERVER['PHP_SELF']), ['settings_company.php', 'settings_localization.php', 'settings_theme.php', 'settings_security.php', 'settings_mail.php', 'settings_notification.php', 'settings_default.php', 'settings_invoice.php', 'settings_quote.php', 'settings_online_payment.php', 'settings_online_payment_clients.php', 'settings_project.php', 'settings_ticket.php', 'settings_ai.php', 'identity_providers.php', 'settings_telemetry.php', 'settings_module.php']) ? 'menu-open' : '') ?>">
<a href="#" class="nav-link">
<p>
SETTINGS
@@ -318,7 +314,7 @@
</li>
<?php
$sql_custom_links = mysqli_query($mysqli, "SELECT custom_link_icon, custom_link_name, custom_link_new_tab, custom_link_uri FROM custom_links
$sql_custom_links = mysqli_query($mysqli, "SELECT * FROM custom_links
WHERE custom_link_location = 4 AND custom_link_archived_at IS NULL
ORDER BY custom_link_order ASC, custom_link_name ASC"
);
@@ -340,7 +336,7 @@
<a href="<?= $custom_link_uri ?>" <?= $target ?> class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == basename($custom_link_uri)) { echo "active"; } ?>">
<i class="fas fa-<?= $custom_link_icon ?> nav-icon"></i>
<p><?= $custom_link_name ?></p>
<i class="fas fa-angle-right nav-icon float-end"></i>
<i class="fas fa-angle-right nav-icon float-right"></i>
</a>
</li>

View File

@@ -8,8 +8,7 @@ require_once "includes/inc_all_admin.php";
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS email_attempts, email_failed_at, email_from, email_from_name, email_id, email_queued_at,
email_recipient, email_recipient_name, email_sent_at, email_status, email_subject FROM email_queue
"SELECT SQL_CALC_FOUND_ROWS * FROM email_queue
WHERE (email_id LIKE '%$q%' OR email_from LIKE '%$q%' OR email_from_name LIKE '%$q%' OR email_recipient LIKE '%$q%' OR email_recipient_name LIKE '%$q%' OR email_subject LIKE '%$q%')
AND DATE(email_queued_at) BETWEEN '$dtf' AND '$dtt'
ORDER BY $sort $order LIMIT $record_from, $record_to"
@@ -21,7 +20,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-inbox me-2"></i>Email Queue</h3>
<h3 class="card-title"><i class="fas fa-fw fa-inbox mr-2"></i>Email Queue</h3>
</div>
<div class="card-body">
<form class="mb-4" autocomplete="off">
@@ -29,24 +28,26 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-sm-4">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search mail queue">
<button class="btn btn-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#advancedFilter"><i class="fas fa-filter"></i></button>
<div class="input-group-append">
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
<div class="col-sm-8">
<div class="dropdown float-end" id="bulkActionButton" hidden>
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="fas fa-fw fa-layer-group me-2"></i>Bulk Action (<span id="selectedCount">0</span>)
<div class="dropdown float-right" id="bulkActionButton" hidden>
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
</button>
<div class="dropdown-menu">
<button class="dropdown-item"
type="submit" form="bulkActions" name="bulk_cancel_emails">
<i class="fas fa-fw fa-ban me-2"></i>Cancel
<i class="fas fa-fw fa-ban mr-2"></i>Cancel
</button>
<div class="dropdown-divider"></div>
<button class="dropdown-item text-danger text-bold"
type="submit" form="bulkActions" name="bulk_delete_emails">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</button>
</div>
</div>
@@ -55,7 +56,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="collapse mt-3 <?php if (isset($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') { echo "show"; } ?>" id="advancedFilter">
<div class="row">
<div class="col-md-3">
<div class="mb-3">
<div class="form-group">
<label>Date range</label>
<input type="text" id="dateFilter" class="form-control" autocomplete="off">
<input type="hidden" name="canned_date" id="canned_date" value="<?= escapeHtml($_GET['canned_date']) ?? '' ?>">
@@ -74,7 +75,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<table class="table table-sm table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
<tr>
<td class="bg-light pe-0">
<td class="bg-light pr-0">
<div class="form-check">
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
</div>
@@ -132,22 +133,22 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
} elseif($email_status == 1) {
$email_status_display = "<div class='text-warning'>Sending</div>";
} elseif($email_status == 2) {
$email_status_display = "<div class='text-danger'>Failed</div><small class='text-secondary font-monospace'>$email_failed_at</small>";
$email_status_display = "<div class='text-danger'>Failed</div><small class='text-secondary text-monospace'>$email_failed_at</small>";
} else {
$email_status_display = "<div class='text-success'>Sent</div><small class='text-secondary font-monospace'>$email_sent_at</small>";
$email_status_display = "<div class='text-success'>Sent</div><small class='text-secondary text-monospace'>$email_sent_at</small>";
}
?>
<tr>
<td class="pe-0 bg-light">
<td class="pr-0 bg-light">
<?php if ($email_status !== 3) { ?>
<div class="form-check">
<input class="form-check-input bulk-select" type="checkbox" name="email_ids[]" value="<?= $email_id ?>">
</div>
<?php } ?>
</td>
<td class="font-monospace"><?= $email_queued_at ?></td>
<td class="text-monospace"><?= $email_queued_at ?></td>
<td><?= "$email_from<br><small class='text-secondary'>$email_from_name</small>" ?></td>
<td><?= "$email_recipient<br><small class='text-secondary'>$email_recipient_name</small>" ?></td>
<td><?= $email_subject ?></td>

View File

@@ -7,22 +7,26 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot me-2"></i>Add AI Model</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Add AI Model</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Provider <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
<select class="form-select select2" name="provider" required>
</div>
<select class="form-control select2" name="provider" required>
<option value="">- Select an AI Provider -</option>
<?php
$sql_ai_providers = mysqli_query($mysqli, "SELECT ai_provider_id, ai_provider_name FROM ai_providers");
$sql_ai_providers = mysqli_query($mysqli, "SELECT * FROM ai_providers");
while ($row = mysqli_fetch_assoc($sql_ai_providers)) {
$ai_provider_id = intval($row['ai_provider_id']);
$ai_provider_name = escapeHtml($row['ai_provider_name']);
@@ -34,19 +38,23 @@ ob_start();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Model Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
</div>
<input type="text" class="form-control" name="model" placeholder="ex gpt-4" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Use Case <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-th-list"></i></span>
<select class="form-select select2" name="use_case">
</div>
<select class="form-control select2" name="use_case">
<option>General</option>
<option>Tickets</option>
<option>Documentation</option>
@@ -54,23 +62,14 @@ ob_start();
</div>
</div>
<div class="mb-3">
<label>Temperature</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-thermometer-half"></i></span>
<input type="number" class="form-control" name="temperature" step="0.1" min="0" max="2" value="" placeholder="Provider default">
</div>
<small class="form-text text-muted">Optional. Leave blank to let the provider use its default - some newer models reject every other value.</small>
</div>
<div class="mb-3">
<div class="form-group">
<textarea class="form-control" rows="8" name="prompt" placeholder="Enter a model prompt:"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ai_model" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_ai_model" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,14 +4,13 @@ require_once '../../includes/modal_header.php';
$model_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT ai_model_ai_provider_id, ai_model_id, ai_model_name, ai_model_prompt, ai_model_use_case, ai_model_temperature FROM ai_models WHERE ai_model_id = $model_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM ai_models WHERE ai_model_id = $model_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$ai_model_ai_provider_id = intval($row['ai_model_ai_provider_id']);
$model_id = intval($row['ai_model_id']);
$model_name = escapeHtml($row['ai_model_name']);
$use_case = escapeHtml($row['ai_model_use_case']);
$temperature = escapeHtml($row['ai_model_temperature']);
$prompt = escapeHtml($row['ai_model_prompt']);
// Generate the HTML form content using output buffering.
@@ -19,8 +18,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot me-2"></i>Editing: <strong><?= $model_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Editing: <strong><?= $model_name ?></strong></h5>
<button type="button" class="close text-light" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -28,14 +29,16 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Provider <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
<select class="form-select select2" name="provider" required>
</div>
<select class="form-control select2" name="provider" required>
<option value="">- Select an AI Provider -</option>
<?php
$sql_ai_providers = mysqli_query($mysqli, "SELECT ai_provider_id, ai_provider_name FROM ai_providers");
$sql_ai_providers = mysqli_query($mysqli, "SELECT * FROM ai_providers");
while ($row = mysqli_fetch_assoc($sql_ai_providers)) {
$ai_provider_id = intval($row['ai_provider_id']);
$ai_provider_name = escapeHtml($row['ai_provider_name']);
@@ -47,19 +50,23 @@ ob_start();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Model Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
</div>
<input type="text" class="form-control" name="model" value="<?= $model_name ?>" placeholder="ex gpt-4" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Use Case <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-th-list"></i></span>
<select class="form-select select2" name="use_case">
</div>
<select class="form-control select2" name="use_case">
<option <?php if ($use_case == 'General') { echo "selected"; } ?>>General</option>
<option <?php if ($use_case == 'Tickets') { echo "selected"; } ?>>Tickets</option>
<option <?php if ($use_case == 'Documentation') { echo "selected"; } ?>>Documentation</option>
@@ -67,22 +74,14 @@ ob_start();
</div>
</div>
<div class="mb-3">
<label>Temperature</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-thermometer-half"></i></span>
<input type="number" class="form-control" name="temperature" step="0.1" min="0" max="2" value="<?= $temperature ?>" placeholder="Provider default">
</div>
<small class="form-text text-muted">Optional. Leave blank to let the provider use its default - some newer models reject every other value.</small>
</div>
<div class="mb-3">
<div class="form-group">
<textarea class="form-control" rows="8" name="prompt" placeholder="Enter a model prompt:"><?= $prompt ?></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ai_model" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_ai_model" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

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

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$provider_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT ai_provider_api_key, ai_provider_api_url, ai_provider_name FROM ai_providers WHERE ai_provider_id = $provider_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM ai_providers WHERE ai_provider_id = $provider_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$provider_name = escapeHtml($row['ai_provider_name']);
@@ -16,8 +16,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot me-2"></i>Editing: <strong><?= $provider_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Editing: <strong><?= $provider_name ?></strong></h5>
<button type="button" class="close text-light" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -25,34 +27,40 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Provider Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-robot"></i></span>
</div>
<input type="text" class="form-control" name="provider" value="<?= $provider_name ?>" placeholder="ex OpenAI" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>URL <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
</div>
<input type="url" class="form-control" name="url" value="<?= $url ?>" placeholder="ex https://ai.company.ext/api" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>API Key</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="api_key" value="<?= $key ?>" placeholder="Enter API key here" maxlength="200">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ai_provider" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_ai_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -10,18 +10,20 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-key me-2"></i>New Key</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-key mr-2"></i>New Key</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-api-details">Details</a>
<a class="nav-link active" data-toggle="pill" href="#pills-api-details">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-api-keys">Keys</a>
<a class="nav-link" data-toggle="pill" href="#pills-api-keys">Keys</a>
</li>
</ul>
<hr>
@@ -33,27 +35,33 @@ ob_start();
<input type="hidden" name="key" value="<?= $key ?>">
<input type="hidden" name="password" value="<?= $decryptPW ?>">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sticky-note"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Key Name" maxlength="255" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Expiration Date <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="expire" min="<?= date('Y-m-d') ?>" max="2999-12-31" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Run as User <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
<select class="form-select select2" name="run_as_user" required>
</div>
<select class="form-control select2" name="run_as_user" required>
<option value="">- Select a user -</option>
<?php
$sql_run_users = mysqli_query($mysqli, "SELECT user_id, user_name FROM users WHERE user_type = 1 AND user_status = 1 AND user_archived_at IS NULL ORDER BY user_name ASC");
@@ -69,28 +77,38 @@ ob_start();
</div>
<div class="tab-pane fade" id="pills-api-keys">
<div class="mb-3">
<div class="form-group">
<label>API Key <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" value="<?= $key ?>" required disabled>
<div class="input-group-append">
<button class="btn btn-default clipboardjs" type="button" data-clipboard-text="<?= $key ?>"><i class="fa fa-fw fa-copy"></i></button>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Login credential decryption password <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-unlock-alt"></i></span>
</div>
<input type="text" class="form-control" value="<?= $decryptPW ?>" required disabled>
<div class="input-group-append">
<button class="btn btn-default clipboardjs" type="button" data-clipboard-text="<?= $decryptPW ?>"><i class="fa fa-fw fa-copy"></i></button>
</div>
</div>
</div>
<br>
<div class="mb-3">
<div class="form-group">
<label>I have made a copy of the key(s)<strong class="text-danger">*</strong></label>
<div class="input-group">
<input class="form-check-input" type="checkbox" name="ack" value="1" required>
<div class="input-group-prepend">
<input type="checkbox" name="ack" value="1" required>
</div>
</div>
</div>
</div>
@@ -99,8 +117,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="add_api_key" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_api_key" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$api_key_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT api_key_expire, api_key_name, api_key_user_id FROM api_keys WHERE api_key_id = $api_key_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM api_keys WHERE api_key_id = $api_key_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$api_key_name = escapeHtml($row['api_key_name']);
$api_key_expire = escapeHtml($row['api_key_expire']);
@@ -14,9 +14,11 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-key me-2"></i>Editing API Key:
<h5 class="modal-title"><i class="fas fa-fw fa-key mr-2"></i>Editing API Key:
<strong><?= $api_key_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
@@ -24,29 +26,35 @@ ob_start();
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="api_key_id" value="<?= $api_key_id ?>">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sticky-note"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Key Name" maxlength="255"
value="<?= $api_key_name ?>" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Expiration Date <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="expire" min="<?= date('Y-m-d') ?>" max="2999-12-31"
value="<?= $api_key_expire ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Run as User <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
<select class="form-select select2" name="run_as_user" required>
</div>
<select class="form-control select2" name="run_as_user" required>
<option value="">- Select a user -</option>
<?php
$sql_run_users = mysqli_query($mysqli, "SELECT user_id, user_name FROM users WHERE user_type = 1 AND user_status = 1 AND user_archived_at IS NULL ORDER BY user_name ASC");
@@ -62,8 +70,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="edit_api_key" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_api_key" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -1,58 +0,0 @@
<?php
require_once '../../includes/modal_header.php';
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-comment-dots me-2"></i>New Canned Response</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-comment-dots"></i></span>
<input type="text" class="form-control" name="name" placeholder="What an agent picks it by" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<label>Ticket Category</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
<select class="form-select select2" name="category">
<option value="0">- All ticket categories -</option>
<?php
$sql_categories = mysqli_query($mysqli, "SELECT category_id, category_name FROM categories WHERE category_type = 'Ticket' AND category_archived_at IS NULL ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql_categories)) {
$category_id_select = intval($row['category_id']);
$category_name_select = escapeHtml($row['category_name']); ?>
<option value="<?= $category_id_select ?>"><?= $category_name_select ?></option>
<?php } ?>
</select>
</div>
<small class="text-secondary">Leave this on all categories for a response that should be offered on every ticket.</small>
</div>
<div class="mb-3">
<label>Response</label>
<textarea class="form-control tinymceTicket" name="body"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_canned_response" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create Canned Response</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -1,72 +0,0 @@
<?php
require_once '../../includes/modal_header.php';
$canned_response_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT canned_response_name, canned_response_body, canned_response_category_id
FROM canned_responses WHERE canned_response_id = $canned_response_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$canned_response_name = escapeHtml($row['canned_response_name']);
$canned_response_category_id = intval($row['canned_response_category_id']);
// Into a textarea, so escaped rather than purified - it is markup being edited, not rendered
$canned_response_body = escapeHtml($row['canned_response_body']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-comment-dots me-2"></i>Editing Canned Response: <strong><?= $canned_response_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="canned_response_id" value="<?= $canned_response_id ?>">
<div class="modal-body">
<div class="mb-3">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-comment-dots"></i></span>
<input type="text" class="form-control" name="name" maxlength="200" value="<?= $canned_response_name ?>" required>
</div>
</div>
<div class="mb-3">
<label>Ticket Category</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
<select class="form-select select2" name="category">
<option value="0" <?php if ($canned_response_category_id == 0) { echo "selected"; } ?>>- All ticket categories -</option>
<?php
$sql_categories = mysqli_query($mysqli, "SELECT category_id, category_name FROM categories WHERE category_type = 'Ticket' AND category_archived_at IS NULL ORDER BY category_name ASC");
while ($category_row = mysqli_fetch_assoc($sql_categories)) {
$category_id_select = intval($category_row['category_id']);
$category_name_select = escapeHtml($category_row['category_name']); ?>
<option value="<?= $category_id_select ?>" <?php if ($canned_response_category_id == $category_id_select) { echo "selected"; } ?>><?= $category_name_select ?></option>
<?php } ?>
</select>
</div>
<small class="text-secondary">Leave this on all categories for a response that should be offered on every ticket.</small>
</div>
<div class="mb-3">
<label>Response</label>
<textarea class="form-control tinymceTicket" name="body"><?= $canned_response_body ?></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_canned_response" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -9,8 +9,10 @@ $category_types_array = ['Expense', 'Income', 'Referral', 'Ticket'];
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul me-2"></i>New <strong><?= escapeHtml(ucwords(str_replace('_', ' ', $category))); ?></strong> Category</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul mr-2"></i>New <strong><?= escapeHtml(ucwords(str_replace('_', ' ', $category))); ?></strong> Category</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -21,11 +23,13 @@ $category_types_array = ['Expense', 'Income', 'Referral', 'Ticket'];
<input type="hidden" name="type" value="<?= $category ?>">
<?php } else { ?>
<div class="mb-3">
<div class="form-group">
<label>Type <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
<select class="form-select select2" name="type" required>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Select Type -</option>
<?php foreach ($category_types_array as $type_select) { ?>
<option><?= $type_select ?></option>
@@ -36,34 +40,40 @@ $category_types_array = ['Expense', 'Income', 'Referral', 'Ticket'];
<?php } ?>
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list-ul"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Category name" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Color <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
</div>
<input type="color" class="form-control col-3" name="color" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-align-left"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Enter a description" maxlength="200">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_category" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create Category</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_category" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create Category</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$category_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT category_color, category_description, category_name, category_type FROM categories WHERE category_id = $category_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_id = $category_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$category_name = escapeHtml($row['category_name']);
@@ -16,8 +16,10 @@ $category_type = escapeHtml($row['category_type']);
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul me-2"></i>Editing category: <strong><?= $category_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul mr-2"></i>Editing category: <strong><?= $category_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -25,34 +27,40 @@ ob_start();
<input type="hidden" name="type" value="<?= $category_type ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list-ul"></i></span>
</div>
<input type="text" class="form-control" name="name" maxlength="200" value="<?= $category_name ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Color <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
</div>
<input type="color" class="form-control col-3" name="color" value="<?= $category_color ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-align-left"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Enter a description" maxlength="200" value="<?= $category_description ?>">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_category" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_category" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -9,23 +9,23 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-file-contract me-2"></i>New Contract Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-file-contract mr-2"></i>New Contract Template</h5>
<button type="button" class="close text-white" data-dismiss="modal"><span>&times;</span></button>
</div>
<!-- Tabs Navigation -->
<ul class="modal-header nav nav-pills nav-justified">
<li class="nav-item">
<a class="nav-link active" id="general-tab" data-bs-toggle="tab" href="#general" role="tab">General Info</a>
<a class="nav-link active" id="general-tab" data-toggle="tab" href="#general" role="tab">General Info</a>
</li>
<li class="nav-item">
<a class="nav-link" id="sla-tab" data-bs-toggle="tab" href="#sla" role="tab">SLA</a>
<a class="nav-link" id="sla-tab" data-toggle="tab" href="#sla" role="tab">SLA</a>
</li>
<li class="nav-item">
<a class="nav-link" id="rates-tab" data-bs-toggle="tab" href="#rates" role="tab">Rates & Support</a>
<a class="nav-link" id="rates-tab" data-toggle="tab" href="#rates" role="tab">Rates & Support</a>
</li>
<li class="nav-item">
<a class="nav-link" id="details-tab" data-bs-toggle="tab" href="#details" role="tab">Details</a>
<a class="nav-link" id="details-tab" data-toggle="tab" href="#details" role="tab">Details</a>
</li>
</ul>
@@ -37,28 +37,34 @@ ob_start();
<!-- General Info Tab -->
<div class="tab-pane fade show active" id="general" role="tabpanel">
<div class="mb-3">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-file-contract"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Contract Template Name" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Template Description <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-align-left"></i></span>
</div>
<input type="text" class="form-control" name="description"
placeholder="Contract Template Description" maxlength="200" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Contract Type <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
<select class="form-select select2" name="type" required>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Select Type -</option>
<?php foreach ($contract_types_array as $type) { ?>
<option><?= $type ?></option>
@@ -67,11 +73,13 @@ ob_start();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Renewal Frequency</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sync-alt"></i></span>
<select class="form-select select2" name="renewal_frequency">
</div>
<select class="form-control select2" name="renewal_frequency">
<option value="">- Select Frequency -</option>
<?php foreach ($renewal_frequency_array as $renewal_frequency) { ?>
<option><?= $renewal_frequency ?></option>
@@ -83,52 +91,64 @@ ob_start();
<!-- SLA Tab -->
<div class="tab-pane fade" id="sla" role="tabpanel">
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>Low Priority Response (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
</div>
<input type="number" class="form-control" name="sla_low_response_time" placeholder="e.g., 24">
</div>
</div>
<div class="mb-3 col-md-6">
<div class="form-group col-md-6">
<label>Low Priority Resolution (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
</div>
<input type="number" class="form-control" name="sla_low_resolution_time" placeholder="e.g., 48">
</div>
</div>
</div>
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>Medium Priority Response (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
</div>
<input type="number" class="form-control" name="sla_medium_response_time" placeholder="e.g., 12">
</div>
</div>
<div class="mb-3 col-md-6">
<div class="form-group col-md-6">
<label>Medium Priority Resolution (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
</div>
<input type="number" class="form-control" name="sla_medium_resolution_time" placeholder="e.g., 24">
</div>
</div>
</div>
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>High Priority Response (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-bolt"></i></span>
</div>
<input type="number" class="form-control" name="sla_high_response_time" placeholder="e.g., 1">
</div>
</div>
<div class="mb-3 col-md-6">
<div class="form-group col-md-6">
<label>High Priority Resolution (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stopwatch"></i></span>
</div>
<input type="number" class="form-control" name="sla_high_resolution_time" placeholder="e.g., 4">
</div>
</div>
@@ -137,34 +157,42 @@ ob_start();
<!-- Rates & Support Tab -->
<div class="tab-pane fade" id="rates" role="tabpanel">
<div class="mb-3">
<div class="form-group">
<label>Standard Hourly Rate</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-dollar-sign"></i></span>
</div>
<input type="text" class="form-control" name="rate_standard" placeholder="e.g., 100">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>After Hours Hourly Rate</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-moon"></i></span>
</div>
<input type="text" class="form-control" name="rate_after_hours" placeholder="e.g., 150">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Support Hours</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="text" class="form-control" name="support_hours" placeholder="e.g., Mon-Fri 9am-5pm" maxlength="100">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Net Terms</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-file-invoice-dollar"></i></span>
</div>
<input type="text" class="form-control" name="net_terms" placeholder="e.g., Net 30" maxlength="50">
</div>
</div>
@@ -172,7 +200,7 @@ ob_start();
<!-- Details Tab -->
<div class="tab-pane fade" id="details" role="tabpanel">
<div class="mb-3">
<div class="form-group">
<textarea class="form-control tinymce" rows="6" name="details" placeholder="Enter Contract Details"></textarea>
</div>
</div>
@@ -182,10 +210,10 @@ ob_start();
<div class="modal-footer">
<button type="submit" name="add_contract_template" class="btn btn-primary text-bold">
<i class="fa fa-check me-2"></i>Create Template
<i class="fa fa-check mr-2"></i>Create Template
</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal">
<i class="fa fa-times me-2"></i>Cancel
<button type="button" class="btn btn-light" data-dismiss="modal">
<i class="fa fa-times mr-2"></i>Cancel
</button>
</div>
</form>

View File

@@ -7,13 +7,7 @@ $contract_types_array = ['Fully Managed', 'Partialy Managed', 'Break/Fix'];
$update_frequency_array = ['Manual', 'Annually', '2 Year', '3 Year', '5 Year', '7 Year'];
// Fetch existing template
$sql = mysqli_query($mysqli, "SELECT contract_template_description, contract_template_details, contract_template_name,
contract_template_net_terms, contract_template_rate_after_hours,
contract_template_rate_standard, contract_template_renewal_frequency,
contract_template_sla_high_resolution_time, contract_template_sla_high_response_time,
contract_template_sla_low_resolution_time, contract_template_sla_low_response_time,
contract_template_sla_medium_resolution_time, contract_template_sla_medium_response_time,
contract_template_support_hours, contract_template_type FROM contract_templates WHERE contract_template_id = $contract_template_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM contract_templates WHERE contract_template_id = $contract_template_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
// Assign locals
@@ -37,23 +31,23 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-file-contract me-2"></i>Edit Contract Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-file-contract mr-2"></i>Edit Contract Template</h5>
<button type="button" class="close text-white" data-dismiss="modal"><span>&times;</span></button>
</div>
<!-- Tabs Navigation -->
<ul class="modal-header nav nav-pills nav-justified">
<li class="nav-item">
<a class="nav-link active" id="general-tab" data-bs-toggle="tab" href="#general" role="tab">General Info</a>
<a class="nav-link active" id="general-tab" data-toggle="tab" href="#general" role="tab">General Info</a>
</li>
<li class="nav-item">
<a class="nav-link" id="sla-tab" data-bs-toggle="tab" href="#sla" role="tab">SLA</a>
<a class="nav-link" id="sla-tab" data-toggle="tab" href="#sla" role="tab">SLA</a>
</li>
<li class="nav-item">
<a class="nav-link" id="rates-tab" data-bs-toggle="tab" href="#rates" role="tab">Rates & Support</a>
<a class="nav-link" id="rates-tab" data-toggle="tab" href="#rates" role="tab">Rates & Support</a>
</li>
<li class="nav-item">
<a class="nav-link" id="details-tab" data-bs-toggle="tab" href="#details" role="tab">Details</a>
<a class="nav-link" id="details-tab" data-toggle="tab" href="#details" role="tab">Details</a>
</li>
</ul>
@@ -66,31 +60,37 @@ ob_start();
<!-- General Info Tab -->
<div class="tab-pane fade show active" id="general" role="tabpanel">
<div class="mb-3">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-file-contract"></i></span>
</div>
<input type="text" class="form-control" name="name"
placeholder="Contract Template Name" maxlength="200" required autofocus
value="<?= $name ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Template Description <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-align-left"></i></span>
</div>
<input type="text" class="form-control" name="description"
placeholder="Contract Template Description" maxlength="200" required
value="<?= $description ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Contract Type <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
<select class="form-select select2" name="type" required>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Select Type -</option>
<?php foreach ($contract_types_array as $type_select) { ?>
<option <?php if ($type == $type_select) { echo "selected"; } ?>><?= $type_select ?></option>
@@ -99,11 +99,13 @@ ob_start();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Renewal Frequency</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sync-alt"></i></span>
<select class="form-select select2" name="renewal_frequency">
</div>
<select class="form-control select2" name="renewal_frequency">
<option value="">- Select Frequency -</option>
<?php foreach ($renewal_frequency_array as $renewal_frequency_select) { ?>
<option <?php if ($renewal_frequency == $renewal_frequency_select) { echo "selected"; } ?>><?= $renewal_frequency_select ?></option>
@@ -115,57 +117,69 @@ ob_start();
<!-- SLA Tab -->
<div class="tab-pane fade" id="sla" role="tabpanel">
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>Low Priority Response (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
</div>
<input type="number" class="form-control" name="sla_low_response_time" placeholder="e.g., 24"
value="<?= $sla_low_resp ?>">
</div>
</div>
<div class="mb-3 col-md-6">
<div class="form-group col-md-6">
<label>Low Priority Resolution (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
</div>
<input type="number" class="form-control" name="sla_low_resolution_time" placeholder="e.g., 48"
value="<?= $sla_low_res ?>">
</div>
</div>
</div>
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>Medium Priority Response (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
</div>
<input type="number" class="form-control" name="sla_medium_response_time" placeholder="e.g., 12"
value="<?= $sla_med_resp ?>">
</div>
</div>
<div class="mb-3 col-md-6">
<div class="form-group col-md-6">
<label>Medium Priority Resolution (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
</div>
<input type="number" class="form-control" name="sla_medium_resolution_time" placeholder="e.g., 24"
value="<?= $sla_med_res ?>">
</div>
</div>
</div>
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>High Priority Response (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-bolt"></i></span>
</div>
<input type="number" class="form-control" name="sla_high_response_time" placeholder="e.g., 1"
value="<?= $sla_high_resp ?>">
</div>
</div>
<div class="mb-3 col-md-6">
<div class="form-group col-md-6">
<label>High Priority Resolution (hrs)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stopwatch"></i></span>
</div>
<input type="number" class="form-control" name="sla_high_resolution_time" placeholder="e.g., 4"
value="<?= $sla_high_res ?>">
</div>
@@ -175,37 +189,45 @@ ob_start();
<!-- Rates & Support Tab -->
<div class="tab-pane fade" id="rates" role="tabpanel">
<div class="mb-3">
<div class="form-group">
<label>Standard Hourly Rate</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-dollar-sign"></i></span>
</div>
<input type="text" class="form-control" name="rate_standard" placeholder="e.g., 100"
value="<?= $rate_standard ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>After Hours Hourly Rate</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-moon"></i></span>
</div>
<input type="text" class="form-control" name="rate_after_hours" placeholder="e.g., 150"
value="<?= $rate_after_hours ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Support Hours</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="text" class="form-control" name="support_hours" placeholder="e.g., Mon-Fri 9am-5pm" maxlength="100"
value="<?= $support_hours ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Net Terms</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-file-invoice-dollar"></i></span>
</div>
<input type="text" class="form-control" name="net_terms" placeholder="e.g., Net 30" maxlength="50"
value="<?= $net_terms ?>">
</div>
@@ -214,10 +236,12 @@ ob_start();
<!-- Details Tab -->
<div class="tab-pane fade" id="details" role="tabpanel">
<div class="mb-3">
<div class="form-group">
<label>Contract Details</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-align-left"></i></span>
</div>
<textarea class="form-control tinymce" rows="6" name="details"
placeholder="Enter Contract Details"><?= $details ?></textarea>
</div>
@@ -229,10 +253,10 @@ ob_start();
<div class="modal-footer">
<button type="submit" name="edit_contract_template" class="btn btn-primary text-bold">
<i class="fa fa-check me-2"></i>Save Changes
<i class="fa fa-check mr-2"></i>Save Changes
</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal">
<i class="fa fa-times me-2"></i>Cancel
<button type="button" class="btn btn-light" data-dismiss="modal">
<i class="fa fa-times mr-2"></i>Cancel
</button>
</div>
</form>

View File

@@ -5,8 +5,7 @@ require_once '../../../includes/cron_jobs.php';
$cron_job_id = intval($_GET['id']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT cron_job_daily_at, cron_job_enabled, cron_job_interval_minutes, cron_job_name,
cron_job_schedule FROM cron_jobs WHERE cron_job_id = $cron_job_id LIMIT 1"));
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM cron_jobs WHERE cron_job_id = $cron_job_id LIMIT 1"));
$registry = cronJobRegistryByName();
$job = $registry[$row['cron_job_name']] ?? null;
@@ -27,27 +26,31 @@ if (empty($cron_job_daily_at)) {
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-clock me-2"></i>Editing: <strong><?= $cron_job_label ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-clock mr-2"></i>Editing: <strong><?= $cron_job_label ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="cron_job_id" value="<?= $cron_job_id ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input" name="enabled" value="1" id="cronJobEnabledSwitch" <?= $cron_job_enabled == 1 ? 'checked' : '' ?>>
<label class="form-check-label" for="cronJobEnabledSwitch">Enabled</label>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="enabled" value="1" id="cronJobEnabledSwitch" <?= $cron_job_enabled == 1 ? 'checked' : '' ?>>
<label class="custom-control-label" for="cronJobEnabledSwitch">Enabled</label>
</div>
<small class="text-muted">A disabled job never runs on its schedule, but Run Now still works.</small>
</div>
<div class="mb-3">
<div class="form-group">
<label>Schedule</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
<select class="form-select" name="schedule" id="cronJobSchedule">
</div>
<select class="form-control" name="schedule" id="cronJobSchedule">
<?php if ($cron_job_interval_safe) { ?>
<option value="Interval" <?= $cron_job_schedule === 'Interval' ? 'selected' : '' ?>>Every so many minutes</option>
<?php } ?>
@@ -59,20 +62,26 @@ ob_start();
<?php } ?>
</div>
<div class="mb-3" id="cronJobIntervalGroup">
<div class="form-group" id="cronJobIntervalGroup">
<label>Run every</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-redo"></i></span>
</div>
<input type="number" class="form-control" name="interval_minutes" value="<?= $cron_job_interval_minutes ?>" min="1" max="1440">
<div class="input-group-append">
<span class="input-group-text">minutes</span>
</div>
</div>
<small class="text-muted">Cron wakes once a minute, so 1 is as often as anything can run.</small>
</div>
<div class="mb-3" id="cronJobDailyGroup">
<div class="form-group" id="cronJobDailyGroup">
<label>Run at</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
</div>
<input type="time" class="form-control" name="daily_at" value="<?= $cron_job_daily_at ?>">
</div>
<small class="text-muted">Your ITFlow timezone. A run missed because the server was off happens at the next opportunity instead of waiting a day.</small>
@@ -80,8 +89,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="edit_cron_job" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_cron_job" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -2,8 +2,10 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-th-list me-2"></i>Create <?= escapeHtml($table) ?> field</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-th-list mr-2"></i>Create <?= escapeHtml($table) ?> field</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -11,16 +13,18 @@
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Label <strong class="text-danger">*</strong></label>
<input type="text" class="form-control" name="label" placeholder="Enter a custom field label" maxlength="255" required autofocus>
</div>
<div class="mb-3">
<div class="form-group">
<label>Type <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-th"></i></span>
<select class="form-select select2" name="type" required>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Select a field type -</option>
<option>Text</option>
</select>
@@ -29,8 +33,8 @@
</div>
<div class="modal-footer">
<button type="submit" name="create_custom_field" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="create_custom_field" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>

View File

@@ -2,24 +2,28 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-th-list me-2"></i>Editing custom field: <strong><?= $custom_field_label ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-th-list mr-2"></i>Editing custom field: <strong><?= $custom_field_label ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="custom_field_id" value="<?= $custom_field_id ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Label <strong class="text-danger">*</strong></label>
<input type="text" class="form-control" name="label" maxlength="255" value="<?= $custom_field_label ?>" required>
</div>
<div class="mb-3">
<div class="form-group">
<label>Type <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-th"></i></span>
<select class="form-select select2" name="type" required>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Select a field type -</option>
<option <?php if ($custom_field_type == 'text') { echo "selected"; } ?> value="text">Text</option>
</select>
@@ -28,8 +32,8 @@
</div>
<div class="modal-footer">
<button type="submit" name="edit_custom_field" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_custom_field" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>

View File

@@ -7,54 +7,68 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt me-2"></i>New Custom Link</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt mr-2"></i>New Custom Link</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list-ul"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Link name" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Order</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
</div>
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>URI <strong class="text-danger">*</strong></label> / <span class="text-secondary">Open New Tab</span>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-external-link-alt"></i></span>
</div>
<input type="text" class="form-control" name="uri" placeholder="Enter Link" maxlength="500" required>
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="new_tab" value="1">
<input type="checkbox" name="new_tab" value="1">
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Icon</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
</div>
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Location <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-home"></i></span>
<select class="form-select select2" name="location" required>
</div>
<select class="form-control select2" name="location" required>
<option value="1">Main Side Nav</option>
<option value="2">Top Nav (Icon Required)</option>
<option value="3">Client Portal Nav</option>
@@ -66,8 +80,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="add_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Create</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,8 +4,7 @@ require_once '../../includes/modal_header.php';
$custom_link_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT custom_link_icon, custom_link_location, custom_link_name, custom_link_new_tab,
custom_link_order, custom_link_uri FROM custom_links WHERE custom_link_id = $custom_link_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM custom_links WHERE custom_link_id = $custom_link_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$custom_link_name = escapeHtml($row['custom_link_name']);
$custom_link_uri = escapeHtml($row['custom_link_uri']);
@@ -19,8 +18,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt me-2"></i>Editing link: <strong><?= $custom_link_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt mr-2"></i>Editing link: <strong><?= $custom_link_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -28,46 +29,58 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list-ul"></i></span>
</div>
<input type="text" class="form-control" name="name" value="<?= $custom_link_name ?>" maxlength="200" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Order</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
</div>
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order" value="<?= $custom_link_order ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>URI <strong class="text-danger">*</strong></label> / <span class="text-secondary">Open New Tab</span>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-external-link-alt"></i></span>
</div>
<input type="text" class="form-control" name="uri" placeholder="Enter Link" maxlength="500" value="<?= $custom_link_uri ?>" required>
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="new_tab" value="1" <?php if ($custom_link_new_tab == 1) { echo "checked"; } ?>>
<input type="checkbox" name="new_tab" value="1" <?php if ($custom_link_new_tab == 1) { echo "checked"; } ?>>
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Icon</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
</div>
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" maxlength="200" value="<?= $custom_link_icon ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Location <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-home"></i></span>
<select class="form-select select2" name="location" required>
</div>
<select class="form-control select2" name="location" required>
<option value="1" <?php if ($custom_link_location === 1) { echo "selected"; } ?> >Main Side Nav</option>
<option value="2" <?php if ($custom_link_location === 2) { echo "selected"; } ?> >Top Nav (Icon Required)</option>
<option value="3" <?php if ($custom_link_location === 3) { echo "selected"; } ?> >Client Portal Nav</option>
@@ -79,8 +92,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="edit_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -7,34 +7,38 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt me-2"></i>Creating Document Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt mr-2"></i>Creating Document Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<input type="text" class="form-control" name="name" placeholder="Template name" maxlength="200">
</div>
<div class="mb-3">
<div class="form-group">
<label>Enter a prompt for the type of IT documentation you want to generate:</label>
<div class="input-group mb-3">
<input type="text" class="form-control" id="aiPrompt" placeholder="e.g. 'A network troubleshooting guide for junior technicians'">
<div class="input-group-append">
<button class="btn btn-info" type="button" id="generateAIContent">
<i class="fa fa-fw fa-magic me-1"></i>Generate with AI
<i class="fa fa-fw fa-magic mr-1"></i>Generate with AI
</button>
</div>
</div>
</div>
<!-- TinyMCE Content -->
<div class="mb-3">
<div class="form-group">
<textarea class="form-control tinymce" name="content"></textarea>
</div>
<div class="mb-3">
<div class="form-group">
<input type="text" class="form-control" name="description" placeholder="Enter a short summary">
</div>
@@ -42,55 +46,41 @@ ob_start();
<div class="modal-footer">
<button type="submit" name="add_document_template" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save template</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_document_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save template</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function () {
$(document).ready(function(){
var button = document.getElementById('generateAIContent');
var promptField = document.getElementById('aiPrompt');
if (!button || !promptField) {
return;
}
button.addEventListener('click', function () {
var prompt = promptField.value.trim();
if (prompt === '') {
$('#generateAIContent').on('click', function(){
var prompt = $('#aiPrompt').val().trim();
if(prompt === '') {
alert('Please enter a prompt.');
return;
}
button.disabled = true;
button.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Generating...';
$('#generateAIContent').prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Generating...');
fetch('/agent/ajax.php?ai_create_document_template', {
$.ajax({
url: '/agent/ajax.php?ai_create_document_template', // The PHP script that calls the OpenAI API
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
credentials: 'same-origin',
body: new URLSearchParams({ prompt: prompt }).toString()
})
.then(function (res) {
if (!res.ok) {
throw new Error('HTTP ' + res.status);
}
return res.text();
})
.then(function (response) {
data: { prompt: prompt },
dataType: 'html',
success: function(response) {
// Assuming you have exactly one TinyMCE instance on the page
// and it's targeting the .tinymce textarea:
tinymce.activeEditor.setContent(response);
})
.catch(function () {
},
error: function() {
alert('Error generating content. Please try again.');
})
.finally(function () {
button.disabled = false;
button.innerHTML = '<i class="fa fa-fw fa-magic me-1"></i>Generate with AI';
});
},
complete: function() {
$('#generateAIContent').prop('disabled', false).html('<i class="fa fa-fw fa-magic mr-1"></i>Generate with AI');
}
});
});
});
</script>

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$document_template_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT document_template_content, document_template_description, document_template_name FROM document_templates WHERE document_template_id = $document_template_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM document_templates WHERE document_template_id = $document_template_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$document_template_name = escapeHtml($row['document_template_name']);
$document_template_description = escapeHtml($row['document_template_description']);
@@ -14,8 +14,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt me-2"></i>Editing template: <strong><?= $document_template_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt mr-2"></i>Editing template: <strong><?= $document_template_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -23,22 +25,22 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<input type="text" class="form-control" name="name" maxlength="200" value="<?= $document_template_name ?>" placeholder="Name" required>
</div>
<div class="mb-3">
<div class="form-group">
<textarea class="form-control tinymce" name="content"><?= $document_template_content ?></textarea>
</div>
<div class="mb-3">
<div class="form-group">
<input type="text" class="form-control" name="description" value="<?= $document_template_description ?>" placeholder="Short summary">
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_document_template" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save template</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_document_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save template</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -12,9 +12,7 @@ $purifier_config->set('Cache.DefinitionImpl', null); // Disable cache by setting
$purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]);
$purifier = new HTMLPurifier($purifier_config);
$sql = mysqli_query($mysqli, "SELECT email_attempts, email_content, email_failed_at, email_from, email_from_name,
email_queued_at, email_recipient, email_recipient_name, email_sent_at, email_status,
email_subject FROM email_queue WHERE email_id = $email_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM email_queue WHERE email_id = $email_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$email_from = escapeHtml($row['email_from']);
@@ -42,8 +40,10 @@ if ($email_status == 0) {
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class='fas fa-fw fa-envelope-open me-2'></i><strong><?= $email_subject ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class='fas fa-fw fa-envelope-open mr-2'></i><strong><?= $email_subject ?></strong></h5>
<button type="button" class="close text-light" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">

View File

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

View File

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

View File

@@ -6,8 +6,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card me-2"></i>Add Payment Provider</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Add Payment Provider</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -16,10 +18,10 @@ ob_start();
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-details">Details</a>
<a class="nav-link active" data-toggle="pill" href="#pills-details">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-expense">Expense</a>
<a class="nav-link" data-toggle="pill" href="#pills-expense">Expense</a>
</li>
</ul>
@@ -29,37 +31,45 @@ ob_start();
<div class="tab-pane fade show active" id="pills-details">
<div class="mb-3">
<div class="form-group">
<label>Provider <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
<select class="form-select select2" name="provider">
</div>
<select class="form-control select2" name="provider">
<option>Stripe</option>
</select>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Publishable key <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
</div>
<input type="text" class="form-control" name="public_key" placeholder="Publishable API Key (pk_...)" maxlength="250">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Secret key <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="private_key" placeholder="Secret API Key (sk_...)" maxlength="250">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Income / Expense Account <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-piggy-bank"></i></span>
<select class="form-select select2" name="account" required>
</div>
<select class="form-control select2" name="account" required>
<option value="">- Select an Account -</option>
<?php
@@ -78,10 +88,12 @@ ob_start();
<small class="form-text text-muted">Should have a seperate account created off the payment provider's name e.g. Stripe</small>
</div>
<div class="mb-3">
<div class="form-group">
<label>Threshold</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="decimal" pattern="[0-9]*\.?[0-9]{0,2}" name="threshold" placeholder="1000.00">
</div>
<small class="form-text text-muted">Will not show as an option at Checkout if invoice amount is above this number, 0 disables the threshold check.</small>
@@ -95,18 +107,20 @@ ob_start();
Payment Processing Fee Expenses get reconciled nighly via the cron
</div>
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input" name="enable_expense" checked value="1" id="enableExpenseSwitch">
<label class="form-check-label" for="enableExpenseSwitch">Enable Expense</label>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="enable_expense" checked value="1" id="enableExpenseSwitch">
<label class="custom-control-label" for="enableExpenseSwitch">Enable Expense</label>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Payment Provider Vendor <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
<select class="form-select select2" name="expense_vendor" required>
</div>
<select class="form-control select2" name="expense_vendor" required>
<option value="0">Expense Disabled</option>
<?php
@@ -125,11 +139,13 @@ ob_start();
<small class="form-text text-muted">Payment Privider name e.g. Stripe</small>
</div>
<div class="mb-3">
<div class="form-group">
<label>Expense Category <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
<select class="form-select select2" name="expense_category" required>
</div>
<select class="form-control select2" name="expense_category" required>
<option value="">- Select a Category -</option>
<?php
@@ -144,10 +160,12 @@ ob_start();
}
?>
</select>
<div class="input-group-append">
<button class="btn btn-secondary ajax-modal" type="button"
data-modal-url="../admin/modals/category/category_add.php?category=Expense">
<i class="fas fa-plus"></i>
</button>
</div>
</div>
<small class="form-text text-muted">Processing Fee, Credit Card Fee etc</small>
</div>
@@ -156,8 +174,8 @@ ob_start();
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_payment_provider" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Add</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_payment_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Add</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,9 +4,7 @@ require_once '../../includes/modal_header.php';
$provider_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT payment_provider_account, payment_provider_expense_category,
payment_provider_expense_vendor, payment_provider_name, payment_provider_private_key,
payment_provider_public_key, payment_provider_threshold FROM payment_providers WHERE payment_provider_id = $provider_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM payment_providers WHERE payment_provider_id = $provider_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$provider_name = escapeHtml($row['payment_provider_name']);
@@ -22,8 +20,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card me-2"></i>Editing: <strong><?= $provider_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Editing: <strong><?= $provider_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -33,10 +33,10 @@ ob_start();
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-details">Details</a>
<a class="nav-link active" data-toggle="pill" href="#pills-details">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-expense">Expense</a>
<a class="nav-link" data-toggle="pill" href="#pills-expense">Expense</a>
</li>
</ul>
@@ -46,27 +46,33 @@ ob_start();
<div class="tab-pane fade show active" id="pills-details">
<div class="mb-3">
<div class="form-group">
<label>Publishable key <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
</div>
<input type="text" class="form-control" name="public_key" placeholder="Publishable API Key (pk_...)" maxlength="250" value="<?= $public_key ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Secret key <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="private_key" placeholder="Secret API Key (sk_...)" maxlength="250" value="<?= $private_key ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Income / Expense Account <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-piggy-bank"></i></span>
<select class="form-select select2" name="account" required>
</div>
<select class="form-control select2" name="account" required>
<option value="">- Select an Account -</option>
<?php
@@ -85,10 +91,12 @@ ob_start();
<small class="form-text text-muted">Should have a seperate account created off the payment provider's name e.g. Stripe</small>
</div>
<div class="mb-3">
<div class="form-group">
<label>Threshold</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="decimal" pattern="[0-9]*\.?[0-9]{0,2}" name="threshold" placeholder="1000.00" value="<?= $threshold ?>">
</div>
<small class="form-text text-muted">Will not show as an option at Checkout if above this number</small>
@@ -102,11 +110,13 @@ ob_start();
Payment Processing Fee Expenses get reconciled nighly via the cron
</div>
<div class="mb-3">
<div class="form-group">
<label>Payment Provider Vendor <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
<select class="form-select select2" name="expense_vendor" required>
</div>
<select class="form-control select2" name="expense_vendor" required>
<option value="0">Expense Disabled</option>
<?php
@@ -127,11 +137,13 @@ ob_start();
<small class="form-text text-muted">Payment Privider name e.g. Stripe</small>
</div>
<div class="mb-3">
<div class="form-group">
<label>Expense Category <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
<select class="form-select select2" name="expense_category" required>
</div>
<select class="form-control select2" name="expense_category" required>
<option value="">- Select a Category -</option>
<?php
@@ -146,10 +158,12 @@ ob_start();
}
?>
</select>
<div class="input-group-append">
<button class="btn btn-secondary ajax-modal" type="button"
data-modal-url="../admin/modals/category/category_add.php?category=Expense">
<i class="fas fa-plus"></i>
</button>
</div>
</div>
<small class="form-text text-muted">Processing Fee, Credit Card Fee etc</small>
</div>
@@ -157,8 +171,8 @@ ob_start();
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_payment_provider" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_payment_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -6,33 +6,39 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram me-2"></i>Creating Project Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram mr-2"></i>Creating Project Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Project Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Project Template Name" maxlength="255" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Description">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_project_template" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Create</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$project_template_id = intval($_GET['project_template_id']);
$sql = mysqli_query($mysqli, "SELECT project_template_description, project_template_name FROM project_templates WHERE project_template_id = $project_template_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM project_templates WHERE project_template_id = $project_template_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$project_template_name = escapeHtml($row['project_template_name']);
$project_template_description = escapeHtml($row['project_template_description']);
@@ -14,26 +14,32 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram me-2"></i>Editing Project Template: <strong><?= $project_template_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram mr-2"></i>Editing Project Template: <strong><?= $project_template_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="project_template_id" value="<?= $project_template_id ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Project Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Project Template Name" maxlength="255" value="<?= $project_template_name ?>" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Description" value="<?= $project_template_description ?>">
</div>
</div>
@@ -41,8 +47,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="edit_project_template" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -9,8 +9,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-life-ring me-2"></i>Adding Ticket Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-life-ring mr-2"></i>Adding Ticket Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -18,11 +20,13 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Ticket Template <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-life-ring"></i></span>
<select class="form-select select2" name="ticket_template_id" required>
</div>
<select class="form-control select2" name="ticket_template_id" required>
<option value="">- Select a Ticket Template -</option>
<?php
@@ -47,10 +51,12 @@ ob_start();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Order</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
</div>
<input type="text" class="form-control" name="order" value="1">
</div>
</div>
@@ -58,8 +64,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="add_ticket_template_to_project_template" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Add</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_ticket_template_to_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Add</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -6,8 +6,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield me-2"></i>New Role</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield mr-2"></i>New Role</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
@@ -17,10 +19,10 @@ ob_start();
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-role-details">Details</a>
<a class="nav-link active" data-toggle="pill" href="#pills-role-details">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-role-permissions">Permissions</a>
<a class="nav-link" data-toggle="pill" href="#pills-role-permissions">Permissions</a>
</li>
</ul>
@@ -31,35 +33,39 @@ ob_start();
<!-- DETAILS TAB -->
<div class="tab-pane fade show active" id="pills-role-details">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
</div>
<input type="text" class="form-control" name="role_name" placeholder="Role Name" maxlength="200" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-chevron-right"></i></span>
</div>
<input type="text" class="form-control" name="role_description" placeholder="Role Description" maxlength="200" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Admin Access <strong class="text-danger">*</strong></label>
<div class="form-check mb-2">
<input type="radio" class="form-check-input" id="admin_no" name="role_is_admin" value="0" checked required>
<label class="form-check-label" for="admin_no">
<div class="custom-control custom-radio mb-2">
<input type="radio" class="custom-control-input" id="admin_no" name="role_is_admin" value="0" checked required>
<label class="custom-control-label" for="admin_no">
No - use permissions on the next tab
</label>
</div>
<div class="form-check">
<input type="radio" class="form-check-input" id="admin_yes" name="role_is_admin" value="1" required>
<label class="form-check-label" for="admin_yes">
<div class="custom-control custom-radio">
<input type="radio" class="custom-control-input" id="admin_yes" name="role_is_admin" value="1" required>
<label class="custom-control-label" for="admin_yes">
Yes - this role should have full admin access
</label>
</div>
@@ -73,7 +79,7 @@ ob_start();
<?php
// Enumerate modules
$sql_modules = mysqli_query($mysqli, "SELECT module_description, module_id, module_name FROM modules");
$sql_modules = mysqli_query($mysqli, "SELECT * FROM modules");
while ($row_modules = mysqli_fetch_assoc($sql_modules)) {
$module_id = intval($row_modules['module_id']);
@@ -92,13 +98,14 @@ ob_start();
$group_id = "perm_group_$module_id";
?>
<div class="mb-3">
<div class="form-group">
<label><?= $module_name_display_safe ?> <strong class="text-danger">*</strong></label>
<div class="btn-group w-100" role="group"
<div class="btn-group btn-group-toggle btn-block" data-toggle="buttons" role="group"
aria-label="Permissions for <?= $module_name_display_safe ?>">
<input class="btn-check"
<label class="btn btn-outline-secondary btn-sm active" title="No Access">
<input
type="radio"
name="<?= $field_name ?>"
id="<?= $group_id ?>_0"
@@ -107,34 +114,41 @@ ob_start();
checked
required
>
<label class="btn btn-outline-secondary btn-sm" title="No Access" for="<?= $group_id ?>_0">None</label>
None
</label>
<input class="btn-check"
<label class="btn btn-outline-primary btn-sm" title="Viewing Only">
<input
type="radio"
name="<?= $field_name ?>"
id="<?= $group_id ?>_1"
value="1"
autocomplete="off"
>
<label class="btn btn-outline-primary btn-sm" title="Viewing Only" for="<?= $group_id ?>_1"><i class="fas fa-fw fa-eye me-1"></i>Read</label>
<i class="fas fa-fw fa-eye mr-1"></i>Read
</label>
<input class="btn-check"
<label class="btn btn-outline-warning btn-sm" title="Read, Edit, Archive">
<input
type="radio"
name="<?= $field_name ?>"
id="<?= $group_id ?>_2"
value="2"
autocomplete="off"
>
<label class="btn btn-outline-warning btn-sm" title="Read, Edit, Archive" for="<?= $group_id ?>_2"><i class="fas fa-fw fa-edit me-1"></i>Modify</label>
<i class="fas fa-fw fa-edit mr-1"></i>Modify
</label>
<input class="btn-check"
<label class="btn btn-outline-danger btn-sm" title="Read, Edit, Archive, Delete">
<input
type="radio"
name="<?= $field_name ?>"
id="<?= $group_id ?>_3"
value="3"
autocomplete="off"
>
<label class="btn btn-outline-danger btn-sm" title="Read, Edit, Archive, Delete" for="<?= $group_id ?>_3"><i class="fas fa-fw fa-trash me-1"></i>Full</label>
<i class="fas fa-fw fa-trash mr-1"></i>Full
</label>
</div>
@@ -150,10 +164,10 @@ ob_start();
<div class="modal-footer">
<button type="submit" name="add_role" class="btn btn-primary text-bold">
<i class="fas fa-check me-2"></i>Create
<i class="fas fa-check mr-2"></i>Create
</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal">
<i class="fas fa-times me-2"></i>Cancel
<button type="button" class="btn btn-light" data-dismiss="modal">
<i class="fas fa-times mr-2"></i>Cancel
</button>
</div>
</form>

View File

@@ -15,7 +15,7 @@ $role_admin = intval($row['role_is_admin']);
$sql_role_user_count = mysqli_query($mysqli, "SELECT COUNT(user_id) FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
$role_user_count = mysqli_fetch_row($sql_role_user_count)[0];
$sql_users = mysqli_query($mysqli, "SELECT user_name FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
$sql_users = mysqli_query($mysqli, "SELECT * FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
// Initialize an empty array to hold user names
$user_names = [];
@@ -35,9 +35,11 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield me-2"></i>Editing role:
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield mr-2"></i>Editing role:
<strong><?= $role_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -45,11 +47,11 @@ ob_start();
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-role-details">Details</a>
<a class="nav-link active" data-toggle="pill" href="#pills-role-details">Details</a>
</li>
<?php if (!$role_admin) { ?>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-role-permissions">Permissions</a>
<a class="nav-link" data-toggle="pill" href="#pills-role-permissions">Permissions</a>
</li>
<?php } ?>
</ul>
@@ -60,37 +62,41 @@ ob_start();
<div class="tab-pane fade show active" id="pills-role-details">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
</div>
<input type="text" class="form-control" name="role_name" placeholder="Role Name" maxlength="200" value="<?= $role_name ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-chevron-right"></i></span>
</div>
<input type="text" class="form-control" name="role_description" placeholder="Role Description" maxlength="200" value="<?= $role_description ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Admin Access <strong class="text-danger">*</strong></label>
<div class="form-check mb-2">
<input type="radio" class="form-check-input" id="admin_yes" name="role_is_admin" value="1"
<div class="custom-control custom-radio mb-2">
<input type="radio" class="custom-control-input" id="admin_yes" name="role_is_admin" value="1"
<?php if ($role_admin) { echo 'checked'; } ?> required>
<label class="form-check-label" for="admin_yes">
<label class="custom-control-label" for="admin_yes">
Yes - this role should have full admin access
</label>
</div>
<div class="form-check">
<input type="radio" class="form-check-input" id="admin_no" name="role_is_admin" value="0"
<div class="custom-control custom-radio">
<input type="radio" class="custom-control-input" id="admin_no" name="role_is_admin" value="0"
<?php if (!$role_admin) { echo 'checked'; } ?> required>
<label class="form-check-label" for="admin_no">
<label class="custom-control-label" for="admin_no">
No - use permissions on the next tab
</label>
</div>
@@ -107,7 +113,7 @@ ob_start();
<?php
// Enumerate modules
$sql_modules = mysqli_query($mysqli, "SELECT module_description, module_id, module_name FROM modules");
$sql_modules = mysqli_query($mysqli, "SELECT * FROM modules");
while ($row_modules = mysqli_fetch_assoc($sql_modules)) {
$module_id = intval($row_modules['module_id']);
$module_name = escapeHtml($row_modules['module_name']);
@@ -122,16 +128,17 @@ ob_start();
}
?>
<div class="mb-3">
<div class="form-group">
<label> <?= $module_name_display ?> <strong class="text-danger">*</strong></label>
<?php
$field_name = "$module_id##$module_name";
$group_id = "perm_group_$module_id";
?>
<div class="btn-group w-100" role="group" aria-label="Permissions for <?= $module_name_display ?>">
<div class="btn-group btn-group-toggle btn-block" data-toggle="buttons" role="group" aria-label="Permissions for <?= $module_name_display ?>">
<input class="btn-check"
<label class="btn btn-outline-secondary btn-sm <?php if ($module_permission == 0) { echo 'active'; } ?>" title="No Access">
<input
type="radio"
name="<?= $field_name ?>"
id="<?= $group_id ?>_0"
@@ -140,9 +147,11 @@ ob_start();
<?php if ($module_permission == 0) { echo 'checked'; } ?>
required
>
<label class="btn btn-outline-secondary btn-sm" title="No Access" for="<?= $group_id ?>_0">None</label>
None
</label>
<input class="btn-check"
<label class="btn btn-outline-primary btn-sm <?php if ($module_permission == 1) { echo 'active'; } ?>" title="Viewing Only">
<input
type="radio"
name="<?= $field_name ?>"
id="<?= $group_id ?>_1"
@@ -150,9 +159,11 @@ ob_start();
autocomplete="off"
<?php if ($module_permission == 1) { echo 'checked'; } ?>
>
<label class="btn btn-outline-primary btn-sm" title="Viewing Only" for="<?= $group_id ?>_1"><i class="fas fa-fw fa-eye me-1"></i>Read</label>
<i class="fas fa-fw fa-eye mr-1"></i>Read
</label>
<input class="btn-check"
<label class="btn btn-outline-warning btn-sm <?php if ($module_permission == 2) { echo 'active'; } ?>" title="Read, Edit, Archive">
<input
type="radio"
name="<?= $field_name ?>"
id="<?= $group_id ?>_2"
@@ -160,9 +171,11 @@ ob_start();
autocomplete="off"
<?php if ($module_permission == 2) { echo 'checked'; } ?>
>
<label class="btn btn-outline-warning btn-sm" title="Read, Edit, Archive" for="<?= $group_id ?>_2"><i class="fas fa-fw fa-edit me-1"></i>Modify</label>
<i class="fas fa-fw fa-edit mr-1"></i>Modify
</label>
<input class="btn-check"
<label class="btn btn-outline-danger btn-sm <?php if ($module_permission == 3) { echo 'active'; } ?>" title="Read, Edit, Archive, Delete">
<input
type="radio"
name="<?= $field_name ?>"
id="<?= $group_id ?>_3"
@@ -170,7 +183,8 @@ ob_start();
autocomplete="off"
<?php if ($module_permission == 3) { echo 'checked'; } ?>
>
<label class="btn btn-outline-danger btn-sm" title="Read, Edit, Archive, Delete" for="<?= $group_id ?>_3"><i class="fas fa-fw fa-trash me-1"></i>Full</label>
<i class="fas fa-fw fa-trash mr-1"></i>Full
</label>
</div>
@@ -187,8 +201,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="edit_role" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_role" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,49 +4,59 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-stopwatch me-2"></i>New SLA</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-stopwatch mr-2"></i>New SLA</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stopwatch"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="SLA name" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-comment"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Short description" maxlength="500">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Response target (business minutes) <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-reply"></i></span>
</div>
<input type="number" class="form-control" name="response_minutes" min="1" placeholder="e.g. 60" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Resolution target (business minutes)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-flag-checkered"></i></span>
</div>
<input type="number" class="form-control" name="resolution_minutes" min="1" placeholder="Leave empty for response-only">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_sla" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Create</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_sla" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -3,7 +3,7 @@ require_once '../../includes/modal_header.php';
$sla_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT sla_description, sla_name, sla_resolution_minutes, sla_response_minutes FROM slas WHERE sla_id = $sla_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM slas WHERE sla_id = $sla_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$sla_name = escapeHtml($row['sla_name']);
$sla_description = escapeHtml($row['sla_description']);
@@ -14,42 +14,52 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-stopwatch me-2"></i>Editing SLA: <strong><?= $sla_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-stopwatch mr-2"></i>Editing SLA: <strong><?= $sla_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="sla_id" value="<?= $sla_id ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stopwatch"></i></span>
</div>
<input type="text" class="form-control" name="name" value="<?= $sla_name ?>" maxlength="200" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-comment"></i></span>
</div>
<input type="text" class="form-control" name="description" value="<?= $sla_description ?>" maxlength="500">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Response target (business minutes) <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-reply"></i></span>
</div>
<input type="number" class="form-control" name="response_minutes" min="1" value="<?= $sla_response_minutes ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Resolution target (business minutes)</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-flag-checkered"></i></span>
</div>
<input type="number" class="form-control" name="resolution_minutes" min="1" value="<?= $sla_resolution_minutes ?: '' ?>" placeholder="Leave empty for response-only">
</div>
<small class="text-muted">Saving recalculates targets on open tickets using this SLA.</small>
@@ -57,8 +67,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="edit_sla" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_sla" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -17,43 +17,53 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-cube me-2"></i>New License Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>New License Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Software name" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Version</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<input type="text" class="form-control" name="version" placeholder="Software version" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Short description">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Type <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
<select class="form-select select2" name="type" required>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Select Type -</option>
<<?php
$sql_software_types_select = mysqli_query($mysqli, "
@@ -71,11 +81,13 @@ ob_start();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>License Type</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
<select class="form-select select2" name="license_type">
</div>
<select class="form-control select2" name="license_type">
<option value="">- Select a License Type -</option>
<?php foreach($license_types_array as $license_type) { ?>
<option><?= $license_type ?></option>
@@ -88,8 +100,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="add_software_template" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_software_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,8 +4,7 @@ require_once '../../includes/modal_header.php';
$software_template_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT software_template_description, software_template_license_type, software_template_name,
software_template_notes, software_template_type, software_template_version FROM software_templates WHERE software_template_id = $software_template_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM software_templates WHERE software_template_id = $software_template_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$software_name = escapeHtml($row['software_template_name']);
$software_version = escapeHtml($row['software_template_version']);
@@ -29,8 +28,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-cube me-2"></i>Editing template: <strong><?= $software_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>Editing template: <strong><?= $software_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -38,35 +39,43 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Software name" maxlength="200" value="<?= $software_name ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Version</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<input type="text" class="form-control" name="version" placeholder="Software version" maxlength="200" value="<?= $software_version ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Short description" value="<?= $software_description ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Type <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
<select class="form-select select2" name="type" required>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Select Type -</option>
<<?php
$sql_software_types_select = mysqli_query($mysqli, "
@@ -86,11 +95,13 @@ ob_start();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>License Type</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
<select class="form-select select2" name="license_type">
</div>
<select class="form-control select2" name="license_type">
<option value="">- Select a License Type -</option>
<?php foreach($license_types_array as $license_type_select) { ?>
<option <?php if($license_type_select == $software_license_type){ echo "selected"; } ?>><?= $license_type_select ?></option>
@@ -103,8 +114,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="edit_software_template" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_software_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -24,18 +24,22 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-tag me-2"></i>New <strong><?= $type_display ?></strong> Tag</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-tag mr-2"></i>New <strong><?= $type_display ?></strong> Tag</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="type" value="<?= $type ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Tag name" maxlength="200" required autofocus>
</div>
</div>
@@ -46,11 +50,13 @@ ob_start();
<?php } else { ?>
<div class="mb-3">
<div class="form-group">
<label>Type <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-th"></i></span>
<select class="form-select select2" name="type" required>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Type -</option>
<option value="1">Client Tag</option>
<option value="2">Location Tag</option>
@@ -63,26 +69,30 @@ ob_start();
<?php } ?>
<div class="mb-3">
<div class="form-group">
<label>Color <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
</div>
<input type="color" class="form-control col-3" name="color" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Icon</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
</div>
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" maxlength="200">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_tag" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Create Tag</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_tag" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create Tag</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$tag_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT tag_color, tag_icon, tag_name, tag_type FROM tags WHERE tag_id = $tag_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_id = $tag_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$tag_name = escapeHtml($row['tag_name']);
@@ -29,8 +29,10 @@ if ($tag_type == 1) {
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-tag me-2"></i><?= $tag_type_display ?> Tag: <strong><?= $tag_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-tag mr-2"></i><?= $tag_type_display ?> Tag: <strong><?= $tag_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -38,34 +40,40 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
</div>
<input type="text" class="form-control" name="name" maxlength="200" value="<?= $tag_name ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Color <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
</div>
<input type="color" class="form-control col-3" name="color" value="<?= $tag_color ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Icon</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
</div>
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" maxlength="200" value="<?= $tag_icon ?>">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_tag" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save changes</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_tag" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save changes</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

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

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$tax_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT tax_name, tax_percent FROM taxes WHERE tax_id = $tax_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM taxes WHERE tax_id = $tax_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$tax_name = escapeHtml($row['tax_name']);
$tax_percent = floatval($row['tax_percent']);
@@ -14,8 +14,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-balance-scale me-2"></i>Editing tax: <strong><?= $tax_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-balance-scale mr-2"></i>Editing tax: <strong><?= $tax_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -23,20 +25,20 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<input type="text" class="form-control" name="name" maxlength="200" value="<?= $tax_name ?>" required>
</div>
<div class="mb-3">
<div class="form-group">
<label>Percent <strong class="text-danger">*</strong></label>
<input type="number" min="0" step="any" class="form-control col-md-4" name="percent" value="<?= $tax_percent ?>">
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_tax" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_tax" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,35 +4,43 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle me-2"></i>New Ticket Status</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle mr-2"></i>New Ticket Status</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-info-circle"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Ticket Status name" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Color <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
</div>
<input type="color" class="form-control col-3" name="color" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>SLA</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stopwatch"></i></span>
<select class="form-select select2" name="pauses_sla">
</div>
<select class="form-control select2" name="pauses_sla">
<option value="0">Resolution clock keeps running</option>
<option value="1">Pause the resolution clock</option>
</select>
@@ -41,8 +49,8 @@ ob_start();
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Create</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,24 +4,23 @@ require_once '../../includes/modal_header.php';
$ticket_status_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT ticket_status_active, ticket_status_color, ticket_status_name, ticket_status_order,
ticket_status_pauses_sla FROM ticket_statuses WHERE ticket_status_id = $ticket_status_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM ticket_statuses WHERE ticket_status_id = $ticket_status_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$ticket_status_name = escapeHtml($row['ticket_status_name']);
$ticket_status_color = escapeHtml($row['ticket_status_color']);
$ticket_status_order = intval($row['ticket_status_order']);
$ticket_status_active = intval($row['ticket_status_active']);
$ticket_status_pauses_sla = intval($row['ticket_status_pauses_sla']);
// Null for a custom status (the admin picks), 1 or 0 for the five built-in ones
$ticket_status_sla_lock = getTicketStatusSlaLock($ticket_status_id);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle me-2"></i>Editing Ticket Status: <strong><?= $ticket_status_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle mr-2"></i>Editing Ticket Status: <strong><?= $ticket_status_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -29,67 +28,67 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
</div>
<input type="text" class="form-control" name="name" maxlength="200" value="<?= $ticket_status_name ?>" required <?php if ($ticket_status_id <= 5) { echo "readonly"; } ?>>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Color <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-paint-brush"></i></span>
</div>
<input type="color" class="form-control col-3" name="color" value="<?= $ticket_status_color ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Order</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
</div>
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order" value="<?= $ticket_status_order ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Status <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-info-circle"></i></span>
<select class="form-select select2" name="status" required>
</div>
<select class="form-control select2" name="status" required>
<option <?php if ($ticket_status_active == 1) { echo "selected"; } ?> value="1">Active</option>
<option <?php if ($ticket_status_active == 0) { echo "selected"; } ?> value="0" <?php if ($ticket_status_id <= 5) { echo "disabled"; } ?>>Inactive</option>
</select>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>SLA</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-stopwatch"></i></span>
<?php if (is_null($ticket_status_sla_lock)) { ?>
<select class="form-select select2" name="pauses_sla">
<option <?php if ($ticket_status_pauses_sla == 0) { echo "selected "; } ?>value="0">Resolution clock keeps running</option>
<option <?php if ($ticket_status_pauses_sla == 1) { echo "selected "; } ?>value="1">Pause the resolution clock</option>
</select>
<?php } else { ?>
<input type="text" class="form-control" value="<?= $ticket_status_sla_lock ? 'Pause the resolution clock' : 'Resolution clock keeps running' ?>" readonly>
<?php } ?>
</div>
<select class="form-control select2" name="pauses_sla">
<option <?php if ($ticket_status_pauses_sla == 0) { echo "selected "; } ?>value="0">Resolution clock keeps running</option>
<option <?php if ($ticket_status_pauses_sla == 1) { echo "selected "; } ?>value="1">Pause the resolution clock</option>
</select>
</div>
<?php if (is_null($ticket_status_sla_lock)) { ?>
<small class="text-muted">Tickets sitting in a paused status never warn or breach on resolution. Time already spent is kept and the deadline moves out when the ticket comes back.</small>
<?php } elseif ($ticket_status_sla_lock) { ?>
<small class="text-muted">Fixed for built-in statuses. Tickets here never warn or breach on resolution - time already spent is kept and the deadline moves out when the ticket comes back.</small>
<?php } else { ?>
<small class="text-muted">Fixed for built-in statuses. Tickets here are being worked, so their resolution clock runs.</small>
<?php } ?>
<small class="text-muted">Tickets sitting in a paused status never warn or breach on resolution. Time already spent is kept and the deadline moves out when the ticket comes back.</small>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -6,51 +6,61 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring me-2"></i>New Ticket Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring mr-2"></i>New Ticket Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-life-ring"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Template name" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Subject</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="subject" placeholder="Subject" maxlength="500">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<textarea class="form-control tinymceTicket" name="details"></textarea>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Short description">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Add it to a Project Template?</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
<select class="form-select select2" name="project_template">
</div>
<select class="form-control select2" name="project_template">
<option value="0">- No -</option>
<?php
$sql_project_templates = mysqli_query($mysqli, "SELECT project_template_id, project_template_name FROM project_templates WHERE project_template_archived_at IS NULL ORDER BY project_template_name ASC");
$sql_project_templates = mysqli_query($mysqli, "SELECT * FROM project_templates WHERE project_template_archived_at IS NULL ORDER BY project_template_name ASC");
while ($row = mysqli_fetch_assoc($sql_project_templates)) {
$project_template_id_select = intval($row['project_template_id']);
$project_template_name_select = escapeHtml($row['project_template_name']); ?>
@@ -62,8 +72,8 @@ ob_start();
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ticket_template" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create Template</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_ticket_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create Template</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -3,8 +3,10 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring me-2"></i>Editing Ticket Template: <?= $ticket_template_name ?></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring mr-2"></i>Editing Ticket Template: <?= $ticket_template_name ?></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -12,38 +14,44 @@
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Template Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-life-ring"></i></span>
</div>
<input type="text" class="form-control" name="name" maxlength="200" value="<?= $ticket_template_name ?>" placeholder="Template name" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Subject</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="subject" maxlength="500" value="<?= $ticket_template_subject ?>" placeholder="Subject">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<textarea class="form-control tinymceTicket" name="details"><?= $ticket_template_details ?></textarea>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="description" value="<?= $ticket_template_description ?>" placeholder="Short description">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ticket_template" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_ticket_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$task_template_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT task_template_completion_estimate, task_template_name, task_template_order FROM task_templates WHERE task_template_id = $task_template_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE task_template_id = $task_template_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$task_template_name = escapeHtml($row['task_template_name']);
@@ -18,8 +18,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-tasks me-2"></i>Editing task</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fa fa-fw fa-tasks mr-2"></i>Editing task</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -27,18 +29,22 @@ ob_start();
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Name the task" maxlength="255" value="<?= $task_template_name ?>" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Estimated Completion Time <span class="text-secondary">(Minutes)</span></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
</div>
<input type="number" class="form-control" name="completion_estimate" placeholder="Estimated time to complete task in mins" value="<?= $task_template_completion_estimate ?>">
</div>
</div>
@@ -46,8 +52,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="edit_ticket_template_task" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_ticket_template_task" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -6,8 +6,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-plus me-2"></i>New User</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-user-plus mr-2"></i>New User</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -15,10 +17,10 @@ ob_start();
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-user-details">Details</a>
<a class="nav-link active" data-toggle="pill" href="#pills-user-details">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-user-access">Restrict Access</a>
<a class="nav-link" data-toggle="pill" href="#pills-user-access">Restrict Access</a>
</li>
</ul>
@@ -28,40 +30,52 @@ ob_start();
<div class="tab-pane fade show active" id="pills-user-details">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Full Name" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Email <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
</div>
<input type="email" class="form-control" name="email" placeholder="Email Address" maxlength="200" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Password <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
</div>
<input type="password" class="form-control" data-toggle="password" name="password" id="password" placeholder="Enter a Password" autocomplete="new-password" minlength="8" required>
<div class="input-group-append">
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
</div>
<div class="input-group-append">
<span class="btn btn-default"><i class="fa fa-fw fa-question" onclick="generatePassword()"></i></span>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Role <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
<select class="form-select select2" name="role" required>
</div>
<select class="form-control select2" name="role" required>
<option value="">- Role -</option>
<?php
$sql_user_roles = mysqli_query($mysqli, "SELECT role_id, role_name FROM user_roles WHERE role_archived_at IS NULL");
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE role_archived_at IS NULL");
while ($row = mysqli_fetch_assoc($sql_user_roles)) {
$role_id = intval($row['role_id']);
$role_name = escapeHtml($row['role_name']);
@@ -73,24 +87,24 @@ ob_start();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Avatar</label>
<input type="file" class="form-control" accept="image/*" name="file">
<input type="file" class="form-control-file" accept="image/*" name="file">
</div>
<div class="mb-3" <?php if(empty($config_smtp_host)) { echo "hidden"; } ?>>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sendEmailCheckBox" name="send_email" value="" checked>
<label for="sendEmailCheckBox" class="form-check-label">
<div class="form-group" <?php if(empty($config_smtp_host)) { echo "hidden"; } ?>>
<div class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" id="sendEmailCheckBox" name="send_email" value="" checked>
<label for="sendEmailCheckBox" class="custom-control-label">
Send user e-mail with login details?
</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="forceMFACheckBox" name="force_mfa" value=1>
<label for="forceMFACheckBox" class="form-check-label">
<div class="form-group">
<div class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" id="forceMFACheckBox" name="force_mfa" value=1>
<label for="forceMFACheckBox" class="custom-control-label">
Force MFA
</label>
</div>
@@ -105,7 +119,7 @@ ob_start();
</div>
<div class="mb-2">
<small class="text-muted me-2">Set all:</small>
<small class="text-muted mr-2">Set all:</small>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="document.querySelectorAll('#accessTable .perm-none').forEach(r => r.checked = true);">No Rule</button>
<button type="button" class="btn btn-sm btn-outline-success" onclick="document.querySelectorAll('#accessTable .perm-allow').forEach(r => r.checked = true);">Allow</button>
<button type="button" class="btn btn-sm btn-outline-danger" onclick="document.querySelectorAll('#accessTable .perm-deny').forEach(r => r.checked = true);">Deny</button>
@@ -124,7 +138,7 @@ ob_start();
<tbody>
<?php
$sql_client_select = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
$sql_client_select = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_client_select)) {
$client_id = intval($row['client_id']);
$client_name = escapeHtml($row['client_name']);
@@ -133,13 +147,13 @@ ob_start();
<tr>
<td class="align-middle"><?= $client_name ?></td>
<td class="text-center align-middle">
<input type="radio" class="form-check-input perm-none" name="client_permission[<?= $client_id ?>]" value="" checked>
<input type="radio" class="perm-none" name="client_permission[<?= $client_id ?>]" value="" checked>
</td>
<td class="text-center align-middle">
<input type="radio" class="form-check-input perm-allow" name="client_permission[<?= $client_id ?>]" value="allow">
<input type="radio" class="perm-allow" name="client_permission[<?= $client_id ?>]" value="allow">
</td>
<td class="text-center align-middle">
<input type="radio" class="form-check-input perm-deny" name="client_permission[<?= $client_id ?>]" value="deny">
<input type="radio" class="perm-deny" name="client_permission[<?= $client_id ?>]" value="deny">
</td>
</tr>
@@ -155,15 +169,15 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="add_user" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Create</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<script>
function generatePassword() {
itflowGet(
jQuery.get(
"/agent/ajax.php", {
get_readable_pass: 'true'
},

View File

@@ -18,14 +18,16 @@ ob_start();
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="row col-7 offset-4">
<div class="input-group">
<div class="input-group-prepend">
<input type="password" class="form-control" placeholder="Enter your account password to continue" name="admin_password" required>
</div>
</div>
</div>
<br>
<button class="btn btn-danger" type="submit" name="ir_reset_user_password"><i class="fas fa-fw fa-key me-2"></i>Reset passwords</button>
<button class="btn btn-danger" type="submit" name="ir_reset_user_password"><i class="fas fa-fw fa-key mr-2"></i>Reset passwords</button>
</form>
</div>
<button type="button" class="btn btn-outline-secondary btn-lg px-5 me-4" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-outline-secondary btn-lg px-5 mr-4" data-dismiss="modal">Cancel</button>
</div>

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$user_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT user_avatar, user_email, user_name FROM users WHERE users.user_id = $user_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE users.user_id = $user_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$user_name = escapeHtml($row['user_name']);
@@ -12,13 +12,13 @@ $user_email = escapeHtml($row['user_email']);
$user_avatar = escapeHtml($row['user_avatar']);
$user_initials = escapeHtml(initials($user_name));
$sql_related_tickets = mysqli_query($mysqli, "SELECT 1 FROM tickets
$sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets
WHERE ticket_assigned_to = $user_id AND ticket_resolved_at IS NULL AND ticket_closed_at IS NULL");
$ticket_count = mysqli_num_rows($sql_related_tickets);
// Related Recurring Tickets Query
$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT 1 FROM recurring_tickets WHERE recurring_ticket_assigned_to = $user_id");
$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM recurring_tickets WHERE recurring_ticket_assigned_to = $user_id");
$recurring_ticket_count = mysqli_num_rows($sql_related_recurring_tickets);
@@ -26,9 +26,11 @@ $recurring_ticket_count = mysqli_num_rows($sql_related_recurring_tickets);
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-slash me-2"></i>Archiving user:
<h5 class="modal-title"><i class="fas fa-fw fa-user-slash mr-2"></i>Archiving user:
<strong><?= $user_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -48,14 +50,16 @@ ob_start();
</center>
<div class="mb-3">
<div class="form-group">
<label>Reassign <?= $ticket_count ?> Open Tickets and <?= $recurring_ticket_count ?> Recurring Tickets To:</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
<select class="form-select select2" name="ticket_assign" required>
</div>
<select class="form-control select2" name="ticket_assign" required>
<option value="0">No one</option>
<?php
$sql_users = mysqli_query($mysqli, "SELECT user_id, user_name FROM users WHERE user_type = 1 AND user_archived_at IS NULL");
$sql_users = mysqli_query($mysqli, "SELECT * FROM users WHERE user_type = 1 AND user_archived_at IS NULL");
while ($row = mysqli_fetch_assoc($sql_users)) {
$user_id_select = intval($row['user_id']);
$user_name_select = escapeHtml($row['user_name']);
@@ -70,8 +74,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="archive_user" class="btn btn-danger text-bold"><i class="fas fa-archive me-2"></i>Archive</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="archive_user" class="btn btn-danger text-bold"><i class="fas fa-archive mr-2"></i>Archive</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$user_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT user_avatar, user_config_force_mfa, user_email, user_name, user_role_id, user_token FROM users
$sql = mysqli_query($mysqli, "SELECT * FROM users
LEFT JOIN user_settings ON users.user_id = user_settings.user_id
WHERE users.user_id = $user_id LIMIT 1"
);
@@ -35,9 +35,11 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-edit me-2"></i>Editing user:
<h5 class="modal-title"><i class="fas fa-fw fa-user-edit mr-2"></i>Editing user:
<strong><?= $user_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -46,10 +48,10 @@ ob_start();
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-user-details<?= $user_id ?>">Details</a>
<a class="nav-link active" data-toggle="pill" href="#pills-user-details<?= $user_id ?>">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-user-access<?= $user_id ?>">Restrict Access</a>
<a class="nav-link" data-toggle="pill" href="#pills-user-access<?= $user_id ?>">Restrict Access</a>
</li>
</ul>
@@ -70,42 +72,54 @@ ob_start();
<?php } ?>
</center>
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Full Name" maxlength="200"
value="<?= $user_name ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Email <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
</div>
<input type="email" class="form-control" name="email" placeholder="Email Address" maxlength="200"
value="<?= $user_email ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>New Password</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
</div>
<input type="password" class="form-control" data-toggle="password" name="new_password" id="password"
placeholder="Leave Blank For No Password Change" autocomplete="new-password">
<div class="input-group-append">
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
</div>
<div class="input-group-append">
<span class="btn btn-default"><i class="fa fa-fw fa-question" onclick="generatePassword()"></i></span>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Role <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
<select class="form-select select2" name="role" required>
</div>
<select class="form-control select2" name="role" required>
<?php
$sql_user_roles = mysqli_query($mysqli, "SELECT role_id, role_name FROM user_roles WHERE role_archived_at IS NULL");
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE role_archived_at IS NULL");
while ($row = mysqli_fetch_assoc($sql_user_roles)) {
$role_id = intval($row['role_id']);
$role_name = escapeHtml($row['role_name']);
@@ -118,15 +132,15 @@ ob_start();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Avatar</label>
<input type="file" class="form-control" accept="image/*" name="file">
<input type="file" class="form-control-file" accept="image/*" name="file">
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="forceMFACheckBox<?= $user_id ?>" name="force_mfa" value="1" <?php if($user_config_force_mfa == 1){ echo "checked"; } ?>>
<label for="forceMFACheckBox<?= $user_id ?>" class="form-check-label">
<div class="form-group">
<div class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" id="forceMFACheckBox<?= $user_id ?>" name="force_mfa" value="1" <?php if($user_config_force_mfa == 1){ echo "checked"; } ?>>
<label for="forceMFACheckBox<?= $user_id ?>" class="custom-control-label">
Force MFA
</label>
</div>
@@ -134,11 +148,13 @@ ob_start();
<?php if (!empty($user_token)) { ?>
<div class="mb-3">
<div class="form-group">
<label>2FA</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-id-card"></i></span>
<select class="form-select" name="2fa">
</div>
<select class="form-control" name="2fa">
<option value="">Keep enabled</option>
<option value="disable">Disable</option>
</select>
@@ -155,7 +171,7 @@ ob_start();
</div>
<div class="mb-2">
<small class="text-muted me-2">Set all:</small>
<small class="text-muted mr-2">Set all:</small>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="document.querySelectorAll('#accessTable<?= $user_id ?> .perm-none').forEach(r => r.checked = true);">No Rule</button>
<button type="button" class="btn btn-sm btn-outline-success" onclick="document.querySelectorAll('#accessTable<?= $user_id ?> .perm-allow').forEach(r => r.checked = true);">Allow</button>
<button type="button" class="btn btn-sm btn-outline-danger" onclick="document.querySelectorAll('#accessTable<?= $user_id ?> .perm-deny').forEach(r => r.checked = true);">Deny</button>
@@ -174,7 +190,7 @@ ob_start();
<tbody>
<?php
$sql_client_select = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
$sql_client_select = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_client_select)) {
$client_id_select = intval($row['client_id']);
$client_name_select = escapeHtml($row['client_name']);
@@ -185,13 +201,13 @@ ob_start();
<tr>
<td class="align-middle"><?= $client_name_select ?></td>
<td class="text-center align-middle">
<input type="radio" class="form-check-input perm-none" name="client_permission[<?= $client_id_select ?>]" value="" <?php if (!$client_is_allow && !$client_is_deny) { echo "checked"; } ?>>
<input type="radio" class="perm-none" name="client_permission[<?= $client_id_select ?>]" value="" <?php if (!$client_is_allow && !$client_is_deny) { echo "checked"; } ?>>
</td>
<td class="text-center align-middle">
<input type="radio" class="form-check-input perm-allow" name="client_permission[<?= $client_id_select ?>]" value="allow" <?php if ($client_is_allow) { echo "checked"; } ?>>
<input type="radio" class="perm-allow" name="client_permission[<?= $client_id_select ?>]" value="allow" <?php if ($client_is_allow) { echo "checked"; } ?>>
</td>
<td class="text-center align-middle">
<input type="radio" class="form-check-input perm-deny" name="client_permission[<?= $client_id_select ?>]" value="deny" <?php if ($client_is_deny) { echo "checked"; } ?>>
<input type="radio" class="perm-deny" name="client_permission[<?= $client_id_select ?>]" value="deny" <?php if ($client_is_deny) { echo "checked"; } ?>>
</td>
</tr>
@@ -206,8 +222,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="edit_user" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="edit_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
@@ -215,7 +231,7 @@ ob_start();
function generatePassword() {
// Send a GET request to ajax.php as ajax.php?get_readable_pass=true
itflowGet(
jQuery.get(
"/agent/ajax.php", {
get_readable_pass: 'true'
},

View File

@@ -22,8 +22,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download me-2"></i>Export Users</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Users</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
@@ -35,19 +37,23 @@ ob_start();
<?php exportTabsFiltersOpen(); ?>
<div class="mb-3">
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name or email">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
<select class="form-select select2" name="archived">
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>

View File

@@ -7,25 +7,31 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-plus"></i>Invite User</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="mb-3">
<div class="form-group">
<label>Email <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
</div>
<input type="email" class="form-control" name="email" placeholder="Email Address" maxlength="200" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Role <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
<select class="form-select select2" name="role" required>
</div>
<select class="form-control select2" name="role" required>
<option value="">- Role -</option>
<!-- //TODO: Pull from roles -->
</select>
@@ -34,8 +40,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="invite_user" class="btn btn-primary text-bold"><i class="fas fa-paper-plane me-2"></i>Send Invite</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="invite_user" class="btn btn-primary text-bold"><i class="fas fa-paper-plane mr-2"></i>Send Invite</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,7 +4,7 @@ require_once '../../includes/modal_header.php';
$user_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT user_avatar, user_email, user_name, user_role_id FROM users WHERE user_id = $user_id AND user_archived_at IS NOT NULL LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_id = $user_id AND user_archived_at IS NOT NULL LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$user_name = str_replace(" (archived)", "", $row['user_name']); //Removed (archived) from user_name
@@ -18,9 +18,11 @@ $user_role_id = intval($row['user_role_id']);
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-redo-alt me-2"></i>Restoring user:
<h5 class="modal-title"><i class="fas fa-fw fa-redo-alt mr-2"></i>Restoring user:
<strong><?= $user_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -39,23 +41,29 @@ ob_start();
<?php } ?>
</center>
<div class="mb-3">
<div class="form-group">
<label>Set a New Password</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
</div>
<input type="password" class="form-control" data-toggle="password" name="new_password"
placeholder="Enter a new password" autocomplete="new-password" required>
<div class="input-group-append">
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Role <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-shield"></i></span>
<select class="form-select select2" name="role" required>
</div>
<select class="form-control select2" name="role" required>
<?php
$sql_user_roles = mysqli_query($mysqli, "SELECT role_id, role_name FROM user_roles WHERE role_archived_at IS NULL");
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE role_archived_at IS NULL");
while ($row = mysqli_fetch_assoc($sql_user_roles)) {
$role_id = intval($row['role_id']);
$role_name = escapeHtml($row['role_name']);
@@ -70,8 +78,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="restore_user" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Restore</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fas fa-times me-2"></i>Cancel</button>
<button type="submit" name="restore_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Restore</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -7,8 +7,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-building me-2"></i>New Vendor Template</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-building mr-2"></i>New Vendor Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -17,13 +19,13 @@ ob_start();
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-details">Details</a>
<a class="nav-link active" data-toggle="pill" href="#pills-details">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-support">Support</a>
<a class="nav-link" data-toggle="pill" href="#pills-support">Support</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-notes">Notes</a>
<a class="nav-link" data-toggle="pill" href="#pills-notes">Notes</a>
</li>
</ul>
@@ -33,34 +35,42 @@ ob_start();
<div class="tab-pane fade show active" id="pills-details">
<div class="mb-3">
<div class="form-group">
<label>Vendor Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Vendor Name" maxlength="200" required autofocus>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Description" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Account Number</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
</div>
<input type="text" class="form-control" name="account_number" placeholder="Account number" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Account Manager</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
</div>
<input type="text" class="form-control" name="contact_name" placeholder="Account manager's name" maxlength="200">
</div>
</div>
@@ -70,59 +80,71 @@ ob_start();
<div class="tab-pane fade" id="pills-support">
<label>Support Phone / <span class="text-secondary">Extension</span></label>
<div class="row g-2">
<div class="form-row">
<div class="col-9">
<div class="mb-3">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div>
<input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="phone" placeholder="Phone Number" maxlength="200">
</div>
</div>
</div>
<div class="col-3">
<div class="mb-3">
<div class="form-group">
<input type="text" class="form-control" name="extension" placeholder="ext." maxlength="200">
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Support Hours</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="text" class="form-control" name="hours" placeholder="Support Hours" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Support Email</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
</div>
<input type="email" class="form-control" name="email" placeholder="Support Email" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Support Website URL</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
</div>
<input type="text" class="form-control" name="website" placeholder="Do not include http(s)://" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Pin/Code</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="code" placeholder="Access Code or Pin" maxlength="200">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>SLA</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-handshake"></i></span>
</div>
<input type="text" class="form-control" name="sla" placeholder="SLA Response Time" maxlength="200">
</div>
</div>
@@ -131,7 +153,7 @@ ob_start();
<div class="tab-pane fade" id="pills-notes">
<div class="mb-3">
<div class="form-group">
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"></textarea>
</div>
@@ -141,8 +163,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" name="add_vendor_template" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create Template</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" name="add_vendor_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create Template</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -4,10 +4,7 @@ require_once '../../includes/modal_header.php';
$vendor_template_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT vendor_template_account_number, vendor_template_code, vendor_template_contact_name,
vendor_template_description, vendor_template_email, vendor_template_extension,
vendor_template_hours, vendor_template_name, vendor_template_notes, vendor_template_phone,
vendor_template_phone_country_code, vendor_template_sla, vendor_template_website FROM vendor_templates WHERE vendor_template_id = $vendor_template_id LIMIT 1");
$sql = mysqli_query($mysqli, "SELECT * FROM vendor_templates WHERE vendor_template_id = $vendor_template_id LIMIT 1");
$row = mysqli_fetch_assoc($sql);
$vendor_name = escapeHtml($row['vendor_template_name']);
$vendor_description = escapeHtml($row['vendor_template_description']);
@@ -28,8 +25,10 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-building me-2"></i>Editing vendor template: <strong><?= $vendor_name ?></strong></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<h5 class="modal-title"><i class="fas fa-fw fa-building mr-2"></i>Editing vendor template: <strong><?= $vendor_name ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
@@ -39,13 +38,13 @@ ob_start();
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#pills-details<?= $vendor_template_id ?>">Details</a>
<a class="nav-link active" data-toggle="pill" href="#pills-details<?= $vendor_template_id ?>">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-support<?= $vendor_template_id ?>">Support</a>
<a class="nav-link" data-toggle="pill" href="#pills-support<?= $vendor_template_id ?>">Support</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#pills-notes<?= $vendor_template_id ?>">Notes</a>
<a class="nav-link" data-toggle="pill" href="#pills-notes<?= $vendor_template_id ?>">Notes</a>
</li>
</ul>
@@ -58,54 +57,70 @@ ob_start();
<div class="tab-pane fade show active" id="pills-details<?= $vendor_template_id ?>">
<div class="mb-3">
<div class="form-group">
<label>Vendor Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Vendor Name" maxlength="200" value="<?= "$vendor_name" ?>" required>
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_name" value="1">
<input type="checkbox" name="global_update_vendor_name" value="1">
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
</div>
<input type="text" class="form-control" name="description" placeholder="Description" maxlength="200" value="<?= $vendor_description ?>">
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_description" value="1">
<input type="checkbox" name="global_update_vendor_description" value="1">
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Account Number</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
</div>
<input type="text" class="form-control" name="account_number" placeholder="Account number" maxlength="200" value="<?= $vendor_account_number ?>">
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_account_number" value="1">
<input type="checkbox" name="global_update_vendor_account_number" value="1">
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Account Manager</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
</div>
<input type="text" class="form-control" name="contact_name" maxlength="200" value="<?= $vendor_contact_name ?>" placeholder="Vendor contact name">
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_contact_name" value="1">
<input type="checkbox" name="global_update_vendor_contact_name" value="1">
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="updateVendorsCheckbox<?= $vendor_template_id ?>" name="update_base_vendors" value="1" >
<label class="form-check-label" for="updateVendorsCheckbox<?= $vendor_template_id ?>">Update All Base Vendors</label>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="updateVendorsCheckbox<?= $vendor_template_id ?>" name="update_base_vendors" value="1" >
<label class="custom-control-label" for="updateVendorsCheckbox<?= $vendor_template_id ?>">Update All Base Vendors</label>
</div>
</div>
@@ -114,11 +129,13 @@ ob_start();
<div class="tab-pane fade" id="pills-support<?= $vendor_template_id ?>">
<label>Support Phone</label>
<div class="row g-2">
<div class="form-row">
<div class="col-8">
<div class="mb-3">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div>
<input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4" value="<?= $vendor_phone_country_code ?>">
<input type="tel" class="form-control" name="phone" maxlength="200" value="<?= $vendor_phone ?>">
</div>
@@ -127,65 +144,87 @@ ob_start();
<div class="col-4">
<div class="input-group">
<input type="text" class="form-control" name="extension" placeholder="Prompts" maxlength="200" value="<?= $vendor_extension ?>">
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_phone" value="1">
<input type="checkbox" name="global_update_vendor_phone" value="1">
</div>
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Support Hours</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="text" class="form-control" name="hours" placeholder="Support Hours" maxlength="200" value="<?= $vendor_hours ?>">
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_hours" value="1">
<input type="checkbox" name="global_update_vendor_hours" value="1">
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Support Email</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
</div>
<input type="email" class="form-control" name="email" placeholder="Support Email" maxlength="200" value="<?= $vendor_email ?>">
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_email" value="1">
<input type="checkbox" name="global_update_vendor_email" value="1">
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Support Website URL</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
</div>
<input type="text" class="form-control" name="website" placeholder="Do not include http(s)://" maxlength="200" value="<?= $vendor_website ?>">
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_website" value="1">
<input type="checkbox" name="global_update_vendor_website" value="1">
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>SLA</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-handshake"></i></span>
</div>
<input type="text" class="form-control" name="sla" placeholder="SLA Response Time" maxlength="200" value="<?= $vendor_sla ?>">
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_sla" value="1">
<input type="checkbox" name="global_update_vendor_sla" value="1">
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Pin/Code</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="code" placeholder="Access Code or Pin" maxlength="200" value="<?= $vendor_code ?>">
<div class="input-group-append">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" name="global_update_vendor_code" value="1">
<input type="checkbox" name="global_update_vendor_code" value="1">
</div>
</div>
</div>
</div>
@@ -193,13 +232,13 @@ ob_start();
<div class="tab-pane fade" id="pills-notes<?= $vendor_template_id ?>">
<div class="mb-3">
<div class="form-group">
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?= $vendor_notes ?></textarea>
</div>
<div class="mb-3">
<div class="form-group">
<label>Update Notes Globally?</label>
<input class="form-check-input" type="checkbox" name="global_update_vendor_notes" value="1">
<input type="checkbox" name="global_update_vendor_notes" value="1">
</div>
</div>
@@ -208,8 +247,8 @@ ob_start();
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary text-bold" name="edit_vendor_template"><i class="fa fa-check me-2"></i>Update Template</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
<button type="submit" class="btn btn-primary text-bold" name="edit_vendor_template"><i class="fa fa-check mr-2"></i>Update Template</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>

View File

@@ -8,7 +8,7 @@ require_once "includes/inc_all_admin.php";
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS module_description, module_id, module_name FROM modules
"SELECT SQL_CALC_FOUND_ROWS * FROM modules
WHERE (module_name LIKE '%$q%' OR module_description LIKE '%$q%')
ORDER BY $sort $order LIMIT $record_from, $record_to"
);
@@ -19,11 +19,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-puzzle-piece me-2"></i>Access Modules</h3>
<h3 class="card-title mt-2"><i class="fas fa-fw fa-puzzle-piece mr-2"></i>Access Modules</h3>
<div class="card-tools">
<div class="btn-group">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/module/module_add.php">
<i class="fas fa-fw fa-plus me-2"></i>New Module
<i class="fas fa-fw fa-plus mr-2"></i>New Module
</button>
</div>
</div>
@@ -34,7 +34,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-md-4">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) {echo stripslashes(escapeHtml($q));} ?>" placeholder="Search Modules">
<div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
</div>
@@ -70,20 +72,20 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</td>
<td>
<?php if ($module_id > 6) { ?>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/module/module_edit.php?id=<?= $module_id ?>">
<i class="fas fa-fw fa-user-edit me-2"></i>Edit
<i class="fas fa-fw fa-user-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_module=<?= $module_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-archive me-2"></i>Delete
<i class="fas fa-fw fa-archive mr-2"></i>Delete
</a>
</div>

View File

@@ -6,8 +6,7 @@ $order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT payment_method_created_at, payment_method_description, payment_method_id,
payment_method_name FROM payment_methods ORDER BY $sort $order");
$sql = mysqli_query($mysqli, "SELECT * FROM payment_methods ORDER BY $sort $order");
$num_rows = mysqli_num_rows($sql);
@@ -15,9 +14,9 @@ $num_rows = mysqli_num_rows($sql);
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card me-2"></i>Payment Methods</h3>
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card mr-2"></i>Payment Methods</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/payment_method/payment_method_add.php"><i class="fas fa-plus me-2"></i>Add Payment Method</button>
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/payment_method/payment_method_add.php"><i class="fas fa-plus mr-2"></i>Add Payment Method</button>
</div>
</div>
<div class="card-body">
@@ -63,18 +62,18 @@ $num_rows = mysqli_num_rows($sql);
<td><?= $payment_method_description ?></td>
<td><?= $payment_method_created_at ?></td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/payment_method/payment_method_edit.php?id=<?= $payment_method_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_payment_method=<?= $payment_method_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>

View File

@@ -6,8 +6,7 @@ $order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT account_name, category_name, payment_provider_description, payment_provider_id,
payment_provider_name, payment_provider_threshold, vendor_name FROM payment_providers
$sql = mysqli_query($mysqli, "SELECT * FROM payment_providers
LEFT JOIN accounts ON payment_provider_account = account_id
LEFT JOIN vendors ON payment_provider_expense_vendor = vendor_id
LEFT JOIN categories ON payment_provider_expense_category = category_id
@@ -20,9 +19,9 @@ $num_rows = mysqli_num_rows($sql);
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card me-2"></i>Payment Providers</h3>
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card mr-2"></i>Payment Providers</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/payment_provider/payment_provider_add.php"><i class="fas fa-plus me-2"></i>Add Provider</button>
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/payment_provider/payment_provider_add.php"><i class="fas fa-plus mr-2"></i>Add Provider</button>
</div>
</div>
<div class="card-body">
@@ -95,21 +94,21 @@ $num_rows = mysqli_num_rows($sql);
<td><?= $vendor_name ?></td>
<td><?= $category ?></td>
<td class="text-center">
<a class="badge bg-dark rounded-pill p-2" href="saved_payment_methods.php"><?= $saved_payment_count ?></a>
<a class="badge badge-dark badge-pill p-2" href="saved_payment_methods.php"><?= $saved_payment_count ?></a>
</td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/payment_provider/payment_provider_edit.php?id=<?= $provider_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_payment_provider=<?= $provider_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i><strong>Delete Provider and</strong>
<i class="fas fa-fw fa-trash mr-2"></i><strong>Delete Provider and</strong>
<ul class="text-xs">
<li>Related Recurring Payments</li>
<li>Related Saved cards</li>

View File

@@ -10,11 +10,7 @@ require_once __DIR__ . "/../includes/check_login.php";
// Only allow running post files via inclusion (prevents people/bots poking them directly)
define('FROM_POST_HANDLER', true);
// Load all admin module POST logic.
// *_model.php is a RESERVED suffix: those files are not handlers, they are inline
// field-parsing fragments that read $_POST at include time, so the glob must not
// pull them in. A handler named *_model.php is silently never loaded - name entity
// handlers around it (admin/post/ai_models.php, not ai_model.php).
// Load all admin module POST logic
if (!empty($session_is_admin)) {
foreach (glob(__DIR__ . "/post/*.php") as $admin_module) {
if (!str_ends_with($admin_module, '_model.php')) {
@@ -26,11 +22,3 @@ if (!empty($session_is_admin)) {
// Logout is shared between portals
require_once __DIR__ . "/../post/logout.php";
require_once __DIR__ . "/../post/misc.php";
// Every handler above exits or redirects, so getting here means no handler claimed
// the request - a blank page and no trace of why. Log it; the usual cause is a
// handler file that never loaded.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$unhandled = implode(', ', array_slice(array_keys($_POST), 0, 10));
logApp('Request', 'warning', "Unhandled POST to admin/post.php - no handler matched. Fields: $unhandled");
}

View File

@@ -1,7 +1,7 @@
<?php
/*
* ITFlow - GET/POST request handler for AI Models ('ai_models')
* ITFlow - GET/POST request handler for AI Models ('ai_model')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
@@ -15,17 +15,9 @@ if (isset($_POST['add_ai_model'])) {
$prompt = escapeSql($_POST['prompt']);
$use_case = escapeSql($_POST['use_case']);
// Blank means "send no temperature at all" - the only setting that works on every
// provider. Anything else rides as a numeric literal, so no quoting.
$temperature = ($_POST['temperature'] ?? '') === '' ? 'NULL' : floatval($_POST['temperature']);
mysqli_query($mysqli,"INSERT INTO ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case', ai_model_ai_provider_id = $provider_id");
mysqli_query($mysqli,"INSERT INTO ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case', ai_model_temperature = $temperature, ai_model_ai_provider_id = $provider_id");
if (!mysqli_affected_rows($mysqli)) {
logApp('AI', 'error', 'Failed to create AI Model ' . $model . ': ' . mysqli_error($mysqli));
flashAlert("AI Model <strong>$model</strong> could not be created - see Admin > App Logs", 'error');
redirect();
}
$ai_model_id = mysqli_insert_id($mysqli);
logAudit("AI Model", "Create", "$session_name created AI Model $model");
@@ -44,11 +36,7 @@ if (isset($_POST['edit_ai_model'])) {
$prompt = escapeSql($_POST['prompt']);
$use_case = escapeSql($_POST['use_case']);
// Blank means "send no temperature at all" - the only setting that works on every
// provider. Anything else rides as a numeric literal, so no quoting.
$temperature = ($_POST['temperature'] ?? '') === '' ? 'NULL' : floatval($_POST['temperature']);
mysqli_query($mysqli,"UPDATE ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case', ai_model_temperature = $temperature WHERE ai_model_id = $model_id");
mysqli_query($mysqli,"UPDATE ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case' WHERE ai_model_id = $model_id");
logAudit("AI Model", "Edit", "$session_name edited AI Model $model");

View File

@@ -1,67 +0,0 @@
<?php
// Canned Responses
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_canned_response'])) {
validateCSRFToken();
$name = escapeSql($_POST['name']);
// The body is TinyMCE HTML, so it goes in the way ticket replies and ticket template
// details do - escaped for the query only, and purified where it is rendered
$body = mysqli_real_escape_string($mysqli, $_POST['body']);
// 0 is the general case: offered on every ticket whatever its category
$category_id = intval($_POST['category']);
mysqli_query($mysqli, "INSERT INTO canned_responses SET canned_response_name = '$name', canned_response_body = '$body', canned_response_category_id = $category_id");
$canned_response_id = mysqli_insert_id($mysqli);
logAudit("Canned Response", "Create", "$session_name created canned response $name", 0, $canned_response_id);
flashAlert("Canned Response <strong>$name</strong> created");
redirect();
}
if (isset($_POST['edit_canned_response'])) {
validateCSRFToken();
$canned_response_id = intval($_POST['canned_response_id']);
$name = escapeSql($_POST['name']);
$body = mysqli_real_escape_string($mysqli, $_POST['body']);
$category_id = intval($_POST['category']);
mysqli_query($mysqli, "UPDATE canned_responses SET canned_response_name = '$name', canned_response_body = '$body', canned_response_category_id = $category_id WHERE canned_response_id = $canned_response_id");
logAudit("Canned Response", "Edit", "$session_name edited canned response $name", 0, $canned_response_id);
flashAlert("Canned Response <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['delete_canned_response'])) {
validateCSRFToken();
$canned_response_id = intval($_GET['delete_canned_response']);
$name = escapeSql(getFieldById('canned_responses', $canned_response_id, 'canned_response_name'));
mysqli_query($mysqli, "DELETE FROM canned_responses WHERE canned_response_id = $canned_response_id");
logAudit("Canned Response", "Delete", "$session_name deleted canned response $name", 0, $canned_response_id);
flashAlert("Canned Response <strong>$name</strong> deleted", 'error');
redirect();
}

View File

@@ -42,18 +42,8 @@ if (isset($_POST['edit_company'])) {
}
}
// The client record standing in for this company. Verified against the clients table
// rather than trusted from the POST, so a stale or hand-made value cannot leave the
// sidebar pointing at a client that no longer exists. 0 clears the designation.
$internal_client_id = intval($_POST['internal_client_id'] ?? 0);
if ($internal_client_id !== 0 && !mysqli_num_rows(mysqli_query($mysqli, "SELECT client_id FROM clients WHERE client_id = $internal_client_id LIMIT 1"))) {
$internal_client_id = 0;
}
mysqli_query($mysqli,"UPDATE companies SET company_name = '$name', company_address = '$address', company_city = '$city', company_state = '$state', company_zip = '$zip', company_country = '$country', company_phone_country_code = '$phone_country_code', company_phone = '$phone', company_email = '$email', company_website = '$website', company_tax_id = '$tax_id' WHERE company_id = 1");
mysqli_query($mysqli,"UPDATE settings SET config_internal_client_id = $internal_client_id WHERE company_id = 1");
logAudit("Settings", "Edit", "$session_name edited company details");
flashAlert("Company <strong>$name</strong> edited");

View File

@@ -3,7 +3,7 @@
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
// Saves and tests return to the tab they were submitted from (see settings_mail.php)
$mail_tabs = ['sending', 'receiving', 'oauth', 'from', 'tests'];
$mail_tabs = ['smtp', 'imap', 'oauth', 'from', 'tests'];
$mail_tab_redirect = 'settings_mail.php';
if (isset($_POST['tab']) && in_array($_POST['tab'], $mail_tabs, true)) {
$mail_tab_redirect .= '?tab=' . $_POST['tab'];
@@ -91,18 +91,6 @@ if (isset($_POST['edit_mail_smtp_settings'])) {
$config_smtp_username = escapeSql($_POST['config_smtp_username'] ?? $config_smtp_username);
$config_smtp_password = escapeSql($_POST['config_smtp_password'] ?? $config_smtp_password);
// The host/port/encryption/password inputs are hidden and disabled for OAuth
// providers, so they never post and the ?? fallbacks above keep whatever the
// install used before. Clear them instead: the endpoint is fixed by provider,
// and the stored values would otherwise be both wrong and unreachable from
// the settings page. The mailbox password is no longer used either.
if ($config_smtp_provider === 'google_oauth' || $config_smtp_provider === 'microsoft_oauth') {
$config_smtp_host = '';
$config_smtp_port = 0;
$config_smtp_encryption = '';
$config_smtp_password = '';
}
mysqli_query($mysqli, "
UPDATE settings SET
config_smtp_provider = '$config_smtp_provider',
@@ -133,15 +121,6 @@ if (isset($_POST['edit_mail_imap_settings'])) {
$config_imap_username = escapeSql($_POST['config_imap_username'] ?? $config_imap_username);
$config_imap_password = escapeSql($_POST['config_imap_password'] ?? $config_imap_password);
// Same as the SMTP handler above - the connection fields are hidden for OAuth
// providers and never post, so drop the leftovers rather than carrying them.
if ($config_imap_provider === 'google_oauth' || $config_imap_provider === 'microsoft_oauth') {
$config_imap_host = '';
$config_imap_port = 0;
$config_imap_encryption = '';
$config_imap_password = '';
}
mysqli_query($mysqli, "
UPDATE settings SET
config_imap_provider = '$config_imap_provider',
@@ -249,7 +228,7 @@ if (isset($_POST['test_email_smtp'])) {
$mail = addToMailQueue($data);
if ($mail === true) {
flashAlert("Test email queued! Check <strong>Admin &gt; Mail queue</strong>");
flashAlert("Test email queued! <a class='text-bold text-light' href='mail_queue.php'>Check Admin > Mail queue</a>");
} else {
flashAlert("Failed to add test mail to queue", 'error');
}
@@ -280,17 +259,26 @@ if (isset($_POST['test_email_imap'])) {
$is_oauth = ($provider === 'google_oauth' || $provider === 'microsoft_oauth');
// Override, don't default - a leftover standard-IMAP host from before the
// switch to OAuth would otherwise make this test fail against the old server
// while the cron parser (which overrides unconditionally) connects fine.
if ($provider === 'google_oauth') {
$host = 'imap.gmail.com';
$port = 993;
$encryption = 'ssl';
if (empty($host)) {
$host = 'imap.gmail.com';
}
if (empty($port)) {
$port = 993;
}
if (empty($encryption)) {
$encryption = 'ssl';
}
} elseif ($provider === 'microsoft_oauth') {
$host = 'outlook.office365.com';
$port = 993;
$encryption = 'ssl';
if (empty($host)) {
$host = 'outlook.office365.com';
}
if (empty($port)) {
$port = 993;
}
if (empty($encryption)) {
$encryption = 'ssl';
}
}
if (empty($host) || empty($port) || empty($username)) {

View File

@@ -33,13 +33,7 @@ if (isset($_POST['edit_ticket_status'])) {
$order = intval($_POST['order']);
$status = intval($_POST['status']);
// Built-in statuses have fixed SLA behaviour. The modal renders it read-only, and a
// read-only field submits nothing - so the value is decided here rather than taken
// from the POST, which also means a hand-made request cannot change it.
$pauses_sla = getTicketStatusSlaLock($ticket_status_id);
if (is_null($pauses_sla)) {
$pauses_sla = intval($_POST['pauses_sla'] ?? 0);
}
$pauses_sla = intval($_POST['pauses_sla'] ?? 0);
mysqli_query($mysqli, "UPDATE ticket_statuses SET ticket_status_name = '$name', ticket_status_color = '$color', ticket_status_order = $order, ticket_status_active = $status, ticket_status_pauses_sla = $pauses_sla WHERE ticket_status_id = $ticket_status_id");

View File

@@ -2,53 +2,12 @@
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
/*
* Hands the update to cron instead of running it in this request. scripts/update_cli.php
* replaces the files this request is executing from and then applies the migrations that
* came with them, which is not something to do half way through a page load - and on a host
* where PHP cannot run external commands it is the only way to update the application at all.
*
* The row is written as well as the settings column: config_update_queued_at is what
* cron/app_update.php acts on, and cron_job_run_now is what gets the dispatcher to look
* before the job's own schedule comes round.
*/
if (isset($_GET['queue_update'])) {
validateCSRFToken();
enforceAdminPermission();
// The files can be newer than the schema - that is the window this whole page exists to
// close - and the column the queue is written to arrives with a migration
if (!settingsColumnExists($mysqli, 'config_update_queued_at')) {
flashAlert("Apply the database update first - queueing needs a schema change this install has not caught up with yet.", 'error');
redirect();
}
mysqli_query($mysqli, "UPDATE settings SET config_update_queued_at = '" . date('Y-m-d H:i:s') . "' WHERE company_id = 1");
mysqli_query($mysqli, "UPDATE cron_jobs SET cron_job_run_now = 1 WHERE cron_job_name = 'app_update'");
logAudit("App", "Update", "$session_name queued an update to be applied by cron");
flashAlert("Update queued - cron will start it within a minute.");
redirect();
}
if (isset($_GET['update'])) {
validateCSRFToken();
enforceAdminPermission();
// Reached only from buttons this install cannot draw without a shell, so anything
// arriving here without one is a crafted request rather than somebody's mistake
if (!shellCommandsAvailable()) {
flashAlert("PHP on this server cannot run Git. Queue the update instead and cron will apply it.", 'error');
redirect();
}
// git fetch downloads the latest from the remote without merging or rebasing anything.
// The hard reset then throws away every local change and makes the working tree match
// the tracked branch exactly.

View File

@@ -54,7 +54,7 @@ if (isset($_POST['add_user'])) {
// Create Settings
mysqli_query($mysqli, "INSERT INTO user_settings SET user_id = $user_id, user_config_force_mfa = $force_mfa");
$sql = mysqli_query($mysqli,"SELECT company_name FROM companies WHERE company_id = 1");
$sql = mysqli_query($mysqli,"SELECT * FROM companies WHERE company_id = 1");
$row = mysqli_fetch_assoc($sql);
$company_name = escapeSql($row['company_name']);
@@ -328,7 +328,7 @@ if (isset($_POST['restore_user'])) {
}
if (isExportRequest('export_users')) {
if (isset($_POST['export_users'])) {
validateCSRFToken();
@@ -355,7 +355,7 @@ if (isExportRequest('export_users')) {
$sql = mysqli_query(
$mysqli,
"SELECT role_name, user_created_at, user_email, user_name, user_status FROM users
"SELECT * FROM users
LEFT JOIN user_roles ON user_role_id = role_id
WHERE (user_name LIKE '%$q%' OR user_email LIKE '%$q%')
AND user_type = 1
@@ -402,7 +402,7 @@ if (isset($_POST['ir_reset_user_password'])) {
// Confirm logged-in user password, for security
$admin_password = $_POST['admin_password'];
$sql = mysqli_query($mysqli, "SELECT user_password FROM users WHERE user_id = $session_user_id");
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_id = $session_user_id");
$userRow = mysqli_fetch_assoc($sql);
if (!password_verify($admin_password, $userRow['user_password'])) {
@@ -411,7 +411,7 @@ if (isset($_POST['ir_reset_user_password'])) {
}
// Get agents/users, other than the current user
$sql_users = mysqli_query($mysqli, "SELECT user_email, user_id FROM users WHERE (user_archived_at IS NULL AND user_id != $session_user_id)");
$sql_users = mysqli_query($mysqli, "SELECT * FROM users WHERE (user_archived_at IS NULL AND user_id != $session_user_id)");
// Reset passwords
while ($row = mysqli_fetch_assoc($sql_users)) {

View File

@@ -8,8 +8,7 @@ if (isset($_GET['project_template_id'])) {
$sql_project_templates = mysqli_query(
$mysqli,
"SELECT project_template_created_at, project_template_description, project_template_name,
project_template_updated_at FROM project_templates
"SELECT * FROM project_templates
WHERE project_template_id = $project_template_id LIMIT 1"
);
@@ -28,9 +27,7 @@ if (isset($_GET['project_template_id'])) {
$project_template_updated_at = escapeHtml($row['project_template_updated_at']);
// Get Associated Ticket Templates
$sql_ticket_templates = mysqli_query($mysqli, "SELECT ticket_template_created_at, ticket_template_description,
project_template_ticket_templates.ticket_template_id, ticket_template_name,
ticket_template_order, ticket_template_subject, ticket_template_updated_at FROM ticket_templates, project_template_ticket_templates
$sql_ticket_templates = mysqli_query($mysqli, "SELECT * FROM ticket_templates, project_template_ticket_templates
WHERE ticket_templates.ticket_template_id = project_template_ticket_templates.ticket_template_id
AND project_template_ticket_templates.project_template_id = $project_template_id
ORDER BY ticket_template_order ASC, ticket_template_name ASC");
@@ -38,7 +35,7 @@ if (isset($_GET['project_template_id'])) {
// Get All Task Templates
$sql_task_templates = mysqli_query($mysqli,
"SELECT task_template_id, task_template_name FROM ticket_templates, task_templates, project_template_ticket_templates
"SELECT * FROM ticket_templates, task_templates, project_template_ticket_templates
WHERE ticket_templates.ticket_template_id = project_template_ticket_templates.ticket_template_id
AND project_template_ticket_templates.project_template_id = $project_template_id
AND ticket_templates.ticket_template_id = task_template_ticket_template_id
@@ -60,22 +57,22 @@ if (isset($_GET['project_template_id'])) {
</ol>
<!-- Project Header -->
<div class="card card-body mb-3">
<div class="card card-body">
<div class="row">
<div class="col-sm-4">
<div class="d-flex">
<i class="fa fa-fw fa-2x fa-project-diagram text-secondary me-3"></i>
<div class="flex-grow-1">
<h3 class="mb-0"><?= $project_template_name ?><span class='badge rounded-pill bg-info ms-2'>Template</span></h3>
<div class="media">
<i class="fa fa-fw fa-2x fa-project-diagram text-secondary mr-3"></i>
<div class="media-body">
<h3 class="mb-0"><?= $project_template_name ?><span class='badge badge-pill badge-info ml-2'>Template</span></h3>
<div><small class="text-secondary"><?= $project_template_description ?></small></div>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="d-flex">
<i class="fa fa-fw fa-2x fa-life-ring text-secondary me-3"></i>
<div class="flex-grow-1">
<div class="media">
<i class="fa fa-fw fa-2x fa-life-ring text-secondary mr-3"></i>
<div class="media-body">
<div>Ticket Templates</div>
<h3 class="mb-0"><?= $ticket_template_count ?></h3>
</div>
@@ -83,9 +80,9 @@ if (isset($_GET['project_template_id'])) {
</div>
<div class="col-sm-3">
<div class="d-flex">
<i class="fa fa-fw fa-2x fa-tasks text-secondary me-3"></i>
<div class="flex-grow-1">
<div class="media">
<i class="fa fa-fw fa-2x fa-tasks text-secondary mr-3"></i>
<div class="media-body">
<div>Task Templates</div>
<h3 class="mb-0"><?= $task_template_count ?></h3>
</div>
@@ -93,28 +90,28 @@ if (isset($_GET['project_template_id'])) {
</div>
<div class="col-sm-2">
<div class="btn-group float-end">
<div class="btn-group float-right">
<button type="button" class="btn btn-primary btn-sm ajax-modal" href="#" data-modal-url="modals/project_template/project_template_ticket_template_add.php?project_template_id=<?= $project_template_id ?>">
<i class="fas fa-fw fa-plus me-2"></i>Add Ticket Template
<i class="fas fa-fw fa-plus mr-2"></i>Add Ticket Template
</button>
<div class="dropdown dropstart text-center ms-3">
<button class="btn btn-secondary btn-sm" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center ml-3">
<button class="btn btn-secondary btn-sm" type="button" id="dropdownMenuButton" data-toggle="dropdown">
<i class="fas fa-fw fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/project_template/project_template_edit.php?project_template_id=<?= $project_template_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit Template
<i class="fas fa-fw fa-edit mr-2"></i>Edit Template
</a>
<?php if ($session_user_role == 3) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?archive_project_template=<?= $project_template_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-archive me-2"></i>Archive (not yet implemented)
<i class="fas fa-fw fa-archive mr-2"></i>Archive (not yet implemented)
</a>
<?php } ?>
<?php if ($session_user_role == 3) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_project_template=<?= $project_template_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
<?php } ?>
</div>
@@ -131,7 +128,7 @@ if (isset($_GET['project_template_id'])) {
<?php if (mysqli_num_rows($sql_ticket_templates) > 0) { ?>
<div class="card card-body card-outline card-dark mb-3">
<h5 class="text-secondary"><i class="fa fa-fw fa-life-ring me-2"></i>Project Ticket Templates</h5>
<h5 class="text-secondary"><i class="fa fa-fw fa-life-ring mr-2"></i>Project Ticket Templates</h5>
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover" id="ticket_templates">
<thead class="text-dark">
@@ -158,7 +155,7 @@ if (isset($_GET['project_template_id'])) {
<tr data-task-id="<?= $ticket_template_id ?>">
<td>
<a href="#" class="drag-handle"><i class="fas fa-bars text-muted me-2"></i></a>
<a href="#" class="drag-handle"><i class="fas fa-bars text-muted mr-2"></i></a>
<a href="ticket_template.php?ticket_template_id=<?= $ticket_template_id ?>">
<?= $ticket_template_name ?>
</a>
@@ -192,7 +189,7 @@ if (isset($_GET['project_template_id'])) {
<!-- Task Templates Card -->
<?php if (mysqli_num_rows($sql_task_templates) > 0) { ?>
<div class="card card-body card-outline card-dark">
<h5 class="text-secondary"><i class="fas fa-fw fa-tasks me-2"></i>Project Task Templates</h5>
<h5 class="text-secondary"><i class="fas fa-fw fa-tasks mr-2"></i>Project Task Templates</h5>
<table class="table">
<?php
while($row = mysqli_fetch_assoc($sql_task_templates)){
@@ -201,7 +198,7 @@ if (isset($_GET['project_template_id'])) {
?>
<tr>
<td>
<i class="far fa-fw fa-check-square text-primary me-3"></i>
<i class="far fa-fw fa-check-square text-primary mr-3"></i>
<?= $task_template_name ?>
</td>
</tr>
@@ -227,7 +224,7 @@ new Sortable(document.querySelector('table#ticket_templates tbody'), {
order: index
}));
itflowPostForm('/agent/ajax.php', {
$.post('/agent/ajax.php', {
update_project_template_ticket_order: true,
csrf_token: '<?= $_SESSION['csrf_token'] ?>',
project_template_id: <?= $project_template_id ?>,

View File

@@ -8,8 +8,7 @@ require_once "includes/inc_all_admin.php";
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS project_template_created_at, project_template_description, project_template_id,
project_template_name FROM project_templates
"SELECT SQL_CALC_FOUND_ROWS * FROM project_templates
WHERE (project_template_name LIKE '%$q%' OR project_template_description LIKE '%$q%')
AND project_template_archived_at IS NULL
ORDER BY $sort $order LIMIT $record_from, $record_to"
@@ -21,9 +20,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-project-diagram me-2"></i>Project Templates</h3>
<h3 class="card-title mt-2"><i class="fas fa-fw fa-project-diagram mr-2"></i>Project Templates</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/project_template/project_template_add.php"><i class="fas fa-plus me-2"></i>New Project Template</button>
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/project_template/project_template_add.php"><i class="fas fa-plus mr-2"></i>New Project Template</button>
</div>
</div>
<div class="card-body">
@@ -33,7 +32,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-md-4">
<div class="input-group mb-3 mb-md-0">
<input type="search" class="form-control" name="q" value="<?php if(isset($q)){ echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search Project Templates">
<div class="input-group-append">
<button class="btn btn-dark"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
@@ -67,7 +68,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$project_template_created_at = escapeHtml($row['project_template_created_at']);
// Get Ticket Template Count
$sql_ticket_templates = mysqli_query($mysqli, "SELECT 1 FROM ticket_templates, project_template_ticket_templates
$sql_ticket_templates = mysqli_query($mysqli, "SELECT * FROM ticket_templates, project_template_ticket_templates
WHERE ticket_templates.ticket_template_id = project_template_ticket_templates.ticket_template_id
AND project_template_ticket_templates.project_template_id = $project_template_id
ORDER BY ticket_template_order ASC, ticket_template_name ASC");
@@ -75,7 +76,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
// Get Tasks Template Count
$sql_task_templates = mysqli_query($mysqli,
"SELECT 1 FROM ticket_templates, task_templates, project_template_ticket_templates
"SELECT * FROM ticket_templates, task_templates, project_template_ticket_templates
WHERE ticket_templates.ticket_template_id = project_template_ticket_templates.ticket_template_id
AND project_template_ticket_templates.project_template_id = $project_template_id
AND ticket_templates.ticket_template_id = task_template_ticket_template_id
@@ -87,9 +88,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<tr>
<td>
<a class="text-dark" href="project_template.php?project_template_id=<?= $project_template_id ?>">
<div class="d-flex">
<i class="fa fa-fw fa-2x fa-project-diagram me-3"></i>
<div class="flex-grow-1">
<div class="media">
<i class="fa fa-fw fa-2x fa-project-diagram mr-3"></i>
<div class="media-body">
<div>
<?= $project_template_name ?>
</div>
@@ -103,18 +104,18 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<td><?= $ticket_template_count ?></td>
<td><?= $task_template_count ?></td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/project_template/project_template_edit.php?project_template_id=<?= $project_template_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<?php if($session_user_role == 3) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_project_template=<?= $project_template_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
<?php } ?>
</div>

View File

@@ -20,11 +20,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-user-shield me-2"></i>Roles</h3>
<h3 class="card-title mt-2"><i class="fas fa-fw fa-user-shield mr-2"></i>Roles</h3>
<div class="card-tools">
<div class="btn-group">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/role/role_add.php">
<i class="fas fa-fw fa-user-plus me-2"></i>New Role
<i class="fas fa-fw fa-user-plus mr-2"></i>New Role
</button>
</div>
</div>
@@ -35,7 +35,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-md-4">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) {echo stripslashes(escapeHtml($q));} ?>" placeholder="Search Roles">
<div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
</div>
@@ -73,7 +75,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$sql_role_user_count = mysqli_query($mysqli, "SELECT COUNT(user_id) FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
$role_user_count = mysqli_fetch_row($sql_role_user_count)[0];
$sql_users = mysqli_query($mysqli, "SELECT user_name FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
$sql_users = mysqli_query($mysqli, "SELECT * FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
// Initialize an empty array to hold user names
$user_names = [];
@@ -93,9 +95,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<tr>
<td>
<a class="<?php if ($role_id !== 3) { ?> ajax-modal" data-modal-url="modals/role/role_edit.php?id=<?= $role_id ?>" <?php } ?> href="#">
<div class="d-flex">
<i class="fas fa-fw fa-2x fa-user-shield text-dark me-2"></i>
<div class="flex-grow-1">
<div class="media">
<i class="fas fa-fw fa-2x fa-user-shield text-dark mr-2"></i>
<div class="media-body">
<div><?= $role_name ?></div>
<div><small class="text-secondary"><?= $role_description ?></small></div>
</div>
@@ -106,21 +108,21 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<td><?= $role_admin ? 'Yes' : 'No' ?></td>
<td>
<?php if ($role_id !== 3) { ?>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/role/role_edit.php?id=<?= $role_id ?>">
<i class="fas fa-fw fa-user-edit me-2"></i>Edit
<i class="fas fa-fw fa-user-edit mr-2"></i>Edit
</a>
<?php if (empty($role_archived_at) && $role_user_count == 0) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_role=<?= $role_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-archive me-2"></i>Archive
<i class="fas fa-fw fa-archive mr-2"></i>Archive
</a>
<?php } ?>

View File

@@ -45,7 +45,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-fw fa-credit-card me-2"></i>Saved Payment Methods</h3>
<h3 class="card-title"><i class="fas fa-fw fa-credit-card mr-2"></i>Saved Payment Methods</h3>
</div>
<div class="card-body">
<form class="mb-4" autocomplete="off">
@@ -53,7 +53,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-md-4">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) {echo stripslashes(escapeHtml($q));} ?>" placeholder="Search Saved Payment Methods">
<div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
<div class="col-md-8">
@@ -131,7 +133,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<td><?= $saved_payment_created_at ?></td>
<td>
<a class="btn btn-outline-danger confirm-link" href="post.php?delete_saved_payment=<?= $saved_payment_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</td>
</tr>

View File

@@ -2,9 +2,7 @@
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli,"SELECT company_address, company_city, company_country, company_currency, company_email,
settings.company_id, company_locale, company_logo, company_name, company_phone,
company_phone_country_code, company_state, company_tax_id, company_website, company_zip FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
$sql = mysqli_query($mysqli,"SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
$row = mysqli_fetch_assoc($sql);
$company_id = intval($row['company_id']);
@@ -29,7 +27,7 @@ $company_initials = escapeHtml(initials($company_name));
<div class="card card-dark">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-fw fa-briefcase me-2"></i>Company Details</h3>
<h3 class="card-title"><i class="fas fa-fw fa-briefcase mr-2"></i>Company Details</h3>
</div>
<div class="card-body">
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
@@ -39,61 +37,73 @@ $company_initials = escapeHtml(initials($company_name));
<div class="col-md-3 text-center">
<?php if ($company_logo) { ?>
<img class="img-thumbnail" src="<?= "../uploads/settings/$company_logo" ?>">
<a href="post.php?remove_company_logo&csrf_token=<?= $_SESSION['csrf_token'] ?>" class="btn btn-outline-danger w-100">Remove Logo</a>
<a href="post.php?remove_company_logo&csrf_token=<?= $_SESSION['csrf_token'] ?>" class="btn btn-outline-danger btn-block">Remove Logo</a>
<hr>
<?php } ?>
<div class="mb-3">
<div class="form-group">
<label>Upload company logo</label>
<input type="file" class="form-control" name="file" accept=".jpg, .jpeg, .png">
<input type="file" class="form-control-file" name="file" accept=".jpg, .jpeg, .png">
</div>
</div>
<div class="col-md-9">
<div class="mb-3">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Company Name" maxlength="200" value="<?= $company_name ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Address</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
</div>
<input type="text" class="form-control" name="address" placeholder="Street Address" maxlength="200" value="<?= $company_address ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>City</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-city"></i></span>
</div>
<input type="text" class="form-control" name="city" placeholder="City" maxlength="200" value="<?= $company_city ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>State / Province</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-flag"></i></span>
</div>
<input type="text" class="form-control" name="state" placeholder="State or Province" maxlength="200" value="<?= $company_state ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Postal Code</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fab fa-fw fa-usps"></i></span>
</div>
<input type="text" class="form-control" name="zip" placeholder="Zip or Postal Code" maxlength="200" value="<?= $company_zip ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Country</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-globe-americas"></i></span>
<select class="form-select select2" name="country">
</div>
<select class="form-control select2" name="country">
<option value="">- Country -</option>
<?php foreach($countries_array as $country_name) { ?>
<option <?php if ($company_country == $country_name) { echo "selected"; } ?>><?= $country_name ?></option>
@@ -103,11 +113,13 @@ $company_initials = escapeHtml(initials($company_name));
</div>
<label>Phone</label>
<div class="row g-2">
<div class="form-row">
<div class="col-md-9">
<div class="mb-3">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div>
<input type="tel" class="form-control col-2" name="phone_country_code" value="<?= $company_phone_country_code ?>" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="phone" value="<?= $company_phone ?>" placeholder="Phone Number" maxlength="200">
</div>
@@ -115,54 +127,39 @@ $company_initials = escapeHtml(initials($company_name));
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Email</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
</div>
<input type="email" class="form-control" name="email" placeholder="Email address" maxlength="200" value="<?= $company_email ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Website</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
</div>
<input type="text" class="form-control" name="website" placeholder="Website address" maxlength="200" value="<?= $company_website ?>">
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Tax ID</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-balance-scale"></i></span>
</div>
<input type="text" class="form-control" name="tax_id" value="<?= $company_tax_id ?>" placeholder="Tax ID" maxlength="200">
</div>
</div>
<hr>
<div class="mb-3">
<label>My Company's Client Record</label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-house-user"></i></span>
<select class="form-select select2" name="internal_client_id">
<option value="0">None</option>
<?php
$sql_internal_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
while ($row_internal_client = mysqli_fetch_assoc($sql_internal_clients)) {
$internal_client_id_select = intval($row_internal_client['client_id']);
$internal_client_name_select = escapeHtml($row_internal_client['client_name']);
?>
<option <?php if ($config_internal_client_id == $internal_client_id_select) { echo "selected"; } ?> value="<?= $internal_client_id_select ?>"><?= $internal_client_name_select ?></option>
<?php } ?>
</select>
</div>
<small class="text-muted">If you keep a client record for your own company - for internal tickets, documentation, assets and credentials - point it out here. Your company name in the sidebar then links straight to it.</small>
</div>
<hr>
<button type="submit" name="edit_company" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="submit" name="edit_company" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
</div>
</div>
</div>

View File

@@ -28,9 +28,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fa fa-fw fa-th-list me-2"></i><?= escapeHtml($table) ?> Fields</h3>
<h3 class="card-title mt-2"><i class="fa fa-fw fa-th-list mr-2"></i><?= escapeHtml($table) ?> Fields</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createCustomFieldModal"><i class="fas fa-plus me-2"></i>Create</button>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#createCustomFieldModal"><i class="fas fa-plus mr-2"></i>Create</button>
</div>
</div>
<div class="card-body">
@@ -40,11 +40,13 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="col-sm-4 mb-2">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search">
<div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
<div class="col-sm-8">
<div class="btn-group float-end">
<div class="btn-group float-right">
<a href="?table=client_assets" class="btn <?php if ($table == 'client_assets') { echo 'btn-primary'; } else { echo 'btn-default'; } ?>">Assets</a>
<a href="?table=clients" class="btn <?php if ($table == 'clients') { echo 'btn-primary'; } else { echo 'btn-default'; } ?>">Clients</a>
</div>
@@ -73,20 +75,20 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
?>
<tr>
<td><a class="text-dark" href="#" data-bs-toggle="modal" data-bs-target="#editCustomFieldModal<?= $custom_field_id ?>"><?= $custom_field_label ?></a></td>
<td><a class="text-dark" href="#" data-toggle="modal" data-target="#editCustomFieldModal<?= $custom_field_id ?>"><?= $custom_field_label ?></a></td>
<td><?= $custom_field_type ?></td>
<td>
<div class="dropdown dropstart text-center">
<button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="dropdown">
<div class="dropdown dropleft text-center">
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#editCustomFieldModal<?= $custom_field_id ?>">
<i class="fas fa-fw fa-edit me-2"></i>Edit
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editCustomFieldModal<?= $custom_field_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_custom_field=<?= $custom_field_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash me-2"></i>Delete
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>

View File

@@ -23,17 +23,19 @@ $net_terms_array = array (
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-cogs me-2"></i>Defaults</h3>
<h3 class="card-title"><i class="fas fa-fw fa-cogs mr-2"></i>Defaults</h3>
</div>
<div class="card-body">
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="mb-3">
<div class="form-group">
<label>Start Page</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-home"></i></span>
<select class="form-select select2" name="start_page" data-tags="true" required>
</div>
<select class="form-control select2" name="start_page" data-tags="true" required>
<?php if (!in_array($config_start_page, array_keys($start_page_select_array))) { ?>
<option selected> <?= escapeHtml($config_start_page) ?></option>
<?php } ?>
@@ -47,15 +49,17 @@ $net_terms_array = array (
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Calendar</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
<select class="form-select select2" name="calendar">
</div>
<select class="form-control select2" name="calendar">
<option value="0">- None -</option>
<?php
$sql = mysqli_query($mysqli, "SELECT calendar_id, calendar_name FROM calendars ORDER BY calendar_name ASC");
$sql = mysqli_query($mysqli, "SELECT * FROM calendars ORDER BY calendar_name ASC");
while ($row = mysqli_fetch_assoc($sql)) {
$calendar_id = intval($row['calendar_id']);
$calendar_name = escapeHtml($row['calendar_name']); ?>
@@ -68,15 +72,17 @@ $net_terms_array = array (
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Transfer From Account</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-exchange-alt"></i></span>
<select class="form-select select2" name="transfer_from_account">
</div>
<select class="form-control select2" name="transfer_from_account">
<option value="0">- None -</option>
<?php
$sql = mysqli_query($mysqli, "SELECT account_id, account_name FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
$sql = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
while ($row = mysqli_fetch_assoc($sql)) {
$account_id = intval($row['account_id']);
$account_name = escapeHtml($row['account_name']); ?>
@@ -89,15 +95,17 @@ $net_terms_array = array (
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Transfer To Account</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-exchange-alt"></i></span>
<select class="form-select select2" name="transfer_to_account">
</div>
<select class="form-control select2" name="transfer_to_account">
<option value="0">- None -</option>
<?php
$sql = mysqli_query($mysqli, "SELECT account_id, account_name FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
$sql = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
while ($row = mysqli_fetch_assoc($sql)) {
$account_id = intval($row['account_id']);
$account_name = escapeHtml($row['account_name']); ?>
@@ -110,15 +118,17 @@ $net_terms_array = array (
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Payment Account</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
<select class="form-select select2" name="payment_account">
</div>
<select class="form-control select2" name="payment_account">
<option value="0">- None -</option>
<?php
$sql = mysqli_query($mysqli, "SELECT account_id, account_name FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
$sql = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
while ($row = mysqli_fetch_assoc($sql)) {
$account_id = intval($row['account_id']);
$account_name = escapeHtml($row['account_name']); ?>
@@ -133,15 +143,17 @@ $net_terms_array = array (
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Expense Account</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
<select class="form-select select2" name="expense_account">
</div>
<select class="form-control select2" name="expense_account">
<option value="0">- None -</option>
<?php
$sql = mysqli_query($mysqli, "SELECT account_id, account_name FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
$sql = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
while ($row = mysqli_fetch_assoc($sql)) {
$account_id = intval($row['account_id']);
$account_name = escapeHtml($row['account_name']); ?>
@@ -154,15 +166,17 @@ $net_terms_array = array (
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Payment Method</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
<select class="form-select select2" name="payment_method">
</div>
<select class="form-control select2" name="payment_method">
<option value="">- None -</option>
<?php
$sql = mysqli_query($mysqli, "SELECT category_name FROM categories WHERE category_type = 'Payment Method' ORDER BY category_name ASC");
$sql = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Payment Method' ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql)) {
$payment_method = escapeHtml($row['category_name']); ?>
<option <?php if ($config_default_payment_method == $payment_method) {
@@ -174,15 +188,17 @@ $net_terms_array = array (
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Expense Payment Method</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-credit-card"></i></span>
<select class="form-select select2" name="expense_payment_method">
</div>
<select class="form-control select2" name="expense_payment_method">
<option value="">- None -</option>
<?php
$sql = mysqli_query($mysqli, "SELECT category_name FROM categories WHERE category_type = 'Payment Method' ORDER BY category_name ASC");
$sql = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Payment Method' ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql)) {
$payment_method = escapeHtml($row['category_name']); ?>
<option <?php if ($config_default_expense_payment_method == $payment_method) {
@@ -194,11 +210,13 @@ $net_terms_array = array (
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Net Terms</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
<select class="form-select select2" name="net_terms">
</div>
<select class="form-control select2" name="net_terms">
<?php foreach ($net_terms_array as $net_term_value => $net_term_name) { ?>
<option <?php if ($config_default_net_terms == $net_term_value) {
echo "selected";
@@ -208,17 +226,19 @@ $net_terms_array = array (
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Client Hourly Rate</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
</div>
<input type="text" class="form-control" inputmode="decimal" pattern="[0-9]*\.?[0-9]{0,2}" name="hourly_rate" value="<?= number_format($config_default_hourly_rate, 2, '.', '') ?>" placeholder="0.00" required>
</div>
</div>
<hr>
<button type="submit" name="edit_default_settings" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
<button type="submit" name="edit_default_settings" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
</form>
</div>

View File

@@ -4,7 +4,7 @@ require_once "includes/inc_all_admin.php";
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-file-invoice me-2"></i>Invoice Settings</h3>
<h3 class="card-title"><i class="fas fa-fw fa-file-invoice mr-2"></i>Invoice Settings</h3>
</div>
<div class="card-body">
<form action="post.php" method="post" autocomplete="off">
@@ -12,58 +12,66 @@ require_once "includes/inc_all_admin.php";
<h4>Invoice</h4>
<div class="mb-3">
<div class="form-group">
<label>Invoice Prefix</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-barcode"></i></span>
</div>
<input type="text" class="form-control" name="config_invoice_prefix" placeholder="Invoice Prefix" maxlength="200" value="<?= escapeHtml($config_invoice_prefix) ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Next Number</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-barcode"></i></span>
</div>
<input type="number" min="0" class="form-control" name="config_invoice_next_number" placeholder="Next Invoice Number" value="<?= intval($config_invoice_next_number) ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Invoice Footer</label>
<textarea class="form-control" rows="4" name="config_invoice_footer"><?= escapeHtml($config_invoice_footer) ?></textarea>
</div>
<h5>Show Tax ID On Invoices</h5>
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input" name="config_invoice_show_tax_id" <?php if ($config_invoice_show_tax_id == 1) { echo "checked"; } ?> value="1" id="customSwitch1">
<label class="form-check-label" for="customSwitch1">Show Tax ID</label>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="config_invoice_show_tax_id" <?php if ($config_invoice_show_tax_id == 1) { echo "checked"; } ?> value="1" id="customSwitch1">
<label class="custom-control-label" for="customSwitch1">Show Tax ID</label>
</div>
</div>
<h5>Invoice Late Fees</h5>
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input" name="config_invoice_late_fee_enable" <?php if ($config_invoice_late_fee_enable == 1) { echo "checked"; } ?> value="1" id="customSwitch2">
<label class="form-check-label" for="customSwitch2">Enable Late Fee</label>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="config_invoice_late_fee_enable" <?php if ($config_invoice_late_fee_enable == 1) { echo "checked"; } ?> value="1" id="customSwitch2">
<label class="custom-control-label" for="customSwitch2">Enable Late Fee</label>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Late Fee %</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-percent"></i></span>
</div>
<input type="number" class="form-control" min="0" max="100" step="0.01" name="config_invoice_late_fee_percent" value="<?= $config_invoice_late_fee_percent ?>">
</div>
<small class="text-secondary">We recommend updating the invoice footer to include policies on your late charges. This will be applied every 30 days after the invoice Due Date.</small>
</div>
<div class="mb-3">
<div class="form-group">
<label>Email address to notify when invoices are paid online <small class="text-secondary">(Ideally a distribution list/shared mailbox)</small></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-bell"></i></span>
</div>
<input type="email" class="form-control" name="config_invoice_paid_notification_email" placeholder="Address to notify for paid invoices, leave blank for none" maxlength="200" value="<?= escapeHtml($config_invoice_paid_notification_email) ?>">
</div>
</div>
@@ -72,18 +80,22 @@ require_once "includes/inc_all_admin.php";
<h4>Recurring Invoice</h4>
<div class="mb-3">
<div class="form-group">
<label>Recurring Prefix</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-barcode"></i></span>
</div>
<input type="text" class="form-control" name="config_recurring_invoice_prefix" placeholder="Recurring Invoice Prefix" maxlength="200" value="<?= escapeHtml($config_recurring_invoice_prefix) ?>" required>
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Next Number</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-barcode"></i></span>
</div>
<input type="number" min="0" class="form-control" name="config_recurring_invoice_next_number" placeholder="Next Recurring Invoice Number" value="<?= intval($config_recurring_invoice_next_number) ?>" required>
</div>
</div>
@@ -92,7 +104,7 @@ require_once "includes/inc_all_admin.php";
<hr>
<button type="submit" name="edit_invoice_settings" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="submit" name="edit_invoice_settings" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
</form>
</div>

View File

@@ -2,7 +2,7 @@
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli,"SELECT company_currency, company_locale FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
$sql = mysqli_query($mysqli,"SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
$row = mysqli_fetch_assoc($sql);
$company_locale = escapeHtml($row['company_locale']);
@@ -15,17 +15,19 @@ $timezones = DateTimeZone::listIdentifiers();
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-globe me-2"></i>Localization</h3>
<h3 class="card-title"><i class="fas fa-fw fa-globe mr-2"></i>Localization</h3>
</div>
<div class="card-body">
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="mb-3">
<div class="form-group">
<label>Language <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-language"></i></span>
<select class="form-select select2" name="locale" required>
</div>
<select class="form-control select2" name="locale" required>
<option value="">- Select a Locale -</option>
<?php foreach($locales_array as $locale_code => $locale_name) { ?>
<option <?php if ($company_locale == $locale_code) { echo "selected"; } ?> value="<?= $locale_code ?>"><?= $locale_name ?></option>
@@ -34,11 +36,13 @@ $timezones = DateTimeZone::listIdentifiers();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Currency <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-money-bill"></i></span>
<select class="form-select select2" name="currency_code" required>
</div>
<select class="form-control select2" name="currency_code" required>
<option value="">- Currency -</option>
<?php foreach($currencies_array as $currency_code => $currency_name) { ?>
<option <?php if ($company_currency == $currency_code) { echo "selected"; } ?> value="<?= $currency_code ?>"><?= "$currency_code - $currency_name" ?></option>
@@ -47,11 +51,13 @@ $timezones = DateTimeZone::listIdentifiers();
</div>
</div>
<div class="mb-3">
<div class="form-group">
<label>Timezone <strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-business-time"></i></span>
<select class="form-select select2" name="timezone" required>
</div>
<select class="form-control select2" name="timezone" required>
<option value="">- Select a Timezone -</option>
<?php foreach ($timezones as $tz) { ?>
<option <?php if ($config_timezone == $tz) { echo "selected"; } ?> value="<?= $tz ?>"><?= $tz ?></option>
@@ -62,7 +68,7 @@ $timezones = DateTimeZone::listIdentifiers();
<hr>
<button type="submit" name="edit_localization" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save</button>
<button type="submit" name="edit_localization" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
</form>
</div>

View File

@@ -4,8 +4,8 @@ require_once "includes/inc_all_admin.php";
// ---- Tiny status dot for tab labels ----------------------------------------
function renderMailStatusDot($on) {
return $on
? '<i class="fas fa-circle text-success ms-2" style="font-size:.5rem;vertical-align:middle;" title="Configured"></i>'
: '<i class="far fa-circle text-muted ms-2" style="font-size:.5rem;vertical-align:middle;" title="Not configured"></i>';
? '<i class="fas fa-circle text-success ml-2" style="font-size:.5rem;vertical-align:middle;" title="Configured"></i>'
: '<i class="far fa-circle text-muted ml-2" style="font-size:.5rem;vertical-align:middle;" title="Not configured"></i>';
}
$smtp_on = !empty($config_smtp_provider);
@@ -14,10 +14,10 @@ $oauth_needed = in_array($config_smtp_provider, ['google_oauth', 'microsoft_oaut
|| in_array($config_imap_provider, ['google_oauth', 'microsoft_oauth'], true);
// ---- Active tab -------------------------------------------------------------
// The tab lives in the URL (?tab=receiving) so it can be linked, bookmarked, survives a
// The tab lives in the URL (?tab=imap) so it can be linked, bookmarked, survives a
// reload, and lets the POST handlers send you back to the tab you saved from
$mail_tabs = ['sending', 'receiving', 'oauth', 'from', 'tests'];
$active_tab = isset($_GET['tab']) && in_array($_GET['tab'], $mail_tabs, true) ? $_GET['tab'] : 'sending';
$mail_tabs = ['smtp', 'imap', 'oauth', 'from', 'tests'];
$active_tab = isset($_GET['tab']) && in_array($_GET['tab'], $mail_tabs, true) ? $_GET['tab'] : 'smtp';
// A direct link to the OAuth tab reveals it even when no OAuth provider is selected yet
if ($active_tab === 'oauth') {
@@ -68,34 +68,34 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-envelope me-2"></i>Mail Configuration</h3>
<h3 class="card-title"><i class="fas fa-fw fa-envelope mr-2"></i>Mail Configuration</h3>
</div>
<div class="card-body">
<ul class="nav nav-tabs" id="mailTabs" role="tablist">
<li class="nav-item">
<a class="nav-link <?php if ($active_tab === 'sending') { echo 'active'; } ?>" href="?tab=sending" data-bs-target="#tab-sending">
<i class="fas fa-fw fa-paper-plane me-1"></i>Sending<?= renderMailStatusDot($smtp_on) ?>
<a class="nav-link <?php if ($active_tab === 'smtp') { echo 'active'; } ?>" href="?tab=smtp" data-target="#tab-smtp">
<i class="fas fa-fw fa-paper-plane mr-1"></i>Sending<?= renderMailStatusDot($smtp_on) ?>
</a>
</li>
<li class="nav-item">
<a class="nav-link <?php if ($active_tab === 'receiving') { echo 'active'; } ?>" href="?tab=receiving" data-bs-target="#tab-receiving">
<i class="fas fa-fw fa-inbox me-1"></i>Receiving<?= renderMailStatusDot($imap_on) ?>
<a class="nav-link <?php if ($active_tab === 'imap') { echo 'active'; } ?>" href="?tab=imap" data-target="#tab-imap">
<i class="fas fa-fw fa-inbox mr-1"></i>Receiving<?= renderMailStatusDot($imap_on) ?>
</a>
</li>
<li class="nav-item" id="tabitem-oauth" style="<?= $oauth_needed ? '' : 'display:none;' ?>">
<a class="nav-link <?php if ($active_tab === 'oauth') { echo 'active'; } ?>" href="?tab=oauth" data-bs-target="#tab-oauth">
<i class="fas fa-fw fa-key me-1"></i>OAuth
<a class="nav-link <?php if ($active_tab === 'oauth') { echo 'active'; } ?>" href="?tab=oauth" data-target="#tab-oauth">
<i class="fas fa-fw fa-key mr-1"></i>OAuth
</a>
</li>
<li class="nav-item">
<a class="nav-link <?php if ($active_tab === 'from') { echo 'active'; } ?>" href="?tab=from" data-bs-target="#tab-from">
<i class="fas fa-fw fa-at me-1"></i>From Addresses
<a class="nav-link <?php if ($active_tab === 'from') { echo 'active'; } ?>" href="?tab=from" data-target="#tab-from">
<i class="fas fa-fw fa-at mr-1"></i>From Addresses
</a>
</li>
<li class="nav-item">
<a class="nav-link <?php if ($active_tab === 'tests') { echo 'active'; } ?>" href="?tab=tests" data-bs-target="#tab-tests">
<i class="fas fa-fw fa-vial me-1"></i>Tests
<a class="nav-link <?php if ($active_tab === 'tests') { echo 'active'; } ?>" href="?tab=tests" data-target="#tab-tests">
<i class="fas fa-fw fa-vial mr-1"></i>Tests
</a>
</li>
</ul>
@@ -103,15 +103,16 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
<div class="tab-content pt-4">
<!-- ============================ SENDING / SMTP ============================ -->
<div class="tab-pane fade <?php if ($active_tab === 'sending') { echo 'show active'; } ?>" id="tab-sending" role="tabpanel">
<div class="tab-pane fade <?php if ($active_tab === 'smtp') { echo 'show active'; } ?>" id="tab-smtp" role="tabpanel">
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="tab" value="sending">
<input type="hidden" name="tab" value="smtp">
<div class="mb-3">
<div class="form-group">
<label>SMTP Provider <small class="text-muted">— outbound</small></label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-cloud"></i></span>
<select class="form-select" name="config_smtp_provider" id="config_smtp_provider">
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-cloud"></i></span></div>
<select class="form-control" name="config_smtp_provider" id="config_smtp_provider">
<option value="" <?php if (empty($config_smtp_provider)) { echo 'selected'; } ?>>None (Disabled)</option>
<option value="standard_smtp" <?php if ($config_smtp_provider === 'standard_smtp') { echo 'selected'; } ?>>Standard SMTP (Username/Password)</option>
<option value="google_oauth" <?php if ($config_smtp_provider === 'google_oauth') { echo 'selected'; } ?>>Google Workspace (OAuth)</option>
@@ -122,23 +123,26 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
</div>
<div id="smtp_conn_fields">
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>SMTP Host</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-server"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-server"></i></span></div>
<input type="text" class="form-control" name="config_smtp_host" placeholder="smtp.yourcompany.com" maxlength="200" value="<?= escapeHtml($config_smtp_host) ?>" required>
</div>
</div>
<div class="mb-3 col-md-3">
<div class="form-group col-md-3">
<label>Port</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-plug"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-plug"></i></span></div>
<input type="text" class="form-control numeric-only" inputmode="numeric" pattern="[0-9]*" maxlength="5" name="config_smtp_port" placeholder="587 / 465 / 25" value="<?= !empty($config_smtp_port) ? intval($config_smtp_port) : '' ?>" required>
</div>
</div>
<div class="mb-3 col-md-3">
<div class="form-group col-md-3">
<label>Encryption</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
<select class="form-select" name="config_smtp_encryption">
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span></div>
<select class="form-control" name="config_smtp_encryption">
<option value="">None</option>
<option <?php if ($config_smtp_encryption == 'tls') { echo "selected"; } ?> value="tls">TLS</option>
<option <?php if ($config_smtp_encryption == 'ssl') { echo "selected"; } ?> value="ssl">SSL</option>
@@ -148,44 +152,48 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
</div>
</div>
<div class="row g-2">
<div class="mb-3 col-md-6" id="smtp_user_group">
<div class="form-row">
<div class="form-group col-md-6" id="smtp_user_group">
<label id="smtp_user_label">SMTP Username</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-user"></i></span></div>
<input type="text" class="form-control" name="config_smtp_username" id="config_smtp_username" placeholder="usually your full email address" maxlength="200" value="<?= escapeHtml($config_smtp_username) ?>">
</div>
<small class="form-text text-muted" id="smtp_user_hint">Leave blank if no authentication is required.</small>
</div>
<div class="mb-3 col-md-6" id="smtp_pass_group">
<div class="form-group col-md-6" id="smtp_pass_group">
<label>SMTP Password</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
<input type="password" class="form-control" data-toggle="password" name="config_smtp_password" placeholder="mailbox or app password" maxlength="200" value="<?= escapeHtml($config_smtp_password) ?>" autocomplete="new-password"><span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-key"></i></span></div>
<input type="password" class="form-control" data-toggle="password" name="config_smtp_password" placeholder="mailbox or app password" maxlength="200" value="<?= escapeHtml($config_smtp_password) ?>" autocomplete="new-password">
<div class="input-group-append"><span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span></div>
</div>
</div>
</div>
<div class="alert alert-info" id="smtp_oauth_pointer" style="display:none;">
<div class="d-flex align-items-center justify-content-between">
<span><i class="fas fa-fw fa-info-circle me-2"></i>This provider uses OAuth — the password is ignored. Enter app credentials in the OAuth tab.</span>
<button type="button" class="btn btn-sm btn-outline-primary goto-oauth ms-3 text-nowrap"><i class="fas fa-fw fa-key me-1"></i>Open OAuth</button>
<span><i class="fas fa-fw fa-info-circle mr-2"></i>This provider uses OAuth — the password is ignored. Enter app credentials in the OAuth tab.</span>
<button type="button" class="btn btn-sm btn-outline-primary goto-oauth ml-3 text-nowrap"><i class="fas fa-fw fa-key mr-1"></i>Open OAuth</button>
</div>
</div>
<hr>
<button type="submit" name="edit_mail_smtp_settings" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save Sending Settings</button>
<button type="submit" name="edit_mail_smtp_settings" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save Sending Settings</button>
</form>
</div>
<!-- ============================ RECEIVING / IMAP ============================ -->
<div class="tab-pane fade <?php if ($active_tab === 'receiving') { echo 'show active'; } ?>" id="tab-receiving" role="tabpanel">
<div class="tab-pane fade <?php if ($active_tab === 'imap') { echo 'show active'; } ?>" id="tab-imap" role="tabpanel">
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="tab" value="receiving">
<input type="hidden" name="tab" value="imap">
<div class="mb-3">
<div class="form-group">
<label>IMAP Provider <small class="text-muted">— inbound ticket inbox</small></label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-cloud"></i></span>
<select class="form-select" name="config_imap_provider" id="config_imap_provider">
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-cloud"></i></span></div>
<select class="form-control" name="config_imap_provider" id="config_imap_provider">
<option value="" <?php if (empty($config_imap_provider)) { echo 'selected'; } ?>>None (Disabled)</option>
<option value="standard_imap" <?php if ($config_imap_provider === 'standard_imap') { echo 'selected'; } ?>>Standard IMAP (Username/Password)</option>
<option value="google_oauth" <?php if ($config_imap_provider === 'google_oauth') { echo 'selected'; } ?>>Google Workspace (OAuth)</option>
@@ -196,23 +204,26 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
</div>
<div id="imap_conn_fields">
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>IMAP Host</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-server"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-server"></i></span></div>
<input type="text" class="form-control" name="config_imap_host" placeholder="imap.yourcompany.com" maxlength="200" value="<?= escapeHtml($config_imap_host) ?>">
</div>
</div>
<div class="mb-3 col-md-3">
<div class="form-group col-md-3">
<label>Port</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-plug"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-plug"></i></span></div>
<input type="text" class="form-control numeric-only" inputmode="numeric" pattern="[0-9]*" maxlength="5" name="config_imap_port" placeholder="993 / 143" value="<?= !empty($config_imap_port) ? intval($config_imap_port) : '' ?>">
</div>
</div>
<div class="mb-3 col-md-3">
<div class="form-group col-md-3">
<label>Encryption</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
<select class="form-select" name="config_imap_encryption">
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span></div>
<select class="form-control" name="config_imap_encryption">
<option value="">None</option>
<option <?php if ($config_imap_encryption == 'tls') { echo "selected"; } ?> value="tls">TLS</option>
<option <?php if ($config_imap_encryption == 'ssl') { echo "selected"; } ?> value="ssl">SSL</option>
@@ -222,31 +233,34 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
</div>
</div>
<div class="row g-2">
<div class="mb-3 col-md-6" id="imap_user_group">
<div class="form-row">
<div class="form-group col-md-6" id="imap_user_group">
<label id="imap_user_label">IMAP Username</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-user"></i></span></div>
<input type="text" class="form-control" name="config_imap_username" placeholder="tickets@yourcompany.com" maxlength="200" value="<?= escapeHtml($config_imap_username) ?>" required>
</div>
<small class="form-text text-muted" id="imap_user_hint">The mailbox address to monitor for incoming tickets.</small>
</div>
<div class="mb-3 col-md-6" id="imap_pass_group">
<div class="form-group col-md-6" id="imap_pass_group">
<label>IMAP Password</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
<input type="password" class="form-control" data-toggle="password" name="config_imap_password" placeholder="mailbox or app password" maxlength="200" value="<?= escapeHtml($config_imap_password) ?>" autocomplete="new-password"><span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-key"></i></span></div>
<input type="password" class="form-control" data-toggle="password" name="config_imap_password" placeholder="mailbox or app password" maxlength="200" value="<?= escapeHtml($config_imap_password) ?>" autocomplete="new-password">
<div class="input-group-append"><span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span></div>
</div>
</div>
</div>
<div class="alert alert-info" id="imap_oauth_pointer" style="display:none;">
<div class="d-flex align-items-center justify-content-between">
<span><i class="fas fa-fw fa-info-circle me-2"></i>This provider uses OAuth — the password is ignored. Enter app credentials in the OAuth tab.</span>
<button type="button" class="btn btn-sm btn-outline-primary goto-oauth ms-3 text-nowrap"><i class="fas fa-fw fa-key me-1"></i>Open OAuth</button>
<span><i class="fas fa-fw fa-info-circle mr-2"></i>This provider uses OAuth — the password is ignored. Enter app credentials in the OAuth tab.</span>
<button type="button" class="btn btn-sm btn-outline-primary goto-oauth ml-3 text-nowrap"><i class="fas fa-fw fa-key mr-1"></i>Open OAuth</button>
</div>
</div>
<hr>
<button type="submit" name="edit_mail_imap_settings" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save Receiving Settings</button>
<button type="submit" name="edit_mail_imap_settings" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save Receiving Settings</button>
</form>
</div>
@@ -257,56 +271,63 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
<input type="hidden" name="tab" value="oauth">
<div class="alert alert-secondary" id="oauth_hint">
<i class="fas fa-fw fa-info-circle me-2"></i>These credentials are shared by any Sending or Receiving provider set to Google / Microsoft OAuth.
<i class="fas fa-fw fa-info-circle mr-2"></i>These credentials are shared by any Sending or Receiving provider set to Google / Microsoft OAuth.
</div>
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>OAuth Client ID</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-id-badge"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-id-badge"></i></span></div>
<input type="text" class="form-control" name="config_mail_oauth_client_id" id="config_mail_oauth_client_id" placeholder="Application (client) ID" maxlength="255" value="<?= escapeHtml($config_mail_oauth_client_id ?? '') ?>">
</div>
</div>
<div class="mb-3 col-md-6">
<div class="form-group col-md-6">
<label>OAuth Client Secret</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
<input type="password" class="form-control" data-toggle="password" name="config_mail_oauth_client_secret" id="config_mail_oauth_client_secret" placeholder="Client secret value" maxlength="255" value="<?= escapeHtml($config_mail_oauth_client_secret ?? '') ?>" autocomplete="new-password"><span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-key"></i></span></div>
<input type="password" class="form-control" data-toggle="password" name="config_mail_oauth_client_secret" id="config_mail_oauth_client_secret" placeholder="Client secret value" maxlength="255" value="<?= escapeHtml($config_mail_oauth_client_secret ?? '') ?>" autocomplete="new-password">
<div class="input-group-append"><span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span></div>
</div>
</div>
</div>
<div class="mb-3" id="tenant_row" style="display:none;">
<div class="form-group" id="tenant_row" style="display:none;">
<label>Tenant ID <small class="text-muted">— Microsoft 365 only</small></label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-building"></i></span></div>
<input type="text" class="form-control" name="config_mail_oauth_tenant_id" placeholder="Directory (tenant) ID, e.g. 00000000-0000-0000-0000-000000000000" maxlength="255" value="<?= escapeHtml($config_mail_oauth_tenant_id ?? '') ?>">
</div>
</div>
<div class="row g-2">
<div class="mb-3 col-md-6">
<div class="form-row">
<div class="form-group col-md-6">
<label>Refresh Token</label>
<textarea class="form-control" name="config_mail_oauth_refresh_token" rows="2" placeholder="Paste a refresh token, or use the Connect button below to fetch one"><?= escapeHtml($config_mail_oauth_refresh_token ?? '') ?></textarea>
</div>
<div class="mb-3 col-md-6">
<div class="form-group col-md-6">
<label>Access Token <small class="text-muted">— optional</small></label>
<textarea class="form-control" name="config_mail_oauth_access_token" rows="2" placeholder="Leave blank — auto-refreshed from the refresh token"><?= escapeHtml($config_mail_oauth_access_token ?? '') ?></textarea>
<small class="form-text text-muted">Expires at: <?= !empty($config_mail_oauth_access_token_expires_at) ? htmlspecialchars($config_mail_oauth_access_token_expires_at) : 'n/a' ?></small>
</div>
</div>
<div class="mb-3" id="ms_connect_group" style="display:none;">
<div class="form-group" id="ms_connect_group" style="display:none;">
<label>Microsoft OAuth Connect (Web)</label>
<div class="input-group"><span class="input-group-text"><i class="fa fa-fw fa-link"></i></span>
<div class="input-group">
<div class="input-group-prepend"><span class="input-group-text"><i class="fa fa-fw fa-link"></i></span></div>
<input type="text" class="form-control" readonly value="<?= htmlspecialchars($mail_oauth_callback_uri) ?>">
<div class="input-group-append">
<button type="submit" name="oauth_connect_microsoft_mail" class="btn btn-outline-primary">
<i class="fab fa-fw fa-microsoft me-2"></i>Connect Microsoft 365
<i class="fab fa-fw fa-microsoft mr-2"></i>Connect Microsoft 365
</button>
</div>
</div>
<small class="form-text text-muted">Add this callback URI in your Entra App Registration, save credentials, then click Connect to store the refresh token automatically.</small>
</div>
<hr>
<button type="submit" name="edit_mail_oauth_settings" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save OAuth Credentials</button>
<button type="submit" name="edit_mail_oauth_settings" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save OAuth Credentials</button>
</form>
</div>
@@ -350,7 +371,7 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
</tbody>
</table>
<button type="submit" name="edit_mail_from_settings" class="btn btn-primary text-bold"><i class="fas fa-check me-2"></i>Save From Addresses</button>
<button type="submit" name="edit_mail_from_settings" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save From Addresses</button>
</form>
</div>
@@ -359,18 +380,18 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
<?php if (!$send_ready && !$imap_ready && !$oauth_has_required_fields) { ?>
<div class="alert alert-secondary mb-0">
<i class="fas fa-fw fa-info-circle me-2"></i>Finish configuring Sending, Receiving, or OAuth (plus at least one From address) to unlock the tests.
<i class="fas fa-fw fa-info-circle mr-2"></i>Finish configuring Sending, Receiving, or OAuth (plus at least one From address) to unlock the tests.
</div>
<?php } ?>
<?php if ($send_ready) { ?>
<div class="mb-4">
<h6 class="text-bold"><i class="fas fa-fw fa-paper-plane me-2"></i>Send a Test Email</h6>
<h6 class="text-bold"><i class="fas fa-fw fa-paper-plane mr-2"></i>Send a Test Email</h6>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="tab" value="tests">
<div class="input-group">
<select class="form-select select2" name="test_email" required>
<select class="form-control select2" name="test_email" required>
<option value="">- Select a From address -</option>
<?php if ($config_mail_from_email) { ?><option value="1"><?= escapeHtml($config_mail_from_name) ?> (<?= escapeHtml($config_mail_from_email) ?>)</option><?php } ?>
<?php if ($config_invoice_from_email) { ?><option value="2"><?= escapeHtml($config_invoice_from_name) ?> (<?= escapeHtml($config_invoice_from_email) ?>)</option><?php } ?>
@@ -378,7 +399,9 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
<?php if ($config_ticket_from_email) { ?><option value="4"><?= escapeHtml($config_ticket_from_name) ?> (<?= escapeHtml($config_ticket_from_email) ?>)</option><?php } ?>
</select>
<input type="email" class="form-control" name="email_to" placeholder="recipient@example.com">
<button type="submit" name="test_email_smtp" class="btn btn-success"><i class="fas fa-fw fa-paper-plane me-2"></i>Send</button>
<div class="input-group-append">
<button type="submit" name="test_email_smtp" class="btn btn-success"><i class="fas fa-fw fa-paper-plane mr-2"></i>Send</button>
</div>
</div>
</form>
</div>
@@ -386,24 +409,24 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
<?php if ($imap_ready) { ?>
<div class="mb-4">
<h6 class="text-bold"><i class="fas fa-fw fa-plug me-2"></i>Test IMAP Connection</h6>
<h6 class="text-bold"><i class="fas fa-fw fa-plug mr-2"></i>Test IMAP Connection</h6>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="tab" value="tests">
<button type="submit" name="test_email_imap" class="btn btn-success"><i class="fas fa-fw fa-inbox me-2"></i>Test IMAP</button>
<button type="submit" name="test_email_imap" class="btn btn-success"><i class="fas fa-fw fa-inbox mr-2"></i>Test IMAP</button>
</form>
</div>
<?php } ?>
<?php if ($oauth_has_required_fields) { ?>
<div>
<h6 class="text-bold"><i class="fas fa-fw fa-sync-alt me-2"></i>Test OAuth Token Refresh</h6>
<h6 class="text-bold"><i class="fas fa-fw fa-sync-alt mr-2"></i>Test OAuth Token Refresh</h6>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="tab" value="tests">
<input type="hidden" name="oauth_provider" value="<?= htmlspecialchars($oauth_provider_for_test) ?>">
<p class="text-muted mb-2">Validates the refresh token and stores a new access token for <?= $oauth_provider_for_test === 'microsoft_oauth' ? 'Microsoft 365' : 'Google Workspace' ?>.</p>
<button type="submit" name="test_oauth_token_refresh" class="btn btn-success"><i class="fas fa-fw fa-sync-alt me-2"></i>Test OAuth Token Refresh</button>
<button type="submit" name="test_oauth_token_refresh" class="btn btn-success"><i class="fas fa-fw fa-sync-alt mr-2"></i>Test OAuth Token Refresh</button>
</form>
</div>
<?php } ?>
@@ -432,36 +455,29 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
// Set when the page was opened directly on the OAuth tab - stops the provider pass hiding it
const forcedOauthTab = <?= $active_tab === 'oauth' ? 'true' : 'false' ?>;
const navLinks = Array.from(document.querySelectorAll('#mailTabs .nav-link'));
const panes = ['tab-sending', 'tab-receiving', 'tab-oauth', 'tab-from', 'tab-tests']
const panes = ['tab-smtp', 'tab-imap', 'tab-oauth', 'tab-from', 'tab-tests']
.map(id => document.getElementById(id)).filter(Boolean);
// Server rendered the initial tab; keep the URL honest as the user clicks around
function syncTabUrl(target, push) {
function syncTabUrl(target) {
const tab = target.replace('#tab-', '');
const url = new URL(window.location.href);
url.searchParams.set('tab', tab);
// pushState, not replaceState, so back/forward step through the tabs
// instead of jumping straight off the settings page
history[push ? 'pushState' : 'replaceState']({ tab: tab }, '', url);
history.replaceState(null, '', url);
}
function activateTab(target, push) {
syncTabUrl(target, push !== false);
navLinks.forEach(l => l.classList.toggle('active', l.getAttribute('data-bs-target') === target));
function activateTab(target) {
syncTabUrl(target);
navLinks.forEach(l => l.classList.toggle('active', l.getAttribute('data-target') === target));
panes.forEach(p => {
const on = ('#' + p.id) === target;
p.classList.toggle('active', on);
p.classList.toggle('show', on);
});
}
window.addEventListener('popstate', function () {
const tab = new URL(window.location.href).searchParams.get('tab') || 'sending';
activateTab('#tab-' + tab, false);
});
navLinks.forEach(l => l.addEventListener('click', function (e) {
e.preventDefault();
activateTab(l.getAttribute('data-bs-target'), true);
activateTab(l.getAttribute('data-target'));
}));
// ---- Provider-driven field visibility ----
@@ -529,13 +545,13 @@ $imap_ready = $imap_standard_ready || $imap_oauth_ready;
: 'xxxxxxxxxxxx.apps.googleusercontent.com';
if (oauthClientSecret) oauthClientSecret.placeholder = anyMs ? 'Entra client secret value' : 'Google client secret';
if (oauthHint) oauthHint.innerHTML = anyMs
? '<i class="fas fa-fw fa-info-circle me-2"></i>Microsoft 365: Client ID / Secret / Tenant from Entra ID; refresh token via the Connect button below.'
: anyOauth ? '<i class="fas fa-fw fa-info-circle me-2"></i>Google Workspace: Client ID / Secret from Google Cloud; refresh token obtained via the consent flow.'
: '<i class="fas fa-fw fa-info-circle me-2"></i>These credentials are shared by any Sending or Receiving provider set to Google / Microsoft OAuth.';
? '<i class="fas fa-fw fa-info-circle mr-2"></i>Microsoft 365: Client ID / Secret / Tenant from Entra ID; refresh token via the Connect button below.'
: anyOauth ? '<i class="fas fa-fw fa-info-circle mr-2"></i>Google Workspace: Client ID / Secret from Google Cloud; refresh token obtained via the consent flow.'
: '<i class="fas fa-fw fa-info-circle mr-2"></i>These credentials are shared by any Sending or Receiving provider set to Google / Microsoft OAuth.';
if (!anyOauth && !forcedOauthTab) {
const oauthLink = document.querySelector('#mailTabs .nav-link[data-bs-target="#tab-oauth"]');
if (oauthLink && oauthLink.classList.contains('active')) activateTab('#tab-sending');
const oauthLink = document.querySelector('#mailTabs .nav-link[data-target="#tab-oauth"]');
if (oauthLink && oauthLink.classList.contains('active')) activateTab('#tab-smtp');
}
}

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