mirror of https://github.com/itflow-org/itflow
Merge pull request #1186 from itflow-org/develop
v25.02.4 - Stable Minor Release
This commit is contained in:
commit
ff80a3db3f
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -2,6 +2,16 @@
|
|||
|
||||
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]
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -848,7 +848,7 @@ ob_start();
|
|||
</div>
|
||||
|
||||
<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"
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ $sql = mysqli_query($mysqli, "SELECT * FROM contacts
|
|||
LEFT JOIN locations ON location_id = contact_location_id
|
||||
LEFT JOIN users ON user_id = contact_user_id
|
||||
WHERE contact_id = $contact_id
|
||||
$client_query
|
||||
LIMIT 1
|
||||
");
|
||||
|
||||
$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
|
||||
ORDER BY service_name ASC"
|
||||
);
|
||||
$service_count = mysqli_num_rows($sql_linked_services);
|
||||
$services_count = mysqli_num_rows($sql_linked_services);
|
||||
|
||||
$linked_services = array();
|
||||
|
||||
|
|
@ -846,7 +846,7 @@ ob_start();
|
|||
</div>
|
||||
|
||||
<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>
|
||||
</a>
|
||||
<a href="#" class="btn btn-secondary"
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
|||
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>";
|
||||
} else {
|
||||
$software_count_display = '';
|
||||
$ticket_count_display = '';
|
||||
}
|
||||
|
||||
// Related Documents Query
|
||||
|
|
|
|||
|
|
@ -1413,4 +1413,91 @@ function logAuth($status, $details) {
|
|||
// Helper function for missing data fallback
|
||||
function getFallback($data) {
|
||||
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.
|
||||
*/
|
||||
|
||||
DEFINE("APP_VERSION", "25.02.3");
|
||||
DEFINE("APP_VERSION", "25.02.4");
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ if (isset($_POST['add_software'])) {
|
|||
$expire = "'" . $expire . "'";
|
||||
}
|
||||
$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");
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ if (isset($_POST['edit_software'])) {
|
|||
$expire = "'" . $expire . "'";
|
||||
}
|
||||
$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");
|
||||
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
|||
<div class="col-sm-8">
|
||||
<div class="btn-toolbar float-right">
|
||||
<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="?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=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=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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue