mirror of https://github.com/itflow-org/itflow
Merge pull request #1206 from itflow-org/develop
Develop to Master for 25.03.4 Release
This commit is contained in:
commit
d92f0fc49b
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -1,6 +1,19 @@
|
|||
# Changelog
|
||||
|
||||
This file documents all notable changes made to ITFlow.
|
||||
|
||||
## [25.03.4]
|
||||
|
||||
### Fixed
|
||||
- Ability to remove additional assets from the ticket details screen.
|
||||
- Fix the ability to remove assets from edit ticket not working when only 1 asset exists.
|
||||
- Fix Database Backup corruption.
|
||||
- Client Portal - show ticket number instead of ticket id in ticket listing.
|
||||
- Add Purchase Reference to copy asset.
|
||||
- Add Link to asset details from the global search.
|
||||
- Fix Bulk assign ticket only showing contacts instead of ITFlow users.
|
||||
|
||||
|
||||
## [25.03.3]
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -364,6 +364,16 @@ ob_start();
|
|||
</div>
|
||||
|
||||
<?php if ($asset_type !== 'Virtual Machine') { ?>
|
||||
<div class="form-group">
|
||||
<label>Purchase Reference</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="purchase_reference" placeholder="eg. Invoice, PO Number" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Purchase Date</label>
|
||||
<div class="input-group">
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ $all_tickets = mysqli_query($mysqli, "SELECT ticket_id, ticket_prefix, ticket_nu
|
|||
$ticket_contact_name = nullable_htmlentities($row['contact_name']);
|
||||
|
||||
echo "<tr>";
|
||||
echo "<td> <a href='ticket.php?id=$ticket_id'> $ticket_prefix$ticket_id</a></td>";
|
||||
echo "<td> <a href='ticket.php?id=$ticket_id'> $ticket_prefix$ticket_number</a></td>";
|
||||
echo "<td> <a href='ticket.php?id=$ticket_id'> $ticket_subject</a></td>";
|
||||
echo "<td>$ticket_contact_name</td>";
|
||||
echo "<td>$ticket_status</td>";
|
||||
|
|
|
|||
|
|
@ -721,7 +721,6 @@ if (isset($_GET['query'])) {
|
|||
} else {
|
||||
$asset_serial_display = $asset_serial;
|
||||
}
|
||||
$asset_mac = nullable_htmlentities($row['asset_mac']);
|
||||
$asset_uri = nullable_htmlentities($row['asset_uri']);
|
||||
$asset_status = nullable_htmlentities($row['asset_status']);
|
||||
$asset_created_at = nullable_htmlentities($row['asset_created_at']);
|
||||
|
|
@ -746,9 +745,9 @@ if (isset($_GET['query'])) {
|
|||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<i class="fa fa-fw text-secondary fa-<?php echo $device_icon; ?> mr-2"></i><?php echo $asset_name; ?>
|
||||
<i class="fa fa-fw text-secondary fa-<?php echo $device_icon; ?> mr-2"></i><a href="asset_details.php?client_id=<?php echo $client_id; ?>&asset_id=<?php echo $asset_id; ?>"><?php echo $asset_name; ?></a>
|
||||
<?php if(!empty($asset_uri)){ ?>
|
||||
<a href="<?php echo $asset_uri; ?>" target="_blank"><i class="fas fa-fw fa-external-link-alt ml-2"></i></a>
|
||||
<a href="<?php echo $asset_uri; ?>" target="_blank"><i class="fas fa-fw fa-external-link-alt ml-2"></i></a>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td><?php echo $asset_type; ?></td>
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@
|
|||
* Update this file each time we merge develop into master. Format is YY.MM (add a .v if there is more than one release a month.
|
||||
*/
|
||||
|
||||
DEFINE("APP_VERSION", "25.03.3");
|
||||
DEFINE("APP_VERSION", "25.03.4");
|
||||
|
|
|
|||
|
|
@ -75,20 +75,23 @@
|
|||
|
||||
<div class="tab-pane fade" id="pills-support">
|
||||
|
||||
<label>Support Phone</label>
|
||||
<label>Support Phone / <span class="text-secondary">Extension</span></label>
|
||||
<div class="form-row">
|
||||
<div class="col-8">
|
||||
<div class="col-9">
|
||||
<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="text" class="form-control" name="phone" placeholder="Phone Number" maxlength="200">
|
||||
<input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4">
|
||||
<input type="tel" class="form-control" name="phone" placeholder="Phone Number" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<input type="text" class="form-control" name="extension" placeholder="Prompts" maxlength="200">
|
||||
<div class="col-3">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="extension" placeholder="ext." maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<span class="input-group-text"><i class="fa fa-fw fa-envelope"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" data-tags="true" name="watcher_email">
|
||||
<option value="">-Select a contact-</option>
|
||||
<option value="">- Select a contact or enter an email -</option>
|
||||
<?php
|
||||
|
||||
$sql_client_contacts_select = mysqli_query($mysqli, "SELECT contact_id, contact_name, contact_email FROM contacts WHERE contact_client_id = $client_id AND contact_email <> '' ORDER BY contact_name ASC");
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@
|
|||
<select class="form-control select2" name="assign_to">
|
||||
<option value="0">Not Assigned</option>
|
||||
<?php
|
||||
$sql_users_select = mysqli_query($mysqli, "SELECT users.user_id, user_name FROM users
|
||||
LEFT JOIN user_settings on users.user_id = user_settings.user_id
|
||||
AND user_type = 1
|
||||
$sql_users_select = 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 DESC"
|
||||
|
|
|
|||
|
|
@ -7,84 +7,68 @@
|
|||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
if (isset($_GET['download_database'])) {
|
||||
|
||||
validateCSRFToken($_GET['csrf_token']);
|
||||
|
||||
// Get All Table Names From the Database
|
||||
$tables = array();
|
||||
$sql = "SHOW TABLES";
|
||||
$result = mysqli_query($mysqli, $sql);
|
||||
global $mysqli, $database;
|
||||
|
||||
while ($row = mysqli_fetch_row($result)) {
|
||||
$backupFileName = date('Y-m-d_H-i-s') . '_backup.sql';
|
||||
|
||||
header('Content-Type: application/sql');
|
||||
header('Content-Disposition: attachment; filename="' . $backupFileName . '"');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
|
||||
if (ob_get_level()) ob_end_clean();
|
||||
flush();
|
||||
|
||||
// Start of dump file — charset declaration
|
||||
echo "-- UTF-8 + Foreign Key Safe Dump\n";
|
||||
echo "SET NAMES 'utf8mb4';\n";
|
||||
echo "SET foreign_key_checks = 0;\n\n";
|
||||
|
||||
// Get all tables
|
||||
$tables = [];
|
||||
$res = $mysqli->query("SHOW TABLES");
|
||||
while ($row = $res->fetch_row()) {
|
||||
$tables[] = $row[0];
|
||||
}
|
||||
|
||||
$sqlScript = "";
|
||||
foreach ($tables as $table) {
|
||||
// Table structure
|
||||
$createRes = $mysqli->query("SHOW CREATE TABLE `$table`");
|
||||
$createRow = $createRes->fetch_assoc();
|
||||
$createSQL = array_values($createRow)[1];
|
||||
|
||||
// Prepare SQLscript for creating table structure
|
||||
$query = "SHOW CREATE TABLE $table";
|
||||
$result = mysqli_query($mysqli, $query);
|
||||
$row = mysqli_fetch_row($result);
|
||||
echo "\n-- ----------------------------\n";
|
||||
echo "-- Table structure for `$table`\n";
|
||||
echo "-- ----------------------------\n";
|
||||
echo "DROP TABLE IF EXISTS `$table`;\n";
|
||||
echo $createSQL . ";\n\n";
|
||||
|
||||
$sqlScript .= "\n\n" . $row[1] . ";\n\n";
|
||||
// Table data
|
||||
$dataRes = $mysqli->query("SELECT * FROM `$table`");
|
||||
if ($dataRes->num_rows > 0) {
|
||||
echo "-- Dumping data for table `$table`\n";
|
||||
while ($row = $dataRes->fetch_assoc()) {
|
||||
$columns = array_map(fn($col) => '`' . $mysqli->real_escape_string($col) . '`', array_keys($row));
|
||||
$values = array_map(function ($val) use ($mysqli) {
|
||||
if (is_null($val)) return "NULL";
|
||||
return "'" . $mysqli->real_escape_string($val) . "'";
|
||||
}, array_values($row));
|
||||
|
||||
|
||||
$query = "SELECT * FROM $table";
|
||||
$result = mysqli_query($mysqli, $query);
|
||||
|
||||
$columnCount = mysqli_num_fields($result);
|
||||
|
||||
// Prepare SQLscript for dumping data for each table
|
||||
for ($i = 0; $i < $columnCount; $i ++) {
|
||||
while ($row = mysqli_fetch_row($result)) {
|
||||
$sqlScript .= "INSERT INTO $table VALUES(";
|
||||
for ($j = 0; $j < $columnCount; $j ++) {
|
||||
|
||||
if (isset($row[$j])) {
|
||||
$sqlScript .= '"' . $row[$j] . '"';
|
||||
} else {
|
||||
$sqlScript .= '""';
|
||||
}
|
||||
if ($j < ($columnCount - 1)) {
|
||||
$sqlScript .= ',';
|
||||
}
|
||||
}
|
||||
$sqlScript .= ");\n";
|
||||
echo "INSERT INTO `$table` (" . implode(", ", $columns) . ") VALUES (" . implode(", ", $values) . ");\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
$sqlScript .= "\n";
|
||||
}
|
||||
|
||||
if (!empty($sqlScript)) {
|
||||
|
||||
$company_name = $session_company_name;
|
||||
// Save the SQL script to a backup file
|
||||
$backup_file_name = date('Y-m-d') . '_ITFlow_backup.sql';
|
||||
$fileHandler = fopen($backup_file_name, 'w+');
|
||||
$number_of_lines = fwrite($fileHandler, $sqlScript);
|
||||
fclose($fileHandler);
|
||||
|
||||
// Download the SQL backup file to the browser
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename=' . basename($backup_file_name));
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: ' . filesize($backup_file_name));
|
||||
ob_clean();
|
||||
flush();
|
||||
readfile($backup_file_name);
|
||||
exec('rm ' . $backup_file_name);
|
||||
}
|
||||
|
||||
// Logging
|
||||
logAction("Database", "Download", "$session_name downloaded the database");
|
||||
//FINAL STEP: Re-enable foreign key checks
|
||||
echo "\nSET foreign_key_checks = 1;\n";
|
||||
|
||||
logAction("Database", "Download", "$session_name downloaded the database.");
|
||||
$_SESSION['alert_message'] = "Database downloaded";
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_POST['backup_master_key'])) {
|
||||
|
|
|
|||
|
|
@ -203,6 +203,10 @@ if (isset($_POST['edit_ticket'])) {
|
|||
$additional_asset_id = intval($additional_asset);
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_assets SET ticket_id = $ticket_id, asset_id = $additional_asset_id");
|
||||
}
|
||||
} else {
|
||||
// If no additional assets are provided, delete them all
|
||||
// This handles cases where the assets input might be cleared or not set at all.
|
||||
mysqli_query($mysqli, "DELETE FROM ticket_assets WHERE ticket_id = $ticket_id");
|
||||
}
|
||||
|
||||
// Get contact/ticket details after update for logging / email purposes
|
||||
|
|
@ -494,6 +498,41 @@ if (isset($_GET['delete_ticket_watcher'])) {
|
|||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_ticket_additional_asset'])) {
|
||||
|
||||
enforceUserPermission('module_support', 2);
|
||||
|
||||
$asset_id = intval($_GET['delete_ticket_additional_asset']);
|
||||
$ticket_id = intval($_GET['ticket_id']);
|
||||
|
||||
// Get ticket / asset details for logging
|
||||
$sql = mysqli_query($mysqli, "SELECT asset_name, ticket_prefix, ticket_number, ticket_status_name, ticket_client_id FROM assets
|
||||
JOIN tickets ON ticket_id = $ticket_id
|
||||
JOIN ticket_statuses ON ticket_status = ticket_status_id
|
||||
WHERE asset_id = $asset_id"
|
||||
);
|
||||
$row = mysqli_fetch_array($sql);
|
||||
|
||||
$ticket_prefix = sanitizeInput($row['ticket_prefix']);
|
||||
$ticket_number = intval($row['ticket_number']);
|
||||
$ticket_status_name = sanitizeInput($row['ticket_status_name']);
|
||||
$asset_name = sanitizeInput($row['asset_name']);
|
||||
$client_id = intval($row['ticket_client_id']);
|
||||
|
||||
mysqli_query($mysqli, "DELETE FROM ticket_assets WHERE ticket_id = $ticket_id AND asset_id = $asset_id");
|
||||
|
||||
// History
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_history SET ticket_history_status = '$ticket_status_name', ticket_history_description = '$session_name removed additional asset $asset_name', ticket_history_ticket_id = $ticket_id");
|
||||
|
||||
// Logging
|
||||
logAction("Ticket", "Edit", "$session_name removed asset $asset_name from ticket $ticket_prefix$ticket_number", $client_id, $ticket_id);
|
||||
|
||||
$_SESSION['alert_type'] = "error";
|
||||
$_SESSION['alert_message'] = "Removed asset <strong>$asset_name</strong> from ticket.";
|
||||
|
||||
header("Location: " . $_SERVER["HTTP_REFERER"]);
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_ticket_asset'])) {
|
||||
|
||||
enforceUserPermission('module_support', 2);
|
||||
|
|
|
|||
|
|
@ -1058,6 +1058,11 @@ if (isset($_GET['ticket_id'])) {
|
|||
data-ajax-id="<?php echo $additional_asset_id; ?>">
|
||||
<i class="fa fa-fw fa-<?php echo $additional_asset_icon; ?> text-secondary mr-2"></i><?php echo $additional_asset_name; ?>
|
||||
</a>
|
||||
<?php if (empty($ticket_closed_at)) { ?>
|
||||
<a class="confirm-link float-right" href="post.php?delete_ticket_additional_asset=<?php echo $additional_asset_id; ?>&ticket_id=<?php echo $ticket_id; ?>" title="Remove asset from ticket">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue