Skip to content

v25.02.4 - Stable Minor Release #1186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion ajax/ajax_asset_details.php
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ function updateAssetNotes(asset_id) {
</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>
Expand Down
6 changes: 3 additions & 3 deletions ajax/ajax_contact_details.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
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);
Expand Down Expand Up @@ -114,7 +114,7 @@
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();

Expand Down Expand Up @@ -846,7 +846,7 @@ function updateContactNotes(contact_id) {
</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"
Expand Down
2 changes: 1 addition & 1 deletion contacts.php
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"
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
Expand Down
87 changes: 87 additions & 0 deletions functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion includes/app_version.php
Original file line number Diff line number Diff line change
Expand Up @@ -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");
4 changes: 2 additions & 2 deletions post/user/software.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
$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");

Expand Down Expand Up @@ -127,7 +127,7 @@
$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");

Expand Down
4 changes: 2 additions & 2 deletions recurring_invoices.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@
<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>
Expand Down