Finetuning software updates, general UI improvements

This commit is contained in:
“VeLiTi”
2026-01-13 14:35:16 +01:00
parent 0d3724395a
commit a0e1d386ad
46 changed files with 317 additions and 120 deletions

View File

@@ -140,7 +140,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this account?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -155,7 +155,6 @@ if (isset($post_content['sn']) && (isset($post_content['payload']) || isset($pos
case 'customer': //update from Portal
$historytype = 'Customer';
$equipmentUpdate = 1;
break;
default:

View File

@@ -1,8 +1,5 @@
<?php
defined($security_key) or exit;
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
//------------------------------------------
// Payment Creation (for Software Upgrades)
//------------------------------------------
@@ -44,7 +41,8 @@ if (!$equipment) {
}
$equipment_id = $equipment['rowID'];
$current_sw_version = trim(strtolower(ltrim($equipment['sw_version'], '0')));
// Normalize software version for comparison (lowercase, trim leading zeros) - same as software_update.php line 96
$current_sw_version = strtolower(ltrim($equipment['sw_version'], '0'));
$sw_version_license = $equipment['sw_version_license'] ?? null;
$hw_version = $equipment['hw_version'] ?? '';
@@ -79,10 +77,13 @@ $path_count_result = $stmt->fetch(PDO::FETCH_ASSOC);
$has_upgrade_paths = ($path_count_result['path_count'] > 0);
if (!$has_upgrade_paths) {
// No upgrade paths defined = FREE (lines 240-242 in software_update.php)
// No upgrade paths defined = FREE (lines 328-331 in software_update.php)
$final_price = '0.00';
if (debug) {
debuglog("DEBUG: No upgrade paths defined for version_id $version_id - upgrade is FREE");
}
} else {
// Check for valid upgrade path FROM current version
// Check for valid upgrade path FROM current version (same logic as software_update.php lines 335-353)
$sql = 'SELECT pup.price, pup.currency
FROM products_software_upgrade_paths pup
JOIN products_software_versions from_ver ON pup.from_version_id = from_ver.rowID
@@ -93,14 +94,28 @@ if (!$has_upgrade_paths) {
$stmt->execute([$version_id, $current_sw_version]);
$upgrade_path = $stmt->fetch(PDO::FETCH_ASSOC);
if (debug) {
debuglog("DEBUG: Looking for upgrade path TO version_id=$version_id FROM current_sw_version='$current_sw_version'");
debuglog("DEBUG: Upgrade path result: " . json_encode($upgrade_path));
}
if ($upgrade_path) {
$final_price = $upgrade_path['price'] ?? '0.00';
$final_currency = $upgrade_path['currency'] ?? 'EUR';
if (debug) {
debuglog("DEBUG: Found upgrade path - price: $final_price $final_currency");
}
} else {
// No upgrade path FROM current version
if (debug) {
debuglog("ERROR: No valid upgrade path from current version '$current_sw_version' to version_id $version_id");
}
http_response_code(400);
echo json_encode(['error' => 'No valid upgrade path from current version'], JSON_UNESCAPED_UNICODE);
echo json_encode([
'error' => 'No valid upgrade path from current version',
'current_version' => $current_sw_version,
'target_version_id' => $version_id
], JSON_UNESCAPED_UNICODE);
exit;
}
}

View File

@@ -22,7 +22,7 @@ $command = ($id == '')? 'insert' : 'update'; //IF rowID = empty then INSERT
if (isset($post_content['delete'])){$command = 'delete';} //change command to delete
// Check for bulk creation
$is_bulk = isset($post_content['bulk']) && $post_content['bulk'] === true;
$is_bulk = isset($post_content['bulk']) && ($post_content['bulk'] === "true" || $post_content['bulk'] === true);
$date = date('Y-m-d H:i:s');
@@ -37,12 +37,24 @@ $input_insert = '';
if ($command == 'insert' && $is_bulk && isAllowed('products_software_licenses',$profile,$permission,'C') === 1){
$version_id = $post_content['version_id'] ?? '';
$serials = $post_content['serials'] ?? [];
$serials_input = $post_content['serials'] ?? '';
// Convert comma-separated string to array and trim whitespace
if (is_string($serials_input)) {
$serials = array_map('trim', explode(',', $serials_input));
} elseif (is_array($serials_input)) {
$serials = $serials_input;
} else {
$serials = [];
}
$transaction_id = $post_content['transaction_id'] ?? '';
$license_type = $post_content['license_type'] ?? 0;
$status = $post_content['status'] ?? 0;
$status = $post_content['status'] ?? 1;
$starts_at = $post_content['starts_at'] ?? date('Y-m-d H:i:s');
$expires_at = $post_content['expires_at'] ?? '2099-12-31 23:59:59'; // effectively permanent
if (empty($version_id) || empty($serials) || !is_array($serials)) {
if (empty($version_id) || empty($serials)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid parameters for bulk creation']);
exit;
@@ -51,8 +63,8 @@ if ($command == 'insert' && $is_bulk && isAllowed('products_software_licenses',$
$accounthierarchy = json_encode(array("salesid"=>$partner->salesid,"soldto"=>$partner->soldto), JSON_UNESCAPED_UNICODE);
// Prepare statement for bulk insert
$sql = 'INSERT INTO products_software_licenses (version_id, license_key, license_type, status, transaction_id, accounthierarchy, created, createdby)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)';
$sql = 'INSERT INTO products_software_licenses (version_id, license_key, license_type, status, starts_at, expires_at, transaction_id, accounthierarchy, created, createdby)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
$stmt = $pdo->prepare($sql);
$created_count = 0;
@@ -60,13 +72,7 @@ if ($command == 'insert' && $is_bulk && isAllowed('products_software_licenses',$
if (empty($serial)) continue;
// Generate UUID for license key
$license_key = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
$license_key = generateUniqueLicenseKey();
try {
$stmt->execute([
@@ -74,6 +80,8 @@ if ($command == 'insert' && $is_bulk && isAllowed('products_software_licenses',$
$license_key,
$license_type,
$status,
$starts_at,
$expires_at,
$transaction_id,
$accounthierarchy,
$date,
@@ -81,9 +89,9 @@ if ($command == 'insert' && $is_bulk && isAllowed('products_software_licenses',$
]);
// Assign license to equipment if serial number exists
$eq_sql = 'UPDATE equipment SET sw_version_license = ? WHERE serialnumber = ? AND accounthierarchy LIKE ?';
$eq_sql = 'UPDATE equipment SET sw_version_license = ? WHERE serialnumber = ? ';
$eq_stmt = $pdo->prepare($eq_sql);
$eq_stmt->execute([$license_key, $serial, '%'.$partner->soldto.'%']);
$eq_stmt->execute([$license_key, $serial]);
$created_count++;
} catch (Exception $e) {
@@ -104,17 +112,8 @@ if ($command == 'update'){
$post_content['updatedby'] = $username;
}
elseif ($command == 'insert'){
// Generate UUID for license key if not provided
if (empty($post_content['license_key'])) {
$post_content['license_key'] = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
// Generate UUID for license key
$post_content['license_key'] = generateUniqueLicenseKey();
$post_content['created'] = $date;
$post_content['createdby'] = $username;
$post_content['accounthierarchy'] = json_encode(array("salesid"=>$partner->salesid,"soldto"=>$partner->soldto), JSON_UNESCAPED_UNICODE);

View File

@@ -83,7 +83,9 @@ function send_mail($to, $subject, $message, $attachment, $attachment_name){
if( !$mail->send() ){
// render error if it is
$tab = array('error' => 'Mailer Error: '.$mail->ErrorInfo );
if(debug){
debuglog(json_encode($tab));
}
exit;
}
else{
@@ -157,7 +159,9 @@ function sendIcsCalendar($appointment, $to, $subject = 'Appointment Confirmation
if (!$mail->send()) {
$tab = array('error' => 'Mailer Error: ' . $mail->ErrorInfo);
if(debug){
debuglog(json_encode($tab));
}
return false;
} else {
return true;
@@ -1067,7 +1071,9 @@ function validate_secure_download_token($token, $secret_key = null) {
// Check JSON parsing with detailed error info
if ($header === null) {
$json_error = json_last_error_msg();
if(debug){
debuglog("JSON decode failed for header. Raw JSON: " . $header_json . " Error: " . $json_error);
}
return ['error' => 'INVALID_TOKEN', 'message' => 'Failed to decode token header JSON: ' . $json_error];
}
if ($payload === null) {
@@ -1228,6 +1234,11 @@ function ioServer($api_call, $data){
include dirname(__FILE__,2).'/settings/settings_redirector.php';
if(debug){
$data_log = is_array($data) ? json_encode($data) : $data;
debuglog($date." - ioServer incoming call: api_call=$api_call, data=" . $data_log);
}
$token = $_SESSION['userkey'] ?? 'authorization_request';
$bearertoken = createCommunicationToken($token);
@@ -1253,6 +1264,10 @@ function ioServer($api_call, $data){
$http_status = curl_getinfo($curl) ?? '200';
curl_close($curl);
if(debug){
debuglog($date." - ioServer: URL=$url, HTTP Code=$http_status, Response=" . substr($resp, 0, 500) . (strlen($resp) > 500 ? '...' : ''));
}
//Check If errorcode is returned
if($http_status['http_code'] == '403' || $http_status['http_code'] == '400') {$resp = generate_payload('NOK');}
@@ -1533,6 +1548,8 @@ function getProfile($profile, $permission){
'software_update' => 'R',
'software_download' => 'R',
'software_available' => 'R',
'history' => 'U',
'payment' => 'U',
'marketing_files' => 'CRUD',
'marketing_folders' => 'CRUD',
'marketing_tags' => 'CRUD',
@@ -1553,11 +1570,16 @@ function getProfile($profile, $permission){
];
// Debug log
if(debug){
debuglog("isAllowed called: page=$page, permission=$permission, action=$action");
}
// 1. Check always allowed
if (isset($always_allowed[$page]) && str_contains($always_allowed[$page], $action)) {
if(debug){
debuglog("Allowed by always_allowed");
}
return 1;
}
@@ -1568,11 +1590,15 @@ function getProfile($profile, $permission){
$page_action = str_contains($user_permission,$action) > 0 ? 1 : 0; //CHECK IF USER IS ALLOWED TO DO THE ACTION
$page_access = str_contains($profile,$page) > 0 ? 1 : 0; //CHECK USER IS ALLOWED TO ACCESS PAGE
if(debug){
debuglog("user_permission=$user_permission, page_action=$page_action, page_access=$page_access");
}
// 2. Check user permissions (standard)
if ($page_access == 1 && $page_action == 1){
if(debug){
debuglog("Allowed by user permissions");
}
return 1;
}
@@ -1580,16 +1606,22 @@ function getProfile($profile, $permission){
if ($page_access == 0) {
foreach ($group_permissions as $granting_page => $grants) {
if (str_contains($profile, $granting_page)) {
if(debug){
debuglog("Found granting_page: $granting_page");
}
if (isset($grants[$page]) && str_contains($grants[$page], $action)) {
if(debug){
debuglog("Allowed by group permissions");
}
return 1;
}
}
}
}
if(debug){
debuglog("Not allowed");
}
// Not allowed
return 0;
}
@@ -5209,7 +5241,7 @@ function updateSoftwareVersionStatus($pdo, $serialnumber = null) {
SET e.sw_version_latest = 1
WHERE psv.latest = 1
AND psv.status = 1
AND lower(e.sw_version) = lower(psv.version)
AND LOWER(TRIM(LEADING "0" FROM e.sw_version)) = lower(psv.version)
AND (lower(psv.hw_version) = lower(e.hw_version) OR lower(psv.hw_version) IS NULL OR lower(psv.hw_version) = "")
AND e.sw_version_latest = 0' . $sn_clause;

View File

@@ -1034,8 +1034,8 @@ function showPaymentModal(option) {
<label style="display: block; margin-bottom: 5px; color: #333; font-weight: 500;">Payment Method *</label>
<select name="payment_method" required style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px;">
<option value="">Select payment method</option>
${typeof MOLLIE_ENABLED !== 'undefined' && MOLLIE_ENABLED ? '<option value="credit_card">Credit Card</option>' : ''}
${typeof PAYPAL_ENABLED !== 'undefined' && PAYPAL_ENABLED ? '<option value="paypal">PayPal</option>' : ''}
${typeof MOLLIE_ENABLED !== 'undefined' && MOLLIE_ENABLED ? '<option value="1">Credit Card</option>' : ''}
${typeof PAYPAL_ENABLED !== 'undefined' && PAYPAL_ENABLED ? '<option value="3">PayPal</option>' : ''}
${typeof PAY_ON_DELIVERY_ENABLED !== 'undefined' && PAY_ON_DELIVERY_ENABLED ? '<option value="bank_transfer">Bank Transfer</option>' : ''}
</select>
</div>

View File

@@ -181,7 +181,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this cartest?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -104,7 +104,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this category?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -106,7 +106,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this communication?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -121,7 +121,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this contract?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -148,7 +148,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this dealer?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -120,7 +120,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this discount?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -152,7 +152,7 @@ if ($delete_allowed === 1 || $equipment_owner === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this asset?\')">';
}
if ($update_allowed === 1 || $equipment_owner === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -171,7 +171,7 @@ $view = '
if ($update_allowed === 1){
$print_btn_class = ($output_excel_display != '') ? 'btn' : 'btn alt';
$view .= '<button class="'.$print_btn_class.' mar-right-2" onclick="printDiv(\'excel_table\')"><i class="fa-solid fa-print"></i></button>';
$view .= '<input type="submit" form="mass_update_form" id="mass_update_submit" value="💾+" onclick="return confirm(\'".$mass_update_confirm_message."\')" class="btn">';
$view .= '<input type="submit" form="mass_update_form" id="mass_update_submit" value="💾" onclick="return confirm(\'".$mass_update_confirm_message."\')" class="btn">';
}
$view .= '</div>';

View File

@@ -59,9 +59,9 @@ if (!empty($invoice_data['customer']['language'])) {
// Generate invoice HTML
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
var_dump($invoice_data);
list($data, $customer_email, $order_id) = generateSoftwareInvoice($invoice_data, $order_number, $invoice_language);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Check for HTML output request
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -92,6 +92,7 @@ $dompdf->render();
$file_name = 'Factuur - ' . $order_id;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Handle different actions
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -124,7 +125,7 @@ if (isset($_POST['email_invoice_to_admin'])) {
$header_redirect = 'Location: index.php?page=order&id=' . $order_id . '&success=invoice_sent_admin';
// Send to bookkeeping if configured
if (defined('invoice_bookkeeping') && invoice_bookkeeping && defined('email_bookkeeping') && email_bookkeeping) {
if (invoice_bookkeeping && email_bookkeeping) {
send_mail(email_bookkeeping, $subject, $message, $attachment, $attachment_name);
}
@@ -133,7 +134,7 @@ if (isset($_POST['email_invoice_to_admin'])) {
}
// Show invoice in browser
if (isset($_POST['show_invoice'])) {
if (isset($_GET['show_invoice'])) {
// Clean output buffer to prevent corrupted PDF
if (ob_get_level()) {
ob_end_clean();

View File

@@ -108,7 +108,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this history?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -33,7 +33,7 @@ if (isset($_GET['success_msg'])) {
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100">Translations</h2>
<input type="submit" name="submit" value="💾+" class="btn">
<input type="submit" name="submit" value="💾" class="btn">
</div>
<?php if (isset($success_msg)): ?>

View File

@@ -35,6 +35,77 @@ if ($update_allowed === 1 && isset($_POST['submit'])) {
}
}
// Handle AJAX API requests
if (isset($_GET['action'])) {
$action = $_GET['action'];
// Suppress errors for API responses to avoid HTML output breaking JSON
error_reporting(0);
ini_set('display_errors', 0);
try {
// Licenses data
if ($action === 'licenses_data') {
// Filter out 'page', 'action', and cache busting timestamp from GET parameters
$filtered_params = $_GET;
unset($filtered_params['page']);
unset($filtered_params['action']);
unset($filtered_params['_t']);
$get_values = urlGETdetails($filtered_params) ?? '';
$api_url = '/v2/products_software_licenses/' . $get_values;
$response = ioServer($api_url, '');
header('Content-Type: application/json');
echo $response;
exit;
}
// Licenses totals
if ($action === 'licenses_totals') {
// Filter out 'page', 'action', and cache busting timestamp from GET parameters
$filtered_params = $_GET;
unset($filtered_params['page']);
unset($filtered_params['action']);
unset($filtered_params['_t']);
$get_values = urlGETdetails($filtered_params) ?? '';
$api_url = '/v2/products_software_licenses/' . $get_values . '&totals=';
$response = ioServer($api_url, '');
header('Content-Type: application/json');
echo $response;
exit;
}
// Software versions for dropdown
if ($action === 'software_versions') {
$api_url = '/v2/products_software_versions/list=&status=1';
$response = ioServer($api_url, '');
header('Content-Type: application/json');
echo $response;
exit;
}
// Handle license status update via AJAX
if ($action === 'update_license' && $_SERVER['REQUEST_METHOD'] === 'POST') {
if ($update_allowed === 1) {
$data = json_encode($_POST, JSON_UNESCAPED_UNICODE);
$response = ioServer('/v2/products_software_licenses', $data);
header('Content-Type: application/json');
echo json_encode(['success' => ($response !== 'NOK'), 'response' => $response]);
} else {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'error' => 'Not allowed']);
}
exit;
}
} catch (Exception $e) {
header('Content-Type: application/json');
echo json_encode(['error' => 'Server error', 'message' => $e->getMessage()]);
exit;
}
}
//GET PARAMETERS
$pagination_page = isset($_GET['p']) ? $_GET['p'] : 1;
$status = isset($_GET['status']) ? '&status='.$_GET['status'] : '';
@@ -197,7 +268,7 @@ $view .= '
<tr style="cursor: pointer;" onclick="openLicenseModal('.htmlspecialchars(json_encode($response), ENT_QUOTES).')">
<td>'.$response->license_key.'</td>
<td>'.$response->version_name.'</td>
<td><span class="status id'.$actual_status.'">'.$status_text.'</span></td>
<td>'.$status_text.'</td>
<td>'.($response->transaction_id ?? '-').'</td>
<td>'.$starts_display.'</td>
<td>'.$expires_display.'</td>
@@ -269,6 +340,17 @@ $view .= '
<input type="text" id="bulk_transaction_id" name="transaction_id" placeholder="e.g., PO-12345" required>
</div>
<div class="form-row">
<div class="form-group half">
<label for="bulk_starts_at">Start Date</label>
<input type="date" id="bulk_starts_at" name="starts_at">
</div>
<div class="form-group half">
<label for="bulk_expires_at">Expiration Date</label>
<input type="date" id="bulk_expires_at" name="expires_at">
</div>
</div>
<div class="form-actions">
<button type="button" onclick="closeBulkLicenseModal()" class="btn alt">Cancel</button>
<button type="submit" class="btn">Create Licenses</button>
@@ -312,13 +394,13 @@ $view .= '
</div>
<div class="detail-group">
<label style="display: block; margin-bottom: 5px; color: #666; font-size: 13px; font-weight: 500;">Starts At</label>
<div id="detail_starts_at" style="padding: 10px 12px; background: #f8f9fa; border-radius: 4px; font-size: 14px;"></div>
<label for="detail_starts_at" style="display: block; margin-bottom: 5px; color: #666; font-size: 13px; font-weight: 500;">Starts At</label>
<input type="date" id="detail_starts_at" name="starts_at" style="width: 100%; padding: 10px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px;">
</div>
<div class="detail-group">
<label style="display: block; margin-bottom: 5px; color: #666; font-size: 13px; font-weight: 500;">Expires</label>
<div id="detail_expires_at" style="padding: 10px 12px; background: #f8f9fa; border-radius: 4px; font-size: 14px;"></div>
<label for="detail_expires_at" style="display: block; margin-bottom: 5px; color: #666; font-size: 13px; font-weight: 500;">Expires</label>
<input type="date" id="detail_expires_at" name="expires_at" style="width: 100%; padding: 10px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px;">
</div>
<div class="detail-group" style="grid-column: 1 / -1;">
@@ -334,8 +416,11 @@ $view .= '
if ($update_allowed === 1) {
$view .= '
<button type="button" id="updateDatesBtn" class="btn" onclick="updateLicenseDates()">
<i class="fa-solid fa-save"></i>
</button>
<button type="submit" name="submit" id="setInactiveBtn" class="btn" style="background: #dc3545;" onclick="return confirm(\'Are you sure you want to set this license as inactive?\')">
<i class="fa-solid fa-ban"></i> Set Inactive
<i class="fa-solid fa-ban"></i>
</button>';
}
@@ -358,12 +443,21 @@ let currentLicenseData = null;
// Modal functions
function openBulkLicenseModal() {
// Set default dates
const today = new Date().toISOString().split(\'T\')[0];
document.getElementById("bulk_starts_at").value = today;
document.getElementById("bulk_expires_at").value = "2099-12-31";
document.getElementById("bulkLicenseModal").style.display = "flex";
}
function closeBulkLicenseModal() {
document.getElementById("bulkLicenseModal").style.display = "none";
document.getElementById("bulkLicenseForm").reset();
// Reset date fields to defaults
const today = new Date().toISOString().split(\'T\')[0];
document.getElementById("bulk_starts_at").value = today;
document.getElementById("bulk_expires_at").value = "2099-12-31";
}
// License detail modal functions
@@ -385,26 +479,25 @@ function openLicenseModal(licenseData) {
let statusClass = "";
if (actualStatus == 0) {
statusText = "Inactive";
statusClass = "id0";
statusClass = "id2";
} else if (actualStatus == 1) {
statusText = "Assigned";
statusClass = "id1";
} else if (actualStatus == 2) {
statusText = "Expired";
statusClass = "id2";
}
// Format dates
const startsDisplay = licenseData.starts_at ? new Date(licenseData.starts_at).toLocaleDateString() : "-";
const expiresDisplay = licenseData.expires_at ? new Date(licenseData.expires_at).toLocaleDateString() : "-";
// Format dates for input fields (YYYY-MM-DD)
const startsValue = licenseData.starts_at ? licenseData.starts_at.split(\' \')[0] : "";
const expiresValue = licenseData.expires_at ? licenseData.expires_at.split(\' \')[0] : "";
// Populate modal fields
document.getElementById("detail_license_key").textContent = licenseData.license_key || "-";
document.getElementById("detail_version_name").textContent = licenseData.version_name || "-";
document.getElementById("detail_status").innerHTML = \'<span class="status \' + statusClass + \'">\' + statusText + \'</span>\';
document.getElementById("detail_transaction_id").textContent = licenseData.transaction_id || "-";
document.getElementById("detail_starts_at").textContent = startsDisplay;
document.getElementById("detail_expires_at").textContent = expiresDisplay;
document.getElementById("detail_starts_at").value = startsValue;
document.getElementById("detail_expires_at").value = expiresValue;
document.getElementById("detail_assigned_serial").textContent = licenseData.assigned_serial || "-";
// Set hidden form field
@@ -462,24 +555,26 @@ document.getElementById("bulkLicenseForm").addEventListener("submit", async func
version_id: document.getElementById("bulk_version_id").value,
serials: serials,
transaction_id: document.getElementById("bulk_transaction_id").value,
license_type: 0,
status: 0
starts_at: document.getElementById("bulk_starts_at").value,
expires_at: document.getElementById("bulk_expires_at").value,
license_type: 1,
status: 1
};
try {
const response = await fetch("api.php/v2/products_software_licenses", {
const response = await fetch("index.php?page=licenses&action=update_license", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + sessionStorage.getItem("token")
"Content-Type": "application/x-www-form-urlencoded",
},
body: JSON.stringify(formData)
body: new URLSearchParams(formData)
});
if (response.ok) {
const result = await response.json();
if (result.success) {
window.location.href = "index.php?page=licenses&success_msg=1";
} else {
alert("Error creating licenses. Please try again.");
alert("Error creating licenses: " + (result.error || "Please try again."));
}
} catch (error) {
console.error("Error:", error);
@@ -496,6 +591,48 @@ function toggleFilters() {
panel.style.display = "none";
}
}
// Update license dates function
async function updateLicenseDates() {
const rowID = document.getElementById("detail_rowID").value;
const starts_at = document.getElementById("detail_starts_at").value;
const expires_at = document.getElementById("detail_expires_at").value;
if (!rowID) {
alert("Invalid license ID");
return;
}
// Convert date to timestamp format (YYYY-MM-DD HH:MM:SS)
const starts_at_timestamp = starts_at ? starts_at + " 00:00:00" : "";
const expires_at_timestamp = expires_at ? expires_at + " 23:59:59" : "";
const formData = {
rowID: rowID,
starts_at: starts_at_timestamp,
expires_at: expires_at_timestamp
};
try {
const response = await fetch("index.php?page=licenses&action=update_license", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams(formData)
});
const result = await response.json();
if (result.success) {
window.location.href = "index.php?page=licenses&success_msg=2";
} else {
alert("Error updating license: " + (result.error || "Please try again."));
}
} catch (error) {
console.error("Error:", error);
alert("Error updating license. Please try again.");
}
}
</script>
<style>
@@ -599,6 +736,17 @@ function toggleFilters() {
justify-content: flex-end;
margin-top: 25px;
}
.form-row {
display: flex;
gap: 15px;
margin-bottom: 20px;
}
.form-group.half {
flex: 1;
margin-bottom: 0;
}
</style>
';

View File

@@ -116,7 +116,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this media?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -77,6 +77,7 @@ $view = '
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100">'.$order['header']['id'].' - '.$order['header']['txn_id'].'</h2>
<a href="index.php?page='.$_SESSION['origin'].'&p='.$_SESSION['p'].'" class="btn alt mar-right-2">←</a>
<a href="index.php?page=factuur&txn_id=' . $order['header']['txn_id'] . '&show_invoice=1" target="_blank" class="btn"><i class="fa-solid fa-file-pdf"></i></a>
';
@@ -198,7 +199,7 @@ if ($order['customer']['email']) {
</div>
<div class="order-detail">
<h3>Contact</h3>
<p style="text-align:right;">' . htmlspecialchars($order['customer']['phone'], ENT_QUOTES) . '</p>
<p style="text-align:right;">' . htmlspecialchars($order['customer']['phone'] ?? '-', ENT_QUOTES) . '</p>
</div>';
} else {
$view .=' <p>The order is not associated with an account.</p>';
@@ -223,14 +224,14 @@ $view .='
<div class="order-detail">
<h3>Address</h3>
<p style="text-align:right;">' . htmlspecialchars($order['customer']['street'], ENT_QUOTES) . '<br>
' . htmlspecialchars($order['customer']['city'], ENT_QUOTES) . '<br>
' . htmlspecialchars($order['customer']['state'], ENT_QUOTES) . '<br>
' . htmlspecialchars($order['customer']['zip'], ENT_QUOTES) . '<br>
' . htmlspecialchars($order['customer']['country'], ENT_QUOTES) . '</p>
' . htmlspecialchars($order['customer']['city'] ?? '-', ENT_QUOTES) . '<br>
' . htmlspecialchars($order['customer']['state'] ?? '-', ENT_QUOTES) . '<br>
' . htmlspecialchars($order['customer']['zip'] ?? '-', ENT_QUOTES) . '<br>
' . htmlspecialchars($order['customer']['country'] ?? '-', ENT_QUOTES) . '</p>
</div>
<div class="order-detail">
<h3>Contact</h3>
<p style="text-align:right;">' . htmlspecialchars($order['customer']['phone'], ENT_QUOTES) . '</p>
<p style="text-align:right;">' . htmlspecialchars($order['customer']['phone'] ?? '-', ENT_QUOTES) . '</p>
</div>
</div>
</div>';
@@ -354,12 +355,6 @@ $view .='
<div class="table order-table">
<table>
<tr>
<td style="width:70px";>
<form action="index.php?page=factuur" method="post">
<input type="hidden" name="txn_id" value="' . $order['header']['txn_id'] . '">
<input type="submit" class="btn" name="show_invoice" value="Show">
</form>
</td>
<td style="width: 157px;">
<form action="index.php?page=factuur" method="post">
<input type="hidden" name="txn_id" value="' . $order['header']['txn_id'] . '">

View File

@@ -113,7 +113,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this partner?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -172,7 +172,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this pricelist?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -152,7 +152,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this product?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -169,7 +169,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this variable?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -134,7 +134,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this product?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -181,7 +181,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this product?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -177,7 +177,7 @@ if ($delete_allowed === 1 && isset($_GET['path_id']) && $_GET['path_id'] != ''){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this upgrade path?\')">';
}
if (($update_allowed === 1 && isset($_GET['path_id'])) || ($create_allowed === 1 && !isset($_GET['path_id']))){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -137,7 +137,7 @@ if ($delete_allowed === 1 && isset($_GET['id'])){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this software version?\')">';
}
if (($update_allowed === 1 && isset($_GET['id'])) || ($create_allowed === 1 && !isset($_GET['id']))){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -103,7 +103,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this product?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -86,7 +86,7 @@ $view ='
<a href="index.php?page=dasboard" class="btn alt mar-right-2">←</a>
';
if ($update_allowed === 1 && $_SESSION['permission'] != 0){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -281,7 +281,7 @@ $view .= '
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100">Profiles</h2>
<input type="submit" name="submit" value="💾+" class="btn">
<input type="submit" name="submit" value="💾" class="btn">
</div>';
if (isset($success_msg)){

View File

@@ -405,5 +405,5 @@ $view .= '
//OUTPUT
echo $view;
template_footer();
echo template_footer();
?>

View File

@@ -125,7 +125,7 @@ $view .= '
<div class="content-title responsive-flex-wrap responsive-pad-bot-3">
<h2 class="responsive-width-100">Settings</h2>
<input type="submit" name="submit" value="💾+" class="btn">
<input type="submit" name="submit" value="💾" class="btn">
</div>
';

View File

@@ -4,9 +4,9 @@ define('standard_profile','dashboard,profile,application,firmwaretool,histories,
/*Superuser*/
define('superuser_profile','admin,dashboard,profile,application,assets,firmwaretool,histories,history,history_manage,marketing,partner,partners,servicereport,servicereports,equipment,equipment_manage,equipment_manage_edit,equipments,equipments_mass_update,product,product_manage,products,products_software,products_versions,user,user_manage,users');
/*Admin*/
define('admin_profile','account,accounts,admin,dashboard,profile,application,assets,buildtool,buildtool,cartest,cartest_manage,cartests,changelog,communication,communication_send,communications,firmwaretool,histories,history,history_manage,marketing,partner,partners,sales,servicereport,servicereports,contract,contract_manage,contracts,equipment,equipment_data,equipment_healthindex,equipment_manage,equipment_manage_edit,equipments,equipments_mass_update,product,product_manage,products,products_software,products_versions,report_build,report_contracts_billing,report_healthindex,reporting,rma,rma_history,rma_history_manage,rma_manage,rmas,user,user_manage,users');
define('admin_profile','account,accounts,admin,dashboard,profile,application,assets,buildtool,buildtool,cartest,cartest_manage,cartests,changelog,communication,communication_send,communications,firmwaretool,histories,history,history_manage,marketing,partner,partners,sales,servicereport,servicereports,software_available,software_download,software_update,softwaretool,contract,contract_manage,contracts,equipment,equipment_data,equipment_healthindex,equipment_manage,equipment_manage_edit,equipments,equipments_mass_update,product,product_manage,products,products_software,products_software_assignment,products_software_assignments,products_versions,report_build,report_contracts_billing,report_healthindex,reporting,rma,rma_history,rma_history_manage,rma_manage,rmas,user,user_manage,users');
/*AdminPlus*/
define('adminplus_profile','account,account_manage,accounts,admin,config,dashboard,profile,settings,api,application,appointment,assets,billing,buildtool,buildtool,cartest,cartest_manage,cartests,catalog,categories,category,changelog,checkout,com_log,communication,communication_send,communications,cronjob,debug,dev,discount,discounts,factuur,firmwaretool,functions,generate_download_token,histories,history,history_manage,identity,identity_dealers,initialize,invoice,language,licenses,logfile,mailer,maintenance,marketing,marketing_delete,marketing_files,marketing_folders,marketing_migrate,marketing_tags,marketing_upload,media,media_manage,media_scanner,media_upload,order,orders,partner,partners,payment,placeorder,pricelists,pricelists_items,pricelists_manage,profiles,register,render_service_report,reset,sales,security,service,servicereport,servicereports,shipping,shipping_manage,shopping_cart,software_available,software_download,software_update,softwaretool,tax,taxes,test,transactions,transactions_items,translation_manage,translations,translations_details,unscribe,upgrades,uploader,vin,webhook_mollie,webhook_paypal,contract,contract_manage,contracts,dealer,dealer_manage,dealers,dealers_media,equipment,equipment_data,equipment_healthindex,equipment_history,equipment_manage,equipment_manage_edit,equipments,equipments_mass_update,product,product_manage,products,products_attributes,products_attributes_items,products_attributes_manage,products_categories,products_configurations,products_media,products_software,products_software_assignment,products_software_assignments,products_software_assignments,products_software_licenses,products_software_upgrade_paths,products_software_upgrade_paths_manage,products_software_version,products_software_version_access_rules_manage,products_software_version_manage,products_software_versions,products_versions,report_build,report_contracts_billing,report_healthindex,report_usage,reporting,rma,rma_history,rma_history_manage,rma_manage,rmas,user,user_credentials,user_manage,users');
define('adminplus_profile','account,account_manage,accounts,admin,config,dashboard,profile,settings,api,application,appointment,assets,billing,buildtool,buildtool,cartest,cartest_manage,cartests,catalog,categories,category,changelog,checkout,com_log,communication,communication_send,communications,cronjob,debug,dev,discount,discounts,factuur,firmwaretool,functions,generate_download_token,histories,history,history_manage,identity,identity_dealers,initialize,invoice,language,licenses,logfile,mailer,maintenance,marketing,marketing_delete,marketing_files,marketing_folders,marketing_migrate,marketing_tags,marketing_update,marketing_upload,media,media_manage,media_scanner,media_upload,order,orders,partner,partners,payment,placeorder,pricelists,pricelists_items,pricelists_manage,profiles,register,render_service_report,reset,sales,security,service,servicereport,servicereports,shipping,shipping_manage,shopping_cart,software_available,software_download,software_update,softwaretool,tax,taxes,test,transactions,transactions_items,translation_manage,translations,translations_details,unscribe,upgrades,uploader,vin,webhook_mollie,webhook_paypal,contract,contract_manage,contracts,dealer,dealer_manage,dealers,dealers_media,equipment,equipment_data,equipment_healthindex,equipment_history,equipment_manage,equipment_manage_edit,equipments,equipments_mass_update,product,product_manage,products,products_attributes,products_attributes_items,products_attributes_manage,products_categories,products_configurations,products_media,products_software,products_software_assignment,products_software_assignments,products_software_assignments,products_software_licenses,products_software_upgrade_paths,products_software_upgrade_paths_manage,products_software_version,products_software_version_access_rules_manage,products_software_version_manage,products_software_versions,products_versions,report_build,report_contracts_billing,report_healthindex,report_usage,reporting,rma,rma_history,rma_history_manage,rma_manage,rmas,user,user_credentials,user_manage,users');
/*Build*/
define('build','dashboard,profile,application,buildtool,buildtool,firmwaretool,products_software');
/*Commerce*/

View File

@@ -71,6 +71,7 @@ $all_views = [
"marketing_folders",
"marketing_migrate",
"marketing_tags",
"marketing_update",
"marketing_upload",
"media",
"media_manage",

View File

@@ -826,4 +826,6 @@ $dealer_type_0 = 'Lokal';
$dealer_type_1 = 'Professionell';
$dealer_type_2 = 'Unternehmens';$user_information_required = 'Benutzerinformationen Erforderlich';
$user_information_description = 'Bitte geben Sie Ihre Daten ein, um mit Software-Updates fortzufahren';
$general_continue = 'Fortfahren';?>
$general_continue = 'Fortfahren';
$payment_method_3 = 'PayPal';
?>

View File

@@ -833,4 +833,5 @@ $dealer_type_2 = 'Corporativo';
$user_information_required = 'Información del Usuario Requerida';
$user_information_description = 'Por favor proporcione su información para continuar con las actualizaciones de software';
$general_continue = 'Continuar';
$payment_method_3 = 'PayPal';
?>

View File

@@ -1039,4 +1039,5 @@ $dealer_type_2 = 'Bedrijf';
$user_information_required = 'Gebruikersinformatie Vereist';
$user_information_description = 'Geef uw gegevens op om door te gaan met software-updates';
$general_continue = 'Doorgaan';
$payment_method_3 = 'PayPal';
?>

View File

@@ -767,4 +767,5 @@ $general_modifier_1 = '+';
$user_information_required = 'Wymagane Informacje o Użytkowniku';
$user_information_description = 'Podaj swoje dane, aby kontynuować aktualizacje oprogramowania';
$general_continue = 'Kontynuuj';
$payment_method_3 = 'PayPal';
?>

View File

@@ -833,4 +833,5 @@ $dealer_type_2 = 'Corporativo';
$user_information_required = 'Informações do Usuário Obrigatórias';
$user_information_description = 'Por favor, forneça suas informações para continuar com as atualizações de software';
$general_continue = 'Continuar';
$payment_method_3 = 'PayPal';
?>

View File

@@ -1053,4 +1053,5 @@ $dealer_type_2 = 'Corporate';
$user_information_required = 'User Information Required';
$user_information_description = 'Please provide your information to continue with software updates';
$general_continue = 'Continue';
$payment_method_3 = 'PayPal';
?>

View File

@@ -104,7 +104,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this shipping?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -188,7 +188,7 @@ $view .= '<div class="content-block">
<div id="connectdevice" style="display:flex; gap: 10px; margin-bottom: 20px;">
<button class="btn" id="connectButton" onClick="connectDeviceForSoftware()" style="min-width: 150px;">
<i class="fa-solid fa-plug"></i> Connect
<i class="fa-solid fa-plug"></i>
</button>
<div id="readStatus" style="flex: 1; background-color: #f1f1f1; border-radius: 4px; overflow: hidden;">
<div id="readBar" style="height: 100%; transition: width 0.3s ease;"></div>

View File

@@ -49,7 +49,7 @@ if (isset($_GET['id'])) {
<?php if ($page == 'Edit'): ?>
<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm('Are you sure you want to delete this tax?')">
<?php endif; ?>
<input type="submit" name="submit" value="💾+" class="btn">
<input type="submit" name="submit" value="💾" class="btn">
</div>
<div class="content-block">

View File

@@ -174,7 +174,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this variable?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';

View File

@@ -178,7 +178,7 @@ if ($delete_allowed === 1){
$view .= '<input type="submit" name="delete" value="X" class="btn red mar-right-2" onclick="return confirm(\'Are you sure you want to delete this user?\')">';
}
if ($update_allowed === 1){
$view .= '<input type="submit" name="submit" value="💾+" class="btn">';
$view .= '<input type="submit" name="submit" value="💾" class="btn">';
}
$view .= '</div>';