mirror of
https://github.com/itflow-org/itflow
synced 2026-03-10 07:44:50 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff80a3db3f | ||
|
|
c7d00d7b0d | ||
|
|
1c6e74b08e | ||
|
|
f8d054f8aa | ||
|
|
e0dfaf2d22 | ||
|
|
757a62c35b | ||
|
|
52a62fc23c | ||
|
|
ad9e4b4fb4 | ||
|
|
4fdd5ae769 |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
This file documents all notable changes made to ITFlow.
|
This file documents all notable changes made to ITFlow.
|
||||||
|
|
||||||
|
## [25.02.4]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Resolved issue preventing the addition or editing of licenses when no vendor was selected.
|
||||||
|
- Fixed several undeclared variables in AJAX contact details.
|
||||||
|
- Corrected the contact ticket count display.
|
||||||
|
- Addressed an issue where clicking "More Details" in AJAX contact/asset details failed to include the `client_id` in the URL.
|
||||||
|
- Fixed an issue with recurring invoices in the client URL: clicking "Inactive" or "Active" would unexpectedly navigate away from the client section.
|
||||||
|
- Added new php function getFieldById() to return a record using just an id and sanitized as well.
|
||||||
|
|
||||||
## [25.02.3]
|
## [25.02.3]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -848,7 +848,7 @@ ob_start();
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer bg-white">
|
<div class="modal-footer bg-white">
|
||||||
<a href="asset_details.php?<?php echo $client_url; ?>asset_id=<?php echo $asset_id; ?>" class="btn btn-primary text-bold"><span class="text-white"><i class="fas fa-info-circle mr-2"></i>More Details</span></a>
|
<a href="asset_details.php?client_id=<?php echo $client_id; ?>&asset_id=<?php echo $asset_id; ?>" class="btn btn-primary text-bold"><span class="text-white"><i class="fas fa-info-circle mr-2"></i>More Details</span></a>
|
||||||
<a href="#" class="btn btn-secondary"
|
<a href="#" class="btn btn-secondary"
|
||||||
data-toggle="ajax-modal" data-ajax-url="ajax/ajax_asset_edit.php" data-ajax-id="<?php echo $asset_id; ?>">
|
data-toggle="ajax-modal" data-ajax-url="ajax/ajax_asset_edit.php" data-ajax-id="<?php echo $asset_id; ?>">
|
||||||
<span class="text-white"><i class="fas fa-edit mr-2"></i>Edit</span>
|
<span class="text-white"><i class="fas fa-edit mr-2"></i>Edit</span>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ $sql = mysqli_query($mysqli, "SELECT * FROM contacts
|
|||||||
LEFT JOIN locations ON location_id = contact_location_id
|
LEFT JOIN locations ON location_id = contact_location_id
|
||||||
LEFT JOIN users ON user_id = contact_user_id
|
LEFT JOIN users ON user_id = contact_user_id
|
||||||
WHERE contact_id = $contact_id
|
WHERE contact_id = $contact_id
|
||||||
$client_query
|
LIMIT 1
|
||||||
");
|
");
|
||||||
|
|
||||||
$row = mysqli_fetch_array($sql);
|
$row = mysqli_fetch_array($sql);
|
||||||
@@ -114,7 +114,7 @@ $sql_linked_services = mysqli_query($mysqli, "SELECT * FROM service_contacts, se
|
|||||||
AND service_contacts.service_id = services.service_id
|
AND service_contacts.service_id = services.service_id
|
||||||
ORDER BY service_name ASC"
|
ORDER BY service_name ASC"
|
||||||
);
|
);
|
||||||
$service_count = mysqli_num_rows($sql_linked_services);
|
$services_count = mysqli_num_rows($sql_linked_services);
|
||||||
|
|
||||||
$linked_services = array();
|
$linked_services = array();
|
||||||
|
|
||||||
@@ -846,7 +846,7 @@ ob_start();
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer bg-white">
|
<div class="modal-footer bg-white">
|
||||||
<a href="contact_details.php?<?php echo $client_url; ?>contact_id=<?php echo $contact_id; ?>" class="btn btn-primary text-bold">
|
<a href="contact_details.php?client_id=<?php echo $client_id; ?>&contact_id=<?php echo $contact_id; ?>" class="btn btn-primary text-bold">
|
||||||
<span class="text-white"><i class="fas fa-info-circle mr-2"></i>More Details</span>
|
<span class="text-white"><i class="fas fa-info-circle mr-2"></i>More Details</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" class="btn btn-secondary"
|
<a href="#" class="btn btn-secondary"
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
|||||||
if ($ticket_count) {
|
if ($ticket_count) {
|
||||||
$ticket_count_display = "<span class='mr-2 badge badge-pill badge-secondary p-2' title='$ticket_count Tickets'><i class='fas fa-fw fa-life-ring mr-2'></i>$ticket_count</span>";
|
$ticket_count_display = "<span class='mr-2 badge badge-pill badge-secondary p-2' title='$ticket_count Tickets'><i class='fas fa-fw fa-life-ring mr-2'></i>$ticket_count</span>";
|
||||||
} else {
|
} else {
|
||||||
$software_count_display = '';
|
$ticket_count_display = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Related Documents Query
|
// Related Documents Query
|
||||||
|
|||||||
@@ -1414,3 +1414,90 @@ function logAuth($status, $details) {
|
|||||||
function getFallback($data) {
|
function getFallback($data) {
|
||||||
return !empty($data) ? $data : '<span class="text-muted">N/A</span>';
|
return !empty($data) ? $data : '<span class="text-muted">N/A</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a specified field's value from a table based on the record's id.
|
||||||
|
* It validates the table and field names, automatically determines the primary key (or uses the first column as fallback),
|
||||||
|
* and returns the field value with an appropriate escaping method.
|
||||||
|
*
|
||||||
|
* @param string $table The name of the table.
|
||||||
|
* @param int $id The record's id.
|
||||||
|
* @param string $field The field (column) to retrieve.
|
||||||
|
* @param string $escape_method The escape method: 'sql' (default, auto-detects int), 'html', 'json', or 'int'.
|
||||||
|
*
|
||||||
|
* @return mixed The escaped field value, or null if not found or invalid input.
|
||||||
|
*/
|
||||||
|
function getFieldById($table, $id, $field, $escape_method = 'sql') {
|
||||||
|
global $mysqli; // Use the global MySQLi connection
|
||||||
|
|
||||||
|
// Validate table and field names to allow only letters, numbers, and underscores
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9_]+$/', $table) || !preg_match('/^[a-zA-Z0-9_]+$/', $field)) {
|
||||||
|
return null; // Invalid table or field name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize id as an integer
|
||||||
|
$id = (int)$id;
|
||||||
|
|
||||||
|
// Get the list of columns and their details from the table
|
||||||
|
$columns_result = mysqli_query($mysqli, "SHOW COLUMNS FROM `$table`");
|
||||||
|
if (!$columns_result || mysqli_num_rows($columns_result) == 0) {
|
||||||
|
return null; // Table not found or has no columns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build an associative array with column details
|
||||||
|
$columns = [];
|
||||||
|
while ($row = mysqli_fetch_assoc($columns_result)) {
|
||||||
|
$columns[$row['Field']] = [
|
||||||
|
'type' => $row['Type'],
|
||||||
|
'key' => $row['Key']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the primary key field if available
|
||||||
|
$id_field = null;
|
||||||
|
foreach ($columns as $col => $details) {
|
||||||
|
if ($details['key'] === 'PRI') {
|
||||||
|
$id_field = $col;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback: if no primary key is found, use the first column
|
||||||
|
if (!$id_field) {
|
||||||
|
reset($columns);
|
||||||
|
$id_field = key($columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the requested field exists; if not, default to the id field
|
||||||
|
if (!array_key_exists($field, $columns)) {
|
||||||
|
$field = $id_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build and execute the query to fetch the specified field value
|
||||||
|
$query = "SELECT `$field` FROM `$table` WHERE `$id_field` = $id";
|
||||||
|
$sql = mysqli_query($mysqli, $query);
|
||||||
|
|
||||||
|
if ($sql && mysqli_num_rows($sql) > 0) {
|
||||||
|
$row = mysqli_fetch_assoc($sql);
|
||||||
|
$value = $row[$field];
|
||||||
|
|
||||||
|
// Apply the desired escaping method or auto-detect integer type if using SQL escaping
|
||||||
|
switch ($escape_method) {
|
||||||
|
case 'html':
|
||||||
|
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); // Escape for HTML
|
||||||
|
case 'json':
|
||||||
|
return json_encode($value); // Escape for JSON
|
||||||
|
case 'int':
|
||||||
|
return (int)$value; // Explicitly cast value to integer
|
||||||
|
case 'sql':
|
||||||
|
default:
|
||||||
|
// Auto-detect if the field type is integer
|
||||||
|
if (stripos($columns[$field]['type'], 'int') !== false) {
|
||||||
|
return (int)$value;
|
||||||
|
} else {
|
||||||
|
return sanitizeInput($value); // Escape for SQL using a custom function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // Return null if no record was found
|
||||||
|
}
|
||||||
@@ -5,4 +5,4 @@
|
|||||||
* Update this file each time we merge develop into master. Format is YY.MM (add a .v if there is more than one release a month.
|
* Update this file each time we merge develop into master. Format is YY.MM (add a .v if there is more than one release a month.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DEFINE("APP_VERSION", "25.02.3");
|
DEFINE("APP_VERSION", "25.02.4");
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ if (isset($_POST['add_software'])) {
|
|||||||
$expire = "'" . $expire . "'";
|
$expire = "'" . $expire . "'";
|
||||||
}
|
}
|
||||||
$notes = sanitizeInput($_POST['notes']);
|
$notes = sanitizeInput($_POST['notes']);
|
||||||
$vendor = sanitizeInput($_POST['vendor'] ?? 0);
|
$vendor = intval($_POST['vendor'] ?? 0);
|
||||||
|
|
||||||
mysqli_query($mysqli,"INSERT INTO software SET software_name = '$name', software_version = '$version', software_description = '$description', software_type = '$type', software_key = '$key', software_license_type = '$license_type', software_seats = $seats, software_purchase_reference = '$purchase_reference', software_purchase = $purchase, software_expire = $expire, software_notes = '$notes', software_vendor_id = $vendor, software_client_id = $client_id");
|
mysqli_query($mysqli,"INSERT INTO software SET software_name = '$name', software_version = '$version', software_description = '$description', software_type = '$type', software_key = '$key', software_license_type = '$license_type', software_seats = $seats, software_purchase_reference = '$purchase_reference', software_purchase = $purchase, software_expire = $expire, software_notes = '$notes', software_vendor_id = $vendor, software_client_id = $client_id");
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ if (isset($_POST['edit_software'])) {
|
|||||||
$expire = "'" . $expire . "'";
|
$expire = "'" . $expire . "'";
|
||||||
}
|
}
|
||||||
$notes = sanitizeInput($_POST['notes']);
|
$notes = sanitizeInput($_POST['notes']);
|
||||||
$vendor = sanitizeInput($_POST['vendor'] ?? 0);
|
$vendor = intval($_POST['vendor'] ?? 0);
|
||||||
|
|
||||||
mysqli_query($mysqli,"UPDATE software SET software_name = '$name', software_version = '$version', software_description = '$description', software_type = '$type', software_key = '$key', software_license_type = '$license_type', software_seats = $seats, software_purchase_reference = '$purchase_reference', software_purchase = $purchase, software_expire = $expire, software_notes = '$notes', software_vendor_id = $vendor WHERE software_id = $software_id");
|
mysqli_query($mysqli,"UPDATE software SET software_name = '$name', software_version = '$version', software_description = '$description', software_type = '$type', software_key = '$key', software_license_type = '$license_type', software_seats = $seats, software_purchase_reference = '$purchase_reference', software_purchase = $purchase, software_expire = $expire, software_notes = '$notes', software_vendor_id = $vendor WHERE software_id = $software_id");
|
||||||
|
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
|||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<div class="btn-toolbar float-right">
|
<div class="btn-toolbar float-right">
|
||||||
<div class="btn-group mr-2">
|
<div class="btn-group mr-2">
|
||||||
<a href="?status=active" class="btn btn-<?php if ($status_filter == "active"){ echo "primary"; } else { echo "default"; } ?>"><i class="fa fa-fw fa-check mr-2"></i>Active</a>
|
<a href="?<?php echo $client_url; ?>status=active" class="btn btn-<?php if ($status_filter == "active"){ echo "primary"; } else { echo "default"; } ?>"><i class="fa fa-fw fa-check mr-2"></i>Active</a>
|
||||||
<a href="?status=inactive" class="btn btn-<?php if ($status_filter == "inactive"){ echo "primary"; } else { echo "default"; } ?>"><i class="fa fa-fw fa-ban mr-2"></i>Inactive</a>
|
<a href="?<?php echo $client_url; ?>status=inactive" class="btn btn-<?php if ($status_filter == "inactive"){ echo "primary"; } else { echo "default"; } ?>"><i class="fa fa-fw fa-ban mr-2"></i>Inactive</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user